En ingeniería de software , el patrón compuesto es un patrón de diseño de particionamiento . El patrón compuesto describe un grupo de objetos que se tratan de la misma manera que una única instancia del mismo tipo de objeto. La intención de un compuesto es "componer" objetos en estructuras de árbol para representar jerarquías de partes y todo. La implementación del patrón compuesto permite a los clientes tratar los objetos individuales y las composiciones de manera uniforme. [1]
Descripción general
El patrón de diseño Composite [2] es uno de los veintitrés patrones de diseño GoF conocidos que describen cómo resolver problemas de diseño recurrentes para diseñar software orientado a objetos flexible y reutilizable, es decir, objetos que son más fáciles de implementar, cambiar, probar y reutilizar.
Problemas que puede resolver el patrón de diseño Composite
- Representar una jerarquía parte-todo para que los clientes puedan tratar los objetos parciales y totales de manera uniforme.
- Representar una jerarquía parte-todo como estructura de árbol.
Al definir (1) Partobjetos y (2) Wholeobjetos que actúan como contenedores de Partobjetos, los clientes deben tratarlos por separado, lo que complica el código del cliente. [3]
Soluciones que describe el patrón de diseño Composite
- Defina una
Componentinterfaz unificada paraLeafobjetos parciales ( ) yCompositeobjetos completos ( ). - Los objetos individuales
Leafimplementan laComponentinterfaz directamente yCompositelos objetos envían solicitudes a sus componentes secundarios.
Esto permite que los clientes trabajen a través de la Componentinterfaz para tratar Leaflos Compositeobjetos de manera uniforme:
Leaflos objetos realizan una solicitud directamente y Compositelos objetos reenvían la solicitud a sus componentes secundarios de manera recursiva hacia abajo en la estructura de árbol. Esto hace que las clases de cliente sean más fáciles de implementar, cambiar, probar y reutilizar.
Vea también el diagrama de clases y objetos UML a continuación.
Motivación
Al trabajar con datos estructurados en árbol, los programadores a menudo tienen que distinguir entre un nodo de hoja y una rama. Esto hace que el código sea más complejo y, por lo tanto, más propenso a errores. La solución es una interfaz que permita tratar objetos complejos y primitivos de manera uniforme. En la programación orientada a objetos , un compuesto es un objeto diseñado como una composición de uno o más objetos similares, todos ellos con una funcionalidad similar. Esto se conoce como una relación " tiene-un " entre objetos. [4] El concepto clave es que puede manipular una sola instancia del objeto del mismo modo que manipularía un grupo de ellos. Las operaciones que puede realizar en todos los objetos compuestos a menudo tienen una relación de mínimo común denominador . Por ejemplo, si define un sistema para representar formas agrupadas en una pantalla, sería útil definir el cambio de tamaño de un grupo de formas para que tenga el mismo efecto (en cierto sentido) que el cambio de tamaño de una sola forma.
Cuándo utilizar
Se debe utilizar el método compuesto cuando los clientes ignoran la diferencia entre composiciones de objetos y objetos individuales. [1] Si los programadores descubren que están utilizando varios objetos de la misma manera y, a menudo, tienen un código casi idéntico para manejar cada uno de ellos, entonces el método compuesto es una buena opción; en esta situación, es menos complejo tratar los primitivos y los compuestos como homogéneos.
Estructura
Diagrama de objetos y clases UML

En el diagrama de clases UML anterior , la clase no hace referencia a las clases y directamente (por separado). En cambio, hace referencia a la interfaz común y puede tratar a y de manera uniforme.
La clase no tiene objetos secundarios e implementa la interfaz directamente.
La clase mantiene un contenedor de
objetos secundarios ( ) y reenvía solicitudes a estos ( ).
ClientLeafCompositeClientComponentLeafCompositeLeafComponentCompositeComponentchildrenchildrenfor each child in children: child.operation()
El diagrama de colaboración de objetos muestra las interacciones en tiempo de ejecución: en este ejemplo, el Clientobjeto envía una solicitud al Compositeobjeto de nivel superior (de tipo Component) en la estructura de árbol. La solicitud se reenvía a (se ejecuta en) todos los objetos secundarios Component( Leafy Compositeobjetos ) hacia abajo en la estructura de árbol.
- Definición de operaciones relacionadas con niños

Hay dos variantes de diseño para definir e implementar operaciones relacionadas con los componentes secundarios, como agregar o eliminar un componente secundario del contenedor ( add(child)/remove(child)) y acceder a un componente secundario ( getChild()):
- Diseño para uniformidad: las operaciones relacionadas con los elementos secundarios se definen en la
Componentinterfaz. Esto permite que los clientes tratenLeaflosCompositeobjetos de manera uniforme. Sin embargo, se pierde la seguridad de tiposLeafporque los clientes pueden realizar operaciones relacionadas con los elementos secundarios en los objetos. - Diseño para la seguridad de tipos: las operaciones relacionadas con los hijos se definen únicamente en la
Compositeclase. Los clientes deben tratar a los objetosLeafyCompositea los objetos de forma diferente. Sin embargo, la seguridad de tipos se obtiene porque los clientes no pueden realizar operaciones relacionadas con los hijos enLeaflos objetos.
El patrón de diseño compuesto enfatiza la uniformidad sobre la seguridad del tipo .
Diagrama de clases UML
.svg/500px-Composite_UML_class_diagram_(fixed).svg.png)
- Componente
- es la abstracción para todos los componentes, incluidos los compuestos
- Declara la interfaz para los objetos en la composición.
- (opcional) define una interfaz para acceder al componente padre en la estructura recursiva y la implementa si es apropiado
- Hoja
- Representa objetos de hojas en la composición.
- Implementa todos los métodos del componente.
- Compuesto
- representa un componente compuesto (componente que tiene hijos)
- Implementa métodos para manipular a los niños.
- Implementa todos los métodos del componente, generalmente delegándolos a sus hijos.

Variación
Como se describe en Patrones de diseño , el patrón también implica incluir los métodos de manipulación de elementos secundarios en la interfaz principal del componente, no solo en la subclase Composite. Las descripciones más recientes a veces omiten estos métodos. [7]
Ejemplo
Esta implementación de C++14 se basa en la implementación anterior a C++98 del libro.
#include <iostream> #include <cadena> #include <lista> #include <memoria> #include <stdexcept>
typedef double Moneda ;
// declara la interfaz para los objetos en la composición.
class Equipment { // Component public : // implementa el comportamiento predeterminado para la interfaz común a todas las clases, según corresponda. virtual const std :: string & getName () { return name ; } virtual void setName ( const std :: string & name_ ) { name = name_ ; } virtual Currency getNetPrice () { return netPrice ; } virtual void setNetPrice ( Currency netPrice_ ) { netPrice = netPrice_ ; } // declara una interfaz para acceder y administrar sus componentes secundarios. virtual void add ( std :: shared_ptr < Equipment > ) = 0 ; virtual void remove ( std :: shared_ptr < Equipment > ) = 0 ; virtual ~ Equipment () = default ; protegido : Equipo () : nombre ( "" ), precio_neto ( 0 ) {} Equipo ( const std :: string & nombre_ ) : nombre ( nombre_ ), precio_neto ( 0 ) {} privado : std :: string nombre ; Moneda precio_neto ; };
// define el comportamiento de los componentes que tienen hijos.
class CompositeEquipment : public Equipment { // Composite public : // implementa operaciones relacionadas con los hijos en la interfaz Component. virtual Currency getNetPrice () override { Currency total = Equipment :: getNetPrice (); for ( const auto & i : equipment ) { total += i -> getNetPrice (); } return total ; } virtual void add ( std :: shared_ptr < Equipment > equipment_ ) override { equipment . push_front ( equipment_ . get ()); } virtual void remove ( std :: shared_ptr < Equipment > equipment_ ) override { equipment . remove ( equipment_ . get ()); } protected : CompositeEquipment () : equipment () {} CompositeEquipment ( const std :: string & name_ ) : equipment () { setName ( name_ ); } private : // almacena los componentes hijos. std :: list < Equipment *> equipment ; };
// representa los objetos de hoja en la composición.
class FloppyDisk : public Equipment { // Hoja public : FloppyDisk ( const std :: string & name_ ) { setName ( name_ ); } // Una hoja no tiene hijos. void add ( std :: shared_ptr < Equipment > ) override { throw std :: runtime_error ( "FloppyDisk::add" ); } void remove ( std :: shared_ptr < Equipment > ) override { throw std :: runtime_error ( "FloppyDisk::remove" ); } };
clase Chasis : público CompositeEquipment { público : Chasis ( const std :: string & nombre_ ) { setName ( nombre_ ); } };
int main () { // Los punteros inteligentes evitan fugas de memoria. std :: shared_ptr < FloppyDisk > fd1 = std :: make_shared < FloppyDisk > ( "Disquete de 3,5 pulgadas" ); fd1 -> setNetPrice ( 19,99 ); std :: cout << fd1 -> getName () << ": netPrice=" << fd1 -> getNetPrice () << '\n' ;
std :: shared_ptr < FloppyDisk > fd2 = std :: make_shared < FloppyDisk > ( "Disquete de 5,25 pulgadas" ); fd2 -> setNetPrice ( 29,99 ); std :: cout << fd2 -> getName () << ": netPrice=" << fd2 -> getNetPrice () << '\n' ;
std :: unique_ptr < Chasis > ch = std :: make_unique < Chasis > ( "Chasis de PC" ); ch - > setNetPrice ( 39.99 ); ch -> add ( fd1 ); ch -> add ( fd2 ); std :: cout << ch -> getName () << ": netPrice=" << ch -> getNetPrice () << '\n' ;
fd2 -> agregar ( fd1 );
}
La salida del programa es
3.5 en disquete : netPrice = 19.99 5.25 en disquete : netPrice = 29.99 Chasis de PC : netPrice = 89.97 Se llama a la terminación después de lanzar una instancia de ' std :: runtime_error ' what () : FloppyDisk :: add
Véase también
Referencias
- ^ ab Gamma, Erich; Richard Helm; Ralph Johnson; John M. Vlissides (1995). Patrones de diseño: elementos de software reutilizable orientado a objetos. Addison-Wesley. pp. 395. ISBN 0-201-63361-2.
- ^ Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1994). Patrones de diseño: elementos de software reutilizable orientado a objetos. Addison Wesley. pp. 163 y siguientes. ISBN 0-201-63361-2.
{{cite book}}: CS1 maint: varios nombres: lista de autores ( enlace ) - ^ "El patrón de diseño compuesto: problema, solución y aplicabilidad". w3sDesign.com . Consultado el 12 de agosto de 2017 .
- ^ Scott Walters (2004). Libro de patrones de diseño de Perl. Archivado desde el original el 8 de marzo de 2016. Consultado el 18 de enero de 2010 .
- ^ "El patrón de diseño Composite - Estructura y colaboración". w3sDesign.com . Consultado el 12 de agosto de 2017 .
- ^ "El patrón de diseño Composite - Implementación". w3sDesign.com . Consultado el 12 de agosto de 2017 .
- ^ Geary, David (13 de septiembre de 2002). "Una mirada al patrón de diseño Composite". Patrones de diseño de Java. JavaWorld . Consultado el 20 de julio de 2020 .
Enlaces externos
- Implementación de patrón compuesto en Java
- Descripción de patrón compuesto del Repositorio de patrones de Portland
- Patrón compuesto en UML y en LePUS3, un lenguaje de modelado formal
- Clase::Delegación en CPAN
- "El fin de la herencia: creación automática de interfaces en tiempo de ejecución para objetos agregados" por Paul Baranowski
- Proyecto de código abierto PerfectJPattern, que proporciona una implementación componentizada del patrón compuesto en Java
- [1] Una implementación persistente basada en Java
- Patrón de diseño compuesto
