Antes de nada te pedimos que participes en la encuesta "Patrones de diseño de software"
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.
Un patrón de diseño tiene 4 elementos
esenciales:
Nombre
del patrón: Permite
describir en pocas palabras un problema de diseño junto con sus soluciones y
consecuencias. Tener un vocabulario de patrones nos permite diseñar con mayor
abstracción y tener un lenguaje común para compartir, transmitir y documentar a
otros compañeros de profesión. A su vez se pueden identificar fácilmente sus
puntos fuertes y sus inconvenientes para decidir cuál es la mejor elección en
cada caso.
Problema:
Describe cuando se debe
aplicar el patrón, explicando el problema y el contexto. En ocasiones el
problema incluye una serie de requisitos que deben producirse para que tenga
sentido aplicar el patrón.
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:
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.
La asociación en tiempo de
ejecución entre una variable y su tipo se conoce como enlace dinámico o dynamic binding .
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
El
polimorfismo 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 oracle
Hay 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.
Consejos:
No se deben declarar
variables como instancias de clases concretas
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.
No hay comentarios:
Publicar un comentario