sábado, 22 de septiembre de 2007

Sigamos solucionando nuestro diseño con los patrones

Siguiendo con nuestra charla de diseño, determinemos la granularidad de los objetos intervinientes .
Los objetos varían enormemente en tamaño y número, y representan cualquier cosa desde hard a sistemas completos, por lo que como decidir que debería ser un objeto?
Veamos algunos patrones:
  • Fachada (GoF) describe como representar subsistemas ó procesos completos como objetos.
  • FlyWeight(GoF) describe como permitir un gran número de granularidad muy fina.
  • Abstract Factory (GoF) produce objetos cuya única resp. es crear otros objetos.
  • Visitor y Command (GoF) generan objetos cuya resp. es implementar una petición en otros objs. ó grupo de objs.

Especificar las interfaces de los objs.

Los patrones nos ayudan a definir interfaces identificando sus elementos clave y los tipos de datos que se envían a la interfaz. También especifican relaciones entre interfaces, ya que: requieren que algunas clases tengan interfaces parecidas ó les imponen restricciones; por ej:

Decorador y Proxy (GoF) requieren que las interfaces de sus objs. sean idénticos a los objs. deocrado y representado, respectivamente.

Especificar las implementaciones

Herencia de clases vs. herencia de interfaces, muchos de los patrones de diseño dependen de esta distinción. Por ej:

  • Los objs. de una cadena en Chain of Responsability (GoF) deben tener un tipo en común, pero normalmente no comparten la misma implementación.
  • En Composite (GoF), el componente define una interfaz común mientras que el compuesto suele definir una implementación en común.
  • Command, Observer, State y Strategy (GoF) suelen implementarse con clases abstractas que son interfaces puras.

En este tema, debemos tener en mente uno de los principios fundamentales del diseño OO: programar para interfaces, no para una implementación. Por que?

  • Ventajas de manipular objs. solamente en términos de la interfaz definida por las clases abstractas. Los clientes no tienen que conocer los tipos específicos de los objs. que usan, basta con que estos adhieran a la interfaz que esperan que esperan los clientes.
  • Reducción del acoplamiento entre subsistemas.
  • No se deben declarar variables como instancias de clases concretas, se ajustan simplemente a la interfaz definida por una clase concreta.
  • Para crear instancias de clases concretas usamos los patrones de creación; estos ofrecen distintos modos de asociar una interfaz con su implementación de manera transparente, y aseguran que el sistema se codifique en términos de interfaces, no de implementación.

Por último, pongamos a funcionar los mecanismos de reutilización:

Herencia vs. Composición

Son las 2 técnicas más comunes para reutilizar funcionalidad en sistemas OO.

La composición requiere que los objs. tengan en cta. las interfaces de otros, lo cual requiere interfaces cuidadosamente diseñadas, que no impidan que un obj. sea utilizado por otros. Optar por la composición ayuda a mantener cada clase encapsulada y centrada en una sola tarea. En los patrones de diseño veremos esta técnica aplicada una y otra vez...otro principio fundamental: favorecer la composición de objetos frente a la herencia de clases.

Pero qué es la composición?: método por el cual una nueva funcionalidad se obtiene mediante la creación de un objeto compuesto por otros objetos, ésta se logra delegando funcionalidad a uno de los objs. de la composición.

Mientras que la Herencia es un método por el cual la nueva funcionalidad se obtiene mediante la extensión de la implementación de un objeto existente. La superclase (generalización) captura los métodos y atributos comunes, y las subclases (especializadas) extienden la implementación con métodos y atrib. adicionales. Es más sencilla de llevar a cabo que la composición, pero...

Desventajas de la herencia:

  • Rompe la encapsulación, ya que expone la subclase a los detalles de implementación de su superclase
  • Reutilización de caja blanca, porque los detalles internos de la superclase son visibles a las subclases
  • Las subclases han de cambiar si la implementación de la superclase cambia

Entonces que usamos?, según Coad, solo utilizar herencia cuando:

  • una subclase expresa "es un tipo de" y no "es un rol desempeñado por".
  • una instancia de una subclase nunca necesita convertirse en un obj. de otra clase
  • una subclase extiende las resp. de su superclase, no las redefine ni las anula.
  • para una clase del problema del dominio actual, la subclase especializa un rol ó una transacción.

Por último utilicemos la Delegación cuando no existan relaciones estáticas entre clases, cuando la herencia no tenga sentido, por ej. para gestionar Roles.

Bien, como vemos obtener un buen diseño es difícil; lo primero que deberíamos saber es : que es un buen diseño?, los conceptos que detallamos antes nos ayudan a dilucidar esta pregunta. Lo segundo será aplicar los principios de diseño de forma sistemática, una buena solución es copiar a los que saben: aplicar los patrones, donde puedan aplicarse.

1 comentario:

Oscar Sandoval dijo...

muy bueno tu blog te dejo el mio...sobre patrones de diseño

http://oscar-sandoval.blogspot.com/2009/09/patrones-grasp-y-gof.html