Llegamos a la penúltima entrada sobre el tema de patrones de Diseño, en esta ocasión trabajaremos un nuevo ejemplo pero enfocado en el Patrón Decorator.......
Así como en la mayoría de los trabajados, vamos a apoyarnos en el libro Head First Design Patterns para crear nuestro ejemplo, un ejemplo sencillo con una problemática por resolver, al final compartiré un enlace donde se podrá descargar el proyecto Java que trabajaremos...
¿Que es?
El Patrón Decorator Permite agregar funcionalidades y responsabilidades a objetos de forma dinámica y transparente para el usuario, esto se realiza por medio de relaciones con otras clases extendiendo su funcionalidad al incorporar las de las clases asociadas, de esta forma el patrón no es dependiente de la Herencia ya que aunque esta puede jugar un papel importante, prevalece el uso de conceptos como la composición al momento de definir comportamientos....
Como vemos en el diagrama de clases, tenemos una SuperClase de la cual heredan clases concretas y las clases Decoradoras que a su vez también pueden ser clases padre de clases decoradoras Concretas...... ¿Que?.....
¿Pero si dijimos que el patrón no es dependiente de la herencia porque hablamos de SuperClases?
La pregunta anterior es muy común, ya que puede llegar a ser muy confusa la definición, sin embargo la respuesta es muy simple, con el Decorator la herencia es requerida ya que los decoradores deben tener el mismo tipo de los objetos a decorar pero no se utiliza la Herencia para lograr el comportamiento....... el comportamiento se da al realizar la composición de decoradores con los componentes concretos.....(Cuando Hablamos de comportamiento, nos referimos a las nuevas funcionalidades que usamos...)
Si se basará en la herencia el comportamiento solo se definiría estáticamente en tiempo de compilación dependiendo de lo que las clases padres hereden, a demás todas las subClases deben heredar comportamientos que puede que no vayan a utilizar.....
El Problema.
Un restaurante de comidas rápidas ofrece 3 tipos de combos (Combo Básico, Combo Familiar, Combo Especial) cada combo tiene características diferentes en cuanto a cantidad, porciones, salsas entre otros, el restaurante también ofrece la posibilidad de aumentar el pedido mediante diferentes porciones adicionales (Tomate, Papas, Carne, Queso), se desea crear un sistema de pedidos que permita al usuario seleccionar el combo deseado, así como armar su propio pedido con las porciones adicionales, el sistema deberá informar sobre el pedido del usuario y el valor total del mismo.
La Solución.
En el problema nos están solicitando algo puntual, fácilmente deducimos que tenemos una familia de Combos en la que podemos usar la herencia, pero si atacáramos nuestro problema solo con este concepto, entonces tendríamos que crear clases concretas por cada posible combinación de porciones adicionales, tal vez esto no seria problema y el sistema funcione, pero si queremos realizar varias combinaciones para crear nuevos pedidos tendríamos que entrar a modificar el código fuente y luego ejecutar nuevamente la aplicación para que los cambios puedan ser visualizados.... por esta razón anteriormente dijimos que usando la herencia el comportamiento solo se definiría estáticamente basados en lo heredado por las clases Padre....
La solución que nos da el patrón Decorator es solo utilizar la herencia para que las clases Decorator tengan el mismo tipo de los objetos a decorar y utilizaremos la composición para determinar el comportamiento de forma dinámica y en tiempo de ejecución basados en concepto de "Usa" relacionando los decoradores con los componentes concretos, así no modificaríamos la lógica de las clases existentes cada vez...... Veamos....
La Aplicación.
Crearemos nuestro sistema ajustándonos al diagrama de clases del patrón, tenemos una SuperClase Combo que representa los combos de comidas rápidas disponibles de la cual heredan los tipos ComboBasico, ComboFamiliar y ComboEspecial, también hereda de el las clases Decorator, en este caso tenemos la clase de Adicionales como el decorador y a su vez las hijas que corresponden a cada porción, siendo estas las clases decoradoras concretas....
Vamos a explicar la Aplicación agrupándola en sus 3 paquetes: Componentes, Decoradores y Principal...
Como vemos en el diagrama de clases, tenemos una SuperClase de la cual heredan clases concretas y las clases Decoradoras que a su vez también pueden ser clases padre de clases decoradoras Concretas...... ¿Que?.....
¿Pero si dijimos que el patrón no es dependiente de la herencia porque hablamos de SuperClases?
La pregunta anterior es muy común, ya que puede llegar a ser muy confusa la definición, sin embargo la respuesta es muy simple, con el Decorator la herencia es requerida ya que los decoradores deben tener el mismo tipo de los objetos a decorar pero no se utiliza la Herencia para lograr el comportamiento....... el comportamiento se da al realizar la composición de decoradores con los componentes concretos.....(Cuando Hablamos de comportamiento, nos referimos a las nuevas funcionalidades que usamos...)
Si se basará en la herencia el comportamiento solo se definiría estáticamente en tiempo de compilación dependiendo de lo que las clases padres hereden, a demás todas las subClases deben heredar comportamientos que puede que no vayan a utilizar.....
El Problema.
Un restaurante de comidas rápidas ofrece 3 tipos de combos (Combo Básico, Combo Familiar, Combo Especial) cada combo tiene características diferentes en cuanto a cantidad, porciones, salsas entre otros, el restaurante también ofrece la posibilidad de aumentar el pedido mediante diferentes porciones adicionales (Tomate, Papas, Carne, Queso), se desea crear un sistema de pedidos que permita al usuario seleccionar el combo deseado, así como armar su propio pedido con las porciones adicionales, el sistema deberá informar sobre el pedido del usuario y el valor total del mismo.
La Solución.
En el problema nos están solicitando algo puntual, fácilmente deducimos que tenemos una familia de Combos en la que podemos usar la herencia, pero si atacáramos nuestro problema solo con este concepto, entonces tendríamos que crear clases concretas por cada posible combinación de porciones adicionales, tal vez esto no seria problema y el sistema funcione, pero si queremos realizar varias combinaciones para crear nuevos pedidos tendríamos que entrar a modificar el código fuente y luego ejecutar nuevamente la aplicación para que los cambios puedan ser visualizados.... por esta razón anteriormente dijimos que usando la herencia el comportamiento solo se definiría estáticamente basados en lo heredado por las clases Padre....
La solución que nos da el patrón Decorator es solo utilizar la herencia para que las clases Decorator tengan el mismo tipo de los objetos a decorar y utilizaremos la composición para determinar el comportamiento de forma dinámica y en tiempo de ejecución basados en concepto de "Usa" relacionando los decoradores con los componentes concretos, así no modificaríamos la lógica de las clases existentes cada vez...... Veamos....
La Aplicación.
Crearemos nuestro sistema ajustándonos al diagrama de clases del patrón, tenemos una SuperClase Combo que representa los combos de comidas rápidas disponibles de la cual heredan los tipos ComboBasico, ComboFamiliar y ComboEspecial, también hereda de el las clases Decorator, en este caso tenemos la clase de Adicionales como el decorador y a su vez las hijas que corresponden a cada porción, siendo estas las clases decoradoras concretas....
Vamos a explicar la Aplicación agrupándola en sus 3 paquetes: Componentes, Decoradores y Principal...
Componentes.
Este paquete contiene la Jerarquía de componentes del patrón, aquí tenemos la SuperClase Combo y sus hijas concretas, el Combo es una clase Abstracta que define una descripción que cada subClase definirá (de que se compone el combo), así como también el método abstracto valor que sera definido por cada subClase que lo implemente.
Las estructura de las clases concretas también es simple, definirán el precio del combo correspondiente y asignaran una descripción. (Cada Clase es igual...)
Decoradores.
Los Decoradores en este caso serán las porciones adicionales, tenemos una clase AdicionalesDecorator que es el decorador principal del cual implementaran los decoradores concretos, esta clase es hija de la clase Combo y proporciona un método abstracto descripcion() para anexar a la descripción del combo, la porción seleccionada por el usuario...
Cada Decorador concreto implementa el método getDescripcion(), agregando a la descripción la porción seleccionada por el usuario, también implementa el método valor() de la clase Combo, en el cual se agrega al valor del combo, el precio de la porción....... como vemos en estos decoradores concretos se aplica la composición en el momento que creamos el objeto combo (la clase Combo es abstracta por lo tanto no puede ser instanciada directamente, por lo tanto el objeto que llega como parámetro al constructor se creó previamente por medio de polimorfismo)....
Principal.
Finalmente en el paquete principal tenemos la clase donde se ejecuta el programa y la ventana que representa el Menú de Selección desde el cual el usuario puede seleccionar el Combo y las porciones a pedir, al enviar el pedido el sistema de forma dinámica por medio del patrón Decorator valida las combinaciones solicitadas y calcula el precio Total del pedido.
Y listo, al aplicar el patrón pudimos crear un menú de comidas que puede ser construido por el usuario, sin necesidad de modificar cada vez el código fuente ya que el armado del menú se realiza en tiempo de ejecución gracias al Decorator!!!
Conclusiones.
Como vemos el patrón nos facilita enormemente el trabajo en este tipo de problemáticas imaginen tener que usar solo la herencia para crear un Menú que cumpla con las necesidades de cada cliente, cuantas combinaciones se tendrían que realizar por cada posible combinación, en cambio con el patrón tan solo necesitamos las clases Bases y los decoradores, gracias a la lógica aplicada, las combinaciones se realizan solas....
La lógica del patrón actúa como envoltorios dependiendo de los posibles tipos de combos y adicionales seleccionadas, si queremos un combo familiar con papas extra entonces se crea esta agrupación, si además escogemos una porción de queso entonces dicha porción es agregada a la anterior...
Cuando usamos este patrón reducimos las posibilidades de generar errores o defectos secundarios no deseados, ya que si queremos añadir nuevas funcionalidades, agregamos nuevo código sin modificar el existente...
Con el Patrón los diseños son resistentes al cambio y lo suficientemente flexibles como para satisfacer necesidades cambiantes.
El patrón a demás de utilizar la herencia y la composición también aplica conceptos de polimorfismo que pueden ser evidenciados en el código fuente..
Como en todo la practica es fundamental, por eso los invito a repasar el Patrón y descargarlo para un mayor entendimiento.
Referencias.
Head First Design Patterns
Descarga.
El ejemplo es muy básico y simple, para entenderlo mejor les dejo el link de descarga para que lo prueben ;)....está en Eclipse pero si usas NetBeans te interesará esta entrada con un vídeo paso a paso de como abrirlo en el......... [recuerden que es gratis.....nada cuesta opinar, compartir o agradecer :)]
También te podría Interesar.
Este paquete contiene la Jerarquía de componentes del patrón, aquí tenemos la SuperClase Combo y sus hijas concretas, el Combo es una clase Abstracta que define una descripción que cada subClase definirá (de que se compone el combo), así como también el método abstracto valor que sera definido por cada subClase que lo implemente.
public abstract class Combo { String descripcion = ""; public String getDescripcion() { return descripcion; } public abstract int valor(); }
Las estructura de las clases concretas también es simple, definirán el precio del combo correspondiente y asignaran una descripción. (Cada Clase es igual...)
public class ComboBasico extends Combo{ public ComboBasico() { descripcion="Porcion de Papas Fritas, " + "salsa, queso, amburgueza sencilla, gaseosa"; } @Override public int valor() { return 6200; } }
Decoradores.
Los Decoradores en este caso serán las porciones adicionales, tenemos una clase AdicionalesDecorator que es el decorador principal del cual implementaran los decoradores concretos, esta clase es hija de la clase Combo y proporciona un método abstracto descripcion() para anexar a la descripción del combo, la porción seleccionada por el usuario...
public abstract class AdicionalesDecorator extends Combo{ public abstract String getDescripcion(); }
Cada Decorador concreto implementa el método getDescripcion(), agregando a la descripción la porción seleccionada por el usuario, también implementa el método valor() de la clase Combo, en el cual se agrega al valor del combo, el precio de la porción....... como vemos en estos decoradores concretos se aplica la composición en el momento que creamos el objeto combo (la clase Combo es abstracta por lo tanto no puede ser instanciada directamente, por lo tanto el objeto que llega como parámetro al constructor se creó previamente por medio de polimorfismo)....
public class Carne extends AdicionalesDecorator{ Combo combo; public Carne(Combo combo) { this.combo=combo; } @Override public String getDescripcion() { return combo.getDescripcion()+" , Porcion de Carne"; } @Override public int valor() { return 2500+combo.valor(); } }
Principal.
Finalmente en el paquete principal tenemos la clase donde se ejecuta el programa y la ventana que representa el Menú de Selección desde el cual el usuario puede seleccionar el Combo y las porciones a pedir, al enviar el pedido el sistema de forma dinámica por medio del patrón Decorator valida las combinaciones solicitadas y calcula el precio Total del pedido.
Y listo, al aplicar el patrón pudimos crear un menú de comidas que puede ser construido por el usuario, sin necesidad de modificar cada vez el código fuente ya que el armado del menú se realiza en tiempo de ejecución gracias al Decorator!!!
Conclusiones.
Como vemos el patrón nos facilita enormemente el trabajo en este tipo de problemáticas imaginen tener que usar solo la herencia para crear un Menú que cumpla con las necesidades de cada cliente, cuantas combinaciones se tendrían que realizar por cada posible combinación, en cambio con el patrón tan solo necesitamos las clases Bases y los decoradores, gracias a la lógica aplicada, las combinaciones se realizan solas....
La lógica del patrón actúa como envoltorios dependiendo de los posibles tipos de combos y adicionales seleccionadas, si queremos un combo familiar con papas extra entonces se crea esta agrupación, si además escogemos una porción de queso entonces dicha porción es agregada a la anterior...
Cuando usamos este patrón reducimos las posibilidades de generar errores o defectos secundarios no deseados, ya que si queremos añadir nuevas funcionalidades, agregamos nuevo código sin modificar el existente...
Con el Patrón los diseños son resistentes al cambio y lo suficientemente flexibles como para satisfacer necesidades cambiantes.
El patrón a demás de utilizar la herencia y la composición también aplica conceptos de polimorfismo que pueden ser evidenciados en el código fuente..
Como en todo la practica es fundamental, por eso los invito a repasar el Patrón y descargarlo para un mayor entendimiento.
Referencias.
Head First Design Patterns
Descarga.
El ejemplo es muy básico y simple, para entenderlo mejor les dejo el link de descarga para que lo prueben ;)....está en Eclipse pero si usas NetBeans te interesará esta entrada con un vídeo paso a paso de como abrirlo en el......... [recuerden que es gratis.....nada cuesta opinar, compartir o agradecer :)]
También te podría Interesar.
- Ejemplo Pátron Singleton.
- Ejemplo Pátron Abstract Factory
- Ejemplo Pátron Delegate.
- Ejemplo Pátron Observer.
- Ejemplo Modelo Vista Controlador.
- Que son los Patrones de Diseño ?
- Manejo de Excepciones en Java
- Conceptos Básicos de Programación Orientada a Objetos.
¿Hay algo que quieras anexar o comentar sobre esta entrada? no dudes en hacerlo.....y si te gustó, te invito a compartir y Suscribirte ingresando al botón "Participar en este sitio" para darte cuenta de mas entradas como esta ;)
muy claro explicados todos los patrones gracias por compartir!
ResponderEliminarNO CUESTA NADA AGRADECER GENTE y menos buenos articulos y sobre todo claros y consciso.
Hola Muchas Gracias, es algo muy facil de hacer que la mayoria de las veces se omite, pero almenos con comentarios como este uno se motiva a continuar... ;) Gracias por comentar, Un Saludo y me alegra que te gustara
EliminarMuy bueno!!!! Y si este ejemplo lo quisiera hacer con una BD???
ResponderEliminarHola, que pena no habia visto tu comentario, y claro que se puede, seria lo mismo, lo que cambia es de donde obtienes la información.....eso si, tendrias que tener cuidado de no realizar tantas transacciones o llamados a la BD, si es algo pequeño no hay problema, pero si es algo grande depronto pueda bajar el rendimiento..... para evitar eso puedes hacer un unico llamado a la BD, cargar los datos y procesarlos como listas y con estas es con las que trabajarias.... un saludo y gracias por comentar....
Eliminarah no! yo me lo sabía con combinaciones del café! ;)
ResponderEliminarGracias Cristian!
La verdad que muy clara y entretenida tu redacción!!
Saludos!
Jose Luis
jajaja ahi ves que aplica para cualquiera ;) muchas gracias por comentar, y lamento la demora (y)
EliminarHola, te felicito es un ejemplo muy bien desarrollado, me aclaró muchas dudas y gracias por compartir el codigo, funciona a la perfección, eso ayuda mucho para entenderlo mejor. Ojalá cada vez haya más personas que les complazca compartir sus conocimientos para que principiantes como yo, sigamos aprendiendo.
ResponderEliminarCon gusto, me alegra que te sirviera y esa es la idea, compartir el conocimiento ;).......gracias por comentar (y)
EliminarUfff, esto me viene genial! A penas hace medio año arranqué con java en la universidad y ya me están matando con muchos de estos patrones!!! Por suerte, veo que desarrollaste varios, me apunto a estudiarlos a todos. Están muy entendibles, y los ejemplos van de diez. Encima con links y todo... fenomenal, te pasaste! Muchas gracias!!!
ResponderEliminarSaludos desde Argentina!
Hola, esa es la idea y me alegra que te pudiera servir... muchos exitos con la carrera!!!
EliminarExcelentes ejemplos y explicaciones!! Muchas gracias por la dedicación. Saludos desde Uruguay.
ResponderEliminarHola, me alegra mucho que te sirva, saludos desde Colombia!!!
Eliminarpor que no hacer la clase combo una interface que implemente las subclases???
ResponderEliminarO sea, no es estrictamente necesario tener una interface para aplicar el patron decorator???
Excelente ejemplo Cristian! Muy útil y sencillo de entender.
ResponderEliminarCon gusto piqui.
EliminarMuchas gracias por la explicación, habia leído sobre este patrón, pero no me quedaba claro por que usaba la herencia, si precisamene la consideraba estática. Es la primera vez que llego al sitio y seguramente va a favoritos.
ResponderEliminarSaludos.
Mauricio desde Córdoba, Argentina.
Hola Mauricio, me alegra que te sirva y espero que ese seguramente se haga seguro jeje un saludo desde Colombia.
EliminarTengo una duda, ¿Como haria para quitar un decorator que ya haya agregado al combo?
ResponderEliminarPD: excelente articulo me ayudo a entenderle al patron.
excelente! muy claro!
ResponderEliminarbuena explicacion sobre el patron decorator
ResponderEliminarExcelente explicacion. Muy clara y precisa.
ResponderEliminarExcelente! muy buen aporte. Gracias
ResponderEliminarEstá muy bien la explicación, acá me gustaría hacerte un aporte constructivo.
ResponderEliminarSi revisas bien, la clase Combo se repite en cada una de las subclases de decorador y en el diagrama UML tienes que el AdicionalesDecorator es quien realmente hace referencia al Combo, así que Combo debería ser un atributo protected sobre la clase AdicionalesDecorator en la cual se pasaría como parámetro en el constructor y se elimina del resto de las subclases.
Muy buena explicacion. Muchas gracias por tus aportes.
ResponderEliminarExcelente aporte muchas gracias por su concepto y ejemplo.
ResponderEliminarSaludos cordiales.
Cristian, muy buen post ante que nada.
ResponderEliminarTe quería consultar lo siguiente en relación a este patrón, supongamos que yo tengo la Clase IMDBPeliculas, cuya funcionalidad es obtener datos en referencia a una película desde el Servidor IMBD. Supongamos que a futuro podríamos tener una fuente de datos de película similar a IMDB pero desde otros proveedor (Amazon, Netflix, etc.). Debería ser posible agregar a nuestro modelo estas nuevas funcionalidades indicando en nuestro Diseño un mecanismo claro para definir cuál proveedor tiene prioridad sobre otros.
Serviría para este caso implementar el Patron Decorator utilizando como Componente Contreto nuestra Clase IMDBPeliculas e ir agregando a nuestra Clase Decorator , subclases como AmazonPeliculas ( decorardor concreto A) y NetflixPeliculas (decorador concreto B).
Muchas Gracias. Un saludo.
gracias querido! muy util tu explicacion! estudiando para un examen de la facultad me sirvio.
ResponderEliminarGracias, por todos los patrones. Me dejas muy claro cada concepto, Y este ejemplo ha sido muy bueno. En mi caso lo aplicare para una aplicación de helados al cual se le agrega productos adicionales, etc.
ResponderEliminarGracias totales.
Excelente aporte gracias!!
ResponderEliminarsaludos desde Bolivia.
Excelente artículo. He leído todo lo referente a patrones. Muchas gracias por tu disposición para hacer aportes tan bien elaborados.
ResponderEliminarMuy buen artículo. Sólo haría unas pequeñas modificaciones al código de ejemplo: instanciaría los Componentes Concretos en verificaSeleccion() y allí aprovecharía el método de acceso para obtener la descripción (evitando cablear y repetir código) dejando en el actionPerformed sólo el método enviarPedido.
ResponderEliminarMuchas gracias enserio! lo necesitaba para un proyecto y la verdad no tenia idea como trabajar con el patrón! gracias! :'D
ResponderEliminarLo he implementado en C# pero cuando selecciono un adicional no me muestra el pedido, solo el valor.
ResponderEliminar¿Qué podría estar causando el que no se vea el pedido?
Excelente aporte , gracias!!!!
ResponderEliminar