Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Principal > OOP
Registrarse FAQ Miembros Calendario Guía de estilo Buscar Temas de Hoy Marcar Foros Como Leídos


Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 28-08-2008
gushynet gushynet is offline
Miembro
 
Registrado: Aug 2008
Posts: 26
Poder: 0
gushynet Va por buen camino
procedimiento de asignación genérico

Hola, tengo unas dudillas a ver si alguien me puede orientar un poco.

Cuando se realiza la siguiente asignacion en delphi:

objeto1 := objeto2;

realmente no se hace la asignacion sino que objeto1 apuntara a la misma posicion de memoria que objeto2.

Estoy intentando hacer un procedimiento que realice la asignacion completa, es decir, el procedimiento debe crear una nueva instancia del objeto pasado por parametro (objeto2), que en principio no sabemos de que tipo es, y copiar el contenido de todas las propiedades del objeto2 en las propiedades del objeto1.

procedimiento TObjeto.Asignar(obj: pointer );

En este punto se me han planteado una serie de dudas sobre la solucion que se me ha ocurrido pero que no he conseguido implementar.


Primero, el tipo del parametro que le paso al metodo es de tipo pointer ya que delphi, si no me equivoco la variable objeto no tiene el objeto en si sino la direccion del mismo, por lo que si el parametro es de tipo pointer, el método aceptara cualquier tipo de objeto ya que como dije todo objeto en delphi es un puntero al objeto. No se si este razonamiento es válido y eficiente pero compilar compila.

Una vez entramos en el metodo los pasos que creo se deberia seguir para realizar la asignacion son los siguientes:

1. Averigua el tipo del objeto pasado por parametro

aqui se que puedo usar propiedades como ClassName o ClassType pero aunque he conseguido acceder a ClassName no he conseguido siquiera cambiar el valor de una propiedad del objeto pasado porque siempre se produce el mismo error en tiempo de ejecucion, error en el acceso a memoria.

2. crea una nueva instancia del objeto una vez averiguado su tipo

3. accedemos a todos los campos del objeto pasado por parametro y copiamos su contenido en las propiedades del objeto creado. Aqui se me plantea el problema de como recorrer todas las propiedades del objeto pasado por parametro.

4. Devolvemos el objeto creado.


Las dudas que tengo son muchas pero ahi van unas cuantas:

- la unica manera de averiguar el tipo del objeto que me han pasado es asignando el pointer a un objeto local de tipo TObject y accediento a la propiedad NameClasses o haciendo una conversion directa:

NombreClase := PTObject(Objeto)^.ClassName;

PTObject es un puntero a TObject.

- ¿como creo una nueva instancia? lo he conseguido hacer si dentro del metodo realizo una conversion (al parametro)al tipo que sé que le estoy pasando al metodo pero la gracia esta en que ese metodo lo pueda usar cualquier independientemente del objeto para el que vaya a realizar la conversion.
- Sabeis de documentacion(en español mejor) sobre la Programacion orientada a objetos en object pascal sin ser el manual de delphi y que tenga ejemplos mas elaborados sobre las referencias de clase ya que no tengo ni idea de en que contexto se usan o para que sirven, aunque sospecho que alguna relacion con el problema que he planteado seguro que tienen.


Gracias de antemano por la ayuda.
Un saludo.

PD:disculpas por lo extendido que me ha quedado el mensaje.
Responder Con Cita
  #2  
Antiguo 28-08-2008
Avatar de Delphius
[Delphius] Delphius is offline
Miembro Premium
 
Registrado: Jul 2004
Ubicación: Salta, Argentina
Posts: 5.598
Poder: 19
Delphius Va camino a la fama
Hola gushynet,
Bienvenido/a a Clubdelphi. Como eres nuevo te pediría que por favor dediques unos minutos para leer la guia de estilo.

Con respecto a tus dudas habría que analizar mejor que es lo que buscas.
No se si estoy comprendiendo bien pero me parece que tu estás buscando implementar un método Assing() genérico, es decir que no dependa del tipo o clase destino ni de la fuente.

Si es eso es imposible, al menos eso es lo que yo tengo entendido.
Una pregunta ¿Tiene sentido assignar los valores de las propiedades de un TGato a un TPerro?

El asunto es que necesariamente debe conocerse las posibles clases de las cuales permite realizar asignaciones y debe haber cierta similitud entre una clase y la otra.
Por ejemplo, en el caso de TGato y TPerro, en parte es posible, si lo entendemos en el contexto en que ambos son descendiente de TAnimal. Pero si TGato desciende de TFelino. y TPerro desciende de TCanino, el grado de parentezco posiblemente nuble o haga díficil una asignación.

Necesariamente el método Assing debe contar y saber de antemano cuales son las clases que asumirá como válida para permitir una copia. Y esto lleva a un principio: obligadamente tus clases son descendientes de TPersistent o bien obedecen a una jerarquia de clases, cuya clase base sea descendiente de TPersistent o algún hijo de ésta.

Al ser los métodos Assing y AssingTo de Persistent virtuales tu debes darle implementación a tus descendientes.

En pocas:
1. Debe existir cierto grado de afinidad entre las clases
2. Deben ser parientes directos o indirectos de TPersistent
3. Deben redefinir el método Assing y AssingTo
4. Debe establecerse cuales son las clases válidas que puede aceptar para copiar

Con respecto a como crear una nueva instancia, sería mejor que expliques mejor la situación. Cuando uno invoca a Assing asume que ya tiene una instancia tanto del objeto Destino como de la fuente.
De cualquier manera, todas las clases cuentan con el método de clase NewInstance, si es que tu idea pasa por crear nuevas instancias de una clase x.

Saludos,
__________________
Delphius
[Guia de estilo][Buscar]
Responder Con Cita
  #3  
Antiguo 28-08-2008
[coso] coso is offline
Miembro Premium
 
Registrado: May 2008
Ubicación: Girona
Posts: 1.677
Poder: 0
coso Va por buen camino
Hola, si lo que quieres es crear una nueva instancia de cierto objeto, entonces ¿no deberias usar directamente el metodo create y luego copiar todas las propiedades? (del TObject, claro, del puntero no podrias sacar nada en claro)
Responder Con Cita
  #4  
Antiguo 28-08-2008
[coso] coso is offline
Miembro Premium
 
Registrado: May 2008
Ubicación: Girona
Posts: 1.677
Poder: 0
coso Va por buen camino
Cita:
con el método de clase NewInstance
o este, que suena bastante afin tambien
Responder Con Cita
  #5  
Antiguo 28-08-2008
Avatar de Delphius
[Delphius] Delphius is offline
Miembro Premium
 
Registrado: Jul 2004
Ubicación: Salta, Argentina
Posts: 5.598
Poder: 19
Delphius Va camino a la fama
Ahora que recuerdo, no es muy conveniente que se invoque a NewInstance en forma directa. Es preferible y más seguro el método Create.

De todas formas, todos los constructores invocan, automáticamente, al método de clase NewInstance para solicitar el espacio de memoria.

El método de clase NewInstance es virtual por tanto puede ser redefinido. Si por casualidad en la etapa del pedido de memoria debe analizarse o chequearse algo al respecto al uso de memoria, es en este método en donde debe programarse.

Por lo general no debe redefinirse este método. Pero un caso excepcional puede ser cuando la clase que se está diseñando debe actuar como un singleton.

Saludos,
__________________
Delphius
[Guia de estilo][Buscar]
Responder Con Cita
  #6  
Antiguo 28-08-2008
Avatar de Caro
*Caro* Caro is offline
Moderadora
 
Registrado: Jul 2004
Ubicación: Cochabamba, Bolivia
Posts: 2.544
Poder: 16
Caro Va por buen camino
Hola gushynet, revisa también este hilo http://www.clubdelphi.com/foros/showthread.php?t=34532

Saluditos
__________________
Disfruten cada minuto de su vida a lado de sus seres queridos como si fuese el ultimo, uno nunca sabe lo que puede pasar.
Responder Con Cita
  #7  
Antiguo 28-08-2008
gushynet gushynet is offline
Miembro
 
Registrado: Aug 2008
Posts: 26
Poder: 0
gushynet Va por buen camino
explicacion mas detallada del problema

antes que nada, gracias por la rapidez de las respuestas. De todas formas pondré el pequeño ejemplo que estoy intentando hacer y de ahi la duda que es puesto en el foro.

Creo que lo de generico lo explique mal. Generico en relacion a una jerarquia de clases, jerarquia que explico a continuacion:

Tengo una clase base llamada TParrafo la cual tiene todos los métodos abstractos y virtuales. Entre las propiedades que tiene hay una llamada "contenido" que es de tipo AnsiString.

Tengo ademas tres clases (TPregunta,TRespuesta y TTematica) que descienden de TParrafo.

TTematica añade una propiedad AnsiString mas llamada "descripcion".


Todas las clases descritas tienen un metodo llamado asignar( declarado como virtual y abstracta en TParrafo ) al cual se le pasa un objeto de tipo parrafo y el metodo asigna el valor de todas las propiedades (en este caso solo una o dos si es de tipo TTematica) del parametro de entrada al objeto actual, el que realiza la llamada al metodo.


mi intencion es que si el objeto que llama a asignar es:
- TParrafo,TPregunta o TRespuesta compartan el mismo codigo ya que los
objetos son iguales, por lo solo se debe cambiar el valor de la propiedad 'contenido'
-Y que en Ttematica cambie la implementacion del metodo para tener en cuenta la propiedad 'descripcion' .

El problema lo encuentro al ser el parametro del metodo de tipo TParrafo. Solo acepta cosas de tipo TParrafo por lo que use un pointer para que asi aceptara cualquier tipo de objeto y dentro de la implementacion especifica del metodo realizar un cast al tipo del objeto que ha llamado al metodo. Por ejemplo:

si tengo un objeto t1 de tipo TTematica y uso su metodo asignar tenemos:

T1.Asignar(T2);

al ser T2 de tipo TTematica se produce un error. Lo mismo pasa si le paso algo de tipo TPregunta o Trespuesta aunque sean iguales a TParrafo, por ser de distinto tipo se produce un error de compilacion. De ahi el usar un pointer y dentro del metodo realizo una conversion.

En definitiva, lo que quiero es usar bien la poo pero no se como montarmelo para hace eso:

una sola implemetacion que sirva para TParrafo,TPregunta y TRespuesta y una reimplementacion en TTematica ya que la clase tiene una propiedad mas, 'descripcion' , sin que el tipo de dato (dentro de la jerarquia creada) que paso por parametro sea un problema.

Espero haberme explicado mejor, pero entre que quiero escribirlo todo y que intento resumir para que no se haga muy grande el mensaje no se si ahora se entendera mejor.

Gracias de antemano por el cable .
Un saludo.
Responder Con Cita
  #8  
Antiguo 28-08-2008
Avatar de roman
roman roman is offline
Moderador
 
Registrado: May 2003
Ubicación: Ciudad de México
Posts: 20.172
Poder: 10
roman Tiene un aura espectacularroman Tiene un aura espectacular
Yo creo que hay algo confuso aquí. Según describes, tienes estas clases:

Código Delphi [-]
type
  TParrafo = class
    ...
    procedure Asignar(P: TParrafo); virtual; abstract;
  end;

  TPregunta = class(TParrafo)
    ...
    procedure Asignar(P: TParrafo); override;
  end;

  TRespuesta = class(TParrafo)
    ...
    procedure Asignar(P: TParrafo); override;
  end;

  TTematica = class(TParrafo)
    ...
    procedure Asignar(P: TParrafo); override;
  end;

En este caso, el método Asignar acepta cualquier objeto de tipo TParrafo y cualquier descendiente de TParrafo. No debe haber ningún error de compilación.

// Saludos
Responder Con Cita
  #9  
Antiguo 29-08-2008
Avatar de Al González
[Al González] Al González is offline
In .pas since 1991
 
Registrado: May 2003
Posts: 5.503
Poder: 23
Al González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en bruto
¡Hola!

Cita:
Empezado por gushynet Ver Mensaje
...objeto1 := objeto2;

realmente no se hace la asignacion sino que objeto1 apuntara a la misma posicion de memoria que objeto2.

Estoy intentando hacer un procedimiento que realice la asignacion completa, es decir, el procedimiento debe crear una nueva instancia del objeto pasado por parametro (objeto2), que en principio no sabemos de que tipo es, y copiar el contenido de todas las propiedades del objeto2 en las propiedades del objeto1.
...
los pasos que creo se deberia seguir para realizar la asignacion son los siguientes:

1. Averigua el tipo del objeto pasado por parametro
...
2. crea una nueva instancia del objeto una vez averiguado su tipo
...
3. accedemos a todos los campos del objeto pasado por parametro y copiamos su contenido en las propiedades del objeto creado. Aqui se me plantea el problema de como recorrer todas las propiedades del objeto pasado por parametro.
...
4. Devolvemos el objeto creado.
...
En esta página podrás descargar una biblioteca de funciones llamada GH Freebrary, es de mi autoría. En la unidad GHFObjects.pas encontrarás una función llamada ghClone que, en esencia, realiza esos pasos que has descrito con el objetivo de duplicar instancias de objetos.

Código Delphi [-]
Uses
  GHFObjects;
...
  Var
    O2 :TObject;
  Begin
    O2 := ghClone (O1);  // Siendo O1 un objeto de cualquier clase

Cabe aclarar que esta rutina de "clonación", después de crear la nueva instancia, copia solamente las propiedades publicadas (que básicamente son las mismas que pueden ser vistas en el inspector de objetos). También considera que el código de estas funciones está probado (en casos reales) con Delphi 7, pero no he terminado de adaptarlo a otras versiones (aunque un compañero del club tuvo la gentileza de adaptarlo a Delphi 6 y 2007 en pasadas semanas, sólo haría falta revisar tales adaptaciones).

Algo quizá más importante de considerar es que la función ghClone llamará al constructor que tenga definido la clase en cuestión sólo si se trata de una descendiente de TComponent (gracias a que el constructor de TComponent es virtual). Si el objeto a clonar no es un componente, ghClone ejecutará el constructor de la clase TObject después de crear la instancia.

Son tres las versiones (sobrecargas) de esta función:
Código Delphi [-]
  Function ghClone (Const Obj :TObject; Const Cls :TClass;
    Const Owner :TComponent = Nil; Const ExcludedProps :String = '')
    :TObject; Overload;
  Begin
    If Obj Is TComponent Then
      Result := TComponentClass (Cls).Create (Owner)
    Else
      Result := Cls.Create;

    Try
      ghCopyProps (Obj, Result, ExcludedProps);
    Except
      Result.Free;
      Raise;
    End;
  End;

  Function ghClone (Const Obj :TObject; Const Owner :TComponent = Nil;
    Const ExcludedProps :String = '') :TObject; Overload;
  Begin
    Result := ghClone (Obj, Obj.ClassType, Owner, ExcludedProps);
  End;
  
  Function ghClone (Const Obj :TObject; Const ExcludedProps :String)
    :TObject; Overload;
  Begin
    Result := ghClone (Obj, Obj.ClassType, Nil, ExcludedProps);
  End;

Como podrás ver, las tres sobrecargas terminan llamando a una función de nombre ghCopyProps, que en otros casos podrías usar para copiar las propiedades de un objeto existente a otro.

El parámetro opcional ExcludedProps te sirve para indicarle a la función que no copie algunas propiedades en particular:
Código Delphi [-]
Var
  C2 :TComponent;
Begin
  C2 := TComponent (ghClone (C1, 'Name;OnClick'));  // Siendo C1 un objeto componente

Cita:
Empezado por gushynet Ver Mensaje
...PD:disculpas por lo extendido que me ha quedado el mensaje.
Creo que esa petición está de sobra, gushynet. El tamaño y estilo de tu mensaje, para ser el primero, muestra un verdadero interés por darse a entender y solucionar el problema.

Un abrazo doble.

Al González.
__________________
Twitter
Código
Blog
WhatsApp para consultas rápidas y asesorías profesionales: +52 1 2711260117

Última edición por Al González fecha: 08-09-2008 a las 01:48:14.
Responder Con Cita
  #10  
Antiguo 30-08-2008
Avatar de roman
roman roman is offline
Moderador
 
Registrado: May 2003
Ubicación: Ciudad de México
Posts: 20.172
Poder: 10
roman Tiene un aura espectacularroman Tiene un aura espectacular
Hola,

Siento que nuestro compañero gushynet se está yendo por un camino equivocado al tratar de resolver con clonaciones lo que puede resolverse con métodos usuales.

Según lo que él describe, yo lo plantearía así: en lugar de poner el método Asignar de la clase base como abstracto, lo implementaría de manera que copie las propiedades comunes, y, tal como él mismo dice, redefiniría este método en TTematica para copiar la propiedad extra.

Código Delphi [-]
interface

type
  TParrafo = class
  private
    FContenido: String;

  public
    property Contenido: String read FContenido write FContenido;
    procedure Asignar(P: TParrafo); virtual;
  end;

  TPregunta = class(TParrafo)
  end;

  TRespuesta = class(TParrafo)
  end;

  TTematica = class(TParrafo)
  private
    FDescripcion: String;

  public
    property Descripcion: String read FDescripcion write FDescripcion;
    procedure Asignar(P: TParrafo); override;
  end;

implementation

{ TParrafo }

procedure TParrafo.Asignar(P: TParrafo);
begin
  Self.FContenido := P.Contenido;
end;

{ TTematica }

procedure TTematica.Asignar(P: TParrafo);
begin
  inherited;

  if (P is TTematica) then
  begin
    Self.FDescripcion := TTematica(P).Descripcion;
  end
end;

end.

Tal como lo pongo aquí, se pueden copiar unos a otros sin problemas. Si una Tematica se asigna a un Parrafo (o Pregunta o Respuesta), el método usado es el de la clase base, por lo que se copia únicamente la propiedad Contenido. Si se asigna a otra Tematica entonce se usa el método redefinido que usa el heredado y añade la copia de la propiedad Descripcion.

Finalmente, si un Parrafo (o Pregunta o Respuesta) se copia a una Tematica, el método Asignar de TTematica copia los campos base (Contenido) pero evita copiar el campo extra que no existe en el objeto fuente.

// Saludos
Responder Con Cita
  #11  
Antiguo 08-09-2008
gushynet gushynet is offline
Miembro
 
Registrado: Aug 2008
Posts: 26
Poder: 0
gushynet Va por buen camino
Antes que nada agradecer las respuestas ya que me han sido de gran ayuda.

Despues de una semana probando las ideas propuestas he conseguido implementar un contenedor generico pero con ciertas restricciones que no he podido quitar.

Hare un pequeño resumen de lo que he hecho:

- He implementado una clase llamada TElemento que es el tipo de objeto que aceptan o devuelven los metodos del contenedor(bueno,los que lo necesitan).

- Esta clase tiene dos metodos abstractos llamados Asignar y Comparar.

- Por otra parte tengo una clase llamada TContenedor, que es la clase base de los posibles contenedores que se implementen. Esta clase no es funcional.

- A partir de TContenedor he definido TListaSimple que implementa una lista simple enlazada en memoria dinamica.

- En el caso concreto de TListaSimple los nodos que almacena son de tipo TNodo cuya estructura consiste en tres campos:

- ID: un entero
- Elemento: TElemento
- Siguiente: puntero a elementos de tipo TNodo.

El contenedor funciona si los objetos que se quieren introducir descienden de TElemento (primera restriccion).

El proceso que sigo para añadir un elemento es el siguiente:

- creo un objeto del mismo tipo que el pasado por parametro. En este punto me ha surgido una duda:

Al González me sugirio la siguiente sintaxis:

Código Delphi [-]o2 := TClaseBase(o1.ClassType).crear(...)


pero no funciona asi que probe la referencia de la clase en lugar de la clase base:

Código Delphi [-]type TClaseBaseClass = class of TClaseBase; ...... o2 := TClaseBaseClass(o1.ClassType).crear(...)


y si funciona. Mi duda es porque funciona asi y no como me sugirio AI Gonzalez ya que encuentro mas coherente y mas intuitiva su solucion que como yo lo he hecho. Que diferencia hay entre usar TClaseBase y TClaseBaseClass?

- Despues de asignar el elemento, lo inserto en la lista dinamica en funcion del campo ID que previamente he calculado y ya esta.



La segunda restriccion es que los objetos que se quieran almacenar en el contenedor deben disponer de los metodos Asignar y Comparar de lo contrario la historia se viene abajo ya que los uso dentro del contenedor de ahi que el usuario que quiera usar el contenedor con su jerarquia de clases debe hacer que su clase base sea descendiente de TElemento que es el que tiene definido de forma abstracta estos metodos. El usuario tiene que preocuparse de implementarlos en sus clases.

Lo que busco para que el contenedor sea totalmente generico y yo ser el hombre mas feliz del mundo mundial es eliminar estas dos restricciones.

La primera restriccion se me ocurrio eliminarla haciendo que en lugar de ser TElemento la clase base para que el contenedor funcione fuese TObject, pero me encontre con el problema de que ni Asignar ni Comparar estan definidos en TObject con lo que tampoco pude eliminar la segunda restriccion.

Supongo que no puedo redefinir la clase TObject añadiendole Asignar y Comparar como metodos abstractos, porque significaria que tendria que dar por sentado que el programador que use mi contenedor tiene redefinido en su aplicacion la clase TObject con los metodos abstractos mencionados cosa que no tendria por que saber ni hacer para poder usar el contenedor.

Y en este punto me he quedado. Al fin y al cabo lo que busco es solucionarle la papeleta al programador y que tenga que saber lo minimo sobre el contenedor para usarlo, y creo que el hecho de que el usuario del contenedor tenga que redefinir los metodos Asignar y Comparar ademas de hacer que sus clases base desciendan de TElemento hace que el objetivo que busco se quede por el camino, para mi gusto son muchas las cosas que tiene que conocer el usuario para usar el contenedor.


Una duda mas:

Sea la siguiente funcion:

Código Delphi [-]function TListaSimple.Actual():TElemento var E:TElemento; begin E := TElemento.create(); E.Asignar(PActual.Elemento); result := E; end;


PActual es un puntero a un nodo de la lista. Esta funcion se complementa con los procedimientos Primero, Ultimo, Siguiente y Anterior para movernos por la lista.

Mi duda es la siguiente: el objeto E es un puntero cuyo espacio (el que ocupa el puntero no a lo que apunta) se libera cuando la funcion termina, es decir, como un tipo simple. Mi pregunta es si a lo que apunta tambien es liberado o soy yo el que tiene que preocuparse de liberar el espacio ocupado por E con el metodo destroy debido a que no es un tipo simple como un entero sino un objeto. Si es asi, significa que esta funcion, a no ser que el que llame a esta funcion se preocupe, cada vez que se llama deja perdido en memoria un espacio equivalente a algo de tipo TElemento o descendiente.


Beno, por ultimo (tranquilidad que no son mas dudas y no sera porque no tenga mas ) agradecer de nuevo las respuestas y la pagina que me han recomendado y por supuesto la paciencia mostrada con las "novelas" que he enviando como mensajes.

Un saludo.

Salud y Delphi
Responder Con Cita
  #12  
Antiguo 08-09-2008
gushynet gushynet is offline
Miembro
 
Registrado: Aug 2008
Posts: 26
Poder: 0
gushynet Va por buen camino
Lo siento por lo del formato del codigo, pero en la vista previa se veia bien, no se que habra pasado.

Un saludo
Responder Con Cita
  #13  
Antiguo 08-09-2008
Avatar de Al González
[Al González] Al González is offline
In .pas since 1991
 
Registrado: May 2003
Posts: 5.503
Poder: 23
Al González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en bruto
Cita:
Empezado por gushynet Ver Mensaje
...
Al González me sugirio la siguiente sintaxis:

Código Delphi [-]o2 := TClaseBase(o1.ClassType).crear(...)


pero no funciona asi que probe la referencia de la clase en lugar de la clase base:

Código Delphi [-]type TClaseBaseClass = class of TClaseBase; ...... o2 := TClaseBaseClass(o1.ClassType).crear(...)


y si funciona. Mi duda es porque funciona asi y no como me sugirio AI Gonzalez ya que encuentro mas coherente y mas intuitiva su solucion que como yo lo he hecho. Que diferencia hay entre usar TClaseBase y TClaseBaseClass?
...
Ahora me percato de la tremenda omisión que tuve. No funcionaba como originalmente lo escribì porque el compilador comprende una expresión de ese estilo como un molde de tipo cuyo resultado es una instancia, no una clase (estaba forzando que el compilador tratara a una clase como un objeto ).

Como lo haces después es realmente una sintaxis adecuada para lograr un molde de tipo de clase.

Saludos desde el "ciber" de mi nuevo vecindario.

Al González.
__________________
Twitter
Código
Blog
WhatsApp para consultas rápidas y asesorías profesionales: +52 1 2711260117
Responder Con Cita
  #14  
Antiguo 08-09-2008
Avatar de Delphius
[Delphius] Delphius is offline
Miembro Premium
 
Registrado: Jul 2004
Ubicación: Salta, Argentina
Posts: 5.598
Poder: 19
Delphius Va camino a la fama
Cita:
Empezado por gushynet Ver Mensaje
Antes que nada agradecer las respuestas ya que me han sido de gran ayuda.

Despues de una semana probando las ideas propuestas he conseguido implementar un contenedor generico pero con ciertas restricciones que no he podido quitar.

Hare un pequeño resumen de lo que he hecho:

- He implementado una clase llamada TElemento que es el tipo de objeto que aceptan o devuelven los metodos del contenedor(bueno,los que lo necesitan).

- Esta clase tiene dos metodos abstractos llamados Asignar y Comparar.

- Por otra parte tengo una clase llamada TContenedor, que es la clase base de los posibles contenedores que se implementen. Esta clase no es funcional.

- A partir de TContenedor he definido TListaSimple que implementa una lista simple enlazada en memoria dinamica.

- En el caso concreto de TListaSimple los nodos que almacena son de tipo TNodo cuya estructura consiste en tres campos:

- ID: un entero
- Elemento: TElemento
- Siguiente: puntero a elementos de tipo TNodo.

El contenedor funciona si los objetos que se quieren introducir descienden de TElemento (primera restriccion).

El proceso que sigo para añadir un elemento es el siguiente:

- creo un objeto del mismo tipo que el pasado por parametro. En este punto me ha surgido una duda:

Al González me sugirio la siguiente sintaxis:

Código Delphi [-]o2 := TClaseBase(o1.ClassType).crear(...)


pero no funciona asi que probe la referencia de la clase en lugar de la clase base:

Código Delphi [-]type TClaseBaseClass = class of TClaseBase; ...... o2 := TClaseBaseClass(o1.ClassType).crear(...)


y si funciona. Mi duda es porque funciona asi y no como me sugirio AI Gonzalez ya que encuentro mas coherente y mas intuitiva su solucion que como yo lo he hecho. Que diferencia hay entre usar TClaseBase y TClaseBaseClass?

- Despues de asignar el elemento, lo inserto en la lista dinamica en funcion del campo ID que previamente he calculado y ya esta.



La segunda restriccion es que los objetos que se quieran almacenar en el contenedor deben disponer de los metodos Asignar y Comparar de lo contrario la historia se viene abajo ya que los uso dentro del contenedor de ahi que el usuario que quiera usar el contenedor con su jerarquia de clases debe hacer que su clase base sea descendiente de TElemento que es el que tiene definido de forma abstracta estos metodos. El usuario tiene que preocuparse de implementarlos en sus clases.

Lo que busco para que el contenedor sea totalmente generico y yo ser el hombre mas feliz del mundo mundial es eliminar estas dos restricciones.

La primera restriccion se me ocurrio eliminarla haciendo que en lugar de ser TElemento la clase base para que el contenedor funcione fuese TObject, pero me encontre con el problema de que ni Asignar ni Comparar estan definidos en TObject con lo que tampoco pude eliminar la segunda restriccion.

Supongo que no puedo redefinir la clase TObject añadiendole Asignar y Comparar como metodos abstractos, porque significaria que tendria que dar por sentado que el programador que use mi contenedor tiene redefinido en su aplicacion la clase TObject con los metodos abstractos mencionados cosa que no tendria por que saber ni hacer para poder usar el contenedor.

Y en este punto me he quedado. Al fin y al cabo lo que busco es solucionarle la papeleta al programador y que tenga que saber lo minimo sobre el contenedor para usarlo, y creo que el hecho de que el usuario del contenedor tenga que redefinir los metodos Asignar y Comparar ademas de hacer que sus clases base desciendan de TElemento hace que el objetivo que busco se quede por el camino, para mi gusto son muchas las cosas que tiene que conocer el usuario para usar el contenedor.


Una duda mas:

Sea la siguiente funcion:

Código Delphi [-]function TListaSimple.Actual():TElemento var E:TElemento; begin E := TElemento.create(); E.Asignar(PActual.Elemento); result := E; end;


PActual es un puntero a un nodo de la lista. Esta funcion se complementa con los procedimientos Primero, Ultimo, Siguiente y Anterior para movernos por la lista.

Mi duda es la siguiente: el objeto E es un puntero cuyo espacio (el que ocupa el puntero no a lo que apunta) se libera cuando la funcion termina, es decir, como un tipo simple. Mi pregunta es si a lo que apunta tambien es liberado o soy yo el que tiene que preocuparse de liberar el espacio ocupado por E con el metodo destroy debido a que no es un tipo simple como un entero sino un objeto. Si es asi, significa que esta funcion, a no ser que el que llame a esta funcion se preocupe, cada vez que se llama deja perdido en memoria un espacio equivalente a algo de tipo TElemento o descendiente.


Beno, por ultimo (tranquilidad que no son mas dudas y no sera porque no tenga mas ) agradecer de nuevo las respuestas y la pagina que me han recomendado y por supuesto la paciencia mostrada con las "novelas" que he enviando como mensajes.

Un saludo.

Salud y Delphi
Disculpa, no logro entender apropiadamente lo que expones aqui con lo anterior. No se si lo que creo entender es correcto: ¿Dices que tienes un objeto que guarda una lista con los elementos, y que sea esta lista la que se copie o asigne a los diferentes objetos?

No se porqué empleas una lista enlazada de nodos, contando ya con con clases te facilitan el trabajo (como ser por ejemplo TObjectList).
Por otro lado, el que tu implementes tu Nodo, te obliga a ti a liberar la memoria... cada New() que haces, deberá en algún momento corresponder con un Dispose().
Por otro lado, no estoy seguro pero tengo entendido que si por casualidad en tu TNodo, el "campo" Elemento es del tipo TObject (o descenciente de éste). Un New() de un TNodo y/o un Dispose() ni crea ni elimina dicho objeto.

A me parece que lo más certero es lo que comenta roman.

Saludos,
__________________
Delphius
[Guia de estilo][Buscar]
Responder Con Cita
  #15  
Antiguo 08-09-2008
gushynet gushynet is offline
Miembro
 
Registrado: Aug 2008
Posts: 26
Poder: 0
gushynet Va por buen camino
buenas de nuevo. Veamos, lo del contenedor generico lo he hecho mas que nada para entender mejor los conceptos relacionados con la poo.

Lo que explica Roman es basicamente lo que he hecho y funciona bien siempre y cuando los objetos que quiera almacenar en el contenedor sean de tipo TElemento o desendiente ya que este objeto tiene las operaciones de asignar y comparar abstractas. Estos metodos los uso dentro del contenedor, concretamente en el metodo que se encarga de añadir un elemento a la lista.


La lista dinamica que implemento usa new y dispose de manera correcta. Ese no es el problema. La lista y en concreto el metodo para añadir un elemento lo que hace es duplicar el elemento pasado por paramero y la funcion asignar es la que realmente asigna los valores al nuevo elemento creado de ahi la necesidad de que los elementos que se quieran guardar en el contenedor deban tener el metodo implementado sino no sabria como asignar el valor de las propiedades de un objeto a otro ya que en principio el contenedor solo se limita a almacenarlos.

Este TElemento es encapsulado en el Nodo que como mencione en el mensaje anterior tiene tres campos, uno de ellos de tipo TElemento. Este campo apunta al objeto duplicado, por lo que el nodo lo que guarda es la ubicacion del objeto en memoria, no el objeto en si.

El metodo para eliminar un nodo de la lista es:

1. busco el elemento en la lista con ayuda de la funcion comparar.
2. una vez encontrado uso el destroy para el elemento al que esta apuntando el campo del Nodo de tipo TElemento.
3. por ultimo libero el nodo de memoria con dispose atualizando tambien los enlaces de la lista que se hayan visto afectados.

En definitiva, el contenedor funciona con objetos de tipo TElemento y desendientes. Y lo que yo quiero es no obligar al usuario a que su jerarquia de clases cuelgue de TElemento para poder usar el contenedor pero si no lo hago asi no podria usar los metodos antes descritos. Y creo que de la forma que lo he hecho aunque funcione ni es eficiente ni elegante.

Con respeto al comentario de Delphius, tienes razon, delphi proporciona clases para lo que busco, pero como se suele decir si quieres lapas hay que mojarse el culo, y pense que la mejor manera de entender la poo en delphi es haciendome yo mismo un contenedor generico y viendo todas las dificultades que se me pueden presentar. En mi caso me quedo mejor con las cosas si le doy vueltas y vueltas hasta encontrar la solucion o al menos el camino para la solucion que si me lo dan todo hecho y mastiado. Soy un masoquista de la programacion que le vamos a hacer.


Con respecto al supuesto despiste cometido por AI Gonzales nada mas agradecerte tu confucion ya que gracias a ello he encontrado un uso a las referencias de clase aunque sigo pensando que tu respuesta me sigue pareciendo mas logica aunque no funcionace



Gracias de antemano por las respuestas. Y bueno, siguo en mi linea de pedir disculpas por lo extenso de las respuestas.


Un saludo.
Responder Con Cita
  #16  
Antiguo 08-09-2008
Avatar de roman
roman roman is offline
Moderador
 
Registrado: May 2003
Ubicación: Ciudad de México
Posts: 20.172
Poder: 10
roman Tiene un aura espectacularroman Tiene un aura espectacular
Yo tengo una opinión. Realmente, eso de querer trabajar indistintamente con cualquier objeto, sin depender de qué clase sea, a final de cuentas, ya se aleja de lo que es la POO. En ésta hay clases y jerarquías de clases, y uno espera tal o cual comportamiento de acuerdo a la jerarquía. Hacer clonaciones genéricas (si bien puede tener su uso), y, en general, intentar trabajar de manera arbitraría con cualquier objeto, está ya muy lejos del polimorfismo, no hay ningún comportamiento que puedas esperar o predecir porque en realidad no tienes objetos sino meros contenedores de datos.

Pero como digo, es sólo una opinión

// Saludos
Responder Con Cita
  #17  
Antiguo 08-09-2008
Avatar de Al González
[Al González] Al González is offline
In .pas since 1991
 
Registrado: May 2003
Posts: 5.503
Poder: 23
Al González Es un diamante en brutoAl González Es un diamante en brutoAl González Es un diamante en bruto
Cita:
Empezado por gushynet Ver Mensaje
...despiste cometido por AI Gonzales...
¿Y ese quién es?
__________________
Twitter
Código
Blog
WhatsApp para consultas rápidas y asesorías profesionales: +52 1 2711260117
Responder Con Cita
  #18  
Antiguo 08-09-2008
[egostar] egostar is offline
Registrado
 
Registrado: Feb 2006
Posts: 6.421
Poder: 19
egostar Va por buen camino
Cita:
Empezado por Al González Ver Mensaje
¿Y ese quién es?
Es una nueva clase generada desde la ghClone

Salud OS
__________________
"La forma de empezar es dejar de hablar y empezar a hacerlo." - Walt Disney
Responder Con Cita
  #19  
Antiguo 08-09-2008
Avatar de Delphius
[Delphius] Delphius is offline
Miembro Premium
 
Registrado: Jul 2004
Ubicación: Salta, Argentina
Posts: 5.598
Poder: 19
Delphius Va camino a la fama
Cita:
Empezado por roman Ver Mensaje
Yo tengo una opinión. Realmente, eso de querer trabajar indistintamente con cualquier objeto, sin depender de qué clase sea, a final de cuentas, ya se aleja de lo que es la POO. En ésta hay clases y jerarquías de clases, y uno espera tal o cual comportamiento de acuerdo a la jerarquía. Hacer clonaciones genéricas (si bien puede tener su uso), y, en general, intentar trabajar de manera arbitraría con cualquier objeto, está ya muy lejos del polimorfismo, no hay ningún comportamiento que puedas esperar o predecir porque en realidad no tienes objetos sino meros contenedores de datos.

Pero como digo, es sólo una opinión

// Saludos
Pues yo comparto tu visión roman.

Me parece que hay algo en el modelo del dominio que está analizando y diseñando gushynet que niebla o entorpece la comprensión real y el propósito de cada clase.

Si pudieramos conocer con más detalles, ya no con una comprensión micro sobre este problema en particular, sino como algo macro (la clase) tal vez podríamos llegar a formular alternativas.

Saludos,
__________________
Delphius
[Guia de estilo][Buscar]
Responder Con Cita
  #20  
Antiguo 08-09-2008
[egostar] egostar is offline
Registrado
 
Registrado: Feb 2006
Posts: 6.421
Poder: 19
egostar Va por buen camino
Cita:
Empezado por Delphius Ver Mensaje
Pues yo comparto tu visión roman.

Me parece que hay algo en el modelo del dominio que está analizando y diseñando gushynet que niebla o entorpece la comprensión real y el propósito de cada clase.

Si pudieramos conocer con más detalles, ya no con una comprensión micro sobre este problema en particular, sino como algo macro (la clase) tal vez podríamos llegar a formular alternativas.

Saludos,
Yo tal vez estoy confundiendome acerca de lo que se habla en este hilo, pero el nuevo Delphi trae algo que se llama generics and anonymous methods.

Salud OS
__________________
"La forma de empezar es dejar de hablar y empezar a hacerlo." - Walt Disney
Responder Con Cita
Respuesta


Herramientas Buscar en Tema
Buscar en Tema:

Búsqueda Avanzada
Desplegado

Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro

Temas Similares
Tema Autor Foro Respuestas Último mensaje
Apuntar a un tipo genérico Guillermo80 Varios 4 09-03-2008 11:48:22
Reutilizar código "generico" adlfv OOP 13 06-09-2005 02:01:04
Ayuda, como llamar a un procedimiento desde otro procedimiento? Ariatna Varios 1 01-02-2005 04:05:35
reporte generico piyugo Impresión 8 07-05-2004 18:20:03
Configuar Generico/solo texto en Win 2000 sperezp Impresión 0 13-01-2004 15:13:43


La franja horaria es GMT +2. Ahora son las 22:40:19.


Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2018, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi
Copyright 1996-2007 Club Delphi