Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Principal > OOP
Registrarse FAQ Miembros Calendario Guía de estilo Temas de Hoy

Grupo de Teaming del ClubDelphi

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 31-08-2005
adlfv adlfv is offline
Miembro
 
Registrado: may 2005
Posts: 39
Poder: 0
adlfv Va por buen camino
Lightbulb Reutilizar código "generico"

Hola a todos.

En el post titulado "Registrar Formas en Delphi" de lpmlpm el autor plantea que trabaja con un conjunto de clases y código más o menos generico como explica a continuación: (Extraido del post)

Cita:
sucede que me he hecho una especie de
Framework GUI con varias clases y ventanas que me ahorran mucho
trabajo y que son de lo más genericas en el desarrollo de un programa
de Base de Datos, es sumamente practico pues se modulariza y
reutilizan cientos (o miles) de lineas de código y es una verdadera
maravilla para proyectos medianos/grandes.

Pero esta manera de trabajar tiene el inconveniente de que hay que
agregar muchas clases de formas en el DPR para poder hacer uso de los
beneficios... por poner un ejemplo, tengo algunas de tantas clases
para manejar los reportes mas o menos asi:

TReportePrototipo ---> TReporteSelector ----> TReporteNiveles
Más adelante nuestro compañero lpmlpm me explica un poco su enfoque de lo que es más o menos general y que se suele usar en muchas aplicaciones. Veamos:

Cita:
...en todos los sistemas queremos generalmente que se puedan identificar los usuarios, que se le puedan asignar permisos individuales y de grupo... En mi caso te puedo mencionar que me gusta que en la aplicación se pueda crear menus personalizados, implementar un escritorio virtual de accesos directos a opciones de la aplicación, poder ver a que y a donde estoy conectado, poder manejar permisos a nivel de catálogos o porque no a nivel de los mismos componentes de los catálogos, me gusta guardar el estado de la última configuración que se uso en cada reporte del programa..
En definitiva la idea es establecer un "nucleo comun" a las aplicaciones de forma que las aplicaciones usen este nucleo y cualquier cambio se vea reflejado en las aplicaciones (como se explica en el post).

Ahora bien, la idea de este post es profundizar un poco más en este tema, y si es posible aportar código, y para eso yo seré el primero en hacerlo. Por favor diganme su opinión, posibles fallos, mejorías, etc.

Ahora explicaré mi caso.
Estoy haciendo un programa más o menos grande, y todavía no tengo claro qué base de datos usar, por lo cual he hecho como un "envoltorio" para lo que es la base de datos, con funciones más o menos genéricas en UBD.pas. La idea de esto, es poder cambiar de forma más o menos facil el DataModule (funciona bien para ejecución, pero para diseño no del todo, pues las vinculaciones al DataModule son en tiempo de diseño...).

Para los mantenimientos de datos (SELECT, INSERT, DELETE, UPDATE): Cada tabla (o casi todas) tienen un formulario de mantenimiento asociado
(con métodos Agregar, Editar, Eliminar, Deshacer, Aplicar) y a este hay un editor asociado (donde se editan los campos). El formulario de mantenimiento hereda de un formulario genérico con un listado donde se presentan los datos y se encuentran los métodos anteriormente citados (BaseListado), y el formulario de edición hereda de un formulario genérico donde se pueden editar los datos (BaseEditor). Como dije anteriormente, cada formulario heredado de BaseListado se relaciona con su editor correspondiente heredado de BaseEditor.

Por ahora esto es el código más o menos genérico que tengo implementado. Todavía no he empleado reportes (porque no estoy seguro de qué herramienta utilizar).

Espero que por favor aportemos ideas entre todos y podamos despues implementarlas en nuestros programas.

Un cordial saludo a todos, y disculpen si el post es muy largo
Archivos Adjuntos
Tipo de Archivo: zip Generico.zip (8,4 KB, 88 visitas)
Responder Con Cita
  #2  
Antiguo 31-08-2005
Avatar de roman
roman roman is offline
Moderador
 
Registrado: may 2003
Ubicación: Ciudad de México
Posts: 20.269
Poder: 10
roman Es un diamante en brutoroman Es un diamante en brutoroman Es un diamante en bruto
Hola, he visto (muy por encima) el código que adjuntas y te pongo una sugerencia.

Para no alargar me enfoco sólo en una parte que aquí resumo:

Código Delphi [-]
type
{$if defined (ZEOS)}
  TMiQuery = TZQuery;
  ...
{$elseif defined(BDE)}
  ...
{$endif}

...

procedure ParamAsDate(Qry: TMiQuery; Nombre: String; Valor: TDateTime);
begin
{$if defiined(ZEOS)}
  Qry.ParamByName(Nombre).AsDate := Valor;
{$elseif defined(BDE)}
  ..
{$endif}
end;

Es decir, la manera en que enfocas el problema es discerniendo el tipo de componente a usar mediante directivas de compilación y programando procedimientos generales que harán uso de los tipos realmente compilados.

Otra forma de enfocar esto es "convirtiendo" estos procedimientos generales en métodos de una clase genérica:

Código Delphi [-]
type
  TQueryGenerico = class
  public
    procedure ParamAsDate(Nombre: String; Valor: TDateTime); virtual; abstract;
  end;

Para cada motor que desees manejar derivas una clase:


Código Delphi [-]
type
  TQueryZeos = class(TQueryGenerico)
  private
    FQuery: TZQuery; // componente 'real'

  public
    constructor Create; // aquí construyes FQuery
    destructor Destroy; override; // aquí destruyes FQuery;

    procedure ParamAsDate(Nombre: String; Valor: TDateTime); override;
    {
      FQuery.ParamByName(Nombre).AsDate := Valor;
    }
  end;

La clase base no implementa los métodos, sólo los define (virtual y abstract) de manera que tu aplicación pueda hacer uso de ellos indistintamente de cuál descendiente es el que realmente se crea.

Al comienzo de tu aplicación, suponiendo que decides usar Zeos, construyes un objeto TQueryZeos:

Código Delphi [-]
QueryGenerica := TQueryZeos.Create;

donde QueryGenerica es de tipo TQueryGenerica. El resto de tu aplicación no dependerá de si usaste TQueryZeos o TQueryBde ya que únicamente hace uso de los métodos declarados en TQueryGenerica. El polimorfismo hace que se llamen los métodos de la clase adecuada.

Esto te permite no depender de directivas de compilación. Cuando desees agregar soporte para un nuevo motor simplemente creas el correspondiente descendiente sin tocar el resto del código ni tener que recompilar la aplicación principal excepto por la parte que toca a la construcción del descendiente específico de TQueryGenerico.

Esto último se puede facilitar mediante el uso de una fábrica de la cual puedo hablar al rato porque ya me tengo que ir.

// Saludos
Responder Con Cita
  #3  
Antiguo 01-09-2005
Avatar de lpmlpm
lpmlpm lpmlpm is offline
Miembro
 
Registrado: ago 2005
Posts: 136
Poder: 19
lpmlpm Va por buen camino
Smile

Que bueno que se abrió este hilo para platicar de este tema tan interesante, y ojalá hayan muchos aportes y algo muy bueno se saque de aqui... en lo personal me llama mucho la atención este tema del diseño y estoy avido de aprender nuevas técnicas

Bien para empezar lo que Roman ha expuesto en su comentario es que para la forma que tienes planteada tu jerarquia te conviene mejor usar el patrón de diseño "Wraper" o tambien conocido como "Adapter", combinado con una especie de "Selector"... estos patrones se explican en la liga que ya habia puesto en el hilo anterior http://delphi.about.com/od/oopindelphi/a/aa010201a.htm pero al final de cuentas vean como siempre caemos en la cuenta de que hay que usar patrones de diseño para estas ondas...

Yo particularmente soy enemigo de 2 cosas: una de usar directivas de compilación para estos casos y otra que nunca utilizo componentes del tipo TTable (TBDETable, TZTable, TIBTable, etc)... prefiero siempre usar una query o bien algo similar a un ClientDataset para trabajar con mis catálogos... eso de los TTables me parece un mal que se trae desde los tiempos del BDE y aun no se nos han quitado las malas mañas.

No uso directivas de compilación para discernir entre una clase y otra porque cuando haces eso terminas llenando todo el proyecto con directivas y cosas redundantes que en vez de simplificar complican mas las cosas y no es esa la idea... tu mismo puedes ver en tus unidades cuantas directivas no tienes que usar hasta para hacer algo infimo...

Mi solución al uso de las directivas esta relacionada con mi segunda preferencia que es de solo usar Querys... me explico, como se que al final de cuentas, ya sea que se use un TZQuery, TAdoQuery, TIBQuery, etc... ese componente que se use va a derivarse de TDataset, asi que no hay mas que buscarle... solo en tu clase generica de los catalogos pon un DataSource y cuando tengas que hacer una operacion con la query trabajala como si fuera un Dataset, y con eso tienes acceso a todas las operaciones Basicas, Insert, Append, Delete, Post, Cancel, etc... y ya tienes medio centavo en la bolsa... ok, ahora viene otra cuestión, si me veo en la necesidad de cambiar el SQL de la query en runtime por alguna operación ¿¿como lo hago sin castear a la clase original?? Muy sencillo, recuerden que otro punto a favor del delphi es su RTTI asi que a usarla!!!

Ve este ejemplo:

Código Delphi [-]
 procedure TF_Busquedas.Buscar(Texto: string);
 var
   S: string;
   i: integer;
   SQL: Tstrings;
 begin
   SQL := TStringList(GetObjectProp(DSBusqueda.DataSet, 'SQL'));
   if Assigned(SQL) then
     with DSBusqueda.DataSet do
       begin
         try
           // Desconectamos los controles
           DisableControls;
           // Cerramos
           Close;
           // Restablecemos la sentencia original
           SQL.Clear;
           SQL.Add(FOriginalSQL);
           // Si no se ha de buscar nada, se abre la tabla y se sale
           if Texto = '' then
             begin
               Open;
               Exit;
             end;
 
           S := '';
           //Le Ponemos las condiciones a la búsqueda...
           SQL.Add(StrCond(pos('WHERE', AnsiUpperCase(SQL.Text)) <> 0, ' AND (',
             'WHERE ('));
           for i := 0 to Pred(Fields.Count) do
             begin
               S := S + StrCond(S <> '', ' or ', '') + '(' + Fields[i].FieldName +
                Format(StrFiltro1,[Texto])+')';
             end;
           S := S + ')';
           SQL.Add(S);
 
           Open;
         finally
           // Conectamos los controles
           EnableControls;
         end
       end;
 end;

Aqui con la funcion GetObjectProp pregunto si el dataset tiene publicada una propiedad llamada SQL, y si es asi pues la tomo y modifico a mi antojo... recorriendo los campos que tenga definidos en ese catalogo y cambiando la query original agregando filtros para buscar un texto en todos los campos que tenga ese catálogo; y todo esto sin tener que preguntar si era Zeos, BDE, o ADO o lo que fuera...

Mi recomendación es que le dejes esa responsibilidad de usar Zeos o DBE a la clase derivada que se va a instanciar al final en tu aplicación, en esa si ya puedes poner o zeos o un IBO o un IBX o lo que necesites...

si necesitaras agregar metodos nuevos a la clase TDataset en tu catálogo entonces mejor piensa en usar otro truco de Delphi que serían las clases interpuestas... aqui te enteras mejor de esto en la web del maestro marteens : http://www.marteens.com/trick46.htm

De igual manera manejarias las conexiones... pues si es ADO se conecta a un AdoConnection, si es IBO a un IBOConnection, etc... pero puedes hacer un modulo de datos genérico exclusivo para administrar las conexiones siguiendo una filosofía similar... aunque al final siempre todo es a gusto personal...

Recuerda, si vas a hacer algo generico piensa entonces en generico
__________________
"Lo mejor de no saber hacer nada es que se tiene mucho tiempo libre."
Responder Con Cita
  #4  
Antiguo 01-09-2005
Avatar de lpmlpm
lpmlpm lpmlpm is offline
Miembro
 
Registrado: ago 2005
Posts: 136
Poder: 19
lpmlpm Va por buen camino
Aqui hay mas ligas a sitios donde se discuten algunos patrones de diseño, cada autor tambien les da su toque personal, por eso no os preocupéis de seguir fielmente las implementaciones, con que capten la idea del modelo de clases planteado es suficiente para que cada quien lo implemente a su manera

http://www.latiumsoftware.com/es/pascal/0030.php

Saludos
__________________
"Lo mejor de no saber hacer nada es que se tiene mucho tiempo libre."
Responder Con Cita
  #5  
Antiguo 02-09-2005
adlfv adlfv is offline
Miembro
 
Registrado: may 2005
Posts: 39
Poder: 0
adlfv Va por buen camino
Question

Hola compañeros.

Me he quedado un poco más deshubicado con sus respuestas, pues no he comprendido muy bien. Voy a contestarle a los dos en el mismo post.

Del post de Roman, No entiendo muy bien lo que dices, he estado pensando lo que comentas y a pesar que veo la ventaja de incluir esos "procedimientos sueltos" a la clase correspondiente, me daría más dolor de cabeza a la hora de cambiar la base de datos. A ver si me explico...

Segun comentas, a la hora de crear el objeto, tengo que utilizar el constructor de la clase concreta, con lo cual si tengo 100 querys creados dinámicamente en el código, tendría que hacer 100 cambios, no?

Cita:
Al comienzo de tu aplicación, suponiendo que decides usar Zeos, construyes un objeto TQueryZeos:

Código Delphi:
QueryGenerica := TQueryZeos.Create;
En cambio con las directivas (sé que es más feo, y más difícil de mantener) no tengo que cambiar nada más que el fichero include, pues se crea a partir del "sobrenombre" que toma la clase, osea: TMiQuery. A mi tampoco me gustan las directivas, pero he logrado estructurar el código de forma que sólo la unit UBD.pas es la única unit que tiene directivas de todo el código. Antes era diferente, tenía directivas por todos lados, y era casi para llorar ... era horrible mantener el código, hasta que se me ocurrió esto. Tal vez no entendí bien lo que propones, si me equivoco en algo o no entendí bien algo, por favor dime... Tal vez lo que planteas tiene realmente ventaja con lo de las fabricas que comentas (que dicho sea de paso, no estoy seguro de lo que es ...).

Del post de Lpmlpm, tampoco tengo claras algunas cosas ...

Comentas que no sueles trabajar con TTable, que sueles trabajar con querys o con ClientDataset. Pero cómo haces si necesitas un TTable? Por qué no sueles usar TTables? Por que si cambias un campo de la BD, se ve muy afectado por errores a consecuencia de dicho cambio? No tengo idea sobre los ClientDataset. Qué ventajas aportan?

Otra cosita, haces muchas referencias a la palabra "catálogo(s)". A qué te refieres cuando hablas de eso, a los metadatos de la base de datos (el diccionario de datos)?

El código que adjuntas lo comprendo a medias, pues la parte final no la comprendo bien, es como una forma de agregar condiciones "OR" a la clausula "WHERE"? Y qué consigues con esto? Es como un método de búsqueda por todos los campos donde aparezca un determinado texto? Está interesante ese método, pero no termino de digerirlo ...

Me gustaría poder hacer un DataModule genérico, de forma que sólo ponga las tablas y los campos, y al final en la ejecución como que se "transformen" los objetos a una clase más particular conservando los atributos base (como campos, consultas...). Es posible esto? Cómo?

Parece mentira, pero estoy trabajando con MySQL, y no he conseguido componentes que no fallen. por ODBC los drivers fallan muchos, confunden los tipos de datos al agregar columnas, por ADO, 3/4 de lo mismo... por MySQLDac, bueno... conseguí una versión vieja que no era compatible con D2005, y retoqué el código... y kabooom... fallá por todos lados. con Zeos, tengo problemas con las transacciones, AutoCommit y todo eso... No sé qué componentes usar. Recuerdo haber probado unos drivers de SciBit que "aparentemente" funcionaban bien, pero caducaban a los 30 días

Muchas gracias por su valioso tiempo y ayuda.

Lástima que no está causando mucho debate este post, pensé que más gente se incorporaría pues es algo muy interesante que desde mi punto de vista, tiene muchas ventajas.

Un cordial saludo...
Responder Con Cita
  #6  
Antiguo 02-09-2005
adlfv adlfv is offline
Miembro
 
Registrado: may 2005
Posts: 39
Poder: 0
adlfv Va por buen camino
Question

Ups... Tuve un error y se posteo 2 veces, por eso edito...

Última edición por adlfv fecha: 02-09-2005 a las 04:53:08.
Responder Con Cita
  #7  
Antiguo 02-09-2005
Avatar de vtdeleon
vtdeleon vtdeleon is offline
Miembro
 
Registrado: abr 2004
Ubicación: RD & USA
Posts: 3.236
Poder: 24
vtdeleon Va por buen camino
Cita:
Empezado por adlfv
Ups... Tuve un error y se posteo 2 veces, por eso edito...
Eso pasa a estas horas de la noche, me paso lo mismo hace unos minutos, problemas del servidor (constantes y a la misma hora).
__________________
Van Troi De León
(Not) Guía, Code vB:=Delphi-SQL, ¿Cómo?
Viajar en el tiempo no es teóricamente posible, pues si lo fuera, ya estarían aqui contándonos al respecto!
Responder Con Cita
  #8  
Antiguo 02-09-2005
Avatar de roman
roman roman is offline
Moderador
 
Registrado: may 2003
Ubicación: Ciudad de México
Posts: 20.269
Poder: 10
roman Es un diamante en brutoroman Es un diamante en brutoroman Es un diamante en bruto
Cita:
Empezado por adlfv
Segun comentas, a la hora de crear el objeto, tengo que utilizar el constructor de la clase concreta, con lo cual si tengo 100 querys creados dinámicamente en el código, tendría que hacer 100 cambios, no?
No.

Es cierto que implica más trabajo pero no donde estás pensando. Te pongo una idea un poco más explícita.

Considera primero estas definiciones de clases:


Código Delphi [-]
type
  TDataClientClass = class of TDataClient;

  TDataClient = class
  public
    constructor Create; virtual;
    function NewConnection: TDataConnection; virtual; abstract;
  end;

  TDataConnection = class
  public
    property Host: String;
    property User: String;
    property Password: String;
    property Database: String;
    property Params: TStrings;

    function NewQuery(Sql: String): TDataQuery; virtual; abstract;
    procedure Open; virtual; abstract;
  end;

  TDataQuery = class
  public
    property Sql: String;

    procedure SetInteger(ParamName: String; Value: Integer); virtual; abstract;
    procedure SetDouble(ParamName: String; Value: Double); virtual; abstract;
    procedure SetString(ParamName: String; Value: String); virtual; abstract;
    procedure SetDate(ParamName: String; Value: TDate); virtual; abstract;
    procedure SetTime(ParamName: String; Value: TTime); virtual; abstract;

    function GetInteger(FieldName: String; Default: Integer): Integer; virtual; abstract;
    function GetDouble(FieldName: String; Default: Double): Double; virtual; abstract;
    function GetString(FieldName: String; Default: String): String; virtual; abstract;
    function GetDate(FieldName: String; Default: TDate): TDate; virtual; abstract;
    function GetTime(FieldName: String; Default: TTime): TTime; virtual; abstract;

    procedure Run; virtual; abstract;
    procedure First; virtual; abstract;
    procedure Next; virtual; abstract;
    function EoQ: Boolean; virtual; abstract;
  end;

TDataQuery representa un Query genérico, tal como lo describí antes. Aquí, los métodos SetXXX establecen valores de parámetros mientras que los métodos GetXXX devuelven valores de campos una vez ejecutada la consulta. Es decir, lo equivalente a las propiedades Params y Fields de un Query. Como antes, todos estos métodos junto con Run (que abre o ejecuta la consulta) y First, Next y EoQ (para navegación) son abstractos y cada descendiente debe implementarlos.

Ahora bien, los objetos Query los produce un objeto DataConnection con su método NewQuery. DataConnection representa una conexión a un servidor; de ahí sus propiedades User, Password y Database. La propiedad Params serviría para establecer otro tipo de parámetros según sea el caso.

TDataConnection también es una clase con métodos abstractos. Un objeto DataConnection lo produce un objeto DataClient con su método NewConnection. DataClient englobaría todo lo que sea común a conexiones a servidores del mismo tipo.

Para cada motor que pienses usar necesitarás crear descendientes adecuados, por ejemplo:


Código Delphi [-]
type
  TZeosDataClient = class(TDataClient)
  end;

  TZeosDataConnection = class(TDataConnection)
  end;

  TZeosDataQuery = class(TDataQuery)
  end;

TZeosDataClient implementará NewConnection para producir un objeto TZeosDataConnection.

TZeosDataConnection implementará NewQuery para producir un objeto TZeosDataQuery.

TZeosDataQuery implementará los métodos SetXXX, GetXXX, Run y de navegación (posiblemente tomándolos de un TZQuery).

Ahora bien; tú puedes, como dices, tener cien objetos DataQuery en tu aplicación; pero si todos los produces con TDataConnection.NewQuery, el día que quieras cambiar el motor que uses, sólo tienes que cambiar la creación del DataConnection específico, que normalmente sólo será uno a menos que ataques varios servidores a la vez.

Aún en este caso no importa, porque los objetos DataConnection son producidos por un objeto DataClient genérico, del cuál sólo habrá uno por motor, digamos ZeosDataClient, encargado de producir cualquier conexión vía Zeos.

En resumen, el curso de tu aplicación podría ser:


Código Delphi [-]
var
  GlobalDataClient: TDataClient;

...

GlobalDataClient := TZeosDataClient.Create;

...

var
  DataConnection: TDataConnection;

...

DataConnection.Host := 'xxx.yyy.zzz';
DataConnection := GlobalDataClient.NewConnection;
DataConnection.User := 'usuario';
DataConection.Password := 'password';

DataConnection.Open;

...

var
  Poductos: TDataQuery;

...

Productos := DataConnection.NewQuery;
Productos.Sql := 'select * from productos';
Productos.Run;

while not Productos.EoQ do
begin

  {
    Aquí usas Productos.GetXXX para llenar tus controles de edición
  }

  Productos.Next;
end;

Cuando cambies de motor, digamos a Firebird, únicamente debes cambiar la línea

GlobalDataClient := TZeosDataClient.Create;

por

GlobalDataClient := TFirebirdDataClient.Create;

Todos los objetos DataConnection que hayas creado en tu aplicación serán ahora creados por TFirebirdDataClient.Connection (por el polimorfismo) y todos los objetos DataQuery que uses serán ahora creados por TFirebirdDataConnection (otra vez por el polimorfismo).

Claro que para ello deberás implementar TFirebirdDataClient, TFirebirdDataConnection y TFirebirdDataQuery, pero esto lo haces una sóla vez y sin tocar la aplicación principal excepto por la línea susomentada ( )

---------------------

Todo esto son sólo ideas al aire y ciertamente no originales y susceptibles de cambios.

Por ejemplo, todos los objetos producidos por NewConnection y NewQuery tendrán que ser destruídos en algún momento. Por ello, en lugar de clases abstractas yo usaría interfaces para aprovechar su autodestrucción. En lugar de GetXXX podrías publicar una función abstracta en la clase base que devuelva un TDataSet que puedes conectar a tus controles DBAware. No me gusta mucho esto último pues TDataSet tiene muchos métodos y propiedades que de alguna manera te permiten actuar directamente con la base de datos con lo cual puedes perder un poco de control. En todo caso podría usarse un TClientDataSet llenado internamente por DataQuery.

Lo importante aquí es simplemente el hecho de poder cambiar de un motor a otro sin- en teoría -grandes cambios.

Yendo más lejos. Para cambiar de motor debes, como dije, cambiar la línea

GlobalDataClient := TZeosDataClient.Create;

pero puedes hacer uso de una fabrica de clientes:


Código Delphi [-]
  TDataClientFactory = class
  public
    procedure RegisterClient(ClientId: String; DataClientClass: TDataClientClass);
    procedure NewClient(ClientId: String): TDataClient;
  end;

  var
    DataClientFactory: TDataClientFactory;

Usando un TStringList, por ejemplo, puedes guadar una asociación entre un identificador y la clase de cliente correspondiente, por ejemplo:

'zeos' => TZeosDataClient

El método RegisterClient('zeos', TZeosDataClient) guardaría dicha asociación de manera que puedes obtener un objeto DataClient partiendo del identificador:

GlobalDataClient := DataClientFactory.NewClient('zeos');

El método NewClient construiría el objeto DataClient polimórficamente partiendo de la referencia de clase asociada a la cadena 'zeos':


Código Delphi [-]
var
  Index: Integer;
  DataClientClass: TDataClientClass;

begin
  Index := FClients.IndexOf(ClientId);
  DataClientClass := TDataClientClass(FClients.Objects[Index]);

  Result := DataClientClass.Create;
end;

Cada motor que implementes deberá hacer uso de DataClientFactory.RegisterClient para registrar su case de objeto DataClient; por ejemplo en la sección initialization de la unidad donde implementes las clases del motor.

Suponte ahora que todas las clases asociadas a un motor las colocas dentro de un paquete. Si usas paquetes dinámicos puedes cargar el paquete asociado a un motor en tiempo d ejecución partiendo sólo del identificador del cliente ('zeos' por ejemplo), de manera que puedes añadir motores sin necesidad siquiera de recompilar la aplicación principal.

// Saludos

Última edición por roman fecha: 02-09-2005 a las 06:46:02.
Responder Con Cita
  #9  
Antiguo 02-09-2005
Avatar de roman
roman roman is offline
Moderador
 
Registrado: may 2003
Ubicación: Ciudad de México
Posts: 20.269
Poder: 10
roman Es un diamante en brutoroman Es un diamante en brutoroman Es un diamante en bruto
Por cierto, respecto a

Cita:
Empezado por adlfv
por MySQLDac, bueno... conseguí una versión vieja que no era compatible con D2005, y retoqué el código... y kabooom... fallá por todos lados
Yo he usado las componentes MyDac para Delphi 7 y me han funcionado muy bien. No sé en cuanto a Delphi 2005 pero estarás de acuerdo que si una versión antigua, no preparada para D2005, falla con D2005, no necesariamente es culpa de las componentes.

Si no mal recuerdo puedes bajar la versión de prueba que ya soporta D2005. Pruébalas y juzga otra vez.

// Saludos
Responder Con Cita
  #10  
Antiguo 02-09-2005
Avatar de lpmlpm
lpmlpm lpmlpm is offline
Miembro
 
Registrado: ago 2005
Posts: 136
Poder: 19
lpmlpm Va por buen camino
Cita:
Empezado por adlfv

Del post de Lpmlpm, tampoco tengo claras algunas cosas ...
Vamos a ello...

Cita:
Comentas que no sueles trabajar con TTable, que sueles trabajar con querys o con ClientDataset. Pero cómo haces si necesitas un TTable? Por qué no sueles usar TTables? Por que si cambias un campo de la BD, se ve muy afectado por errores a consecuencia de dicho cambio? No tengo idea sobre los ClientDataset. Qué ventajas aportan?
Para que puedo necesitar un TTable que no pueda hacer con un TQuery???... Desde donde lo veo, al trabajar con TTables te estas limitando a ti mismo, haz visto lo que se tarda tu programa cuando haces un Open de una tabla de miles de registros???... y una vez que empieza no hay como pararla, aparte de que te limitas a que en ese objeto solo vas a poder acceder a los campos de una sola tabla de tu modelo de base de datos, lo cual resuelves muy facil y elegantemente cuando con una query puedes tomar en un solo dataset información de aqui y de alla, enlazando varias tablas para poder mostrar la información de manera presentable e incluso segun el motor de BD que uses puedes mandar a pedirle al servidor solo los n primeros registros, etc. Como siempre lo he dicho todo a final de cuentas es a gusto personal y a mi no me gusta usar TTables, me siento mucho mas libre cuando uso Querys, además de que va mas a doc, puesto que siempre trabajo con servidores SQL.

Ahora los ClientDataset, estos son una especie de TQuery's especiales donde aparte de la propiedad SQL normal tambien tienen un UpdateSQL, InsertSQL y DeleteSQL, que te permite escribir sentencias SQL para cada una de las operaciones sobre los registros de tu Dataset, asi puedes controlar que cuando se cambie un registro en tu tabla el cambio afecte a solo x campos o bien que se refleje en otras tablas, etc... todo lo que se te ocurra que puedes hacer con sentencias SQL. Un ejemplo de esto es el componente IBDataset de la pestaña de Interbase. Creo que en zeos no existe algo similar, pero en suites como Asta, FIBPlus o IBObjects siempre vas a encontrar un componente que tengas estas características y es que realmente creo que es la forma adecuada de trabajar con componentes de datos.

Cita:
Otra cosita, haces muchas referencias a la palabra "catálogo(s)". A qué te refieres cuando hablas de eso, a los metadatos de la base de datos (el diccionario de datos)?
sorry, igual y es por las discrepancias culturales, yo le llamo catálogos a las tablas de la base de datos y tambien suelo llamarle asi por ende a las formas que las administran... sorry por los malosentendidos, trataré de ser mas claro en lo futuro.

Cita:
El código que adjuntas lo comprendo a medias, pues la parte final no la comprendo bien, es como una forma de agregar condiciones "OR" a la clausula "WHERE"? Y qué consigues con esto? Es como un método de búsqueda por todos los campos donde aparezca un determinado texto? Está interesante ese método, pero no termino de digerirlo ...
es de lo mas sencillo, tal como tu dices la idea del metodo ese es modificar una sql de modo que me devuelva un conjunto de datos con todos aquellos registros en los que encuentre un determinado texto en todos los campos que la query original tenga en su sentencia SELECT... suelo usarlo como dice el ejemplo en mis ventanas de búsquedas, pues para el usuario es muy practico escribir lo que se acuerde de un cliente ya sea su nombra o apellido o seguro social o lo que sea y el sistema le da las opciones de que escoja a quien se refiere específicamente mostrandole las concordancias que encontro... cuando sabes como usar esta tecnica tus sistemas quedan muy muy practicos y la usabilidad de los mismos se incrementa muchisimo.

el resto del algoritmo es adecuar la sentencia a partir de una variable que es donde se especifica como es que el motor de BD utilizado puede interpretar la sentencia de busqueda, por ponerte un ejemplo en MySQL no existe la sentencia CONTAINING solo se puede usar LIKE, pero en Firebird CONTAINING funciona mejor pues busca sin tomar en cuenta mayusculas y minusculas con lo que la busqueda es aun mas genérica... cosas asi son las que resuelves con este tipo de variables de configuración...

Cita:
Me gustaría poder hacer un DataModule genérico, de forma que sólo ponga las tablas y los campos, y al final en la ejecución como que se "transformen" los objetos a una clase más particular conservando los atributos base (como campos, consultas...). Es posible esto? Cómo?
Usa como te digo procedimientos genéricos que trabajen solo con conjuntos TDataset, y deja para tus clases a instanciar la desición final de cual motor de BD es la que se va a usar.

Cita:
Parece mentira, pero estoy trabajando con MySQL, y no he conseguido componentes que no fallen.
Al respecto solo comentar que con MySQL no se cuales sean los mejores componentes pues he trabajado poco con MySQL, vaya ninguna aplicación que le represente un verdadero desafio al motor de BD, los zeos me han funcionado para lo que he necesitado; pero si mi trabajo fuerte lo hago sobre Firebird, y te puedo comentar que los mejores componentes con los que he trabajado por sobre todos los que he usado con todos los motores de base de datos han sido los IBObjects, la integración con el motor de BD es estupenda, puedes actualizar registros individuales en el momento que necesites, es la única suite de todas que maneja sesiones persistentes (mantiene un cursor virtual de la solicitud activa en la base de datos para asi minimizar el tráfico de información por la red lo que la hace rapidisima para conexiones lentas y rápidas), además de que es de lo mas estable que hay. Además de que el motor de Firebird me parece muy superior y por muchisimo al motor de MySQL, las prestaciones son infinitamente superiores.

Cita:
Lástima que no está causando mucho debate este post, pensé que más gente se incorporaría pues es algo muy interesante que desde mi punto de vista, tiene muchas ventajas.
De verdad que tambien yo pensaba que habría mas movimiento por aqui, que mal que despues de mas de 130 visitas al hilo no haya tantas opiniones como uno quisiera. Creo que todos aqui nos dedicamos a esto mismo que aqui estamos discutiendo y es nuestro pan de cada dia, se me hace muy dificil pensar que a solo nosotros nos interese comentar este tipo de temas.

Saludos
__________________
"Lo mejor de no saber hacer nada es que se tiene mucho tiempo libre."
Responder Con Cita
  #11  
Antiguo 03-09-2005
adlfv adlfv is offline
Miembro
 
Registrado: may 2005
Posts: 39
Poder: 0
adlfv Va por buen camino
Hola de nuevo compañeros.

Muchisimas gracias por contestarme, de verdad aprecio la buena voluntad y el tiempo que dedican a leer lo que posteo, a pensar, y a responder. Que Dios se los pague y les multiplique su tiempo invertido.

De nuevo les responderé a los dos en el mismo post, pero pueden contestar todos a lo que deseen.

Roman: Creo que ya voy entendiendo el código que planteas, pero para serte sincero creo que necesitaré algo de tiempo para "hacer la digestión" de dicho código, y poder adaptarlo a los requerimientos del programa. Tengo una pequeña gran pregunta , y es... Esa técnica es compatible con desarrollo DB Aware? Porque en el la implementación que llevo a cabo, no es un desarrollo puro de clases/objetos, sino es un hibrido... Al comienzo era todo DB Aware, luego fuí viendo la ventaja de las clases, y ahora tengo una cosa rara hibrida DB Aware-clases/objetos que me tiene un poco despistado. La verdad es que no me termina de gustar la técnica DB Aware, pues no sé pero siempre me deja un sabor de boca de "estar haciendo algo mal"... No me parece una técnica buena como tal, pues cualquier cambio pequeño, supone muuucho tiempo para re-actualizar los campos de los DataSets.

Explico un poco más... La linea que sigo en el proyecto es algo tal que así...

(1) Cualquier entrada de datos, debe hacerse por clases... Tengo una clase llamada TOperaciones, donde estan métodos que me ayudan a realizar operacones, y las operaciones como tal, como son: CheckIn, CheckOut, Cambio de habitacion, Realizar un mantenimiento... Todos los datos de las operaciones se pasan como argumentos, en lugar de hacer asignaciones a propiedades... todavía no tengo claro si esto es lo correcto o no. Tambien me he planteado el hacer clases especificas para cada operacion, es decir, una clase general TOperacion (con lo "posiblemente generico" a todas las operaciones) y luego TOperacionCheckIn, TOperacionCheckOut... y que todas hereden de TOperacion. Todavía no sé cual es mejor, y estoy pensando en ventajas e inconvenientes, incluso para pruebas en DUnit y todo eso.
(2) Cualquier SELECT, UPDATE, y DELETE se hace mediante DB Aware. Esto lo veo más como una sección de mantenimiento... En general, existe un formulario de listado para cada tabla, y dicho formulario tiene métodos para modificar (se muestra un editor asociado), y por último se puede eliminar también de ese mismo formulario.

No tengo claro si esta técnica "hibrida" es correcta o no, y si me va a traer más dolores de cabeza que soluciones, si pudieran aportar también ideas sobre este tema, sería muy interesante (o tal vez deba abrir otro post para esto, para no mezclar ideas...)

Lpmlpm: La verdad es que no sé si todo lo que hace un TTable, lo puede hacer un TQuery, pero por ejemplo, recuerdo que me confundía todo a la hora de hacer una actualización o un borrado con un Query, es decir, recuerdo que alguna vez probé a hacer todo desde un Query como si fuera un TTable, y me daba algunos errores. Nunca he probado los DataClient, pero tal y como comentas parece la forma más "natural" y lógica de trabajar, pues una actualización generalmente tiene dependencias y una eliminación también, y es posible que haya algún interés en hacer más cosas aparte de la simple operación como tal, como por ejemplo realizar operaciones en cascada y cosas así.

La verdad es que me siento un poco abrumado y desorientado, pues me estoy enfrentando a algo más grande de lo que pensaba, y no tengo una "linea clara" a seguir...

Disculpen si pregunto tanto, o si molesto tanto con algunas cosas, pero me gustaría intentar plantear todo lo mejor posible desde el comienzo pues ya he tenido que "retocar" y "reconstruir" muchas cosas por malos planteamientos de base, y eso no es agradable .

Muchas gracias de nuevo por su ayuda, les estoy muy agradecido.

Un cordial ShowMessage('Saludo ');
Responder Con Cita
  #12  
Antiguo 05-09-2005
Avatar de lpmlpm
lpmlpm lpmlpm is offline
Miembro
 
Registrado: ago 2005
Posts: 136
Poder: 19
lpmlpm Va por buen camino
Cita:
Empezado por adlfv

Lpmlpm: La verdad es que no sé si todo lo que hace un TTable, lo puede hacer un TQuery, pero por ejemplo, recuerdo que me confundía todo a la hora de hacer una actualización o un borrado con un Query, es decir, recuerdo que alguna vez probé a hacer todo desde un Query como si fuera un TTable, y me daba algunos errores.
Depende de cual tipo de conexión hayas usado en su tiempo... seguro que en el BDE antiguo si debe haber habido diferencias, yo ya tiene mucho tiempo que no toco el BDE para nada...

Cita:
La verdad es que me siento un poco abrumado y desorientado, pues me estoy enfrentando a algo más grande de lo que pensaba, y no tengo una "linea clara" a seguir...

Disculpen si pregunto tanto, o si molesto tanto con algunas cosas, pero me gustaría intentar plantear todo lo mejor posible desde el comienzo pues ya he tenido que "retocar" y "reconstruir" muchas cosas por malos planteamientos de base, y eso no es agradable .
Ese es precisamente el problema que yo quería evitarme cuando empece a construir mi nucleo de aplicaciones, entre otras cosas el poder presentar siempre información actualizada, en todo momento, y hacer una buena explotación de la información que esta en la base de datos...

Por ejemplo me sucede con cierta regularidad que un cliente llega y me dice que tiene un sistema que alguien mas le hizo en digamos MySQL o en Access pero que hay muchas cosas que le gustaría saber a partir de la información que esta en su base de datos, osea me dicen "Quisiera poder sacar un listado de todos los clientes que me deben mas de tanto y que me los agrupara por colonia, y me diera asu vez subtotales por giro del cliente, etc... información que ahi esta pero que quien hizo la aplicación no se preocupo de poder mostrarle a su dueño... ese tipo de cosas ahora para mi son de lo mas sencillas, puedo montar una aplicación de reporteo donde el puede sacar el reporte que quiera y como lo quiera y que el cliente siga usando su aplicación original. eso creo que es un valor agregado que es padrisimo poder mantener basado en un nucleo e ir dandole mantenimiento a todo al mismo tiempo tanto a mis aplicaciones como a mis servicios de reporteo o de administracion de recursos y seguridad, etc...

Si no fue para este tipo de cosas que se inventó la POO entonces que alguien me diga para que fue

Y no solo eso sino que tambien el poder trabajar independientemente ya sea con Asta, IBO, ADO, etc... me permite poder agregar extensiones o complementaciones a sistemas de otras empresas, en fin, las posibilidades son infinitas...

Yo te exhorto a que continues con esa labor de crear código genérico, pues por experiencia propia te puedo asegurar que te verás muy beneficiado de ello en el futuro, es una lástima que no haya habido la participación que se hubiera esperado en este hilo... igual en otra ocasión en que las conciencias esten un poco mas despiertas
__________________
"Lo mejor de no saber hacer nada es que se tiene mucho tiempo libre."
Responder Con Cita
  #13  
Antiguo 06-09-2005
adlfv adlfv is offline
Miembro
 
Registrado: may 2005
Posts: 39
Poder: 0
adlfv Va por buen camino
Hola de nuevo

Cita:
..puedo montar una aplicación de reporteo donde el puede sacar el reporte que quiera y como lo quiera y que el cliente siga usando su aplicación original...
Con qué herramienta de reportes trabajas? Eso se puede hacer en FastReport? No sabes de alguna web u otra referencia que hable de "nucleos genéricos"? Porque se me hace difícil por mi corta experiencia imaginar un nucleo completamente general (o lo más general posible) para hacer muchas cosas...

Por ejemplo, he estado pensando en un nucleo para el acceso a menús, acciones, componentes, etc. basado en un control de accesos configurable, es decir, por ejemplo poder crear un Rol (o perfil) de usuario, y asignarle varios permisos como por ejemplo "Mantenimiento de clientes", "Listar estado de ganancias", "Eliminar artículo", y después asignar dicho rol a uno o varios usuarios. Sé que eso existe ya, pero casi todo lo que he visto no es compatible con D2005 .

Lo de los reportes que comentas me dejó impactado... La verdad no termino de imaginar cómo podría hacer eso...

Mi meta es hacer algo que aunque lleve trabajo, sea muy bueno, y bien depurado para el futuro.

Un cordial saludo, y muchas gracias por tu ayuda .

Sí, es una lástima que no se una más gente
Responder Con Cita
  #14  
Antiguo 06-09-2005
Avatar de lpmlpm
lpmlpm lpmlpm is offline
Miembro
 
Registrado: ago 2005
Posts: 136
Poder: 19
lpmlpm Va por buen camino
Cita:
Empezado por adlfv
Con qué herramienta de reportes trabajas? Eso se puede hacer en FastReport? No sabes de alguna web u otra referencia que hable de "nucleos genéricos"? Porque se me hace difícil por mi corta experiencia imaginar un nucleo completamente general (o lo más general posible) para hacer muchas cosas...
En la empresa donde trabajo tienen licencias de ReportBuilder...que es buenisimo, para mi el mejor reporteador que hay en el mercado...
Pero asi como te digo que lo genérico que tengo también es indistinto del reporteador que uses... al final de cuentas en todos pones componentes de unas clases determinadas sobre bandas o subcomponentes, asi que tambien independizar el nucleo del reporteador en cuestión tampoco esta muy guajiro... de hecho lo que tengo hecho es independiente del reporteador, pues habrá clientes y aplicaciones que no requieran el poder de ReportBuilder y con la misma tecnología empotrada sobre un FreeReports (la versión gratis de FastReports que tambien es muy bueno) es mas que suficiente...

Cita:
Por ejemplo, he estado pensando en un nucleo para el acceso a menús, acciones, componentes, etc. basado en un control de accesos configurable, es decir, por ejemplo poder crear un Rol (o perfil) de usuario, y asignarle varios permisos como por ejemplo "Mantenimiento de clientes", "Listar estado de ganancias", "Eliminar artículo", y después asignar dicho rol a uno o varios usuarios. Sé que eso existe ya, pero casi todo lo que he visto no es compatible con D2005 .
Te dire a grandes razgos como lo solucioné yo... los perfiles y permisos los llevo en la Base de Datos en unas estructuras ya predefinidas, y esas opciones que dices las manejo como TAction's dentro de un TActionList... como puedes saber es muy fácil recorrer las acciones dentro del ActionList al iniciar tu aplicación y actualizarlas a partir de lo que tengas almacenado en la BD... trabajando ordenadamente y si sigues tus propias politicas puedes hacer muy fácilmente tu gestor de permisos... y las acciones como sabes las puedes asociar a botones, menues, etc... y en un mismo objeto no puedes tener 2 TActions que se llamen igual asi que lo puedes individualizar a partir del propio nombre que les hayas asignado a las TActions, o si te quieres ver mas meticuloso a Tags que tu les asignes, etc...

Saludos
__________________
"Lo mejor de no saber hacer nada es que se tiene mucho tiempo libre."
Responder Con Cita
Respuesta



Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro


La franja horaria es GMT +2. Ahora son las 19:34:59.


Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi
Copyright 1996-2007 Club Delphi