Ir al contenido


Foto

[RESUELTO] Crear una sola instancia


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

#1 Rolphy Reyes

Rolphy Reyes

    Advanced Member

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

Escrito 24 enero 2010 - 11:08

Saludos.

Compañeros en estos días he estado trabajando en crear mis propias acciones predefinidas en el ActionList para que queden de manera persistente. Este proceso es parecido al de crear componentes, cabe aclarar que no soy dado a crear componentes simplemente me fajo con mis clases.

Con lo anterior prácticamente no tengo problemas, la situación o problema surge en que tengo unos Datos guardados en la Base de Datos, entonces tengo un DataModule solo para esa tabla con una serie de métodos y propiedades que me devuelven la informaciones necesarias.

Veo que la única opción que tengo (hasta donde puedo percibir) es crear el DataModule cada vez por cada acción y ando buscando es crear el DataModule y que las acciones accedan a este sin tener que crear cada vez y que al final de la aplicación dicha instancia se destruya.

Espero ser claro.
  • 0

#2 Marc

Marc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.484 mensajes
  • LocationMallorca

Escrito 24 enero 2010 - 01:14

Hola Rolphy.

Creo que no acabo de entender el problema. Cada datamodule (al igual que los formularios) tiene asignada por defecto una variable global (que yo siempre elimino manualmente), y también por defecto Delphi lo añade para crear durante el arranque de la aplicación. Lo único que tienes que hacer es que al cerrar la aplicación se haga el cierre de esos datamodules (a los que puedes acceder mediante su variable global).

Si el problema está en que no quieres que se creen a menos que se necesiten, entonces elimínalos de la creación durante el arranque de la aplicación, y cuando vayas a utilizarlos, simplemente haz la siguiente comprobación :

if Assigned dmMiDataModule then Application.CreateForm(TdmMiDataModule, dmMiDataModule);

De la misma forma, al cerrar la aplicación solo tienes que hacer la misma comprobación :

if Assigned dmMiDataModule then dmMiDataModule.Free;

¿ Te refieres a esto ?.

  • 0

#3 Rolphy Reyes

Rolphy Reyes

    Advanced Member

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

Escrito 24 enero 2010 - 01:19

Saludos.

Disculpa, pero veo que en absoluto no comprendiste mi mensaje.

No tengo ese inconveniente, eso lo manejo a la perfección.  Mi problema consiste en que estoy creando Acciones predefinidas y en una de ella necesito acceder a datos que están en la BD a través de un DataModule.

Como dije en mi mensaje anterior, por lo pronto visualizo que tengo que crear el DataModule cada vez que se instancia una acción y lo que quiero es crear una sola instancia del DataModule y que mis Acciones accedan a esta instancia y hagan lo que deben de hacer.

Espero ser más claro.
  • 0

#4 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 24 enero 2010 - 01:26

Hola Rolphy,
No termino de comprender.
¿Dices que por cada acción te ves obligado a crear un datamodule, y tu quieres evitarte esto y que accedan a un único datamodule?

Yo a decir verdad no le veo demasiado complicaciones invocando simplemente a dicha variable que por defecto define Delphi.

Quisiera en lo posible que seas un poco más gráfico. ¿Podemos ver un poco del código?
Te agradecería que nos comentes con más detalles porque no comprendo.

Saludos,
  • 0

#5 Rolphy Reyes

Rolphy Reyes

    Advanced Member

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

Escrito 24 enero 2010 - 02:00

Saludos.

Si Delphius, me veo obligado a tener que crear una instancia de ese DataModule por cada acción, porque cada acción debe de buscar su correspondiente Registro en la Base de Datos.

Te muestro el código:

Esta unidad contiene la acción básica de donde las demás heredan.


delphi
  1. unit UFWCustomAction;
  2.  
  3. interface
  4.  
  5. uses ActnList, Controls;
  6.  
  7. type
  8.   TFwClickModalResult = procedure (Sender: TObject; ModalResult: TModalResult) of object;
  9.  
  10.   TFwCustomAction = Class(TAction)
  11.   private
  12.  
  13.   protected
  14.  
  15.   public
  16.     function HandlesTarget(Target: TObject): Boolean; override;
  17.     procedure UpdateTarget(Target: TObject); override;
  18.   End;
  19.  
  20.  
  21. implementation
  22.  
  23. { TFwCustomAction }
  24.  
  25. function TFwCustomAction.HandlesTarget(Target: TObject): Boolean;
  26. begin
  27.   Result := True;
  28. end;
  29.  
  30. procedure TFwCustomAction.UpdateTarget(Target: TObject);
  31. begin
  32.   if Target <> Nil  then
  33.     Enabled := True
  34.   Else
  35.     Enabled := False;
  36. end;
  37.  
  38. end.



Esta unidad es la que hace el trabajo.


delphi
  1. unit UFWCallFormAction;
  2.  
  3. interface
  4.  
  5. uses
  6.   UFWCustomAction, Classes, Forms, SysUtils, UDmCustomConnDB,
  7.   UFrmCustomViewMDIData, UFrmCustomViewModalData, UFrmCustomViewMantenimiento,
  8.   UDmFormApplication, UTiposFW;
  9.  
  10. type
  11. { TTipoVista = (tvMantenimiento, tvBusqueda, tvConsulta, tvReporte, tvProceso);
  12.  
  13.   TTipoOperacion = (toAgregar, toModificar, toBuscar, toConsultar, toMultiAgregar);
  14.   //}
  15.  
  16.   TFwCustomCallFormAction = class(TFwCustomAction)
  17.   private
  18.     { private declarations }
  19.     FFormId: Integer;
  20.     FTipoVista: TTipoVista;
  21.     FTipoOperacion: TTipoOperacion;
  22.     FDmConnection: TDmCustomConnDB;
  23.     procedure SetFormId(const Value: Integer);
  24.     procedure SetTipoVista(const Value: TTipoVista);
  25.     procedure SetTipoOperacion(const Value: TTipoOperacion);
  26.     procedure SetDmConnection(const Value: TDmCustomConnDB);
  27.   protected
  28.     { protected declarations }
  29.     property FormId : Integer read FFormId write SetFormId;
  30.     property TipoVista : TTipoVista read FTipoVista write SetTipoVista;
  31.     property TipoOperacion : TTipoOperacion read FTipoOperacion write SetTipoOperacion;
  32.     property DmConnection : TDmCustomConnDB read FDmConnection write SetDmConnection;
  33.     procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  34.   public
  35.     { public declarations }
  36.     constructor Create(AOwner: TComponent); override;
  37.   end;
  38.  
  39.   TFwCallFormMDIAction = class(TFwCustomCallFormAction)
  40.   private
  41.     { private declarations }
  42.     function IsActive(const AClassName : String) : Boolean;
  43.   protected
  44.     { protected declarations }
  45.     FMyDmForm : TDmFormApplication;
  46.     FMyFormMDI : TFrmCustomViewMDIData;
  47.     FMyFormModal : TFrmCustomViewModalData;
  48.     FMyFormMDIClass : TFrmCustomViewMDIDataClass;
  49.     FMyFormModalClass : TFrmCustomViewModalDataClass;
  50.     FMyFormMDIMantClass : TFrmCustomViewMantenimientoClass;
  51.   public
  52.     { public declarations }
  53.     constructor Create(AOwner : TComponent); override;
  54.     destructor Destroy; override;
  55.     procedure ExecuteTarget(Target: TObject); override;
  56.   published
  57.     { published declarations }
  58.     property FormId;
  59.     property TipoVista default tvMantenimiento;
  60.     property DmConnection;
  61.   end;
  62.  
  63. implementation
  64.  
  65. uses UInstanciaMenu, StrUtil, UResourceFrameWork;
  66.  
  67. { TFwCustomCallFormAction }
  68.  
  69. constructor TFwCustomCallFormAction.Create(AOwner: TComponent);
  70. begin
  71.   inherited Create(AOwner);
  72.   FFormId := 0;
  73.   FTipoVista := tvMantenimiento;
  74.   FTipoOperacion := toAgregar;
  75. end;
  76.  
  77. procedure TFwCustomCallFormAction.Notification(AComponent: TComponent;
  78.   Operation: TOperation);
  79. begin
  80.   inherited;
  81.   if (AComponent = FDmConnection) and (Operation = opRemove) then
  82.     FDmConnection := Nil;
  83. end;
  84.  
  85. procedure TFwCustomCallFormAction.SetDmConnection(const Value: TDmCustomConnDB);
  86. begin
  87.   if FDmConnection <> Value then
  88.     FDmConnection := Value;
  89. end;
  90.  
  91. procedure TFwCustomCallFormAction.SetFormId(const Value: Integer);
  92. begin
  93.   if FFormId <> Value then
  94.     FFormId := Value;
  95. end;
  96.  
  97. procedure TFwCustomCallFormAction.SetTipoOperacion(const Value: TTipoOperacion);
  98. begin
  99.   if FTipoOperacion <> Value then
  100.     FTipoOperacion := Value;
  101. end;
  102.  
  103. procedure TFwCustomCallFormAction.SetTipoVista(const Value: TTipoVista);
  104. begin
  105.   if FTipoVista <> Value then
  106.     FTipoVista := Value;
  107. end;
  108.  
  109. { TFwCallFormMDIAction }
  110.  
  111. constructor TFwCallFormMDIAction.Create(AOwner: TComponent);
  112. begin
  113.   inherited Create(AOwner);
  114.   if not (csDesigning in ComponentState) then
  115.   begin
  116.     FMyDmForm := TDmFormApplication.Create(Self, DmConnection);
  117.   end;
  118. end;
  119.  
  120. destructor TFwCallFormMDIAction.Destroy;
  121. begin
  122.   if not (csDesigning in ComponentState) then
  123.   begin
  124.     FMyDmForm.Free;
  125.     FMyDmForm := Nil;
  126.   end;
  127.   inherited Destroy;
  128. end;
  129.  
  130. procedure TFwCallFormMDIAction.ExecuteTarget(Target: TObject);
  131. begin
  132.   FMyDmForm.SetLocate(FFormId);
  133.   if not IsActive(FMyDmForm.FormClass) then
  134.   begin
  135.     Try
  136.       case TipoVista of
  137.         tvMantenimiento:
  138.         begin
  139.           FMyFormMDIMantClass := TFrmCustomViewMantenimientoClass(GetClass(FMyDmForm.FormClass));
  140.           FMyFormMDI := FMyFormMDIMantClass.Create(GetInstanciaMenuPrincipal, DmConnection, FMyDmForm.DataModuleClass, FMyDmForm.DialogClass);
  141.         end;
  142.         tvBusqueda, tvConsulta, tvReporte, tvProceso:
  143.         begin
  144.           FMyFormMDIClass := TFrmCustomViewMDIDataClass(GetClass(FMyDmForm.FormClass));
  145.           FMyFormMDI := FMyFormMDIClass.Create(GetInstanciaMenuPrincipal, DmConnection, FMyDmForm.DataModuleClass, TipoVista);
  146.         end;
  147.       end;
  148.       FMyFormMDI.Show;
  149.     Except on E : Exception do
  150.       begin
  151.         FMyFormMDI.Free;
  152.         raise Exception.Create(E.Message + CLRF + StrFormNoRegistro);
  153.       end;
  154.     End;
  155.   end;
  156. end;
  157.  
  158. function TFwCallFormMDIAction.IsActive(const AClassName: String): Boolean;
  159. var
  160.   I : Integer;
  161. begin
  162.   Result := False;
  163.   with GetInstanciaMenuPrincipal do
  164.   begin
  165.     for I := 0 to MDIChildCount -1 do
  166.     begin
  167.       if CompareText(MDIChildren[I].ClassName, AClassName) = 0 then
  168.       begin
  169.         If MdiChildren[i].WindowState = wsMinimized then
  170.           MdiChildren[i].WindowState := wsNormal;
  171.         MdiChildren[i].BringToFront;
  172.         Result := True;
  173.         Break;
  174.       end;
  175.     end;
  176.   end;
  177. end;
  178.  
  179. end.



Fíjense en el constructor TFwCallFormMDIAction.Create donde instancio el DataModule cada vez que se crea la Acción a eso es que me refiero.

Pero he optado por publicar unas propiedades a la acción y yo llenar desde el inicio de la aplicación esas propiedades, además analice que es más efectivo de este otra manera.
  • 0

#6 Marc

Marc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.484 mensajes
  • LocationMallorca

Escrito 24 enero 2010 - 03:47

Permíteme que insista, ¿ porqué no puedes utilizar la variable global que declara Delphi en la clase del DataModule ?. De esta forma te aseguras que solo habrá una instancia del DataModule, la que esté asociada a esa variable global.

Si no quieres crear el DataModule en el momento de arrancar la aplicación, puedes hacer lo que te sugerí de comprobar si está creado en el momento de usarlo. Es decir, en el constructor TFwCallFormMDIAction.Create.

De esta forma, la primera vez que se use la acción de creará una instancia del DataModule, y cualquier uso posterior de la acción ya podrá reutilizar esa instancia.
  • 0

#7 Rolphy Reyes

Rolphy Reyes

    Advanced Member

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

Escrito 24 enero 2010 - 04:11

Saludos.

Gracias Marc por su interés.  Pero no puedo crear el DataModule al arrancar la aplicación debido a como tengo codificado los mismos, de la manera en que están se le tiene que enviar a DataModule de conexión el debe de conectarse.

Hago esto porque con gano la posibilidad de que mis DataModule se puedan conectar a más de una BD. 
  • 0

#8 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 24 enero 2010 - 09:52

Hola Rolphy ahorita entiendo mejor, aunque me mareo un poquito con tanto código.

De lo entiendo lo que buscas es que cuando se crea la primer acción que se cree el correspondiente DataModule. En las siguientes creaciones de acciones buscas que éstas "apunten" y referencien al mismo DataMododule creado en la primera.

Si es eso tengo dos puntos de vistas:
1. Optar por la singletonmanía. Es decir que el DataModule a crear tenga el comportamiento de un singleton. Debo decir que al comienzo estaba pensado que podría tratarse de un caso viable de este patrón, pero necesitaba asegurarme si así lo fuera.
El patron singleton básicamente lo que establece es que exista y sólo se permita una instancia de una clase dada.
Espero que no ven malas intenciones en esto, pero una buena discusión, debate, análisis y ejemplos de como implementar un singleton puede verse en este hilo de CD.

2. Es posible, y entendible, que exista una pequeña anomalía y defecto en tu diseño. No cuadra totalmente la idea de que una "Acción" deba asumir la responsabilidad de crear un DataModule. Se le está atribuyendo una responsabilidad que, visto conceptualmente, no es deseable ya que se está acoplando dos clases que conceptualmente no tienen demasiada vinculación (es más, es un vínculo casual, débil) y perdiendo un poco de cohesión.
En principio (si bien hay sus excepciones) quien crea, es quien destruye. Y Esto lleva a la idea de un vínculo fuerte. De hecho, sería un escenario particular de agregación por composición 1-1.

El objetivo que tu planteas, a mi humilde punto de vista, va en contra. Observo una vinculación débil, que exista una referencia o visibilidad de atributo.
Simplemente se desliga la responsabilidad a la Acción de crear. Quien debe crear el DataModule es posible que sea otra clase (muy posiblemente quien administra y controla al grupo de las acciones).
Es decir el escenario sugiere que ni bien se crea la Acción se le asocia la instancia del DataModule.

Algo como esto:



delphi
  1. Accion.ModuloDatos := MiDataModule;



o esto:



delphi
  1. Accion.AsociarCon(MiDataModule);



Obviamente en este escenario la Clase Accion debe ofrecer un campo o atributo que permite tener referencia al DataModule. Luego cuando esta Acción deba enviar un mensaje al DataModule al que está relacionada hace algo como:



delphi
  1. procedure Accion.ModuloDatosHacerEsto;
  2. begin
  3. FModuloDatos.HazEsto;
  4. end;



Cuando se libera la Acción simplemente se asocia el puntero nulo y no se libera al DataModule al que estaba asociada.

Nota que los dos escenarios que he descripto no son mutuamente excluyentes. Si se opta por hacer al DataModule un singleton, las nuevas creaciones apuntarán a una único y mismo objeto.
Por otra parte si no se opta por hacerlo singleton bastará con hacer que cada Acción se asocie con el DataModule que se cree oportunamente y he aquí que incluso se puede aprovechar la tan cuestionada variable que nos ofrece Delphi.

Espero que se me entienda.

Me gustaría saber que opinan los demás. Es posible que hay algo que no esté viendo bien.

Saludos,
  • 0

#9 Marc

Marc

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.484 mensajes
  • LocationMallorca

Escrito 25 enero 2010 - 03:12

Hola Rolphy.

Siento insistir, pero es que aún no entiendo donde está el inconveniente. Dices que no puedes crear los DataModule en el arranque de la aplicación por tal como están codificados, ¿ pero porqué no puedes crearlos al usarlos la primera vez ?.

Lo que te sugerí anteriormente es que en tu constructor TFwCallFormMDIAction.Create hagas esta comprobación :

if not Assigned(DmFormApplication) then DmFormApplication := TDmFormApplication.Create(Self, DmConnection);

Siendo DmFormApplication la variable global que crea Delphi con cada DataModule, y que por tanto apunta a una instancia única del DataModule.

NOTA: Por cierto, en el arranque de la aplicación también puedes hacer las llamadas a crear los DataModules, pasando por parámetro la conexión. Simplemente habrás tenido que crear antes el DataModule donde reside la conexión.

PD : Delphius tiene toda la razón cuando dice que la Acción no es lugar más elegante donde crear los DataModules.
  • 0

#10 Rolphy Reyes

Rolphy Reyes

    Advanced Member

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

Escrito 25 enero 2010 - 07:04

Saludos.

Gracias Delphius y Marc por su interés en responder.

Después de un largo análisis he optado por publicar un par de propiedades a las acciones y realizar la asignación de valores al crearse la aplicación en vez de crear un DataModule desde la acción, creando dicho DataModule al inicio de la aplicación y su destrucción una vez terminada la asignación de valores.

Como bien mencionas Delphius no existe ese enlace fuerte entre ambas clases, además de ser ineficiente porque la acción tiene un solo registro en la BD y como lo planteaba al inicio cada vez que una acción se ejecutara se iba a buscar sus datos, tomando más tiempo y realizando request innecesarios.

El tema del singleton lo utilizo salvo que no sabía como se llamaba propiamente, ambos artículos que citaste están muy buenos e interesantes se le puede sacar gran provecho.

Gracias.
  • 0

#11 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 25 enero 2010 - 09:05

Me alegro que se me haya entendido y haber ayudado a que encontraras la solución. :)

Si el tema de los patrones te interesa, yo en lo poco de experiencia que tengo, puedo recomendarte ampliamente el libro "UML y Patrones. Una Introducción al Análisis y Diseño Orientado a Objetos y al Proceso Unificado" De Craig Larman.
Es un libro excelente, relativamente sencillo de entender, y ofrece una mirada extra de lo que es y puede dar este maravilloso paradigma.
Como complemento a este el "UML Gota a Gota" de Marin Fowler y Kendall Scott. Este es más simple y menos técnico. Como contra, por querer ser simple se ha dejado muchas cosas al vuelo (a mi entender)
Zarko, en delphi.about.com elaboró unos artículos de excelente calidad (como lo acostumbrado) sobre algunos patrones. A mi me ayudaron a comprender mejor los patrones que he estado estudiando y analizando.
Recuerdo que había un sitio, en inglés por supuesto, que tiene una amplia recopilación de patrones ¡son cientos! Creo que tengo entre mis fuentes bibliográficas una referencia al sitio. Déjame buscar.

Saludos,
  • 0




IP.Board spam blocked by CleanTalk.