PDA

Ver la Versión Completa : Class Helpers


vtdeleon
05-05-2006, 14:51:29
En BDS 2006 y desde Delphi 8 existe una extensión del lenguaje de Object Pascal llamada Class Helpers esta nueva característica nos permite definir nuevos comportamientos para alguna clase previamente definida de manera natural y su práctica nos ofrece muchos beneficios.Muy interesante
Enlace (http://cgarcia.blogspot.com/2006/05/extendiendo-la-vcl-sin-usar-herencia.html)

Al González
05-05-2006, 18:48:13
¡Hola a todos!

Desconocía esta característica en Delphi (mas no el concepto de "ayudante de clase"), quizá por no haber salido aún de la versión 7. Sería muy bueno poder acceder cuando menos a los elementos protegidos de la clase ayudada.

De todas formas no deja de tener otras limitantes. Veo este mecanismo de los ayudantes de clases como un paso intermedio hacia el concepto de Herencia Insertada que he mencionado en algunas ocasiones:

http://www.clubdelphi.com/foros/showpost.php?p=29604&postcount=10
http://www.clubdelphi.com/foros/showpost.php?p=41664&postcount=7

Un abrazo ayudante.

Al González. :)

roman
05-05-2006, 19:06:40
Estimado Al, me parece recordar que ya alguna vez te había pedido que profundizaras más en ese concepto por el que abogas y me comentaste que pronto lo harías. Quiza me he perdido algún mensaje pero sigo esperando :p

El punto es que no me queda claro. Si tienes una clase A y las clases B y C derivan de ella, ¿lo que tú propones es poder agregar funcionalidad posterior a la clase A de manera que B y C se beneficien de ello? De ser así, ¿no rompería ello con el principio OOP de que una clase debe estar cerrada para modificación aunque abierta para ampliación? Por otra parte, si bien no en Delphi, me parece que este concepto que buscas lo tiene JavaScript, ¿lo has visto? Si no mal recuerdo, puedes usar A.proptotype para alterar A y todos sus descendientes.

Bueno, me encantará ver más acerca del tema.

// Saludos

Al González
05-05-2006, 20:38:44
¡Hola a todos!


...alguna vez te había pedido que profundizaras más en ese concepto por el que abogas y me comentaste que pronto lo harías...
Lo siento Román, ahora recuerdo aquella petición. No había profundizado en el tema porque tenía la esperanza de que alguien más propusiera sus ideas al respecto y así construir una enriquecida teoría entre varios desarrolladores; el tema de este hilo me parece propicio para comenzar a calentar motores :). De cualquier forma, el ejemplo que expuse en el segundo enlace mencionado puede resultar útil para comprender mejor la idea.


...no me queda claro. Si tienes una clase A y las clases B y C derivan de ella, ¿lo que tú propones es poder agregar funcionalidad posterior a la clase A de manera que B y C se beneficien de ello? De ser así, ¿no rompería ello con el principio OOP de que una clase debe estar cerrada para modificación aunque abierta para ampliación?...
Técnicamente no se trata de una modificación a la clase A sino a la jerarquía de una o más clases. Se crea una nueva clase A2 como hija de A, pero usada como nuevo padre directo de B y C. Es decir, A2 se inserta en la jerarquía de clases entre A y B, y entre A y C. Ninguna clase es modificada si entendemos por modificación la alteración del código fuente que una clase añade, solo les cambiamos el padre a las clases que deseamos hereden nueva funcionalidad. Una especie de manipulación genética, claro, sin los altos riesgos que eso conlleva en la biología real :D.


...me parece que este concepto que buscas lo tiene JavaScript, ¿lo has visto? Si no mal recuerdo, puedes usar A.proptotype para alterar A y todos sus descendientes...
Vaya, no conocía esa característica, será interesante echarle un vistazo.


...me encantará ver más acerca del tema...
Pos échanos la mano, ¿tú como ves este asunto? ¿será bueno estudiarlo a fondo y en su caso proponer algo a DevCo?


Un abrazo insertado.

Al González. :)

roman
05-05-2006, 21:23:57
Técnicamente no se trata de una modificación a la clase A sino a la jerarquía de una o más clases. Se crea una nueva clase A2 como hija de A, pero usada como nuevo padre directo de B y C. Es decir, A2 se inserta en la jerarquía de clases entre A y B, y entre A y C. Ninguna clase es modificada si entendemos por modificación la alteración del código fuente que una clase añade, solo les cambiamos el padre a las clases que deseamos hereden nueva funcionalidad. Una especie de manipulación genética, claro, sin los altos riesgos que eso conlleva en la biología real :D.


Claro, no pensaba que la propuesta fuera simplemente alterar el código de A. Pero vamos a ver, una clase (o su interfaz) representa en cierta medida un contrato al cual debe ajustarse. La clase A ciertamente no romperá dicho contrato pero sí lo harán las clases B y C junto con todos sus descendientes.

Ahora bien, la idea original, si te he entendido, es:

Agregar funcionalidad común a las clases B y C sin repetir código. De cualquier forma, agregar funcionalidad significa derivar una nueva clase. Así que de cualquier manera estaríamos hablando de crear clases B' y C' descendientes de B y C. Pero en tal caso podríamos crear A1 descendiente de A y luego B' descendiente de A1 y B y C' descendiente de A1 y C. ¡Ah! Pero Delphi no tiene herencia múltiple. Bueno, entonces, ¿no sería eso, la herencia múltiple, lo que habría que pedir?

// Saludos

Al González
06-05-2006, 08:31:22
¡Hola a todos!


...la idea original, si te he entendido, es:

Agregar funcionalidad común a las clases B y C sin repetir código. De cualquier forma, agregar funcionalidad significa derivar una nueva clase. Así que de cualquier manera estaríamos hablando de crear clases B' y C' descendientes de B y C. Pero en tal caso podríamos crear A1 descendiente de A y luego B' descendiente de A1 y B y C' descendiente de A1 y C. ¡Ah! Pero Delphi no tiene herencia múltiple. Bueno, entonces, ¿no sería eso, la herencia múltiple, lo que habría que pedir?...
Si, indudablemente creo que la herencia múltiple sería muy útil en Object Pascal. No obstante, bajo ese esquema se tendrían que definir tres nuevas clases —A1, B' y C'— (y otras más cuando aparecen nuevas clases prospectos de esa misma ampliación), mientras que con herencia insertada, y para resolver el mismo problema, es necesario definir una sola clase nada más. Y no sólo se trata de agregar nuevos campos, propiedades o métodos, sino también de poder redefinir el comportamiento de algún método virtual desde un punto central que repercuta en varias (selectivamente) o todas las clases que descienden de alguna clase en especial.

Haciendo a un lado las posturas conservadoras (acción esencial en el avance de toda tecnología), es imposible negar que sería muy padre (chévere, agradable) poder añadir sin complicaciones una propiedad NivelSeguridad a todos los TEdit, TMemo, y TComboBox que agregamos a una forma la semana pasada; o hacer que todo botón oprimido, sea de clase TButton, TcxButton, etc., registre en una bitácora la fecha y hora de su accionar; o redefinir el método virtual TDataSet.SetActive para llevar un conteo de aperturas en todos los conjuntos de datos que en adelante agreguemos a nuestra aplicación, sean de clase TClientDataSet, TADOQuery, TIBTable, o de alguna otra clase de componente data set que posiblemente aún no tengamos y bajaremos de Internet en unos días.


Esta sana discusión se torna interesante. Gracias por seguir externando tus valiosas opiniones Román. Me gustaría invitar a Antonio Castillo, Federico (DCA) y a todos los demás programadores Delphi a compartir sus puntos de vista.

Un abrazo chévere.

Al González. :)

lpmlpm
06-05-2006, 19:05:42
o redefinir el método virtual TDataSet.SetActive para llevar un conteo de aperturas en todos los conjuntos de datos que en adelante agreguemos a nuestra aplicación, sean de clase TClientDataSet, TADOQuery, TIBTable, o de alguna otra clase de componente data set que posiblemente aún no tengamos y bajaremos de Internet en unos días.


De hecho esto aunque limitadamente, es posible de implementar ahora asi como las demás funcionalidades que mencionas; el ejemplo del TDataset sería algo como esto en BDS2006:


TMiDataset = class helper for TDataset
public
class var FActivaciones: Integer;
procedure SetActive(Value: Boolean); reintroduce;
end;

implementation

procedure TMiDataset.SetActive (Value: Boolean);
begin
[...]
Inc(FActivaciones);
end;

//Y se usaría en algo como esto:
procedure TForm2.Button2Click(Sender: TObject);
begin
Query1.Open;
Caption := IntToStr(Table1.FActivaciones);
end;


Claro el problema es que el método SetActive es un método Protegido de la clase TDataset, por lo que no es posible ahora como estan las cosas poder re-definirlo, tal vez un poco mas adelante; pero la cuestión es que si es posible agregar funcionalidades extras; por ejemplo para agregarle un método mas a todos los derivados de TCustomDBGrid:


TMiGrid = class helper for TCustomDBGrid
public
procedure AjustaColumnas;
end;

procedure TMiGrid.AjustaColumnas;
const
DEFBORDER = 3;
var
temp, n: Integer;
lmax: array[0..30] of Integer;
begin
DataSource.DataSet.DisableControls;
Canvas.Font := Font;
for n := 0 to Columns.Count - 1 do
lmax[n] := Canvas.TextWidth(Fields[n].FieldName) + DEFBORDER;
DataSource.DataSet.First;
while not DataSource.DataSet.EOF do
begin
for n := 0 to Columns.Count - 1 do
begin
temp := Canvas.TextWidth(trim(Columns[n].Field.DisplayText)) + DEFBORDER;
if temp > lmax[n] then lmax[n] := temp;
end;
DataSource.DataSet.Next;
end;
DataSource.DataSet.First;
for n := 0 to Columns.Count - 1 do
if lmax[n] > 0 then
Columns[n].Width := lmax[n];
DataSource.DataSet.EnableControls;
end;


Vaya, me parece que la sintaxis existe para poder aplicar nuevas funcionalidades a diferentes niveles de la jerarquia de clases.

Para el caso de los Memo y los TEdit puedes aplicar el ayudante al nivel de la clase TCustomEdit, aún no existe la funcionalidad de poder incluir datos extras en los ayudantes a excepción de las variables de clase, por eso este contador sería común a todas las instancias de clases derivadas de TDataset (ya decia antes que era un poco limitado), esperemos que en la próxima versión ya se puedan usar variables de campos comunes.

Se han ido incluyendo algunas mejoras del lenguaje desde la versión 7, muchas de ellas para ofrecer compatibilidades con los esquemas de .Net, lo bueno es que estas funcionalidades las han hecho disponibles en el esquema Win32 tambien, Además del "For in", están: function inlining, class visibility, class methods/fields/properties, class helpers, nested types, operator overloading, y namespaces.

Particularmente ahora soy un fan de las "live templates" y las uso bastante, una vez las empiezas a dominar son una verdadera maravilla.

Saludos
Carlos G

Edito: si alguien me puede proporcionar un buen ejemplo que justifique el requerir Herencia Múltiple ojalá lo pueda compartir, hasta ahora no he encontrado un buen ejemplo para ello, y ojalá lo explicara con peras, manzanas, abejitas y florecitas porque con los A y A' se hace uno un enredadero marca llorarás. :-)

Andreano
12-04-2008, 03:50:08
Class Helper fue creado para permitir extender clases que son definidas como SELEAD.o si no quiere utilizar herencia.
Para simular una herencia múltipla, solamente utilizando interface.

Ejemplo:


type
IFoo = interface
procedure Hello;
procedure Walk;
end;
IBar = interface
procedure Hello;
procedure Talk;
end;
ICombo = interface(IFoo, IBar)
procedure ChewBubbleGum;
end;
TMyClass = class(TObject, ICombo)
procedure Hello; // bound to IFoo.Hello and IBar.Hello by default
procedure Walk;
procedure Talk;
procedure ChewBubbleGum;
end;
var
I: ICombo;
F: IFoo;
X: TMyClass;
begin
X := TMyClass.Create;
F := X;
F.Hello;
F.Walk;
I := X;
I.Hello; // syntax error: ambiguous call (IFoo.Hello and IBar.Hello)
IBar(I).Hello; // ok
(I as IBar).Hello; // ok
I.Walk;
I.Talk;
I.ChewBubbleGum;
end.