Design Patterns: Elements of Reutilizable Object-Oriented Software (1994) es unlibro de ingeniería de software que describe patrones de diseño de software . El libro fue escrito por Erich Gamma , Richard Helm , Ralph Johnson y John Vlissides , con un prólogo de Grady Booch . El libro está dividido en dos partes, con los dos primeros capítulos explorando las capacidades y las dificultades de la programación orientada a objetos, y los capítulos restantes describiendo 23 patrones de diseño de software clásicos . El libro incluye ejemplos en C++ y Smalltalk .
Ha sido influyente en el campo de la ingeniería de software y se considera una fuente importante para la teoría y la práctica del diseño orientado a objetos. Se han vendido más de 500.000 copias en inglés y en otros 13 idiomas. [1] A los autores se les suele llamar la Banda de los Cuatro (GoF). [2] [3] [4] [5]
El libro comenzó en una sesión informal en la reunión de la OOPSLA de 1990 , "Hacia un manual de arquitectura", donde Erich Gamma y Richard Helm se conocieron y descubrieron su interés común. Más tarde se les unieron Ralph Johnson y John Vlissides. [6] El libro se publicó originalmente el 21 de octubre de 1994, con derechos de autor de 1995, y se puso a disposición del público en la reunión de la OOPSLA de 1994.
El capítulo 1 es un análisis de las técnicas de diseño orientado a objetos , basadas en la experiencia de los autores, que creen que conducirían a un buen diseño de software orientado a objetos, incluyendo:
Los autores afirman las siguientes ventajas de las interfaces sobre la implementación:
El uso de una interfaz también conduce a la vinculación dinámica y al polimorfismo , que son características centrales de la programación orientada a objetos.
Los autores se refieren a la herencia como reutilización de caja blanca , donde caja blanca hace referencia a la visibilidad, porque los detalles internos de las clases padre suelen ser visibles para las subclases . Por el contrario, los autores se refieren a la composición de objetos (en la que los objetos con interfaces bien definidas se utilizan dinámicamente en tiempo de ejecución por objetos que obtienen referencias a otros objetos) como reutilización de caja negra porque no es necesario que los detalles internos de los objetos compuestos sean visibles en el código que los utiliza.
Los autores analizan en profundidad la tensión entre la herencia y la encapsulación y afirman que, según su experiencia, los diseñadores abusan de la herencia (Gang of Four 1995:20). El peligro se indica de la siguiente manera:
Advierten que la implementación de una subclase puede llegar a estar tan ligada a la implementación de su clase padre que cualquier cambio en la implementación de la clase padre obligará a la subclase a cambiar. Además, afirman que una forma de evitar esto es heredar solo de clases abstractas, pero luego señalan que hay una reutilización mínima del código.
Se recomienda usar la herencia principalmente cuando se aumenta la funcionalidad de componentes existentes, se reutiliza la mayor parte del código antiguo y se agregan cantidades relativamente pequeñas de código nuevo.
Para los autores, la "delegación" es una forma extrema de composición de objetos que siempre se puede utilizar para reemplazar la herencia. La delegación involucra dos objetos: un "emisor" se pasa a sí mismo a un "delegado" para permitir que el delegado haga referencia al remitente. Por lo tanto, el vínculo entre dos partes de un sistema se establece solo en tiempo de ejecución, no en tiempo de compilación. El artículo Callback tiene más información sobre la delegación.
Los autores también analizan los llamados tipos parametrizados, que también se conocen como genéricos ( Ada , Eiffel , Java , C# , Visual Basic (.NET) y Delphi ) o plantillas ( C++ ). Estos permiten definir cualquier tipo sin especificar todos los demás tipos que utiliza; los tipos no especificados se suministran como "parámetros" en el punto de uso.
Los autores admiten que la delegación y la parametrización son muy poderosas, pero añaden una advertencia:
Los autores distinguen además entre " agregación ", donde un objeto "tiene" o "es parte" de otro objeto (lo que implica que un objeto agregado y su propietario tienen tiempos de vida idénticos) y conocimiento, donde un objeto simplemente "sabe de" otro objeto. A veces, el conocimiento se denomina "asociación" o relación de "uso". Los objetos de conocimiento pueden solicitar operaciones entre sí, pero no son responsables entre sí. El conocimiento es una relación más débil que la agregación y sugiere un acoplamiento mucho más flexible entre objetos, lo que a menudo puede ser deseable para lograr la máxima capacidad de mantenimiento en los diseños.
Los autores emplean el término "kit de herramientas" donde otros podrían utilizar hoy "biblioteca de clases", como en C# o Java. En su lenguaje, los kits de herramientas son el equivalente orientado a objetos de las bibliotecas de subrutinas, mientras que un " marco " es un conjunto de clases cooperantes que conforman un diseño reutilizable para una clase específica de software. Afirman que las aplicaciones son difíciles de diseñar, los kits de herramientas son más difíciles y los marcos son los más difíciles de diseñar.
Los patrones de creación son aquellos que crean objetos, en lugar de tener que instanciarlos directamente. Esto le da al programa más flexibilidad para decidir qué objetos deben crearse para un caso determinado.
Los patrones estructurales se ocupan de la composición de clases y objetos. Utilizan la herencia para componer interfaces y definir formas de componer objetos para obtener nuevas funciones.
La mayoría de los patrones de diseño de comportamiento se ocupan específicamente de la comunicación entre objetos.
En 2005, la ACM SIGPLAN otorgó el Premio al Logro en Lenguajes de Programación de ese año a los autores, en reconocimiento al impacto de su trabajo "en la práctica de la programación y el diseño de lenguajes de programación ". [7]
Se ha criticado el concepto de patrones de diseño de software en general y los patrones de diseño en particular. Una crítica principal a los patrones de diseño es que son simplemente soluciones alternativas a las características faltantes en C++, reemplazando características abstractas elegantes con patrones concretos extensos, convirtiéndose esencialmente en un "compilador humano". Paul Graham escribió: [8]
Cuando veo patrones en mis programas, lo considero un signo de problemas. La forma de un programa debería reflejar únicamente el problema que necesita resolver. Cualquier otra regularidad en el código es una señal, al menos para mí, de que estoy usando abstracciones que no son lo suficientemente potentes; a menudo, de que estoy generando a mano las expansiones de alguna macro que necesito escribir.
Peter Norvig demuestra que 16 de los 23 patrones en Design Patterns son simplificados o eliminados por las características del lenguaje en Lisp o Dylan . [9] Hannemann y Kiczales hicieron observaciones relacionadas, implementaron varios de los 23 patrones de diseño utilizando un lenguaje de programación orientado a aspectos ( AspectJ ) y demostraron que las dependencias a nivel de código se eliminaron de las implementaciones de 17 de los 23 patrones de diseño y que la programación orientada a aspectos podría simplificar las implementaciones de patrones de diseño. [10]
En una entrevista con InformIT en 2009, Erich Gamma afirmó que los autores del libro habían tenido una discusión en 2005 sobre cómo habrían refactorizado el libro y llegaron a la conclusión de que habrían recategorizado algunos patrones y añadido algunos más, como objeto/interfaz de extensión, inyección de dependencia, objeto de tipo y objeto nulo. Gamma quería eliminar el patrón Singleton, pero no hubo consenso entre los autores para hacerlo. [11]