Jump to content


Photo

Concepto: Método de Clase


  • Please log in to reply
4 replies to this topic

#1 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6295 posts
  • LocationArgentina

Posted 09 January 2009 - 01:39 PM

Hasta ahora venimos trabajando con instancias de una clase. También podemos hacer uso de una clase sin instanciar objeto alguno. Todo gracias a los métodos de clase.

Métodos de clase

Los métodos de clase son igual que los métodos de siempre. Lo que los diferencia de los "comunes" es que éstos se declaran anteponiendo la palabra "Class":



delphi
  1. class function NombreMetodo(ListaParametros): Tipo;
  2. class procedure NombreMetodo(ListaParametros);
  3. ...
  4.  
  5. class function NombreMetodo(ListaParametros): Tipo;
  6. begin
  7. ...
  8. end;
  9.  
  10. class procedure NombreMetodo(ListaParametros);
  11. begin
  12. ....
  13. end;



Como dije al comienzo: el método sirve para trabajar con la clase y como el método "apunta" a la clase (mediante el parámetro implícito self) y no a la instancia, no se puede emplear éstos métodos para acceder a propiedades, atributos, etc.

¿Entonces de que sirve tenerlos?
En realidad sirven para dar funcionalidad a nivel de clase, y no de una instancia. Es decir que los métodos son aplicados en un concepto mucho más abstracto.
Funcionan independientemente de si hay o no una clase, por ello es que se puede hacer cosas como éstas:



delphi
  1. var Clase: TMiClase;
  2. ...
  3. TMiClase.MetodoClase; // Se usa a la clase
  4. Clase.MetodoClase; // se invoca al método a través de una instancia



Contar con métodos de clase nos permite trabajar fuera del contexto de una instancia y aplicar operaciones más generales (recuerde que una instancia es un caso particular, o concreto de una clase). Por ejemplo: ¿es necesario contar con un objeto para saber la cantidad que han sido creados? No necesariamente. Puede emplearse una variable privada u oculta que lleve el conteo de instancias a medida que se crean y liberan los objetos y emplear un método de clase para consultar su valor.

De modo similar, ¿es necesario contar con instancias si lo que deseamos saber es si una clase pertenece a una jerarquía u otra, o si cuenta con alguna funcionalidad?
No. Porque lo que define a éstas acciones no es a una instancia, sino al conjunto de éstas instancias: la clase.
Cualquier dato, funcionalidad, y/o comportamiento que desee conocer sobre una clase puede ser consultado, y aplicado mediante métodos de clase.

Existen muchos de hecho TObject ofrece los más básicos:



delphi
  1. class function InitInstance(Instance: Pointer): TObject;
  2. function ClassType: TClass;
  3. class function ClassName: ShortString;
  4. class function ClassNameIs(const Name: string): Boolean;
  5. class function ClassParent: TClass;
  6. class function ClassInfo: Pointer;
  7. class function InstanceSize: Longint;
  8. class function InheritsFrom(AClass: TClass): Boolean;
  9. class function MethodAddress(const Name: ShortString): Pointer;
  10. class function MethodName(Address: Pointer): ShortString;
  11. class function GetInterfaceEntry(const IID: TGUID): PInterfaceEntry;
  12. class function GetInterfaceTable: PInterfaceTable;
  13. class function NewInstance: TObject; virtual;



Eso si, más comunes son las funciones que los procedimientos. Lo importante del concepto es que cualquier operatoria que el negocio o contexto nos indique que se aplica a un nivel conceptual puede ser enfocado mediante esta técnica.

A los métodos de clase le son aplicables todos los conceptos a cualquier método: sobrecargarlos, sobreescribirlos, etc.

En el próximo post dejo un ejemplo muy sencillo de uso.
  • 1

#2 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6295 posts
  • LocationArgentina

Posted 09 January 2009 - 01:49 PM

En este post voy a añadir un ejemplo muy sencillo de como usar lo visto.

Digamos que es de interés que nuestra clase TPerro, ya un tanto gastadita y cansada, cuente con la capacidad de saber la cantidad de hembras. Un enfoque de como se podría llevar a cabo es éste:



delphi
  1. unit Unit2;
  2.  
  3. interface
  4.  
  5. type
  6.   TSexo = (sHembra, sMacho);
  7.  
  8.   TPerro = class
  9.   private
  10.     FSexo: TSexo;
  11.     procedure EstablecerSexo(Valor: TSexo);
  12.   public
  13.     // propiedades
  14.     property Sexo: TSexo read FSexo write EstablecerSexo;
  15.  
  16.     // constructores y destructores
  17.     constructor Create(TipoSexo: TSexo);
  18.     destructor Destroy; override;
  19.  
  20.     // métodos de clase
  21.     class function HayHembras(var Cantidad: integer): boolean;
  22.     class procedure ResetearHembras;
  23.   end;
  24.  
  25. implementation
  26.  
  27. var CantHembras: integer; // como puede apreciarse se oculta esta variable
  28.  
  29. { TPerro }
  30.  
  31. constructor TPerro.Create(TipoSexo: TSexo);
  32. begin
  33.   inherited Create;
  34.   Sexo := TipoSexo;
  35. end;
  36.  
  37. destructor TPerro.Destroy;
  38. begin
  39.   if (Sexo = sHembra)
  40.       then dec(CantHembras);
  41.  
  42.   inherited Destroy;
  43. end;
  44.  
  45. procedure TPerro.EstablecerSexo(Valor: TSexo);
  46. begin
  47.   if (Valor = sHembra)
  48.     then inc(CantHembras);
  49.  
  50.   FSexo := Valor;
  51. end;
  52.  
  53. class function TPerro.HayHembras(var Cantidad: integer): boolean;
  54. begin
  55.   result := (CantHembras <> 0);
  56.   Cantidad := CantHembras;
  57. end;
  58.  
  59. class procedure TPerro.ResetearHembras;
  60. begin
  61.   CantHembras := 0;
  62. end;
  63.  
  64. initialization
  65.   CantHembras := 0;
  66.  
  67. end.



Como se puede apreciar, se decrementa e incrementa una variable oculta según se crean o liberan los objetos.

Un posible uso, muy sencillo para comprobar si esto funciona es el siguiente:



delphi
  1. var Perro1, Perro2, Perro3: TPerro;
  2.     cant: integer;
  3.     hay: boolean;
  4. begin
  5.   hay := TPerro.HayHembras(cant); // Mire: ¡ estamos usando la clase!
  6.   ShowMessage(IntToStr(cant)); // debería haber hembras todavía
  7.  
  8.   Perro1 := TPerro.Create(sHembra);
  9.   hay := Perro1.HayHembras(cant); // ¡Y ahora usamos un objeto!
  10.   ShowMessage(IntToStr(cant));    // debería haber 1
  11.  
  12.   Perro2 := TPerro.Create(sMacho);
  13.   hay := Perro2.HayHembras(cant);
  14.   ShowMessage(IntToStr(cant));    // deberían seguir habiendo 1
  15.  
  16.   Perro3 := TPerro.Create(sHembra);
  17.   hay := Perro3.HayHembras(cant);
  18.   ShowMessage(IntToStr(cant));    // ahora debería haber 2
  19.  
  20.   Perro3.Free;
  21.   hay := Perro1.HayHembras(cant);  //no iteresa que perro se use!
  22.   ShowMessage(IntToStr(cant));    // ahora debería haber 1
  23.  
  24.   Perro2.Free;
  25.   Perro1.Free;
  26.  
  27.   hay := Perro1.HayHembras(cant);  //no iteresa que perro se use!
  28.   ShowMessage(IntToStr(cant));    // ahora debería haber 1



Si ejecuta el código anterior, deberá recibir mensajes con la cantidad de hembras: 0, 1, 1, 2, 1, 0.

Algo muy común en éstos métodos es que cualquier dato que necesiten es consultado y/o guardado temporalmente en algún sector oculto. Puede complementarse la técnica con funciones y métodos externos a una clase ("sueltos") para facilitar el código.

Espero que con este sencillo código quede ilustrado como se usa los métodos de clase.

Saludos,
  • 0

#3 JoseAntonio

JoseAntonio

    Newbie

  • Miembros
  • Pip
  • 2 posts

Posted 12 May 2009 - 02:28 PM

Hola, bueno yo uso los metodos de clase generalmente para traer un resultado con una funcion de clase, esta funcion de clase crea un formulario, intercepta las interacciones del usuario con dicho formulario y finalmente retorna el resultado deseado en un parametro o en el mismo result de la funcion, aqui el ejemplo:



delphi
  1. class function TfrmSelBeneficio.GetBeneficio(ACurrentType: string): string;
  2. var
  3.   SQLStr: string;
  4.   ADataSet: IrsClientDataSet;
  5.   AfrmSelBeneficio: TfrmSelBeneficio;
  6. begin
  7.   try
  8.     AfrmSelBeneficio := TfrmSelBeneficio.Create(Application);
  9.     SQLStr := ' SELECT COD_BENEFICIO, DESCRIPCION, DES_TIP FROM MA_TABLABENEFICIO ' +
  10.               ' WHERE COD_BENEFICIO LIKE ' + QuotedStr('%' +ACurrentType + '%') +
  11.               ' ORDER BY COD_BENEFICIO';
  12.     ADataSet := CentralClient.Database[''].GetDataset(SQLStr);
  13.     AfrmSelBeneficio.dscBeneficio.DataSet := ADataSet.Dataset;
  14.     AfrmSelBeneficio.edtBeneficio.Text := ACurrentType;
  15.     if AfrmSelBeneficio.ShowModal = mrOk then
  16.       result := AfrmSelBeneficio.fCodBeneficio
  17.     else
  18.       result := ''
  19.   finally
  20.     AfrmSelBeneficio.free;
  21.   end;
  22. end;




  • 0

#4 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6295 posts
  • LocationArgentina

Posted 12 May 2009 - 09:15 PM

Hola JoseAntonio,
Gracias por aportar más al tema.

No discuto que se puede aprovechar los métodos de Clase para propósitos como el que describes, pero si debo admitir que me siento intrigado por él.
Generalmente para un método de clase se espera trabajar a un nivel de clase, en lo general, y esto lleva a pensar que no deberí­a interferir en otros objetos.

Hay que tener presente que en un método de clase no puede tenerse acceso a propiedades ni métodos del propio objeto (1). Es por ello que hay que analizar objetivamente si debe ser método a nivel de clase o a nivel de objeto.

En tu ejemplo creas en memoria unos componentes y luego lo liberas. Esto no siempre podrá ser recomendable. Todo dependerá de las necesidades y conveniencia.

(1) En realidad si es posible acceder a ciertas propiedades, al menos en la etapa del método de clase NewInstance cuando se está llevando a cabo la creación del objeto. Pero esto requiere bajarse al nivel de ensamblador, emplear algunas variables auxiliares, tal vez algún que otro cast... A mi humilde punto de vista, no creo que existan demasiadas ventajas y mucha necesidad de valerse de una técnica como ésta.

De todas formas no deja de ser interesante tu punto de vista.

Saludos,
  • 0

#5 JoseAntonio

JoseAntonio

    Newbie

  • Miembros
  • Pip
  • 2 posts

Posted 14 May 2009 - 12:07 AM

En tu ejemplo creas en memoria unos componentes y luego lo liberas. Esto no siempre podrá ser recomendable. Todo dependerá de las necesidades y conveniencia..

Saludos,


Bueno en mi caso no creo componentes en memoria sino un formulario completo: TfrmSelBeneficio (generalmente modales con sus propios controles) y si bien confieso que nunca he usado funciones de clase para otros aspectos, esta tecnica me resulta muy util en los proyectos que desarrollo  puesto que permite abstraer toda una interaccion de usuario en una sola linea de codigo que es la que llama a la funcion de clase, muy acorde con el principio de encapsulamiento.
  • 0




IP.Board spam blocked by CleanTalk.