Ir al contenido


Foto

Objetos únicos (Singleton) con herencia de TInterfacedObject


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

#1 Delphius

Delphius

    Advanced Member

  • Moderadores
  • PipPipPip
  • 6.295 mensajes
  • LocationArgentina

Escrito 14 noviembre 2011 - 11:20

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:


php
  1. unit UObserver;
  2.  
  3. interface
  4.  
  5. uses SysUtils, Classes;
  6.  
  7. resourcestring
  8. rsc_out_of_range = 'Index out of range (%d)';
  9. rsc_too_many_observers = 'Too many Observers suscribed';
  10.  
  11. type
  12. // Excepciones contempladas
  13. ETooManyObserversException = Exception;
  14. EOutOfRangeObserversException = Exception;
  15.  
  16. IAbstractObserver = interface
  17. ['{A914C135-2D64-419C-BC4A-C501C73F4071}']
  18. end;
  19.  
  20. IAbstractSubject = interface
  21. ['{1ED689AE-E68F-4897-B065-9DE09321D078}']
  22. function Suscribe(Observer: IAbstractObserver): integer;
  23. function UnSuscribe(Observer: IAbstractObserver): Integer; overload;
  24. procedure UnSuscribe(index: integer); overload;
  25. end;
  26.  
  27.  
  28. TAbstractSubject = class(TInterfacedObject, IAbstractSubject)
  29. private
  30. FObservers: TInterfaceList;
  31. function GetObserver(Index: Integer): IAbstractObserver;
  32. procedure SetObserver(Index: Integer; Observer: IAbstractObserver);
  33. public
  34. function Suscribe(Observer: IAbstractObserver): Integer; virtual;
  35. function UnSuscribe(Observer: IAbstractObserver): Integer; overload; virtual;
  36. procedure UnSuscribe(index: integer); overload; virtual;
  37.  
  38. constructor Create;
  39. destructor Destroy; override;
  40. function Counts: Integer;
  41. property Observers[I: integer]: IAbstractObserver read GetObserver write SetObserver;
  42. end;
  43.  
  44. implementation
  45.  
  46. { TAbstractSubject }
  47.  
  48. function TAbstractSubject.Counts: Integer;
  49. begin
  50. Result := FObservers.Count;
  51.  
  52. constructor TAbstractSubject.Create;
  53. begin
  54. inherited Create;
  55. FObservers := TInterfaceList.Create;
  56.  
  57. destructor TAbstractSubject.Destroy;
  58. begin
  59. if FObservers.Count > 0
  60. then raise ETooManyObserversException.Create(rsc_too_many_observers);
  61. FObservers.Free;
  62. inherited Destroy;
  63.  
  64. function TAbstractSubject.GetObserver(Index: Integer): IAbstractObserver;
  65. begin
  66. if (Index >= 0) and (Index <= FObservers.Count - 1)
  67. then Result := FObservers[Index] as IAbstractObserver
  68. else raise EOutOfRangeObserversException.Create(Format(rsc_out_of_range,[Index]));
  69.  
  70. procedure TAbstractSubject.SetObserver(Index: Integer;
  71. Observer: IAbstractObserver);
  72. begin
  73. if (Index >= 0) and (Index <= FObservers.Count - 1)
  74. then FObservers[Index] := Observer as IAbstractObserver
  75. else raise EOutOfRangeObserversException.Create(Format(rsc_out_of_range,[Index]));
  76.  
  77. function TAbstractSubject.Suscribe(Observer: IAbstractObserver): integer;
  78. begin
  79. Result := FObservers.Add(Observer);
  80.  
  81. function TAbstractSubject.UnSuscribe(Observer: IAbstractObserver): integer;
  82. begin
  83. Result := FObservers.Remove(Observer);
  84.  
  85. procedure TAbstractSubject.UnSuscribe(Index: integer);
  86. begin
  87. if (Index >= 0) and (Index <= FObservers.Count - 1)
  88. then FObservers.Delete(Index)
  89. else EOutOfRangeObserversException.Create(Format(rsc_out_of_range,[Index]));
  90.  

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:


delphi
  1. type
  2. // eventos que los observadores deben implementar
  3. TNewValueEvent = procedure(Sender: TObject; value: Integer) of object;
  4. TChangeValueEvent = procedure(Sender: TObject; value: string) of object;

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:
 


delphi
  1. TNewStateOfSalesEvent = procedure(Sender: TObject; Sale: TSale; var State: TSaleState) of object;

La idea es demostrarle el principio y no llenar de eventos  más complicados.
Extiendo:
 


delphi
  1. type
  2. IObserverReal = interface(IAbstractObserver)
  3. ['{2FA0A572-80FA-4090-83E5-3CE606EF307D}']
  4. // evento: TNewValueEvent
  5. function GetNewValueEvent: TNewValueEvent;
  6. procedure SetNewValueEvent(Event: TNewValueEvent);
  7. // evento: TChangeValueEvent
  8. function GetChangeValueEvent: TChangeValueEvent;
  9. procedure SetChangeValueEvent(Event: TChangeValueEvent);
  10.  
  11. // propiedades para los eventos
  12. property OnNewValueEvent: TNewValueEvent read GetNewValueEvent
  13. write SetNewValueEvent;
  14. property onChangeValueEvent: TChangeValueEvent read GetChangeValueEvent
  15. write SetChangeValueEvent;
  16. end;


delphi
  1. TSubjectReal = class(TAbstractSubject)
  2. private
  3. FNewValue: integer;
  4. FChangeValue: string;
  5.  
  6. procedure SetNewValue(Value: Integer);
  7. procedure SetChangeValue(Value: string);
  8.  
  9. // se "sobreescriben" los métodos de la propiedad vectorial
  10. // para no tener que estar empleando constantemente moldeos
  11. function GetObserver(Index: Integer): IObserverReal;
  12. procedure SetObserver(Index: Integer; Observer: IObserverReal);
  13. public
  14. // métodos para notificación
  15. procedure NotifyNewValue;
  16. procedure NotifyChangeValue;
  17.  
  18. // se "sobreescriben" (ocultan) los métodos Suscribe y UnSuscribe para que
  19. // cepten el tipo de observador concreto
  20. function Suscribe(Observer: IObserverReal): Integer;
  21. function UnSuscribe(Observer: IObserverReal): Integer;
  22.  
  23. // los valores a los campos se hacen por propiedad
  24. property NewValue: Integer read FNewValue write SetNewValue;
  25. property ChangeValue: string read FChangeValue write SetChangeValue;
  26.  
  27. // aqui la 2da parte del truco para sobreescribir la propiedad
  28. // vectorial por defecto
  29. property Observers[I: Integer]: IObserverReal read GetOBserver
  30. write SetObserver; default;
  31. end;

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:
 


delphi
  1. function TSubjectReal.GetObserver(Index: Integer): IObserverReal;
  2. begin
  3. Result := inherited Observers[Index] as IObserverReal;
  4. end;
  5.  
  6. procedure TSubjectReal.NotifyChangeValue;
  7. var j, n: Integer;
  8. event: TChangeValueEvent;
  9. begin
  10. n := Counts;
  11. if n > 0
  12. then begin
  13. for j := 0 to n - 1 do
  14. begin
  15. event := Observers[j].OnChangeValueEvent;
  16. if Assigned(event)
  17. then event(Self,FChangeValue);
  18. end;
  19. end;
  20. end;
  21.  
  22. procedure TSubjectReal.NotifyNewValue;
  23. var j,n: Integer;
  24. event: TNewValueEvent;
  25. begin
  26. n := Counts;
  27. if n > 0
  28. then begin
  29. for j := 0 to n - 1 do
  30. begin
  31. event := Observers[j].onNewValueEvent;
  32. if Assigned(event)
  33. then event(self,FNewValue);
  34. end;
  35. end;
  36. end;
  37.  
  38. procedure TSubjectReal.SetChangeValue(Value: string);
  39. begin
  40. if Value <> FChangeValue
  41. then begin
  42. FChangeValue := Value;
  43. NotifyChangeValue;
  44. end;
  45. end;
  46.  
  47. procedure TSubjectReal.SetNewValue(Value: Integer);
  48. begin
  49. if Value <> FNewValue
  50. then begin
  51. FNewValue := Value;
  52. NotifyNewValue;
  53. end;
  54. end;
  55.  
  56. procedure TSubjectReal.SetObserver(Index: Integer;
  57. Observer: IObserverReal);
  58. begin
  59. inherited Observers[Index] := Observer;
  60. end;
  61.  
  62. function TSubjectReal.Suscribe(Observer: IObserverReal): Integer;
  63. begin
  64. Result := inherited Suscribe(Observer);
  65. end;
  66.  
  67. function TSubjectReal.UnSuscribe(Observer: IObserverReal): Integer;
  68. begin
  69. Result := inherited UnSuscribe(Observer);
  70. end;

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:


delphi
  1. Initialization
  2. Instance := TSubjectReal.Create;
  3.  
  4. Finalization
  5. Instance.Free;

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.embarcade...m/article/22576
www.delphi3000.com/article.asp?ID=1736
http://en.wikipedia....ngleton_pattern
http://es.wikipedia.org/wiki/Singleton
http://www.castle-ca...o.uk/single.htm
http://sourcemaking....terns/singleton

También se ha discutido esto en CD, y yo  incluso participé:
http://www.clubdelph...ead.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,


  • 0

#2 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.092 mensajes
  • LocationRepública Dominicana

Escrito 15 noviembre 2011 - 06:26

Saludos.

De plano no he leído tu exposición completa pero observando tu código puedo "entender" por donde vas y te recomiendo el estudio de este Framework que te puede ayudar bastante, como lo ha hecho conmigo.
  • 0

#3 Delphius

Delphius

    Advanced Member

  • Moderadores
  • PipPipPip
  • 6.295 mensajes
  • LocationArgentina

Escrito 15 noviembre 2011 - 07:36

Hola Rolphy, te agradezco que vinieras a mi rescate.


Sería más que oportuno que destinaras unos minutos a leer el rollo, en cuanto tuvieras un más que bien y largo descanso y por supuesto ganas.


Tengo una duda respecto al framework que me comentas ¿La descarga viene con el código fuente?
Para resumir lo que busco es hacer a ciertos sujetos concretos un singleton sin afectar a sus hermanos ni a su clase base. Y el hecho de que estos estén implementando interfaces me complica el panorama.


Saludos,
  • 0

#4 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.092 mensajes
  • LocationRepública Dominicana

Escrito 15 noviembre 2011 - 07:41

Saludos.

@Delphius, la descarga viene con su fuentes.

Descarga.
Autor.

Desde que tenga suficiente tiempo le echo una leída; te reitero que este FrameWork te puede ayudar bastante y quizás te aclare algunas dudas.
  • 0

#5 Delphius

Delphius

    Advanced Member

  • Moderadores
  • PipPipPip
  • 6.295 mensajes
  • LocationArgentina

Escrito 15 noviembre 2011 - 08:15

Gracias por la aclaración amigo.
Descargando  (y)


Disculpa por ponerme pesadito con el tema. Si dices que me ayudará a aclarar mis dudas, y sabiendo que viene de ti pues que mejor. Tendré que tenerle fe.


Mientras que no esté llena de interfaces, ¡esta todo bien! jeje. Estuve ideando alternativas de cómo implementar un buen mecanismo de Observer, y este me resultó dentro de todo bien a gusto y equilibrado. Soy consciente de que no es algo bien profesional, ni espero que lo sea; mientras me ayude y funcione como lo indica el principio está bien.
En términos reales un IAbstractSubject debería ofrecer más cosas, ni que decir que ofrecer un mecanismo dinámico para registrar eventos de cualquier tipo (naturalmente apoyándose en alguna interface y estructura básica) y que luego un TAbstractSubject pasará a implementar.
En mi cabeza me surje al menos alguna interface hipotética TNotificationEvent que almacenase el sujeto que notifica, el valor viejo, el valor nuevo y el tipo de dato... quizá posiblemente apoyándose en Variants. Es decir un TAbstractSubject que no sólo registre Observadores sino también los eventos (o mejor dicho las notificaciones de eventos).


Muy seguramente este framework ofrece funcionalidades bien abstractas. Mi idea es sólo centrarme en conseguir singleton. Lo demás viene de agregado.


Saludos,
  • 0

#6 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.092 mensajes
  • LocationRepública Dominicana

Escrito 15 noviembre 2011 - 08:23

Saludos.

Para ser bastante claro y honesto, la implementación que tiene este Framework con respecto al Observer/Subject la copie y pegue dentro de uno que estoy realizando por lo bien realizado que la encontré.

Otra referencia donde utilizan interfaces con este patrón es Ian Marteens. Te puede servir como ejemplo sencillo.

P.D. Me siento un tanto alagado con eso de que vas a confiar en las referencias y señalamientos que hice.  :|
  • 0

#7 Delphius

Delphius

    Advanced Member

  • Moderadores
  • PipPipPip
  • 6.295 mensajes
  • LocationArgentina

Escrito 15 noviembre 2011 - 09:04

Saludos.

Para ser bastante claro y honesto, la implementación que tiene este Framework con respecto al Observer/Subject la copie y pegue dentro de uno que estoy realizando por lo bien realizado que la encontré.

Eso estuve notando. Tiene un diseño bastante KIS-S que no el insultivo KISS. Ahora entiendo el porqué me lo aconsejaste, está muy limpio y ordenado por dentro.  (y)
Se ha ganados sus puntos.
Me concentré en ver como diseñó el patrón Observer y justamente hace más o menos lo que yo infería.
Lo que no descubrí todavía, que seguramente en algún punto está, como tratar con singletons o mejor dicho como lograr singletons... que es donde me quiero concentrar.

Otra referencia donde utilizan interfaces con este patrón es Ian Marteens. Te puede servir como ejemplo sencillo.

Tomando nota  :)

P.D. Me siento un tanto alagado con eso de que vas a confiar en las referencias y señalamientos que hice. 

¿Porqué no? Por lo que he podido apreciar de ti, eres una persona muy analítica como yo. Y no eres de los que se tiran de una a aplicar código sino que busca documentarse, estudiarlo practicarlo un tiempo y luego expandirlo y llevarlo a algo serio. Eso es lo que te llevó a ese framework, y estoy seguro de que no lo sugerirías sin haberle dado un más que merecido vistazo.
Por ello se que tu eres de confiar  :) , ambos estamos en la misma batalla: ¡dominar patrones! Tenemos un objetivo en común... y la derrota no debe estar en nuestro diccionario. Hace tiempo que venimos intercambiando opiniones sobre el tema.


Y pensar que tu fuiste el que comenzó preguntándome sobre los patrones; ahora resulto ser yo quien consulta.  :D  Eso es bueno... quiere decir que tu evolucionaste más rápido amigo. Y además se explica porque yo dejé de estudiar e investigar sobre patrones desde hace 2 años  8o|




Una alternativa está cobrando peso en mi cabezotonta en los últimos 15 minutos es la directamente disponer de una clase que registre cada clase que actúe de Singleton y directamente buscar en lista si está o no para no estar peléandome con los peligros de sobrescribir NewInstance y FreeInstance y jugar con el conteo de referencia y propagar la singletonmania hacia arriba en el árbol. Cuando se crea la instancia se agrega y apoyándome en un atributo referenciado por Clase. Es decir: ¡una Fábrica de Singleton!  :|  Menuda cosa irónica... ¿Quien lo creería?


Saludos,
  • 0

#8 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.092 mensajes
  • LocationRepública Dominicana

Escrito 15 noviembre 2011 - 09:46


Una alternativa está cobrando peso en mi cabezotonta en los últimos 15 minutos es la directamente disponer de una clase que registre cada clase que actúe de Singleton y directamente buscar en lista si está o no para no estar peléandome con los peligros de sobrescribir NewInstance y FreeInstance y jugar con el conteo de referencia y propagar la singletonmania hacia arriba en el árbol. Cuando se crea la instancia se agrega y apoyándome en un atributo referenciado por Clase. Es decir: ¡una Fábrica de Singleton!  :|  Menuda cosa irónica... ¿Quien lo creería?


Saludos,


Saludos.

De momento se me ocurre algo como el método RegisterClass de Delphi, claro esta, que adaptado a tus necesidades.

Registers a class of persistent object so that it’s class type can be retrieved.

Unit

Classes

Category

streaming utilities

Delphi syntax:

procedure RegisterClass(AClass: TPersistentClass);

C++ syntax:

extern PACKAGE void __fastcall RegisterClass(System::TMetaClass* AClass);

Description

Call RegisterClass to register a class with the streaming system. Form classes and component classes that are referenced in a form declaration (instance variables) are automatically registered.  Any other classes used by an application must be explicitly registered by calling RegisterClass if instances are to be saved.

Once classes are registered, they can be loaded or saved by the component streaming system. IdentToInt returns nil (Delphi) or NULL (C++) when passed the class name of an unregistered class, and FindClass raises an exception for unregistered classes.

The AClass parameter is the class that is descended from TPersistent. Put the call to RegisterClass in a Register procedure. In Delphi, you can also put the call in the initialization section of the unit in which the class is defined. In C++, the call can also go in the namespace of the compilation unit that defines the class.

If the class is already registered, RegisterClass does nothing. If a different class with the same name is already registered, RegisterClass raises an EFilerError exception.

Note: Registering a component using the RegisterNoIcon or RegisterComponents method does not automatically register the class. It is still necessary to call RegisterClass for components.


  • 0

#9 pcicom

pcicom

    Advanced Member

  • Miembro Platino
  • PipPipPip
  • 265 mensajes
  • LocationMéxico

Escrito 15 noviembre 2011 - 10:11

atchiu..  jjjjiiiiiiiiiiiiuuuuch................  chin ya me contagie ...!!!!
  • 0

#10 Delphius

Delphius

    Advanced Member

  • Moderadores
  • PipPipPip
  • 6.295 mensajes
  • LocationArgentina

Escrito 15 noviembre 2011 - 11:26

Hola Rolphy,


Yo más estaba pensando en ir por algo de éste estilo:
 


php
  1. type
  2. TAbstractSubjectClass = class of TAbstractSubject;
  3.  
  4.  
  5. TFactorySingleton = class
  6. private
  7. FSingletons: TStringList;
  8. public
  9. function RegisterSingleton(Singleton: TAbstractSubject): integer;
  10. function UnRegisterSingleton(Singleton: TAbstractSubject): integer; overload;
  11. procedure UnRegisterSingleron(Index: integer);
  12. GetInstance(ClassName: string): TAbstractSubject;
  13. end;

Ahora cada sujeto singleton interesado se registra en la factoría:


php
  1. function TFactorySingleton.RegisterSingleton(Singleton: TAbstractSubject): integer;
  2. begin
  3. result := FSingletons.AddObject(Singleton.ClassName, TObject(Singleton));

Cada vez que se requiera acceder al singleton, debe pasarse por la factoría:
 


php
  1. function FactorySingleton.GetInstance(ClassName: string): TAbstractSubject;
  2. var pos: integer;
  3. SubjectClass: TAbstractSubjectClass;
  4. Subject: TAbstractSubject;
  5. FSingletons: TStringList;
  6. begin
  7. pos := FSingletons.IndexOf(ClassName);
  8. if pos <> -1
  9. then begin
  10. if not Assigned(FSingletons.Objects[pos])
  11. then begin
  12. SubjectClass := TAbstractSubjectClass(FSingletons.Objects[pos]);
  13. Subject := SubjectClass.Create;
  14. FSingletons.Objects[pos] := Subject;
  15. end;
  16. result := TAbstractSubject(FSingletons.Objects[pos]);
  17. end;

Y ahora las clases clientes simplemente trabajan así:


delphi
  1. Fabrica.GetInstance('TConcreteSubject10').HazAlgo;

Claro está... esto no sigue impidiendo que se haga en algún otro lado un .Create de dicha clase.

¿Tu cómo lo ves?


Saludos,


  • 0

#11 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.092 mensajes
  • LocationRepública Dominicana

Escrito 15 noviembre 2011 - 11:52

Saludos.

¿Porqué tengo la sensación de que estas implementado el patrón Observer (aunque sea a medias)?  ;) ;) (y) (y)

De hecho esta bastante claro y limpio, sobre todo funcional.
  • 0

#12 Delphius

Delphius

    Advanced Member

  • Moderadores
  • PipPipPip
  • 6.295 mensajes
  • LocationArgentina

Escrito 15 noviembre 2011 - 12:35


Hola Rolphy,
No si... el tema de patrón Observer lo tengo comido, y bien gustoso. De ese no me quejo que me funciona elegantemente.


De lo que me quejo es que a algunos Sujetos deben ser Singleton, y allí está el meollo del asunto.
Para garantizar un buen Singleton es necesario sobrescribir los métodos NewInstance y FreeInstance, y hacer las debidas llamadas a los de sus padres. Llendo al caso, el NewInstance de cualquier hipotético TConcreteSubject debe hacer un:


delphi
  1. inherithed NewInstance;

Y luego de éste a su padre... y así subiendo a por las ramas. El punto está justamente en TInterfacedObject. La clase base que da origen a TAbstractSubject. TInterfacedObject sobreescribe estos métodos como bien sabemos para administrar el conteo de referencia, puesto que así lo exige las interfaces y no tener que tocarlo para nada más.
Un Sujeto singleton debería volver a meter narices e este método y llevar otro sistema de conteo de referencia que rompa con el esquema de las interfaces.
Yo estimo que debería haber alguna forma pero mi cabeza no llega a ello.


Y ahora que lo estado analizando, el llevar a cabo esta clase fábrica de singletons es más una molestia que una ayuda. Es igual, o incluso peor, que disponer de simple una variable:


delphi
  1. initialization
  2. Instancia := TSujetoConcretoSingleton.Create;
  3.  
  4.  
  5. initialization
  6. Instancia.Free;

Con esa fábrica aumento indirección, dispongo de una clase más (¡y que además, esta también es un singleton!) y es una chapuza. Por que si bien pareciera un singleton... tranquilamente puedo obviar a la fábrica y mandar a construir al objeto con un create. Y da lo mismo que manejar directamente una variable global.


Ahora mis clases clientes hacen cosas como esto:


delphi
  1. procedure TClaseCliente.HacerAlgoConElSujetoConcretoSingleton;
  2. begin
  3. Instancia.HazEstoYCuidadoDeNoMatarte;
  4. end;

Agrr... que no me agrada hacer estas cosas, y puede estallar ante cualquier metida de pata. Pero al menos evito una clase más, estar registrando cada sujeto y acoplandolo hacia la fábrica; Patrones, por hoy... ¡los Odio! :(

Saludos,


  • 0

#13 Rolphy Reyes

Rolphy Reyes

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.092 mensajes
  • LocationRepública Dominicana

Escrito 15 noviembre 2011 - 01:27

Saludos.

Quizás estos caballeros te puedan servir: TAggregatedObject y TContainedObject.

Puede que con estos puedas construir los Singleton más fácil.

{ TAggregatedObject and TContainedObject are suitable base
  classes for interfaced objects intended to be aggregated
  or contained in an outer controlling object.  When using
  the "implements" syntax on an interface property in
  an outer object class declaration, use these types
  to implement the inner object.

  Interfaces implemented by aggregated objects on behalf of
  the controller should not be distinguishable from other
  interfaces provided by the controller.  Aggregated objects
  must not maintain their own reference count - they must
  have the same lifetime as their controller.  To achieve this,
  aggregated objects reflect the reference count methods
  to the controller.

  TAggregatedObject simply reflects QueryInterface calls to
  its controller.  From such an aggregated object, one can
  obtain any interface that the controller supports, and
  only interfaces that the controller supports.  This is
  useful for implementing a controller class that uses one
  or more internal objects to implement the interfaces declared
  on the controller class.  Aggregation promotes implementation
  sharing across the object hierarchy.

  TAggregatedObject is what most aggregate objects should
  inherit from, especially when used in conjunction with
  the "implements" syntax.  }

  TAggregatedObject = class(TObject)
  private
    FController: Pointer;  // weak reference to controller
    function GetController: IInterface;
  protected
    { IInterface }
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  public
    constructor Create(const Controller: IInterface);
    property Controller: IInterface read GetController;
  end;

  { TContainedObject is an aggregated object that isolates
    QueryInterface on the aggregate from the controller.
    TContainedObject will return only interfaces that the
    contained object itself implements, not interfaces
    that the controller implements.  This is useful for
    implementing nodes that are attached to a controller and
    have the same lifetime as the controller, but whose
    interface identity is separate from the controller.
    You might do this if you don't want the consumers of
    an aggregated interface to have access to other interfaces
    implemented by the controller - forced encapsulation.
    This is a less common case than TAggregatedObject.  }

  TContainedObject = class(TAggregatedObject, IInterface)
  protected
    { IInterface }
    function QueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall;
  end;


  • 0

#14 Delphius

Delphius

    Advanced Member

  • Moderadores
  • PipPipPip
  • 6.295 mensajes
  • LocationArgentina

Escrito 15 noviembre 2011 - 04:48

Hola Rolphy,
Me parece interesante lo que me acabas de pasar. Recuerdo haber leído algo hace un tiempo sobre esas clases, pero no le presté atención. Tendré que darme una leída al tema y hacer algunas pruebas.


Saludos,
  • 0