FTP | CCD | Buscar | Trucos | Trabajo | Foros |
|
Registrarse | FAQ | Miembros | Calendario | Guía de estilo | Temas de Hoy |
|
Herramientas | Buscar en Tema | Desplegado |
#1
|
|||
|
|||
Discusión OOP: Un sistema de avisos, usado por varias clases
Hola, os comento este tema, al que ahora le estoy dando vueltas:
Se trata de incluir un sistema de avisos (visuales) en un programa. Hay ciertas entidades o clases en el proyecto que son suceptibles de generar avisos: Por ejemplo, la entidad 'factura' debe dar avisos de facturas pendientes, así como la clase 'agenda' genera avisos cuando hay una cita apuntada para el dia actual. Este tipo de funcionalidad seguro que a muchos ya se os ha presentado. Lo ideal sería mostrar información del aviso y un enlace al formulario dónde se puedan ver los datos de la entidad/objeto que ha generado dicho aviso. En este post me gustaria conocer/discutir cómo organizariais el modelado de objetos. Yo lo organizaria de la siguiente manera, hablando desde el punto de vista de UML. Cita:
Bueno, no sé si os habré liado mucho. Espero vuestros comentarios. Gracias por vuestra atención y un saludo. |
#2
|
||||
|
||||
Hola Bauhaus1975,
He visto que me haz enviado un mensaje al correo. Bueno, déjame decirte que si había visto el hilo y he estado organizando y reordenando en mi cabeza tus palabras y algo no me agrada mucho. Por empezar debo comentarte que tengo mis reservas de que basarse en ejemplos en Java para exponerlos a Delphi sea una buena idea. Sobre todo debido a que en Java hay demasiado purismos y existen ciertas diferencias en como resolver las cosas respecto a Delphi. Si bien tiendo a pensar en purismos, me parece que Java llega a un extremo exagerado y prefiero llegar a un purismo que me brinda Delphi. Debido a esto, no me atrevo a decir que la forma de como lo estás organizando este bien, ni tampoco puedo asegurar de que esté mal. Ten presente que cada persona puede ver al asunto de forma diferente, pero ello no implica que uno sea malo y otro sea bueno. En POO existe el gris... sino mira a aquel animal símbolo máximo de que en la naturaleza y en POO las cosas no son simples hechos concretos y una simple lógica binaria: el ornitorrinco. Asi que de entrada no tomes a mis palabras como la verdad absoluta. Si tu sientes que tu modelo sirve, quedate con él. No les mucha vuelta al asunto. Me gustarías que expusieras de otro modo tu modelo y que reordenaras tus ideas. Temo no interpretar apropiadamente tus palabras y me gustaría que seas un poco más gráfico/a. Disculpame pero no veo un punto de vista UML el asunto Lo que más me nubla y cuesta interpretar es la diferencia entre la clase Gestion y la clase Aviso... ¿al final que hace cada una? ¿Que relación existe entre una y otra? En Delphi existen las interfaces. Lee desde la página 801 de la Cara Oculta de Delphi 4 (puedes acceder a una copia del mismo en .pdf en el FTP del club), allí está expuesto el tema de las interfaces. Recomiendo además la lectura de la ayuda que ofrece Delphi al respecto. Habría que ver hasta que punto es bueno tener esa interfaz. En Delphi, no es tan necesario estar definiendo interfaces. De vez en cuando añadirlas provoca más ruido... en principio bastaría con una clase que implemente los métodos necesarios. Para asesorarte bien, necesitaría conocer mejor el negocio o contexto. Ver estas simples clases es la mitad del problema. Deberías saber que sin analizar en contexto no se puede tomar una decisión. Saludos, |
#3
|
|||
|
|||
Gracias Delphius por responder. He leido atentamente las lecturas que me has recomendado y he estudiado tus consejos. También he visto que puedo usar interfaces si lo considerase oportuno. Paso ahora a profundizar un poco más en el tema:
Cita:
Pensemos un programa para gestionar Clientes, Facturas, Citas... que cuenta con entidades como 'Agenda', para apuntar hitos o citas y 'Factura', entre otras. Necesito mostrar avisos, por ejemplo en forma de lista al entrar al programa, o cuando sea necesario. Por tanto, se generarán avisos por aquellas entidades suceptible de generar avisos (Agenda o Factura). Sería adecuado que la ventana / form que muestre los avisos presente un texto descriptivo del aviso y un enlace al formulario que trate el dato 'a revisar'. Cita:
La Clase 'Aviso' la había pensado para que ofreciera funcionalidad para mostrar los avisos. En una ventana con la lista antes comentada. La Interfaz 'Aviso' es la que define el/los método/s necesarios para obtener los avisos. A implementarse en las Clases de tipo 'Gestion'. Mi idea de usar una interfaz era para garantizar que el método que obtenga los avisos en cada entidad garantizara homogeneidad, es decir, que ofrezca un patrón de comportamiento. Por esto había pensado en usar una interfaz, si bien no es obligatorio. Y no porque estuviera pensando en términos de Java o traduciendo un programa de Java. La idea de este post era dialogar con gente que ha tenido que implementar este caso para ver qué decisión han optado, o áprender de personas más expertas su opnión de como implementar este caso. Es por eso que a lo mejor necesito que me acerqueis a los 'purismos' que comentas de Delphi para ir implementando este caso de la mejor manera. Muchas gracias por la atención y un saludo. |
#4
|
||||
|
||||
Estamos hablando en formas astractas. (Class TAviso, TGestion) y cómo las he de utilizar luego?
A cómo dijo delphius anteriormente, eso depende del punto de vista de cada uno. En lo personal YO antes de escribir e implementar una clase la visualizo desde el punto de como la he de llamar, cómo la quiero llamar y qué grado de fácilidad y flexivilidad quiero que tenga cunado la quiera llamar. Si yo fuera tú, primero me pondría a pensar en que grado de dificultad que tendré al momento de utilizar clases o interfaces, así cómo también implementarlas. Según la tarea y objetivo que buscas yo mejor dejaría las clases a un lado me utilizaría mejor algun tipo de función en, por ejemplo, la unidad de facturación que me devuelva todos los avisos de facturación y así sucesivamente. Saludos. |
#5
|
||||
|
||||
Hola,
Creo que confunde un poco la terminología que usas. En principio, tal como yo lo entendería, para un sistema de avisos no necesitas ninguna interfaz ni clase ancestra común. Simplemente necesitarías un objeto central GestorAvisos con un método público ColocarAviso: Código:
+------------------------------+ | GestorAvisos | +------------------------------+ | +ColocarAviso(Aviso: TAviso) | +------------------------------+ No obstante, lo que tú tienes más parece ser una lista de pendientes. En tal caso, creo que podrías partir de una interfaz: Código:
+-----------------------+ | IEntidad | +-----------------------+ | +getListaPendientes() | +-----------------------+ Código:
+--------------------------------------------+ | GestorPendientes | +--------------------------------------------+ | -FEntidades | +--------------------------------------------+ | +RegistrarEntidad(Entidad: IEntidad) | | +EnumerarPendientes() | +--------------------------------------------+ // Saludos |
#6
|
||||
|
||||
¡Orden en la sala!
Siempre quise decir eso Pues creo que no se termina de comprender, bien el asunto. Y todo se debe a los nombres tanto de las clases como de sus métodos. Vayamos al primer punto elemental a cuestionar: ¿Quién da a conocer los avisos? ¿Y quien los genera? No es lo mismo gestionar, que generar A como yo lo entiendo, TFactura y TAgenda son quienes GENERAN avisos (aunque creo que el término más adecuado sería PUBLICAR), los cuales son luego administrados por una sola clase TGestorAviso (por darle un nombre). TGestorAviso tiene por tanto la responsabilidad de ordenarlos, clasificarlos, etc. En pocas, hará con lo avisos lo que deba hacer. Al final, es ésta clase quien terminará mostrando los avisos al usuario. Una manera de enfocar el tema es algo similar a como lo detalle roman. TAgenda, TFactura y demás clases interesadas se registran a este TGestor. TGestor recibirá de todas las entidades que se registren los avisos. Si no me equivoco en el término, creo que aquí podría ser útil las "clases amigas". Algo similar a como funciona TParam y TParams, y otras colleciones. Todas las entidades que publiquen avisos debe heredar de una clase base, a falta de imaginación la llamo: TPublicadorAviso. TPublicadorAviso está diseñado para mantener una referencia del Gestor al que se asociará y contará con al menos tres métodos: Suscribirse(), PublicarAviso() y Desuscribrirse() (sería mejor Reigstrar, pero no encuentro su antónimo) Código:
+------------------------------------------+ | TPublicadorAviso | +------------------------------------------+ | - FGestorAviso: TGestorAviso | +------------------------------------------+ | + Suscribirse(GestorAviso: TGestorAviso) | | + PublicarAviso(Aviso: TAviso) | | + Desuscribirse | +------------------------------------------+
Como se ve, se delegan algunas cosas a un TGestorAviso, que muy posiblemente sea un Singleton. Ahora TGestorAviso debe tener dos listas, una para llevar sus Publicadores y otra con los avisos. Ya podemos ir viendo algunos de sus métodos: Código:
+-------------------------------------------+ | TGestorAviso | +-------------------------------------------+ | - FPublicadores: TObjectList | | - FAvisosPendientes: TObjectList | +-------------------------------------------+ | + Registrar(Publicador: TPublicadorAviso) | | + AgregarPendientes(Aviso: TAviso) | | + Eliminar(Publicador: TPublicadorAviso | | + MostrarAviso(Aviso) | +-------------------------------------------+ Registrar() debería agregar en la lista de publicadores al PublicadorAviso, de forma similar, AgregarPendientes() agrega el aviso a su lista. Un enfoque similar, habría que determinar cual es el más conveniente, es que el TGestorAviso no maneje la lista, sino que cada PublicadorAviso maneje la propia. En este caso, tal vez TGestorAviso disponga de métodos como ObtenerPendientes() y reciba como parámetro un TPublicadorAviso, y de este modo TPublicadorAviso le hace llegar o conocer sus avisos. Luego, el TGestor "recorre" el listado de estos avisos y actúa en razón a ellos. Ahora podría ser útil un PublicarLista. Código:
+------------------------------------------+ | TPublicadorAviso | +------------------------------------------+ | - FGestorAviso: TGestorAviso | | - FListaPendientes: TObjectList | +------------------------------------------+ | + Suscribirse(GestorAviso: TGestorAviso) | | + PublicarAviso(Aviso: TAviso) | | + PublicarLista | | + Desuscribirse | +------------------------------------------+ Saludos, |
#7
|
||||
|
||||
Yo me permito insistir un poco en la nomeclatura. El hecho de haya facturas pendientes es una situación, no un aviso. Porque mientras persista dicha situación, el objeto fuente tendría que estar generando el mismo aviso, que es lo que no me cuadra.
Ahora, más allá de los términos exactos, creo que éste es un caso en donde las interfaces aplican muy bien. Una entidad agenda y una entidad facturas, en principio podrían no tener nada en común salvo el hecho de poder publicar una lista de pendientes. En este caso, derivar ambas de una clase común podria ser algo forzado y no natural para efectos de la aplicación. Cita:
Cita:
Cita:
que básicamente sería lo que llamé EnumerarPendientes. // Saludos |
#8
|
||||
|
||||
Aquí va mi idea más desarrollada:
Partiremos de una clase TPendiente (si quieren llamarla TAviso está bien ) que represente un pendiente:
Fuente sería el nombre de la entidad que genera el pendiente o aviso y Descripcion sería la descripción del pendiente. Obviamente la estructura real de esta clase dependerá de lo que se quiera publicar. Luego tenemos la interfaz que publica mensajes
Lo que está entre corchetes es el GUID (global unique identifier) de la interfaz y es necesario declararlo (Ctrl+Shift+G) para poder moldear una interfaz con el operador as. getListaPendientes deberá llenar la lista que se le pasa como parámetro con los pendientes que haya. Después tenemos el gestor de pendientes:
Este gestor mantiene una lista interna de las entidades que se registren para publicar pendientes. Obsérvese que es una lista de interfaces, no de clases. Dicha lista se crearía en el constructor Create y se destruiría en el destructor. Un entidad que quiera publicar pendientes se registra con el método RegistrarPublicador, quien simplemente añade la entidad a la lista interna. El método EnumerarPendientes es el encargado de recorrer todos los pendientes de todas las entidades registradas. Pero este método no publica el pendiente, ya que esto es materia de la parte visual de la aplicación, que decidirá si pone el pendiente en un ListView, en un Memo, o lo manda por correo, o cualquier cosa que quiera hacer con el pendiente. Para ello, al método se le pasa como parámetro un procedimiento, que será el encargado de publicar el pendiente. El tipo TPublicarPendiente se define asi:
Así, por ejemplo, el formulario principal puede declarar un método
que pondrá el pendiente en el control visual que desee. Cuando se requiera listar los pendientes se llamará al gestor:
El método EnumerarPendientes podría implementarse así:
Como dije antes, EnumerarPendientes simplemente recorre la lista de pendientes más no hace nada con ellos excepto llamar al método que se le pasa como parámetro. Esto nos permite usar distintos métodos según sea el caso. Por ejemplo, además del método PublicarPendiente del formulario principal, podríamos tener un EnviarPendiente, que se encargue de mandar el pendiente por correo, de manera que
tendría el efecto de enviar por correo la lista de pendientes. // Saludos |
#9
|
||||
|
||||
Cita:
Hace falta claridad en las explicaciones de Bauhaus1975 sobre el contexto en forma intregral (macro) del problema y no en éstas simples clases. Cita:
Cita:
Efectivamente, creo que debemos partir de este punto. ¿Qué debemos entender por aviso? Además, me suena un tanto extraño de que entidades, un tanto dispares como ser TAgenda y Factura deban dar respuesta a una misma problemática. ¿Que és este TAgenda? ¿Qué es esta TFactura? ¿Qué tiene que ver citas, agendas y facturas? No se, pero no me gusta mucho mezclar citas con negocios. Yo pido que Bauhaus1975 se explique apropiadamente. Cita:
Saludos, |
#10
|
||||
|
||||
Impresionante roman.
Cuando estaba respondiendo ví que habías puesto algo más concreto. Me sorprende lo de pasar como parámetro el procedimiento. No se me hubiera ocurrido. Hoy aprendo algo nuevo: desconocía TInterfaceList. Saludos, |
#11
|
||||
|
||||
Cita:
// Saludos |
#12
|
||||
|
||||
Cita:
// Saludos |
#13
|
||||
|
||||
Cita:
Gracias por el dato. Saludos, |
#14
|
|||
|
|||
Muy buenas compañeros. Me alegra ver como ha crecido el interés en el post (y eso que había empezado flojo).
Es cierto, probablemente lo que nos parece bien explicado no se entienda por diferente uso de términos. Y sin duda, los nombres que tomé inicialmente eran demasiado genéricos. Cita:
Hasta donde yo había pensado, una descripción y un enlace que pueda abrir un formulario para acceder al dato y poder cambiar esa situación. Y como apunto más adelante una propiedad 'prioridad' = (urgente, normal) Cita:
Es más, por eso he puesto así el ejemplo. Porque el sub-sistema que estamos definiendo quede aislado del contexto. Es decir, igual que hemos hablado de facturas y agenda podemos pensar en un artículo que se haya agotado y hay que pedirlo. (serviría a cualquier proyecto con entidades suceptibles de generar 'pendientes') Otra cosa, como apuntais: No todos los avisos son iguales. Me explico, a lo mejor es bueno mostrar 'de vez en cuando' en la ventana de avisos los artículos que faltan, pero a lo mejor en cuanto a las facturas pendientes de pago es suficiente con que aparezcan al iniciarse el programa. Por tanto puede que el objeto que represente a un item 'aviso' tenga 'prioridad' = (urgente, normal) prioridad normal podría ser sólo para mostrar en la ventana al iniciarse el programa, y urgente para mostrar periódicamente. He leido detenidamente el análisis de Roman y me parece lo más acertado. Y, el uso del InterfaceList y el paso del procedimiento como parámetro impresionante ejemplo de elegancia. Entonces podemos resumir las clases y sus responsabilidades: TPendiente: representa a un item. con elementos como (prioridad, fuente, descripcion) IPublicador: La interfaz que define el método para obtener los pendientes de clase con getListaPendientes(Lista: TList); TGestorPendientes: Se encarga de ofrecer el registro a las clases que quieran publicar, y de enumerar los items a publicar TPublicarPendiente: Un tipo procedimiento para que la acción 'publicar' sea cualquier cosa que implemente una clase encargada del tema gráfico u otros. Podría quedar por sacar punta a como añadir la acción de abrir formulario de la entidad a la que se refiera el item (TPendiente) Para el tema de controlar avisos de alta prioridad se puede incorporar un sistema de control periódico, si no recuerdo mal esto se ha tratado antes en otros post. (Abajo en la lista de relacionados puede verse algún ejemplo) Espero que este laborioso estudio ofrezca información de este sistema y muchos compañeros puedan implementarlo cuando les haga falta. Yo estoy ya deseando de implementarlo, a ver si puedo sacar hueco porque estoy con varias cosas a la vez. Un saludo. Última edición por Bauhaus1975 fecha: 17-02-2009 a las 18:51:31. |
#15
|
||||
|
||||
Cita:
Cita:
Por otra parte estaba pensando que una ampliación que puede hacerse es la de incluir al gestor de mensajes en la interfaz IPublicador con objeto de que cada publicador pueda notificar al gestor cuando hay algún cambio en su lista de pendientes. // Saludos |
#16
|
|||
|
|||
Cita:
|
|
|
Temas Similares | ||||
Tema | Autor | Foro | Respuestas | Último mensaje |
Discusión sobre Patrones de diseño | Delphius | OOP | 3 | 31-05-2008 20:38:03 |
Agenda con Avisos | luxus | Conexión con bases de datos | 5 | 11-12-2007 22:23:38 |
Avisos parroquiales | jam | Humor | 3 | 07-04-2006 09:15:31 |
Capturar Errores y/o avisos | sergio_015 | Varios | 5 | 11-02-2004 06:06:35 |
lista de discusion | allende | Varios | 2 | 03-12-2003 20:21:19 |
|