FTP | CCD | Buscar | Trucos | Trabajo | Foros |
|
Registrarse | FAQ | Miembros | Calendario | Guía de estilo | Temas de Hoy |
|
Herramientas | Buscar en Tema | Desplegado |
|
#1
|
|||
|
|||
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:
Cita:
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 |
#2
|
||||
|
||||
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:
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:
Para cada motor que desees manejar derivas una clase:
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:
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 |
#3
|
||||
|
||||
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:
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."
|
#4
|
||||
|
||||
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."
|
#5
|
|||
|
|||
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:
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... |
#6
|
|||||||
|
|||||||
Cita:
Cita:
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:
Cita:
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:
Cita:
Cita:
Saludos
__________________
"Lo mejor de no saber hacer nada es que se tiene mucho tiempo libre."
|
#7
|
|||
|
|||
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. |
#8
|
||||
|
||||
Cita:
__________________
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! |
|
|
|