sábado, 28 de marzo de 2015

Aprendiendo patrones de diseño

El propósito de este post es dejar claro mi punto de vista sobre los patrones de diseño. Aquí se intenta expresar mi comprensión acerca de los mismos, la forma como los veo, la manera como se me hace fácil identificarlos para la solución de problemas porque para mí son herramientas.

Mi entendimiento de los patrones puede ser debatible, mi explicación sobre ellos es simple, parroquial, si se quiere; esto no es un tratado de arquitectura de Software. Intento llevar el conocimiento del tema a gentes no expertas en dicho campo.

Mi propósito es usar la didáctica, para intentar llevar el conocimiento de este tema que puede ser difícil de captar.

Para comenzar un mapa mental de cada uno de los tipos de patrones de diseño que se verán:

Creacionales:

Crear un objeto, dependiendo del lenguaje es algo sencillo, en C# se trata de usar la palabra reservada new.

Entonces, porque pensar en tener patrones de diseño para crear objetos y no simplemente usar esta palabra reservada y la respuesta es: porque para resolver problemas, en ciertas situaciones, debemos crear de forma más inteligente estos objetos y así tener un código más mantenible, con mayor rendimiento y donde podamos solucionar inconvenientes particulares que tengamos de una forma óptima.

Creacionales

Object Pool:

ObjectPool

Claves:

  • El patrón object pool es un patrón de diseño de software que usa un conjunto de objetos inicializados preparados para su uso. Esto es más efectivo normalmente que crear y destruir los objetos bajo demanda. Un cliente del pool le pedirá un objeto para realizar las operaciones con éste. Cuando el cliente termina retorna el objeto al pool para que lo retenga hasta que vuelva a necesitarse. Es decir, los objetos no se crean (salvo la primera vez) ni se destruyen, simplemente se van reciclando.
  • Este patrón de diseño suele mejorar significativamente el rendimiento en situaciones donde el costo de iniciar una instancia de clase es alto y cuando el número de instancias simultáneas no es muy alto.
  • Es común usar los Object Pools para la creación de recursos gráficos, conexiones a base de datos, sockets y en general cualquier objeto cuya creación es costosa. En ciertas ocasiones el uso de este patrón es más costoso.

Ejemplo:

https://drive.google.com/file/d/0B_iGTrI7aN4oaGNFWVBGemZPZ1U/view?usp=sharing

 

Factory Method:

FactoryMethod

Claves:

  • Definir una interface para crear un objeto, pero dejar a las subclases decidir cuál clase instanciar. Factory method permite a una clase diferir la instanciación a subclases.
  • Crear un objeto y ocultar su complejidad al cliente.
  • Dependiendo de una condición particular, un objeto particular responde. Se evitan los if-else.
  • En el mundo de los objetos unos se deben comunicar con otros. Pero si para comunicarme, en un método de una clase con el método de un objeto de otra clase, debo crear la instancia de este otro objeto en el cuerpo del método estoy creando un acoplamiento entre las clases. Ahora, entre las mejores prácticas al diseñar nuestras clases es tener en mente el principio de inversión de la dependencia, cuando vemos que hay dependencia entre éstas. El principio dice: Los módulos de nivel superior siempre deben depender de abstracciones en lugar de módulos de nivel inferior directamente. Por tanto, debemos diseñar nuestras clases de manera que siempre dependan de interfaces o clases abstractas en lugar de clases concretas.

Ejemplo:

https://drive.google.com/file/d/0B_iGTrI7aN4oWEJIUHpoSUlMZkU/view?usp=sharing

 

Abstract Factory:

AbstractFactory

Claves:

  • Proporcionar una interface para crear familias de objetos relacionados o dependientes sin especificar sus clases concretas.
  • Un sistema debe ser independiente de cómo sus productos son creados, compuestos y representados.
  • Un sistema debe ser configurado con una de múltiples familias de productos.
  • Se desea proporcionar una librería de clases y revelar solo sus interfaces no sus implementaciones.
  • Una familia de objetos relacionados es diseñada para ser usados juntos y se requiere hacer cumplir dicha restricción.
  • Desacopla las clases concretas del cliente. El cliente interactúa con interfaces.
  • Ejemplo: UI tollkits (Botones, Listas, Ventanas) para las distintas plataformas Windows, Mac, Linux.

Ejemplo:

https://drive.google.com/file/d/0B_iGTrI7aN4oalVxaG8tR1ZldTg/view?usp=sharing

Builder:

Builder

Claves:

  • El algoritmo para crear un objeto complejo debe ser independiente de las partes que lo conforman y como éstas son ensambladas.
  • El proceso de construcción debe permitir diferentes representaciones del objeto que es construido.
  • Construir un objeto complejo por intermedio de sus partes.

Ejemplo:

https://drive.google.com/file/d/0B_iGTrI7aN4oXzg4eTRBVGFPR3c/view?usp=sharing

Prototype:

Prototype

Claves:

  • Especifica la clase de objetos a crear mediante la clonación de un prototipo que es una instancia ya creada. La clase de los objetos que servirán de prototipo deberá incluir en su interfaz la manera de solicitar una copia, que será desarrollada luego por las clases concretas de prototipos.
  • Cuando las clases a instanciar son especificadas en tiempo de ejecución, por ejemplo, por carga dinámica.
  • Para evitar la construcción de una jerarquía de clases de fábricas que es paralela a la jerarquía de clases de productos.
  • Cuando las instancias de una clase pueden tener uno de pocas diferentes combinaciones de estados. Es más conveniente instalar un número correspondiente de prototipos y clonarlos en lugar de instanciar la clase manualmente cada vez con el estado apropiado.
  • Obtener una copia de un objeto de un contexto dado y proceder con dicha copia con algunas operaciones independientes.
  • Cuando el costo de crear el objeto es muy alto o no se tienen los detalles para hacerlo. Es mejor solicitar al objeto una copia o clon de él mismo.

  • Cuando se deben hacer muchas operaciones, es mejor tener a un clon de un objeto haciendo cada uno una operación, que tener al mismo objeto haciendo cada operación.

Ejemplo:

https://drive.google.com/file/d/0B_iGTrI7aN4oTURjUDZMU09fem8/view?usp=sharing

 

Singleton:

Singleton

Claves:

  • Asegurar que una clase tiene una única instancia y proporcionar un punto de acceso global a ésta.
  • Debe existir exactamente una instancia de una clase y ésta debe ser accesible a los clientes desde un punto de acceso conocido.
  • Cuando una única instancia debería ser extensible solo por subclases y los clientes deberían ser capaces de extender la instancia sin modificar su código.
  • Se requieren definir variables globales.

Ejemplo:

https://drive.google.com/file/d/0B_iGTrI7aN4oZGpTRFBZM3hGQ3c/view?usp=sharing

Model View Controler (MVC)

MVC

Variantes:

HMVC (MVC Jerárquico)

MVA (Modelo-Vista-Adaptador)

MVP (Modelo-Vista-Presentador)

MVVM (Modelo-Vista Vista-Modelo)

Claves:

  • Modularizar la funcionalidad de la interface de usuario en una aplicación web de manera que sea fácil la modificación de las partes individuales.

Ejemplo:

https://drive.google.com/file/d/0B_iGTrI7aN4oeFJSaHlnQzR0RXM/view?usp=sharing

Estructurales

Estos patrones solucionan problemas de composición (agregación) de clases y objetos.

Estructurales

Adapter o Wrapper

Claves:

  • Convertir la interface de una clase en otra interface que el cliente espera. Permite a las clases trabajar juntas que de otra forma no sería posible debido a la incompatibilidad de interfaces.
  • Se desea usar una clase existente, pero su interfaz no coincide con lo que se necesita.
  • Se desea crear una clase reusable que coopera con clases no relacionadas o imprevistas, es decir, clases que no necesariamente tienen interfaces compatibles.
  • Se necesita usar varias subclases ya existentes, pero es poco práctico adaptar su interfaz subclasificando cada uno. Un objeto adaptador puede adaptar la interfaz de su clase padre.
  • Existen varias clases que se quieren usar desde un cliente, pero estas tienen nombres del método que se desea usar diferentes. Podemos lograr llamar a los métodos de estas clases de una misma forma, creando Adaptadores, donde en cada uno se defina el mismo nombre de método y se llame a los métodos particulares de los objetos adaptados.

Adapter

Ejemplo:

https://drive.google.com/file/d/0B_iGTrI7aN4ocm90VHZ1ZlJidFk/view?usp=sharing

Bridge

Claves:

  • Desacoplar una abstracción de su implementación para que las 2 puedan variar independientemente.
  • Se desea evitar un enlazado permanente entre una abstracción y su implementación. Este podría ser el caso cuando la implementación debe ser seleccionada o conmutada en tiempo de ejecución.
  • Ambas las abstracciones y sus implementaciones podrían ser extensibles por subclases. En este caso el patrón permite combinar las diferentes abstracciones e implementaciones y extenderlas independientemente.
  • Los cambios en la implementación de una abstracción no deberían impactar los clientes, o sea que el código no tendría que ser recompilado.
  • Se desea ocultar la implementación de una abstracción completamente de los clientes.

Bridge

Ejemplo:

https://drive.google.com/file/d/0B_iGTrI7aN4oUkN5T0xrUVhXOXc/view?usp=sharing

Composite

Claves:

  • Componer objetos en estructuras de árbol para representar jerarquías Parte-Todo. Composite permite a los clientes tratar a los objetos individuales y a las composiciones de objetos de manera uniforme.
  • Se desea representar una jerarquía Parte-Todo de objetos.
  • Se desea que los clientes sean capaces de ignorar la diferencia entre composiciones de objetos y objetos individuales. Los clientes tratarán todos los objetos en la estructura de composición de manera uniforme.

Composite

Ejemplo:

https://drive.google.com/file/d/0B_iGTrI7aN4oRkZWZlZDbGFBYnM/view?usp=sharing

Decorator

Claves:

  • Es similar al adapter incluso es también conocido como Wrapper. Sirve para agregar responsabilidades a un objeto de forma dinámica. Proporciona una alternativa flexible frente al uso de subclases para extender la funcionalidad.
  • Agregar responsabilidades a objetos individuales dinámica y transparentemente, es decir, sin afectar otros objetos.
  • Para las responsabilidades que se pueden retirar.
  • Cuando la extensión por medio de subclases es impráctica. A veces, un gran número de extensiones independientes son posibles y producirían una explosión de subclases para soportar cada combinación. O una definición de clase puede estar oculta o no disponible para tener subclases.
  • Crear un objeto con funcionalidad básica y agregarle otra de forma dinámica.
  • Los decoradores le agregan a las componentes nuevas responsabilidades y manejo de nuevos estados, que de otra forma se tendrían que agregar a la propia componente, limitando así una evolución clara del objeto y de pronto teniendo que agregar comportamientos que no son muy utilizados.
Decorator

Ejemplo:

https://drive.google.com/file/d/0B_iGTrI7aN4oU1o0RnRBZ3Y1RFE/view?usp=sharing

Facade

Claves:

  • Proporciona una interfaz unificada a un conjunto de interfaces en un subsistema. La fachada define una interfaz de alto nivel que hace que el subsistema sea más fácil de usar.
  • Se desea proporcionar una interfaz simple a un subsistema complejo.
  • Hay muchas dependencias entre clientes y la implementación de clases de una abstracción. Se introduce la fachada para desacoplar el subsistema de los clientes y otros susbsistemas, por tanto se promueve la independencia de subsistemas y la portabilidad.
  • Se desea poner en capas los subsistemas. Usar una fachada para definir el punto de acceso a cada subsistema. Si los subsistemas son dependientes entonces se pueden simplificar las dependencias entre ellos haciendo que se comuniquen entre sí solo a través de sus fachadas.

Facade

Ejemplo:

https://drive.google.com/file/d/0B_iGTrI7aN4oWWZLRVpIMlZRRUU/view?usp=sharing

Flyweight

Claves:

  • Reducir la redundancia cuando gran cantidad de objetos poseen información similar.
  • Utilizar este patrón cuando todas estas sentencias sean verdad:

Una aplicación usa un gran número de objetos.

Los costos de almacenamiento son altos por la gran cantidad de objetos.

La mayoría de los estados del objeto puede ser hecha extrínseca.

Muchos grupos de objetos pueden ser reemplazados por relativamente pocos objetos compartidos una vez su estado extrínseco es removido.

La aplicación no depende de la identidad del objeto. Ya que los objetos flyweight pueden ser compartidos, las pruebas de identidad retornarán True para objetos conceptualmente distintos.

  • Reducir el número de objetos a crear, decrementar memoria y uso de recursos. Por tanto, incrementar el rendimiento.
  • Estado intrínseco de objetos: Es intrínseco al objeto y puede ser compartido. Es independiente del contexto.
  • Estado extrínseco de objetos: Varía con el contexto y no puede ser compartido.
  • Ejemplo: Vamos a dibujar los caracteres AAABBZZAA en una pantalla, si de los caracteres pensamos en una forma tal que definamos su estado intrínseco, es decir, que pueda ser compartido, entonces, solo necesitaríamos crear los caracteres A,B y Z (3), en este caso manejaríamos como un estado extrínseco la posición donde se dibuja el carácter. Si no lo hiciéramos así tendríamos que crear los 9 objetos asociados a los caracteres a pintar.

Flyweight

Ejemplo:

https://drive.google.com/file/d/0B_iGTrI7aN4oclRycU9DTEZObk0/view?usp=sharing

Proxy

Claves:

  • Proporciona un sustituto o placeholder para otro objeto y así controlar su acceso a éste.
  • Un proxy remoto proporciona una representación local para un objeto en un diferente espacio de direcciones. Esto puede involucrar los procesos de marshalling y unmarshalling de datos. Pero toda la lógica es encapsulada en el proxy, para el cliente es transparente.

Marshalling: Es el proceso de hacer disponible un objeto a través de la red o dominio de aplicación. Es el acto de tomar datos del ambiente en el que se está y exportarlos a otro ambiente.

UnMarshalling: Crear un objeto de los datos exportados en el proceso de Marshalling.

  • Un proxy virtual crea objetos costosos en demanda. Se usan por ejemplo para darle una respuesta inmediata al cliente, mientras que el proxy espera la respuesta que es demorada del objeto real, cuando la respuesta llega, el proxy se la entrega al cliente.
  • Un proxy de protección controla el acceso al objeto original. Se coloca seguridad.

Proxy

Ejemplo:

https://drive.google.com/file/d/0B_iGTrI7aN4oMzJieF9kcGpZNWc/view?usp=sharing 

Module

Claves:

  • Agrupar varios elementos relacionados, como clases, singletons y métodos en una entidad única.

Module

De comportamiento

Ofrecen soluciones respecto a la interacción y responsabilidad entre clases y objetos, así como los algoritmos que encapsulan.

Comportamiento

Chain of Responsability

Claves:

  • Evitar el acoplamiento entre el remitente de una petición y su receptor, proporcionando a más de un objeto la oportunidad de manejar la petición. Encadenar los objetos receptores y pasar la petición a lo largo de la cadena hasta que un objeto la maneje.
  • Más de un objeto puede manejar una petición y el manejador no se conoce a priori. El manejador debe determinarse de manera automática.
  • Se desea emitir un requerimiento a uno de varios objetos sin especificar el receptor explícitamente.
  • El conjunto de objetos que pueden manejar un requerimiento puede ser especificado dinámicamente.
  • Cuando un objeto no puede llevar a cabo una operación le dice a su superior.

ChainOfResponsability

Ejemplo:

https://drive.google.com/file/d/0B_iGTrI7aN4oc0xVZUVhLVNsbTA/view?usp=sharing

Command

Claves:

  • Encapsular un requerimiento como un objeto y permitirle parametrizar clientes con diferentes requerimientos, colas o log de requerimientos y soportar las operaciones de deshacer.
  • Parametrizar objetos con respecto a una operación a ejecutar.
  • Especificar, encolar y ejecutar requerimientos en distintos tiempos.
  • Soportar la operación deshacer (Undo).
  • Soportar logueo de cambios de forma que puedan ser replicados en caso de que el sistema falle.
  • Estructurar un sistema alrededor de operaciones de alto nivel construidas sobre operaciones primitivas.
  • Encapsular todos los requerimientos que vienen del invocador en objetos, pasarlos al receptor y permitir que el receptor tome las acciones.
  • Convertir operaciones o métodos en objetos.
  • No se conoce nada acerca de la operación a ser ejecutada o del receptor de dicha operación.

Command

Ejemplo:

https://drive.google.com/file/d/0B_iGTrI7aN4oTlpjX2hqdk5CZ3c/view?usp=sharing

Interpreter

Claves:

  • Dado un lenguaje, definir una representación para su gramática junto con un intérprete que utiliza la representación para interpretar sentencias en el lenguaje.
  • Usarlo cuando haya un lenguaje a interpretar y pueda representar sentencias en el lenguaje como árboles de sintaxis abstractos.
  • El patrón trabaja bien cuando:
    • La gramática es simple.
    • La eficiencia no es una preocupación crítica.

Interpreter

Ejemplo:

https://drive.google.com/file/d/0B_iGTrI7aN4oOXpYVXhfYm1uVUE/view?usp=sharing

Iterator

Claves:

  • Proporciona una forma de acceder a los elementos de un objeto agregado secuencialmente si exponer su representación subyacente.
  • Para acceder al contenido de un objeto agregado sin exponer su representación interna.
  • Para soportar múltiples recorridos de los objetos agregados.
  • Para proporcionar una interfaz uniforme para atravesar diferentes estructuras agregadas.
  • Recorrer una colección de objetos.
  • Separación de la colección y de cómo ésta es iterada o recorrida. El cliente no se preocupa por cosas como el número de elementos recorridos o chequear condiciones de límites en la cantidad de elementos

Iterator

Ejemplo:

https://drive.google.com/file/d/0B_iGTrI7aN4oYm11X3pndVExbE0/view?usp=sharing

Mediator

Claves:

  • Definir un objeto que encapsula como un conjunto de objetos interactúa. Este patrón promueve el bajo acoplamiento manteniendo a los objetos sin referirse a cada uno explícitamente y permite variar su interacción de forma independiente.
  • Un conjunto de objetos se comunica de una bien definida pero compleja forma.
  • Reusar un objeto es difícil porque este se comunica con muchos otros objetos.
  • Un comportamiento que se distribuye entre varias clases debe ser personalizable sin un montón de subclases.
  • Define un objeto que coordine la comunicación entre objetos de distintas clases, pero que funcionan como un conjunto.
  • Cuando se desea por ejemplo implementar una aplicación de chat o un juego en el que interactúen varios. Para coordinar la comunicación se usa este patrón.

Mediator

Ejemplo:

https://drive.google.com/file/d/0B_iGTrI7aN4oZHB6eTEtNzRQNjg/view?usp=sharing

Memento

Claves:

  • Sin violar la encapsulación, capturar y externalizar el estado interno de un objeto, para que el objeto pueda ser restaurado a dicho estado después.
  • Salvar el estado de un objeto, para volver a éste si es requerido.

Memento

https://drive.google.com/file/d/0B_iGTrI7aN4oa3ZJZ2JKSkhjNDA/view?usp=sharing

Observer

Claves:

  • Define una dependencia uno a muchos entre objetos de tal forma que cuando el objeto cambia su estado todos los objetos dependientes son notificados y actualizados automáticamente.
  • Cuando una abstracción tiene dos aspectos, uno dependiente del otro. Encapsular esos aspectos en objetos separados le permite variar y reusarlos independientemente.
  • Cuando el cambio de un objeto implique el cambio de otros y no se sabe cuántos objetos requieren ser cambiados.
  • Cuando un objeto debería ser capaz de notificar a otros sus cambios sin hacer suposiciones de cómo estos son.
  • Para notificar el cambio del estado de un objeto a otros. Se puede usar en casos donde hay un productor y un consumidor.

Observer

Ejemplo:

https://drive.google.com/file/d/0B_iGTrI7aN4oeXFfaGRwS2lyYm8/view?usp=sharing

State

Claves:

  • Permitir a un objeto alterar su comportamiento cuando su estado interno cambia. El objeto parecerá cambiar su clase.
  • El comportamiento de un objeto depende de su estado y debe cambiar su comportamiento en tiempo de ejecución dependiendo de ese estado.
  • Operaciones tienen sentencias condicionales grandes que dependen de estado del objeto. Este estado suele estar representado por una o más constantes enumeradas. A menudo, varias operaciones contendrán esta misma estructura condicional. El State pone cada rama del condicional en una clase separada. Esto le permite tratar el estado del objeto como un objeto en sí mismo, que puede variar de forma independiente de otros objetos.
  • Se puede usar en casos donde hay que capturar la información del usuario por partes o en diferentes pantallas.

State

Ejemplo:

https://drive.google.com/file/d/0B_iGTrI7aN4obVk2TjZSU01kM28/view?usp=sharing

Strategy

Claves:

  • Definir una familia de algoritmos, encapsular cada uno y hacerlos intercambiables. Strategy permite al algoritmo variar independientemente de los clientes que lo usa.
  • Muchas clases relacionadas difieren solo en su comportamiento. Startegy permite proporcionar una manera de configurar una clase con una de muchos comportamientos.
  • Se necesita diferentes variaciones de un algoritmo.
  • Un algoritmo usa datos que los clientes no deberían conocer. Usar el patrón para evitar exponer estructuras de datos complejas y específicas al algoritmo.
  • Una clase define muchos comportamientos y aparecen como sentencias condicionales múltiples en sus operaciones. En lugar de muchos condicionales, mover las ramas condicionales relacionadas en su propia clase Strategy.

Strategy

https://drive.google.com/file/d/0B_iGTrI7aN4oTlkyTDJ2VGNIdW8/view?usp=sharing

Templete Method

Claves:

  • Define el esqueleto de un algoritmo en una operación, difiriendo algunos pasos a subclases. Este patrón permite a las subclases redefinir ciertos pasos de un algoritmo sin cambiar la estructura del algoritmo.
  • Implementar las partes invariantes de un algoritmo una vez y dejar en subclases implementar lo que puede variar.
  • Cuando un comportamiento común entre clases podría ser factorizado y localizado en una clase común para evitar duplicación de código.
  • Para controlar extensiones de subclases.

TemplateMethod

Ejemplo:

https://drive.google.com/file/d/0B_iGTrI7aN4oUHpOOWRQaUIzWjQ/view?usp=sharing

Visitor

Claves:

  • Representa una operación a ser ejecutada sobre los elementos de la estructura de un objeto. Permite definir una nueva operación sin cambiar las clases de los elementos sobre los cuales opera.
  • Una estructura de objeto contiene muchas clases de objetos con interfaces diferentes, y desea realizar operaciones en estos objetos que dependan de sus clases concretas.
  • Muchas operaciones distintas y no relacionadas deben realizarse sobre los objetos en una estructura de objetos, y se desea evitar "contaminar" sus clases con estas operaciones. Visitor le permite mantener las operaciones relacionadas juntas definiéndolas en una clase. Cuando la estructura del objeto es compartida por muchas aplicaciones, utilice Visitor para poner las operaciones en sólo aquellas aplicaciones que lo necesiten.
  • Las clases que definen la estructura de objetos raramente cambian, pero a menudo se quieren definir nuevas operaciones a lo largo de la estructura. Cambiar la estructura de las clases requiere la redefinición de la interfaz para todos los visitantes, lo cual es potencialmente costoso. Si las estructuras de clase de los objetos cambian con frecuencia, entonces es probablemente mejor definir las operaciones en esas clases.
  • Es como recibir a un visitante que realiza acciones en su visita, acciones muy diferentes a las que normalmente se hacen.

Visitor

Ejemplo:

https://drive.google.com/file/d/0B_iGTrI7aN4oS3hpdGxRVnl1TzQ/view?usp=sharing