Ir al contenido


Foto

[RESUELTO] Expandir TCollectionItem


  • Por favor identifícate para responder
20 respuestas en este tema

#1 cadetill

cadetill

    Advanced Member

  • Moderadores
  • PipPipPip
  • 994 mensajes
  • LocationEspaña

Escrito 15 noviembre 2011 - 11:12

Buenas,

A ver si me se explicar porque estoy muy espeso hoy jejjejeje

Bien, resulta que tengo una clase (TGMMarker) la cual tiene una propiedad que es una colección (TMarkers) de TCollectionItems (TMarker). Hasta ahí todo bien.

Ahora quiero heredar de TGMMarker (no hay problema) para crear una nueva clase. Esta nueva clase necesita que sus TCollectionItems tengan más propiedades que los TMarker que heredo de TGMMarke. ¿Cómo puedo hacer ésto?

Espero que haya quedado claro, sino preguntar y miro de explicarme mejor jejeje

Nos leemos
  • 0

#2 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 15 noviembre 2011 - 11:40

Ahora quiero heredar de TGMMarker (no hay problema) para crear una nueva clase. Esta nueva clase necesita que sus TCollectionItems tengan más propiedades que los TMarker que heredo de TGMMarke. ¿Cómo puedo hacer ésto?


Deriva una clase de TMarker y en el constructor de tu clase derivada de TGMMarker construyes tu nuevo TMarker. Ahora tienes el dilema de realizar un cast para el uso se tus nuevas propiedades o crear una propiedad que sobrecargue la vieja TMarker...

Saludos.
  • 0

#3 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 15 noviembre 2011 - 11:44

Hola,


Pues, yo también estoy espeso... y tengo mi cabeza en off pero... ¿Y donde está el problema?
Se deriva de esa clase item como toda la vida.




delphi
  1. TMinuevoItem = class(...)




Y como dice escafandra, sobreescribes la propiedad vectorial para que acepte esta nueva clase.


Es como volver a aplicar la colección.


Saludos,
  • 0

#4 andres1569

andres1569

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 431 mensajes

Escrito 15 noviembre 2011 - 12:06

Hola,

Cuando creas una colección le pasas en su constructor el tipo de clase de los elementos que va a manejar (el respectivo descediente de TCollectionItem). Aquí la pega puede que la tengas en que TGMMarker crea su colección pasándole como tipo base TMarker. Si creas una nueva clase descendiente de TGMMarker que quieres que maneje otro tipo base para su colección deberás ingeniarte un mecanismo "virtual" para que cada clase indique el tipo base (derivado de TCollectionItem) que quiere emplear. Puedes por ejemplo definir una función virtual de este estilo:




delphi
  1. (...)
  2. constructor TGMMarker.Create; 
  3. begin
  4.   ...
  5.   MiCollection := TMarkers.Create(GetBaseItemClass );
  6.   ...
  7. end;
  8.  
  9. function TGMMarker.GetBaseItemClass : TCollectionItemClass;  // este método es virtual
  10. begin
  11.   result := TMarker;
  12. end;
  13.  
  14. // y en la clase derivada ...
  15.  
  16. function TGMMarkerDesc.GetBaseItemClass : TCollectionItemClass;  // este método es override
  17. begin
  18.   result := TMarkerAmpliado;
  19. end;



Como ves, en el constructor de la colección le pasamos una referencia a la función virtual que devuelve el tipo de clase de elemento deseado.

Saludos
  • 0

#5 cadetill

cadetill

    Advanced Member

  • Moderadores
  • PipPipPip
  • 994 mensajes
  • LocationEspaña

Escrito 16 noviembre 2011 - 09:17

Buenas,

Efectivamente, Andrés, eso es lo que andaba buscando. El hecho de heredar de mi TCollectionItems no era el problema. El problema era decirle a mi TCollection que lo que iba a almacenar era de la clase nueva

Ahora, ya para terminar de ponerlo bonito.....

Tengo lo siguiente:

Por un lado TGMMarker => TMarkers (TCollection) => TMarker (TCollectionItem)
Y por otro TGMRoute => TMarkers => TRoute (extiende la clase TMarker)

En TGMMarker tengo



delphi
  1.   published
  2.     property MarkerList: TMarkers read FMarkerList write FMarkerList;



Por lo que si yo accedo por código a los TRoute de TGMRoute me devuelve un TMarker, obligándome a hacer una conversión de tipos para acceder a las propiedades de TRoute



delphi
  1. TRoute(GMRoute1.MarkerList[0]).StopOver := False; // por ejemplo



Está claro que puedo deribar de TMarkers para crear una clase TRoutes (por ponerle un nombre) y que mediante la técnica mostrada por Andrés, el constructor de TGMMarker puede crear una colección o otra



delphi
  1. constructor TGMMarker.Create(aOwner: TComponent);
  2. begin
  3.   inherited;
  4.  
  5.   // GetCollectionClass es una función virtual que te devuelve un TMarkersClass
  6.   FMarkerList := GetCollectionClass.Create(Self, GetCollectionItemClass);
  7. end;



Pero como MarkerList es de tipo TMarkers, siempre me devuelve un TMarker. Pregunta..... ¿Hay alguna manera para devolver un TRoute en lugar de un TMarker para así evitar tener que hacer la conversión de tipos?

Gracias

Nos leemos


  • 0

#6 andres1569

andres1569

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 431 mensajes

Escrito 16 noviembre 2011 - 11:47

Hola Cadetill,

Pero como MarkerList es de tipo TMarkers, siempre me devuelve un TMarker. Pregunta..... ¿Hay alguna manera para devolver un TRoute en lugar de un TMarker para así evitar tener que hacer la conversión de tipos?



Esa conversión de tipos que quieres ahorrar al usuario de los componentes la puedes delegar a la clase que alberga la colección, creando una propiedad matricial que devuelva el tipo de Item deseado para cada caso. Más o menos como el ejemplo:



delphi
  1. type
  2.  
  3. TGMMarker = class(TComponent)
  4. private
  5.   function GetMarker(Index: Integer) : TMarker;
  6. public
  7.   property Markers[Index: Integer] : TMarker read GetMarker;
  8. end;
  9.  
  10. // en la clase derivada
  11.  
  12. TGMRoute = class(TGMMarker)
  13. private
  14.   function GetRoute(Index: Integer) : TRoute;
  15. public
  16.   property Routes[Index: Integer] : TRoute read GetRoute;  // Opción A
  17.  
  18. // o bien
  19.   property Markers[Index: Integer] : TRoute read GetRoute;  // Opción B
  20.  
  21. end;
  22.  
  23. // (...)
  24.  
  25. implementation
  26.  
  27. function TGMMarker.GetMarker(Index: Integer) : TMarker;
  28. begin
  29.   result := TMarket(FMarkerList[Index]);
  30. end;
  31.  
  32. // (...)
  33.  
  34. function TGMRoute.GetRoute(Index: Integer) : TRoute;
  35. begin
  36.   result := TRoute(MarkerList[Index]);
  37. end;




En el ejemplo, en la segunda clase TGMRoute, podemos (opción A) crear una propiedad alternativa Routes que no anularía a la heredada Markers, de modo que al usar Routes devolvería el tipo que deseas, y se seguiría pudiendo usar Markers desde un objeto TGMRoute para acceder a los mismos elementos devueltos como TMarker.


O bien podemos (opción B) crear una propiedad Markers llamada igual que la del componente ancestro, pero que devuelve el tipo deseado (TRoute). Al redefinir una propiedad con el mismo nombre que el de la clase ancestra, la nueva propiedad tiene prioridad cuando se la llama desde un componente del tipo TGMRoute. Pero la pega de esta segunda opción es que perdemos polimorfismo ya que si quisiéramos acceder indistintamente a elementos de ambos tipos de componentes (TGMMarker y TGMRoute) llamando a la propiedad matricial Markers, Delphi accedería a los elementos tal como están definidos en la clase ancestra, es decir que devolvería elementos TMarker para todos los casos (eso es la teoría, no lo he probado para decirlo con total seguridad).


Pero como ves, la técnica consiste no en acceder a los elementos desde sus colecciones sino desde la clase (componente en este caso) que las alberga mediante esa propiedad de tipo matricial. Tampoco es mala idea para estos propósitos habilitar una propiedad Count dentro del componente TGMMarker que devuelva el número de elementos de la colección, de modo que para el accseso a los elementos ni siquiera hiciera falta referenciar la colección.


Saludos

  • 0

#7 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 16 noviembre 2011 - 10:21

Tengo entendido que cuando se está sobreescribiendo la propiedad vectorial debe añadirse la cláusula default; de otro modo seguirá tomando a la de su clase base.  ;)
Por otro lado, ¿no sería más adecuado hacer:




delphi
  1. result := MarketList[Index] as TRoute;




que




delphi
  1. result := TRoute(MarketList[Index]);




Aunque yo acostumbro hacer esto, que creería que es más seguro:




delphi
  1. result := inherited Markers[Index] as TRoute;




En los fuentes y ejemplos que he visto siguen esa forma.


Saludos,
  • 0

#8 cadetill

cadetill

    Advanced Member

  • Moderadores
  • PipPipPip
  • 994 mensajes
  • LocationEspaña

Escrito 17 noviembre 2011 - 02:22

Buenas

Entiendo por donde vas Andrés. Quieres que en lugar de usar la propiedad vectorial de TCollection suba esa acción a su owner. No me parece mala idea :)

Referente a lo que comenta delphius del default, no se si será o no obligatorio, pero es buena idea, así también te evitas de tener que referenciar la propiedad y puedes escribir cosas como



delphi
  1. GMRoute[i].MiPropiedad := XXXX;



Ahora, lo que ya no se es lo que comenta a continuación :D ¿Puedes explicarme qué diferencia hay en los 2 casos? El inherited creo que es necesario para que use el Get de TMarkets por si se hace algo ahí, no?

Gracias, estoy aprendiendo mucho con estos componentes :D

Nos leemos

  • 0

#9 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 17 noviembre 2011 - 11:43

Hola,
Recuerda que dije "creería", aunque tengo un poquitín de duda.


El punto es que se está haciendo uso de una sobresescritura de una propiedad. Y en realidad, lo único que se consigue con dicha sobreescritura es alterar el tipo de dato. En términos concretos quien en verdad hace todo el trabajo y tiene almacenado los objetos para ser utilizados por esta propiedad es la clase base, los demás simplemente hacen casting.


Debería suponerse que fueran equivalente ambas formas, tanto con como sin el inherited. Pero por precaución, yo obligo explícitamente a que pase por la propiedad vectorial de la clase base. No porque se requiera hacer algo más como comentas, sino porque es la base quien en realidad trabaja.


También hago uso de as para garantizar el cast. Con la otra forma se hace un cast implícito y técnicamente sólo es una conversión "al vuelo".


Y como dije... además así es como lo vi en las fuentes  :D  :p


Saludos,
  • 0

#10 andres1569

andres1569

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 431 mensajes

Escrito 17 noviembre 2011 - 12:31

Hola,

me parece buena idea lo de usar Default, pero no es obligatorio para sobrescribir una propiedad con el mismo nombre, sino que se usa únicamente en propiedades vectoriales, y sólo en una de ellas por cada clase, para definir dicha propiedad como la "propiedad vectorial por defecto", ahorrando algo de código al acceder a ella, como muestra el ejemplo que ha puesto Cadetill.

En cuanto a lo demás que comentas, Delphius, repecto a usar inherited al igual que tú no veo realmente la diferencia ni que una forma sea más eficiente que otra. Sobre el uso del operador as, no lo estimo necesario cuando sabemos que el objeto es de un tipo, algo que sucede en este caso.
Saludos


Saludos
  • 0

#11 cadetill

cadetill

    Advanced Member

  • Moderadores
  • PipPipPip
  • 994 mensajes
  • LocationEspaña

Escrito 18 noviembre 2011 - 01:38

Buenas,

Una pregunta, Andrés, ¿por algún motivo haces la propiedad vectorial ReadOnly? Es que no me había fijado en ese detalle hasta ahora que le he podido meter mano al tema jejeje

Gracais

Nos leemos

  • 0

#12 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 18 noviembre 2011 - 06:37

Hola,

me parece buena idea lo de usar Default, pero no es obligatorio para sobrescribir una propiedad con el mismo nombre, sino que se usa únicamente en propiedades vectoriales, y sólo en una de ellas por cada clase, para definir dicha propiedad como la "propiedad vectorial por defecto", ahorrando algo de código al acceder a ella, como muestra el ejemplo que ha puesto Cadetill.

Se bien que default sólo es válido para las propiedades vectoriales, y que para establecer una de ellas (si hay más de una) como por defecto (lo que permite hacer miClase[].Algo). Pero recuerdo haber leído en un artículo que cuando uno está sobrescribiendo la propiedad es aconsejable utilizar default para que asuma a ésta propiedad, que está pensada para trabajar con el tipo sobrescrito y no la de la clase base.


En cuanto a lo demás que comentas, Delphius, repecto a usar inherited al igual que tú no veo realmente la diferencia ni que una forma sea más eficiente que otra. Sobre el uso del operador as, no lo estimo necesario cuando sabemos que el objeto es de un tipo, algo que sucede en este caso.
Saludos

Yo no dije que sea más eficiente una que otra. No noto diferencia en una u otra, ya que al menos en teoría son equivalente. Nomás lo hago de esa forma por dos cosas: 1) Seguridad de que no meteré la pata; o al menos debería serlo y 2) En muchas referencias y en la misma VCL se hace de esa forma (que por algo será)


Saludos,
  • 0

#13 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 18 noviembre 2011 - 06:44

Buenas,

Una pregunta, Andrés, ¿por algún motivo haces la propiedad vectorial ReadOnly? Es que no me había fijado en ese detalle hasta ahora que le he podido meter mano al tema jejeje

Gracais

Nos leemos

Hola.

Yo creo que es a modo de ejemplo nomás.
Pero seguro que habrá casos en los que es deseable que sea read-only únicamente, y otros en los que se necesita read/write. Por ejemplo, en mi implementación del patrón Observer, la clase TSubject posee una propiedad vectorial Observers[] de sólo lectura ya que técnicamente el Subject no tiene responsabilidad para estar alterando a como guste a los observadores. Más bien son estos últimos quienes dan su consentimiento e interés de Suscribirse/Desuscribirse.
Un buen motivo para que sea read-only es para evitar "pisar" algún item ya definido y que se ha o está utilizando.

Saludos,

  • 0

#14 andres1569

andres1569

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 431 mensajes

Escrito 18 noviembre 2011 - 12:00

Hola:

Buenas,

Una pregunta, Andrés, ¿por algún motivo haces la propiedad vectorial ReadOnly? Es que no me había fijado en ese detalle hasta ahora que le he podido meter mano al tema jejeje


Pues sí lo hice a propósito, ya que no tiene mucho sentido que sea de escritura. Esta propiedad está pensada para ser pública, no publicada, por lo tanto no interviene a la hora de guardar los datos en el stream del formulario (1ª razón), y además devuelve objetos, por lo que con acceder a cada uno de ellos ya podemos modificar sus propiedades (2ª razón, para esto da igual que se acceda a ellos desde una propiedad Read-Only como es Markers). Además, el hecho de que ya exista la propiedad publicada MarkerList, heredera de TCollection, y que la tengas definida como lectura/escritura ya garantiza que Delphi pueda leerla y recuperarla del .DFM y que puedas manejar la lista desde el editor de colecciones que estás empleando.

Digamos que la gestión de adición y eliminación de elementos de la lista se hace a través de los métodos de la clase TCollection correspondiente, y la propiedad Markers sólo sirve para recorrerlos.


Hola,

me parece buena idea lo de usar Default, pero no es obligatorio para sobrescribir una propiedad con el mismo nombre, sino que se usa únicamente en propiedades vectoriales, y sólo en una de ellas por cada clase, para definir dicha propiedad como la "propiedad vectorial por defecto", ahorrando algo de código al acceder a ella, como muestra el ejemplo que ha puesto Cadetill.

Se bien que default sólo es válido para las propiedades vectoriales, y que para establecer una de ellas (si hay más de una) como por defecto (lo que permite hacer miClase[].Algo). Pero recuerdo haber leído en un artículo que cuando uno está sobrescribiendo la propiedad es aconsejable utilizar default para que asuma a ésta propiedad, que está pensada para trabajar con el tipo sobrescrito y no la de la clase base.


Buen apunte, supongo que te refieres a que de esa forma se sustituye, en nuestro ejemplo, la propiedad Routers por Markers como propiedad vectorial por defecto en la clase TGMRoute.



En cuanto a lo demás que comentas, Delphius, repecto a usar inherited al igual que tú no veo realmente la diferencia ni que una forma sea más eficiente que otra. Sobre el uso del operador as, no lo estimo necesario cuando sabemos que el objeto es de un tipo, algo que sucede en este caso.
Saludos

Yo no dije que sea más eficiente una que otra. No noto diferencia en una u otra, ya que al menos en teoría son equivalente. Nomás lo hago de esa forma por dos cosas: 1) Seguridad de que no meteré la pata; o al menos debería serlo y 2) En muchas referencias y en la misma VCL se hace de esa forma (que por algo será)


Saludos,


Ok


Saludos
  • 0

#15 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.448 mensajes
  • LocationMéxico

Escrito 18 noviembre 2011 - 12:12

[off-topic]

Caramba!!!

Me lo pueden explicar con manzanitas :s

[/off-topic]

Salud OS
  • 0

#16 andres1569

andres1569

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 431 mensajes

Escrito 18 noviembre 2011 - 12:15

[off-topic]

Caramba!!!

Me lo pueden explicar con manzanitas

[/off-topic]

Pues hasta Delphi XE2, todo esto funciona bajo Windows, no con Apple  :p :p
  • 0

#17 egostar

egostar

    missing my father, I love my mother.

  • Administrador
  • 14.448 mensajes
  • LocationMéxico

Escrito 18 noviembre 2011 - 12:21


[off-topic]

Caramba!!!

Me lo pueden explicar con manzanitas

[/off-topic]

Pues hasta Delphi XE2, todo esto funciona bajo Windows, no con Apple :p :p


Ah vaya ya decía yo, estaba con la marca equivocada  :D :D :D

La verdad es que fuera de broma me quedo con los ojos cuadrados y la boca abierta, son cosas que rebasan mi nivel de abstracción.  :cry: aunque debo dejar de ser vago y ponerme a estudiar :)

Salud OS
  • 0

#18 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 18 noviembre 2011 - 10:33

Hola,


Eliseo es muy fácil. Haz de cuenta que en ver las clases que se han propuesto tienes cualquier clase de colección. Pongamos por ejemplo la de TColumns y TColumn. Como sabemos, podemos acceder a la colección de TColumns gracias a la propiedad vectorial [] que hereda desde TColletionItem. Esta propiedad devuelve un objeto de clase TColumn.
Ahora nuestro querido compañero necesita ampliar este concepto y tener sus propios TMiColumns y TMiColumn.
Básicamente el "trabajo sucio" ya está hecho en las clase base, en realidad el mayor fuerte está ahora en sobreescribir esta propiedad vectorial para que trabaje con la nueva clase "item" TMiColumn.


De allí es que surje este debate.
Si quieres repasar este tema en la Cara Oculta de D4 está explicado sobre las propiedades vectoriales en el capítulo dedicado a Propiedades.


Y ahora que lo pienso... ¿alguno de ustedes se ha encontrado con propiedades indexadas? Es una característica bastante interesante pero no muy vista.


Saludos,
  • 0

#19 cadetill

cadetill

    Advanced Member

  • Moderadores
  • PipPipPip
  • 994 mensajes
  • LocationEspaña

Escrito 19 noviembre 2011 - 08:47

Exacto Delphius, un TDBGrid y sus columnas es un buen ejemplo.

Eliseo, imagina que quiero hacerme mi propio TMyDBGrid para añadirle funcionalidades



delphi
  1. TMyDBGrid = class(TDBGrid)
  2. .....



También sabes que el TDBGrid tiene columnas (TColumn => TCollectionItem) a las que accedes a través de su propiedad Columns (TDBGridColumns => TCollection) gracias a su propiedad vectorial Items que, al estar declarada como default, no hace falta que referenciemos esa propiedad para acceder a una columna



delphi
  1. DBGrid1.Columns.Items[0].Width := 10;
  2.  
  3. // es lo mismo que
  4.  
  5. DBGrid1.Columns[0].Width := 10;



Bien, ahora imagina que en nuestro nuevo grid, no sólo queremos añadir nuevas funcionalidades al TDBGrid, sino también a las columnas. Pues toda la explicación de cómo hacerlo la tienes en este hilo :)

Espero haber aclarado algo tus dudas (a mi así lo han logrado los compañeros)

Nos leemos

  • 0

#20 andres1569

andres1569

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 431 mensajes

Escrito 21 noviembre 2011 - 01:16

Hola, una pequeña aclaración sobre este asunto:

Normalmente, cuando se deriva una clase de TCollectionItem, se tiende a derivar otra clase de TCollection, como el ejemplo de TColumn y TDBGridColumns que habéis puesto. En estos casos, la clase que deriva de TCollection suele redefinir la propiedad llamada Items para que apunte a los mismos elementos pero "casteados" al tipo específico que usa dicha colección, como lo que está programando Cadetill. Pero en este caso concreto, puesto que se crean dos descendientes de TCollectionItem (TMarker y TRoute) y tan sólo una clase de TCollection (TMarkers), propuse que la propiedad vectorial la implementara el componente propietario de la colección  (TGMMarker o TGMRoute en nuestro caso), para no tener que crear descendientes innecesarios de TCollection sólo para ese fin.

Saludos
  • 0




IP.Board spam blocked by CleanTalk.