Introducción
Los patrones de diseño son una pieza fundamental en el desarrollo del software. Un patrón de diseño en software es un problema que ocurre una y otra vez en nuestro entorno y que puede resolverse repetidamente del mismo modo sin necesidad de implementar una solución diferente cada vez. Es decir, los patrones de diseño son soluciones exitosas y contrastadas a problemas comunes.
Solución: Describe los elementos que constituyen, las relaciones, responsabilidades y colaboraciones entre ellos. El patrón proporciona una descripción abstracta de un problema de diseño y como se resuelve a través de una serie de elementos. En el caso de Java y los lenguajes de programación orientados a objetos, estos elementos serán los interfaces, clases y objetos.
Consecuencias: Son las ventajas e inconvenientes de aplicar el patrón. Son fundamentales para evaluar las alternativas de diseño y comprender los costes y beneficios de aplicar cada patrón. Incluyen entre otras el impacto sobre la flexibilidad, extensibilidad y potabilidad de un sistema.
Conceptos fundamentales previos:
Para poder entender bien los patrones de diseño, sus ventajas e inconvenientes es necesario tener claros una serie de conceptos que utilizaremos frecuentemente en el desarrollo orientado a objetos.Interfaces de Objetos
Cada operación declarada para un objeto especifica el nombre de un método junto con sus parámetros y tipo de respuesta. Es lo que se conoce cómo signatura o firma de un método.Al conjunto de todas las firmas de métodos de un objeto se le denomina interfaz del objeto.Las interfaces son fundamentales en los lenguajes de programación orientada a objetos. Las interfaces permiten a dos objetos interactuar entre sí evitando la necesidad de que estos conozcan la implementación concreta de cada uno de ellos.
Por otro lado, para una variable que referencia a un objeto debemos conocer dos tipos:- Tipo estático: Se interpreta en tiempo de compilación y es fijo.
- Tipo dinámico: Se interpreta en tiempo de ejecución y es variable.
El enlace dinámico nos permite sustituir objetos en tiempo de ejecución por otros que tengan la misma interfaz. Esta capacidad de sustituir unos objetos por otros es lo que se conoce como polimorfismo. Para más detalles consultar enlace-dinamico-en-java
Elpolimorfismo permite simplificar las definiciones de los clientes, desacoplar objetos y modificar la relaciones entre ellos en tiempo de ejecución. Para más detalles consultar polimorfismo-en-java
Clases de Objetos
La implementación de un objeto queda definida por su clase. Una clase específica los datos, la representación interna del objeto y define las operaciones o métodos que puede realizar.Las Clases se pueden relacionar entre sí implementando sistemas de Herencia. La Herencia de Clases nos permite hacer nuestros sistemas extensibles, mejor desacoplados y más reutilizables. Para saber más sobre Herencia podemos consultar la documentación de oracleHay una serie de conceptos que es importante entender sobre las relaciones entre clases:
- Clase Padre o Superclase: Es la clase que está en la parte más alta de la relación de Herencia.
- Clase Hija o Subclase: Es una clase que hereda de la clase padre y por tanto incluye las definiciones de datos y métodos que define la clase padre. Estas subclases pueden ampliar o redefinir el comportamiento de las clases padre.
- Clase Abstracta: Es aquella cuyo propósito es definir una interfaz común para sus subclases. Una clase abstracta delega parte de su implementación en sus subclases. Dado que su implementación no es completa, no se pueden instanciar objetos de este tipo de clases.
Programar para Interfaces
La herencia de clases es un mecanismo para extender las funcionalidad de una aplicación reutilizando la funcionalidad de las clases Padres. Nos permite definir rápidamente un nuevo tipo de objeto basándonos en otros y obtener nuevas implementaciones casi sin esfuerzo.La herencia también permite definir familias de objetos con interfaces idénticas. Cuando la herencia se usa correctamente todas las clases que derivan de una clase abstracta o interfaz pueden responder a los métodos definidos en la interfaz. Consiguiendo dos cosas:- Los clientes no tienen porque conocer los tipos específicos de los objetos que utilizan, ya que basta con que conozcan la interfaz o clase abstracta.
- Los clientes desconocen las clases que implementan dichos objetos, ya que basta con que conozcan la interfaz o clase abstracta.
Reutilización
Las dos técnicas más comunes para reutilizar funcionalidad en lenguajes orientados a objetos son la herencia de clases y la composición de objetos.- La herencia permite definir una implementación en función de otra. A esta modo de reutilización se le denomina “caja blanca” porque con la herencia tenemos visibilidad de la interioridad de las clases padres.
- La composición de objetos es una alternativa que se consigue ensamblando o componiendo objetos para obtener una funcionalidad más compleja. A este modo de reutilización se le denomina “caja negra” porque los detalles internos de los objetos no son visibles.
Delegación
Con la delegación tendremos dos objetos encargados de tratar una petición. Un primer objeto recibe la solicitud de una petición y este delega en un segundo objeto la funcionalidad.Ejemplo de Clase Ventana y Clase Rectángulo:
- Por Herencia: Si aplicamos herencia tendremos una clase Padre Rectángulo y una clase Hija Ventana.
- Por Composición: Si aplicamos composición tendremos una clase Ventana que está compuesta por una variable de instancia de la clase Rectángulo. Con esta estructura las operaciones serán delegadas en la clase Rectángulo. Es decir, en lugar de hacer que Ventana sea un Rectángulo esta segundo estructura implica que Ventana tiene un Rectángulo.
Diseñar para el cambio
La clave para maximizar la reutilización de funcionalidad reside en anticiparse a los nuevos requisitos y modificaciones sobre los requisitos existentes. Con esto conseguiremos diseñar sistemas capaces de evolucionar.- Un diseño que no tenga en cuenta el cambio sufre el riesgo de tener que ser rediseñado en el futuro.
- Un rediseño desencadenará una serie de acciones como redefiniciones, re implementaciones, ejecución de pruebas, modificación de clientes y por tanto una serie de costes en tiempo y esfuerzo que podemos evitar haciendo un buen diseño inicial.
Suscríbete al boletín de novedades
En el próximo artículo:
- Veremos las diferentes maneras de agrupar y categorizar los patrones de diseño.
- Veremos los criterios utilizados para categorizar los patrones.
- Estudiaremos brevemente la motivación de cada uno de los patrones definidos por GoF (banda de los cuatro)
- Y recuerda ... Si no quieres perderte las próximas publicaciones. No olvides Suscribirte.