¿Un TCollectionItem puede ser objeto de otra clase?
#1
Escrito 29 febrero 2012 - 04:44
A ver si me se explicar jejeje
Por un lado tengo una clase que "tiene" una TCollection
TGMInfoWindow -> TInfoWindows (TCollection) -> TInfoWindow (TCollectionItem)
Y por otro lado tengo otra clase (TMarker) que necesita tener un objeto TInfoWindow. Se que es posible poner a la clase TMarker un objeto de tipo TInfoWindow, pero la duda es, ¿Es correcto hacer esto? Si no lo fuera... ¿Cómo lo haríais?
Gracias
Nos leemos
#2
Escrito 01 marzo 2012 - 01:09
Dado que no me gusta la idea de usar en una clase que no es una TCollection, un TCollectionItem, he pensado cómo poder hacerlo (a no ser que alguien con más conocimientos que yo me diga que sí es buena idea)
La idea sería hacer una clase que derive de TPersistent con lo que quiero que haga mi TInfoWindow , por ejemplo (y reduciendo la clase a la mínima expresión)
TBaseInfoWindow = class(TPersistent) private FContent: TStrings; published property Content: TStrings read FContent write FContent; end;
Con esto, ya podría crear una propiedad en la clase TMarker sin problemas.
Luego, y dado que Delphi no permite la herencia múltiple, en TInfoWindow (TCollectionItem) la idea sería hacer lo mismo que en TMarker, pero en este caso creo que quedaría "feo" en el Inspector de Objetos (¿¿¿1 sola propiedad que sea un objeto??? uf uf uf). Para mirar de salvar este "mal aspecto" y dado que TBaseInfoWindow no va a tener mucha cosa, puedo poner una propiedad/variable privada de tipo TBaseInfoWindow y mirar de repetir las propiedades de éste en la parte published y programar el read y write para que acceda a las propiedades del objeto, algo así
TInfoWindow = class(TCollectionItem) private FBaseInfoWindow: TBaseInfoWindow; // objeto privado de la clase que quiero heredar function GetContent: TStrings; procedure SetContent(const Value: TStrings); published property Content: TStrings read GetContent write SetContent; // propiedad de la clase que quiero heredar end; ....... function TInfoWindow.GetContent: TStrings; begin Result := FBaseInfoWindow.Content; end; procedure TInfoWindow.SetContent(const Value: TStrings); begin FBaseInfoWindow.Content.Assign(Value); end;
¿Qué os parece?
Gracias
Nos leemos
#3
Escrito 01 marzo 2012 - 07:46
Si me puedes dar más pistas tal vez pueda apreciar cual es la mejor salida al tema.
Saludos,
#4
Escrito 01 marzo 2012 - 09:56
A ver, te comento.
Como sabrás, estoy con el tema de los componentes para encapsular la API de Google Maps. Pues bien, de momento tengo definida esta gerarquía de clases (unas imágenes valen más que 1000 palabras dicen)
Bien, yo quiero que mis TMarker tengan un objeto de tipo TInfoWindow (que es un TCollectionItem). Como dije en el primer mensaje, por poder, Delphi deja hacerlo, y preguntaba que, aunque Delphi deje hacerlo, si era correcto. Si no lo fuera, en el segundo mensaje propongo una solución, que sería hacer lo siguiente
Es decir, crear una clase que haga lo que tendría que hacer el original TInfoWindow (TBaseInfoWindow) y ponerla como propiedad publicada tanto en TMarker como en el TInfoWindow.
Si pensamos en el Inspector de Objetos y cómo se vería esta propiedad en él (al ser un objeto saldía el nombre de la propiedad con la opción de desplegar para ver sus propiedades), veo que en el TMarker puede quedar bien (hay varias propiedades y ésta sería una más). Pero en el TInfoWindow puede quedar como un pegote (sólo tendría esta propiedad que necesitaría ser "desplegada" para acceder a sus características). De ahí la "paranoia" mia del segundo post.
Espero haberme explicado algo mejor
Gracias
Nos leemos
#5
Escrito 01 marzo 2012 - 12:44
Me ha confundido un poco el diagrama, no lograba entenderlo hasta que le noté el error: están mal formada las relaciones, las puntas de flecha van al revés.
Creo entender tu duda: resulta ser que tanto TMaker y TInfoWindow (siendo que ambos heredan de TColletionItem) necesitarían de una propiedad en común denominada Content que les ofrecería a ambas clases algo que les he de utilidad. Y estás en la duda de si es conveniente disponer de una clase base de la cual ambos hereden e implemente los mecanismos set y get seguros y adecuados a cada uno ó si directamente conviene mantener esta propiedad en la clase TInfoWindow y directamente vincular a TMaker con TInfoWindow mediante un atributo para que éste de forma indirecta haga uso de Content.
Viendo así las cosas me inclino por la opción de disponer de una clase base con dicha propiedad y que TMaker y TInfoWindow la publiquen, y si es necesario que implementen y/o sobreescriban los métodos set y get.
Si te fijas, la VCL está llena de clases abstractas con muchas propiedades y métodos protegidos que luego sus descendientes le cambian la visibilidad a público y/o publicado. Las clases TCustomXXX son un buen ejemplo.
Ahora bien, esa clase base yo la haría heredar de TColletionItem y pondría en ella lo necesario y común que hiciera a un TColletionItem especializado tanto para el contexto de la dupla {TMakers, TMaker} como a la {TInfoWindows, TInfoWindow}... Quizá no sea apropiadado el nombre TBaseInfoWindow, quizá un TCustomItemContext El nombre da a entender que se trata de un TColletionItem cualquiera (abstracto) que posee la propiedad Context... cosas de semántica
Es decir algo como:
+--------+ +-------------+ | TMaker | | TInfoWindow | +--------+ +-------------+ \ / \ / V V +--------------------+ | TCustomItemContext | +--------------------+ | | V +----------------+ | TColletionItem | +----------------+
El sentido de la fecha significa "hereda de".
Ahora bien, no sería de extrañarse, ahora viendo este TCustom si nos aparece la duda de si por casualidad no apareciera una clase TCustomContext que heredara de TColletion y por tanto ahora TMakers y TInfoWindows podrían heredar de ésta.
Permíteme aclararte que es legal hacer que una colección, (o un ítem de una colección... incluso; aunque para este caso yo me lo preguntaría varias veces si es lo más apropiado) tenga como propiedad (y por tanto un vínculo o relación) hacia otra colección; más no así hacia un ítem de esa segunda colección. ¿Porqué? Porque se supone que el acceso al ítem es por medio de su "administrador" o "dueño".
Si en verdad el contexto {TMakers, TMaker} necesita colaborar con el de {TInfoWindows, TInfoWindow} por algo más hallá de una simple propiedad que uno tuviera y el otro no entonces yo propondría que sea el TMaker quien tenga visibilidad de atributo hacia TInfoWindows de modo que si un ítem de TMakers (TMaker) debe acceder a algo de TInfoWindow/TInfoWindows lo haga por medio de su dueño. De este modo TMakers delega parte de su trabajo a TInfoWindows. Espero que se me entienda.
Las necesidades y otras consideraciones de diseño a tener en cuenta dirán que es lo más apropiado. Pero así, como me lo ilustras me inclino por el TCustomItemContext
Saludos,
#6
Escrito 01 marzo 2012 - 04:43
Respecto a las flechas del diagrama, pues tienes razón, es lo que tiene el hacerlo en 5min, que no te ficjas en las cosas jejejeje Ya lo he corregido, creo que ahora está bien
Respecto a la duda, creo que no me has entendido o yo no te he entendido a ti, jejeje
No es que TMaker y TInfoWindow necesiten una propiedad en común (que son varias, eso era un mero ejemplo), sino que esas propiedades son la clase TInfoWindow
TInfoWindow = class(TCollectionItem) private ..... public ..... published property Content: TStrings read FContent write FContent; property DisableAutoPan: Boolean read FDisableAutoPan write FDisableAutoPan; property MaxWidth: Integer read FMaxWidth write FMaxWidth; // 0 = no MaxWidth property PixelOffset: TSize read FPixelOffset write FPixelOffset; // (0,0) = no pixelOffset end;
La clase TInfoWindow representa esos balloon que aparecen con información al hacer clic encima de un marcador (por ejemplo) en los mapas de Google. Estas ventanas con información pueden estar ligadas al mapa directamente (de ahí que TGMInfoWindow herede de TGMLinkedComponent => componente de acceso al mapa) o bien a un objeto del mapa (como el TMarker).
Dado que un TMarker puede contener una ventana de información (TInfoWindow), pero no puede contener (o no debería contener) un objeto de tipo TCollectionItem, necesito alguna clase que me represente esa ventana pero que herede de TPersistent. Por eso pensé en la creación de la clase TBaseInfoWindow, que realmente es un TInfoWindow, y que puede ser una propiedad de los objetos que van al mapa (como TMarker).
Ahora bien, si creo esa clase, ¿cómo quedaría TInfoWindow? Pues algo así
TInfoWindow = class(TCollectionItem) private ..... public ..... published property BaseInfoWindow: TBaseInfoWindowread FBaseInfoWindow write FBaseInfoWindow; end;
Claro, eso queda fatal en el Inspector de Objetos, por eso había pensado lo explicado en el segundo mensaje.
A ver si ahora nos entendemos jejejeje
Gracias de nuevo
Nos leemos
#7
Escrito 01 marzo 2012 - 05:05
Evidentemente entendí cualquier otra cosa
Ummm, estoy reordenando las cosas en mi cabeza y todavía no logro coordinar algo que me cuadre.
Algo me dice que tal vez, y sólo tal vez, las cosas no pasen ni por lo uno ni lo otro; sino de un poco de rediseño y alterará en parte tu estructura. Por el momento me inclino hacia un poco de clase de asociación y a una aplicación de agregación compartida; aunque sin encontrar una forma de unir los cabos.
Necesito de mucho combustible en mi estómago y de un reseteo para lograr unir conceptos y tener algo más fresco. No quisiera aventarme otra respuesta que no te conduzca a la nada.
Si no tienes prisa...
Saludos,
#8
Escrito 01 marzo 2012 - 05:17
Quizá te parezca que no viene al caso pero me ayudará a comprender tu modelo y en cómo están distribuidas las funciones y responsabilidades de cada clase.
Si, el diagrama ahora está bien. No hacía falta corregirlo; aunque se agradece. Mi cabeza ni bien comprendió el sentido hacía la corrección.
Saludos,
#9
Escrito 02 marzo 2012 - 01:41
TGMBase: clase base de todos los componentes de acceso a la API de Google Maps. Contiene las propiedades genéricas a todos los componentes que son 3, el "acerca de", el idioma con el que se quiere mostrar las excepciones lanzadas por el componente en caso de error y una propiedad string donde pongo la url a la API de Google Maps de la parte que encapsula el componente que herede de TGMBase para una mayor y fácil referencia.
TGMMap: componente que diseña el mapa plano, sin objetos, sólo con lo necesario para visualizar una zona y desplazarte por la misma (zoom, scroll, ......). También es el que gestiona la comunicación con el mapa a nivel de eventos y/o cambio de cualquier tipo de propiedad/objeto del mapa. A su vez, mantiene un TList con todo objeto que se linca a él y que representa algún objeto en el mapa (marcadores, ventanas de información, lineas, polilíneas, rectángulos,......)
TGMLinkedComponent: clase base para cualquier componente que se quiera lincar a TGMMap. Controla el lincado y deslincado de un mapa, el borrado del componente mapa lincado o el propio borrado y tiene algunos métodos para comunicarse con TGMMap para el envío/recepción de información hacia/del mapa (recordemos que sólo TGMMap se comunica con el mapa).
TGMMarker: componente que mantiene una relación de marcadores (clase TMarker) mediante una TCollection (TMarkers)
TGMInfoWindow: componente que mantiene una relación de ventanas de información o balloons (clase TInfoWindow) mediante una TCollection (TInfoWindows)
Las demás ya son de Delphi jejejeje
Gracias
Nos leemos
#10
Escrito 02 marzo 2012 - 11:51
Creo que analizando las cosas desde una perspectiva más "global" me será más fácil entender el problema, y el cómo has estado llevando tu diseño.
Espero que no te moleste si me demoro en comentarte algo.
Saludos,
#11
Escrito 02 marzo 2012 - 01:34
Nos leemos
#12
Escrito 08 marzo 2012 - 04:50
Disculpa Xavier pero no he podido darme tiempo para ver esto. Estoy muy apretado de tiempo... con mucha suerte el domingo pueda tener más tiempo libre y poder estudiar esto con detalle. De lo que estuve analizando y pensando se podría hacer unos cuantos cambios a esa jerarquía de clases y se podría aprovechar de un poco de polimorfismo y algunos conceptos prestados de patrones que harían más liviano tu diseño y quizá estabilizar algunos puntitos que quizá estén medios flojos.
Se que no es demasiado bueno retocar una jerarquía de clases pero a como lo entiendo, me parece mejor.
Espero que no te moleste que te haya dejado descolgado; tenme paciencia.
Si avanzaste en algo, o estuviste aplicando algunos cambios no estaría mal que nos los comentes.
Saludos,
#13
Escrito 09 marzo 2012 - 02:28
Tranquilo, sin prisas :-)
A nivel de los componentes, realmente sí he retocado algo la gerarquía de clases, no mucho, pero algo. Te adjunto un archivo con los diagramas actuales (formato JPG). También verás un archivo .uml que es el diseño en sí (se abre con un programa opensource (staruml) la web del cual encontrarás en el readme.txt) por si quieres ver/retocar el diseño completo
A nivel de gerarquía, decir que las colecciones que tendrán las clases que se podrán unir al mapa (TMarkers, TInfoWindows, TPolylines, TPolygons,....) no heredan directamente de TCollection, sino de una clase intermedia. Lo mismo pasará con los TCollectionItems. Esto lo he hecho para pasar gran parte del trabajo repetitivo a estas nuevas clases (TLinkedComponents y TLinkedComponent).
Es en la nueva clase TLinkedComponent (TCollectionItem) donde he puesto una propiedad protegida de tipo TBaseInfoWindow. La clase TBaseInfoWindows representa una ventana de información (un balloon). Las clases que deriven de TLinkedComponent lo único que tendrán que hacer es publicar esta propiedad.
Si quieres también podría colgar los fuentes, los componentes TGMMap y TGMMarker son funcionales y, quizás viendo el código, se entienda algo mejor.
Gracias de nuevo
Nos leemos
#14
Escrito 09 marzo 2012 - 12:02
Tomo nota de tus cambios y ya he descargado el archivo adjunto. Lo veré en cuanto pueda.
A StarUML ya lo conozco... yo también lo utilizo.
Si los fuentes requieren de alguna biblioteca de terceros no serviría de mucho que lo descargue; aunque también podría ayudar a entenderle la mano; te comento que utilizo D6.
Si tu crees conveniente y apropiado colocar los fuentes, por mi está bien; ahora si no están liberados todavía (o no lo has considerado todavía, y/o si son pagos) creería que lo más apropiado sería que no los envíes.
Saludos,
#15
Escrito 10 marzo 2012 - 05:21
Los componentes van a ser opensource, bajo licencia GPL o MPL, eso aun no está decidido, así que no es problema en colgar los fuentes. No uso ni componentes ni librerías de terceros (al menos por ahora, más adelante quizás use una librería para la lectura de archivos JSON)
Imagino que te compilarán sin muchos problemas en D6. Yo no lo tengo aquí instalado, pero sí en el trabajo. El lunes podría probar si compila en esa versión. De momento están compilados en D2010. Mi idea era compilarlos en D6, D7, D2007, D2010 y DXE2, que son los que tengo en el trabajo instalados, pero a la que estuviera la cosa un poco más madura.
Lo que sí no garantizo es que te salte algún error no controlado, que aun no he podido hacerle un fuerte juego de pruebas xD
Archivos adjuntos
#16
Escrito 23 marzo 2012 - 12:21
Mil disculpas Xavier por tenerte abandonado. No pensé que me tuviera consumiendo tanto tiempo mi proyecto y algunos pequeños dolores de cabeza y pérdidas de neuronas
Sinceramente no se en que momento pueda sentarme a hecharle ojo. Si bien tu me dices que no tienes prisa, siento que yo te quedo en falta
Si lograse avanzar y llegar al hito programado más cercano en mi proyecto antes de tiempo si podría darme el relax y concentrarme en esto. El poco relax que me he estado permitiendo son unos minutos al día de facebook, como para "apagar" y desenchufarme, tanto de proyecto como de Delphi.
Ojalá estés pudiendo avanzar, y por lo que estuve viendo en los foros estos minutos, ya has estado liberando tu biblioteca. Considero a estas alturas, que si ya te ha estado funcionando bien según tu diseño es mejor no tocarlo; y en todo caso dejar estos tipos de cambios como para una versión posterior.
Mis más sinceras disculpas por no poder asistirte en tiempo y forma. ¡A ver si alguien más se anima a meter mano al hilo!
Saludos,
#17
Escrito 25 marzo 2012 - 01:32
Y si los cambios son buenos, da igual que se tenga que hacer un cambio de estructura en la jerarquía de clases, por eso no hay problema ;-)
Nos leemos