![]() |
![]() |
| Paypal | FTP | CCD | Buscar | Trucos | Trabajo | Foros |
|
|||||||
| Registrarse | FAQ | Miembros | Calendario | Guía de estilo | Temas de Hoy |
![]() |
|
|
Herramientas | Buscar en Tema | Desplegado |
|
|
|
#1
|
||||
|
||||
|
Objetos únicos (Singleton) con herencia de TInterfacedObject
Hola,
Buenas a todos. Tengo una preguntita filosa, y ya me corté demasiado tratando de responderme. A ver si algún instruído me da alguna línea porque estoy completamente OFF. Advertencia: los temas discutidos a continuación pueden derivar en una enfermedad que puede resultar difícil de curar... patronitis. ¡Si... voy a hacer incapié a los no muy queridos patrones! por una buena cantidad de desarrolladores. Les empiezo a relatar el panorama. El macro objetivo que me planteo es disponer de una Fachada; pero no es una cualquiera. Por lo general las Fachadas se implementan para delegar sus funciones a un grupo de clases, y ofrecer una interfaz única o común de acceso a cierta parte. Vista desde afuera, la fachada actúa como si se comportase un subsistema y sirven para ocultar detalles que a la(s) clase(s) cliente(s) poco le deberían interesar. Como fachada, por lo generalmente son únicas y se las estila implementar como Singleton... es aquí mi problema. Pero necesito ofrecer más detalles para que vean el porqué. Mi Fachada tiene como naturaleza el comportamiento de un Sujeto/Observador; algo habitual y de esperarse de una fachada. Como Sujeto le permite enviar mensajes al exterior y comunicarse con clases "Oyentes" u "Observadores" de capas superiores. Como Observador puede enterarse de algunos comportamientos internos o de capas inferiores. Debido a naturaleza de mi problema, me resulta conveniente disponer no de una única Fachada sino de múltiples que no es más que un principio del "Divide y vencerás" consiguiendo así lo que en la jerga se llaman Fachada de Sesión, o Fachadas de Caso de Uso. En mi caso el diseño está dirigido por Caso de Uso. Pues eso... ya tengo mis Fachadas de Caso de Uso, que trabajan bien… interna y operativamente. Aparece ahora, como detalle menor una clase Coordinador que las controla. Esto dio origen a que aparezca una delgadita capa de Aplicación, y ahora estas Fachadas simplemente “ocultan” y delegan las verdaderas implementaciones a la capa Dominio, quien en realidad concentra el verdadero corazón de la lógica como bien sabemos. Hasta allí se entiende, y todo está de maravillas. Es aquí en donde puse mi fuerte a fin de ganar estabilización en el acceso al Dominio. Dentro del Dominio, existen también clases controladoras, que regulan y centralizan el acceso a clases que son aprovechadas desde diferentes lados. Es como una autopista en donde hay reciprocidades, vínculos que pueden ir y cambiar de lado. Lo básico: Implementar Sujeto/Observador Hay muchas maneras de llegar a este patrón, y cada una tiene sus pros y contras. Debido a que por característica de mi sistema los oyentes no tienen una interfaz común y cada uno tiene sus particularidades y en donde los casos de uso se pueden ir dando y ejecutando no necesariamente en un orden pre-establecido (bueno, inicialmente cuando no hay otra actividad y se está en su etapa más elemental si existe linealidad pero de allí en mas las cosas se van dando azarosamente) debía conseguir un diseño dinámico en donde cada fachada (Sujeto) pudiera interactuar con diferentes Observadores. Además estos "enlaces" deben ofrecer un buen equilibrio de acoplamiento y que no esté demasiado apegado a un grupo concreto y definido de clases (vamos… a una rama puntual dentro del árbol). Como aparecen muchos Sujetos/Observadores (y no únicamente en esta capa, sino también en Dominio, y en una capa Base que tiene un mini y ultrabásico framework de persistencia) aprovecho la idea de definir un Sujeto/Observador abstracto que me aporte el cuerpo básico y de allí poder hacer herencias y donde cada “dupla” de Sujeto/Observador amplíe el concepto y defina sus propios eventos en base a la naturaleza de sus intereses. Además esto está motivado porque ya disponía de una capa de Servicio Técnico que me da apoyo a muchas cosas para el Dominio y superiores. ¡Que mejor que agregar también este Observer abstracto reutilizable! Y que me podría llevar a otros lados. Así es que llego a tener una interfaz IAbstractObserver y IAbstractSubject. Además, de una clase TAbstractSubject que implementa esta última. IAbstractObserver define los métodos para suscribirse/desuscribirse y TAbstractSubject los implementa y añade lo necesario para operar con los observadores. Para mantener la lista de observadores se apoya en la clase TInterfaceList. Es así que llegué a este código:
Aquí es donde se extiende el concepto y adquiere forma ya cada Sujeto, y en lo particular a la capa de aplicación en las Fachadas. La idea es ahora que para cada contexto de Sujeto/Objeto se herede una interfaz desde IAbstractObserver y una clase desde TAbstractSubject, para ejemplo digamos… IObserverReal y TSubjectReal… o si prefieren para ilustrarlo mejor: IConcretesubject e IConcreteObserver. Ahora será responsabilidad de cada clase Sujeto/Observador definir su “contrato” y establecer que eventos van a notificar y dar respuestas. Por bajar a tierra, digamos que TSujetoVentas y IObservadorVentas trabajan con eventos que hacen a las ventas: EventoNuevaVenta, EventoVentaActualizada; mientras que TSujetoRecibo y IObservadorRecibo tienen lo suyo para los recibos, y así etc. ¿Se entiende? Lo maravilloso de que el sujeto espere una interfaz es que se desapega de cualquier clase, justo lo que busco. Una vez entendido en forma abstracta, vamos a lo concreto. Gracias al lindo poder de los eventos, damos forma a los contratos. A modo de ejemplo digamos que trabajaremos con esto:
Sender es el Sujeto que notifica, y value… la info que cambia o lo que interesa publicar. Es simple: quien y qué. Podría definirse contratos más complejos como:
La idea es demostrarle el principio y no llenar de eventos más complicados. Extiendo:
Creo que pueden darse una idea con sólo ver la definición de cómo se podrían implementar. Aquí la muestra para aclararles:
Este esquema es moderadamente flexible, armonioso y extensible. Quizá tiene la desventaja de que el sujeto debe necesariamente heredar desde TInterfacedObject y existen escenarios en donde quizá no es deseable heredar de esta clase. Después de todo hay otras maneras de llegar a patrón. Y seguramente se pueden aprovechar algunas ideas de esta singular propuesta y adaptarlas a otro esquema. Nomás presento un esquema que me parece interesante… y más en particular en lo que a hace a mis fachadas (que son mayoría; las otras clases Sujetos [que no fachadas] son una minoría y más que nada están pensadas para notificar a clases ajenas a sus “clases vecinas” sin generar más acoplamiento del necesario). Ahora presentado lo que tengo, voy al problema. Como mencioné antes, las fachadas suelen ser únicas… y aquí no es la excepción. Quisiera que éstas sean y actúen como Singleton. La idea más simple y evita problemas y no estar con demasiados patrones es directamente tener una variable a quien atacar desde las clases clientes e interesadas:
Como con todo patrón… hay diversas maneras de llegar a Roma. Un método yendo a lo purista pasa por aprovechar los métodos NewInstance y FreeInstance y sobrescribirlos, y forzar la creación de una única instancia y devolver ésta. Otros enfoques que pueden servir son sobrescribir y definir un propio constructor y destructor, también se puede dejar al público una función de tipo GetInstance() que encapsule el trabajo pesado… o una mezcla de todo esto. El asunto es que me gustaría que los observadores, y las potenciales clases clientes, tengan visibilidad de atributo de este singleton (después de todo, ¡ya están acopladas!). Esto motivado principalmente para mantener una comunicación dual Sujeto < = > Observador más directa, y no tener que estar atacando a una función GetInstance() o acceder a la variable suelta Instance y que en realidad no hay control alguno de que en otro lado no haga un Create. Habiendo comunicaciones desde y hacia varios lados donde intervienen varias fachadas y otros interesados en comunicarse, busco algo que me aporte más seguridad pero que no traiga consecuencias mayores a un rediseño en mis fachadas y sujetos singleton. Aclaro que no todos los Sujetos son Singleton por lo que no me es viable la posibilidad de hacer Singleton a TAbstractSubject. De allí que en parte, cada Sujeto se defina o no como Singleton… La máxima posibilidad que se podría plantear es hacer una nueva herencia, desde TAbstractSubject, y tener un TAbstractSingletonSubject pero ahora resultará que sólo se permitirá la creación de una única instancia de entre alguna de los sujetos singleton concretos. Pero no quiero añadir más clases, quisiera evitar eso. Intento analizarlo por el lado de sobrescribir NewInstance/FreeInstance que dentro de todo ofrece quizá la vía más segura de trabajar. Pero me marea y desconcierta el hecho de que no se trata únicamente de clases simples, sino que en fondo están las interfaces y esto toca de lleno el problema del conteo de referencia… en donde la liberación final tiene lugar cuando se llega a cuenta cero. Y sabiendo además que ya TIbterfacedObject redefine su NewInstance, y ahora que yo vuelva a tocarlo… ¡es para dudar!. He estado siguiendo las siguientes fuentes, tratando de encontrar la iluminación divina y de comprender en cómo llegar a un Singleton cuando se está operando con interfaces: http://edn.embarcadero.com/article/22576 www.delphi3000.com/article.asp?ID=1736 http://en.wikipedia.org/wiki/Singleton_pattern http://es.wikipedia.org/wiki/Singleton http://www.castle-cadenza.demon.co.uk/single.htm http://sourcemaking.com/design_patterns/singleton También se ha discutido esto en el foro incluso, y yo incluso participé: http://www.clubdelphi.com/foros/showthread.php?t=62510 Pero aún así, y sabiendo que esas puestas están basadas más que nada en objetos “puros” no me convence la forma de llevar a la práctica cuando se trata de interfaces. No logro unir los conceptos entre los artículos y esto me confunde. ![]() Habiendo presentado el contexto, y en base al código expuesto y el manejo de estas interfaces locas ¿Qué me aconsejan? Disculpen semejante rollo, pero debía exponer lo que hice, y porqué para que me entiendan lo mejor posible. Espero no aburrirlos. Saludos, |
|
#2
|
|||
|
|||
|
Hola Delphius.
No he terminado de entender muy bien tu problema, pero según planteas, diría que lo que quieres es tener una interfaz (o varias) que sería tu fachada y un montón de "clientes" que serían tus observadores qeu actúan sobre dicha interfaz. Tu problema es que sólo debe haber un único objeto que implemente la interfaz (o más bien, uno por cada interfaz que tengas), ¿es así? Te recomiendo (desde mi corta experiencia) que revises el "Spring Framework" para Delphi. Este framework independiza la sección "interface" de la sección "implementation" en las units de Delphi, de tal forma que tus interfaces NO dependen de las clases que las implementan. Para lograrlo, en el "initialization" de la unit donde defines tu clase que implementas la interfaz llamas a una función del tipo "Esta Clase implementa Esta intefaz", entonces el sistema ya sabe qué clase buscar, sin necesidad de tenerla registrada en ningún sitio. Además, y esto creo que será lo que más te interese, puedes delegar la creación de la clase a una función, de tal forma que desde esta función puedes crear una nueva instancia, usar una que ya tengas, etc... Te dejo un vídeo explicativo: http://cc.embarcadero.com/item/28573 Puedes descargártelo de aquí, junto con ejemplos, etc... http://code.google.com/p/delphi-spri...ource/checkout NOTA: necesitas Delphi 2010 o superior para que te funcione. Espero que te sirva. Un saludo, LoPiTaL |
|
#3
|
||||
|
||||
|
Hola LopitaL no dispongo de dicha versión de Delphi, olvidé mencionar este hecho. Utilizo Delphi 6.
Nuevamente explico: Tengo mis clases SujetoFachadaConcreta 1..N (Sujetos concretos que actúan de Fachada), que por por diseño heredan de TAbstractSubject siendo ésta quien implementa la interface IAbstractSubject. Los Sujetos, no necesariamente van a implementar únicamente esta interface. Es posible de esperar que tengan que implementar otras interfaces que le doten de más comportamiento; como por ejemplo que incluso deba actuar como un Observador de otra. Es decir, visto desde afuera es una fachada Sujeto, pero por dentro además, es un Observador asociado a otro Sujeto. Es así que hasta se puede hablar de Observadores de Observadores. Por ejemplo, Este nuevo sujeto fachada es además un observador. Y no necesariamente de forma comportamiento de forma "recursiva" dentro del contexto de Sujeto/Observador. Sino de otros:
Y así se podría continuar... No existe una relación 1-1 explícita en lo que hace a clases e interface, por tanto la respuesta es No se puede dar el caso de que exista una única clase implemente una única interface. Es más, si lo vemos en términos abstractos todos, y cualesquiera, de los Sujetos heredados de TAbstractSubject están implementando indirectamente la interface IAbstractSubject. El único lugar en donde tiene lugar la interpretación 1-1 es para TAbstractSubject e IAbstractSubject ¡No hay, ni tiene sentido que otras clases implementen esta interface! ¡Que es la que hace de base para todos los sujetos! Entonces por cada Fachada o mejor dicho Sujeto concreto existe una interface que hereda de IAbstractObserver, aquí si se podría entender cierta relación 1-1 entre clase Sujeto e interface. Cada Sujeto tiene un contrato con una interface Observer concreta (esa es la idea, el sujeto no va a publicar cosas ajenas a su comportamiento y diseño... es como decir que un SujetoVenta tenga posibilidad de notificar cosas relacionados con los Pagos). Esta interface concreta dispone de los métodos y eventos que cualquier Observador deberá implementar. El punto es que NINGUN Sujeto concreto implementa estas interfaces observer concretas, más bien las registra en su lista, y les notifica a cualquier objeto observador que las implemente. Son los múltiples Observadores que darán respuesta a esta interface. Por ello es que no existe una relación 1-1 entre la interface Observer y alguna clase que actúe de Observer. Pueden, y se darán casos, en que una clase cualquiera sea Observador de más de una interface:
Para el ejemplo, este observador está interesado en las respuestas que provengan, de TConcreteSubject10, y TConcreteSubject09; si respetásemos las convenciones de nombramiento. Espero que con ello se entienda que en realidad no necesariamente existe una única clase que implemente una interface, salvo puntualmente para TAbstractSubject e IAbstractSubject. Ahora bien, no todo Sujeto actúa de Fachada (que es otro patrón ni una extensión a patrón Observer); ni necesariamente a la inversa Toda Fachada es un Sujeto aunque por lo habitualmente es de esperar. Además, no todo Sujeto y/o Fachada será Singleton; pero por lo general las Fachadas si se comportan como Singleton. Singleton es otro patrón... que lo único que indica es que se redefina los contructores virtuales de clase para garantizar de que se cree una única instancia. De modo que ante cualquier otro intento de crear se regrese ésta. Aquí es donde surje el problema. Tengo tanto sujetos concretos como fachadas sujetos que serán singletons, como otras que no lo serán (también se pueden encontrar clases NO sujetos y que sean Singleton). Mi pregunta es ¿Cómo dotar a las clases adecuadas de un comportamiento Singleton cuando ésta hereda e implementa alguna interfaz? Para el caso que se está analizando aquí: observa que TAbstractSubject ya viene con un constructor y destructor, y la idea es que el concepto de un Singleton no se propague a este nivel y termine resultando en que una única clase cualquiera hija y no se creen el resto... es decir debo tener tanto mis Sujetos singleton como mis sujetos no singletons sin que afecte a TAbstractSubject. De allí que mi idea es definir a cada sujeto concreto si será o no un singleton. El peligro y mi confusión está en que no se trata de simples sujetos sino que éstos implementan interfaces y cuando se utilizan éstas las clases y los recursos no se liberan sino es hasta tener un conteo de referencia igual a cero (no hay clase, sea del tipo que sea, que la implemente creada). Por tanto... en un esquema de interfaces no se habla de singleton ni ¡se esperan interface singleton! Son los objetos, de ciertas clases particulares que serán singleton. ¿Se me entiende ahora? ![]() |
![]() |
|
|
Temas Similares
|
||||
| Tema | Autor | Foro | Respuestas | Último mensaje |
| nombres unicos en TXT | diego007 | Varios | 2 | 19-04-2010 15:52:03 |
| Campos unicos al introducir | sisne | OOP | 8 | 11-04-2010 19:10:48 |
| Distincion de mayusculas en campos unicos | xerkan | Firebird e Interbase | 4 | 01-09-2004 18:45:46 |
| Multiple Rows in singleton select | IVAND | SQL | 4 | 14-08-2004 21:11:38 |
| Valores unicos en tablas mySQL | jmselesan | MySQL | 1 | 05-08-2003 16:26:48 |
|