Tabla de contenidos
"...describa un problema que sucede una y otra vez en nuestro entorno, y luego describa el núcleo de la solución a ese problema, de tal forma que pueda utilizar esa solución un millón de veces más, sin siquiera hacerlo dos veces de la misma manera." - Christopher Alexander
Este capítulo presenta el importante y aún no tradicional enfoque de los patrones para el diseño de programas.
El avance reciente más importante en el diseño orientado a objetos es probablemente el movimiento de los patrones de diseño, inicialmente narrado en "Design Patterns", por Gamma, Helm, Johnson y Vlissides (Addison Wesley, 1995), que suele llamarse el libro de la "Banda de los Cuatro" (en inglés, GoF: Gang of Four). El GoF muestra 23 soluciones para clases de problemas muy particulares. En este capítulo se discuten los conceptos básicos de los patrones de diseño y se ofrecen ejemplos de código que ilustran los patrones escogidos. Esto debería abrirle el apetito para leer más acerca de los patrones de diseño, una fuente de lo que se ha convertido en vocabulario esencial, casi obligatorio, para la programación orientada a objetos.
En principio, puede pensar en un patrón como una manera especialmente inteligente e intuitiva de resolver una clase de problema en particular. Parece que un equipo de personas han estudiado todos los ángulos de un problema y han dado con la solución más general y flexible para ese tipo de problema. Este problema podría ser uno que usted ha visto y resuelto antes, pero su solución probablemente no tenía la clase de completitud que verá plasmada en un patrón. Es más, el patrón existe independientemente de cualquier implementación particular y puede implementarse de numerosas maneras.
Aunque se llaman "patrones de diseño", en realidad no están ligados al ámbito del diseño. Un patrón parece apartarse de la manera tradicional de pensar sobre el análisis, diseño e implementación. En cambio, un patrón abarca una idea completa dentro de un programa, y por lo tanto puede también abarcar las fases de análisis y diseño de alto nivel. Sin embargo, dado que un patrón a menudo tiene una implementación directa en código, podría no mostrarse hasta el diseño de bajo nivel o la implementación (y usted no se daría cuenta de que necesita ese patrón hasta que llegase a esas fases).
El concepto básico de un patrón puede verse también como el concepto básico del diseño de programas en general: añadir capas de abstracción. Cuando se abstrae algo, se están aislando detalles concretos, y una de las razones de mayor peso para hacerlo es separar las cosas que cambian de las cosas que no. Otra forma de verlo es que una vez que encuentra una parte de su programa que es susceptible de cambiar, querrá prevenir que esos cambios propagen efectos colaterales por su código. Si lo consigue, su código no sólo será más fácil de leer y comprender, también será más fácil de mantener, lo que a la larga, siempre redunda en menores costes.
La parte más difícil de desarrollar un diseño elegante y mantenible a menudo es descubrir lo que llamamos el "vector de cambio". (Aquí "vector" se refiere al mayor gradiente tal y como se entiende en ciencias, no como la clase contenedora.) Esto implica encontrar la cosa más importante que cambia en su sistema o, dicho de otra forma, descubrir dónde están sus mayores costes. Una vez que descubra el vector de cambios, tendrá el punto focal alrededor del cual estructurar su diseño.
Por lo tanto, el objetivo de los patrones de diseño es encapsular el cambio. Si lo enfoca de esta forma, ya habrá visto algunos patrones de diseño en este libro. Por ejemplo, la herencia podría verse como un patrón de diseño (aunque uno implementado por el compilador). Expresa diferencias de comportamiento (eso es lo que cambia) en objetos que tienen todos la misma interfaz (esto es lo que no cambia). La composición también podría considerarse un patrón, ya que puede cambiar dinámica o estáticamente los objetos que implementan su clase, y por lo tanto, la forma en la que funciona la clase. Normalmente, sin embargo, las características que los lenguajes de programación soportan directamente no se han clasificado como patrones de diseño.
También ha visto ya otro patrón que aparece en el GoF: el «iterador». Esta es la herramienta fundamental usada en el diseño del STL, descrito en capítulos anteriores. El iterador esconde la implementación concreta del contenedor a medida que se avanza y se seleccionan los elementos uno a uno. Los iteradores le ayudan a escribir código genérico que realiza una operación en todos los elementos de un rango sin tener en cuenta el contenedor que contiene el rango. Por lo tanto, cualquier contenedor que pueda producir iteradores puede utilizar su código genérico.
La contribución más importante del GoF puede que no sea un patrón, si no una máxima que introducen en el Capítulo 1: «Prefiera siempre la composición de objetos antes que la herencia de clases». Entender la herecia y el polimorfismo es un reto tal, que podría empezar a otorgarle una importancia excesiva a estas técnicas. Se ven muchos diseños excesivamente complicados (el nuestro incluído) como resultado de ser demasiado indulgentes con la herencia - por ejemplo, muchos diseños de herencia múltiple se desarrollan por insistir en usar la herencia en todas partes.
Una de las directrices en la «Programación Extrema» es "haga la cosa más simple que pueda funcionar". Un diseño que parece requerir de herencia puede a menudo simplificarse drásticamente usando una composición en su lugar, y descubrirá también que el resultado es más flexible, como comprenderá al estudiar algunos de los patrones de diseño de este capítulo. Por lo tanto, al considerar un diseño, pregúntese: ¿Podría ser más simple si usara Composición? ¿De verdad necesito Herencia aquí, y qué me aporta?