stringtranslate.com

Patrón compuesto

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

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

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

Un ejemplo de diagrama de clases y objetos UML para el patrón de diseño Composite. [5]

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 ( ).ClientLeafCompositeClientComponentLeafComposite
LeafComponent
CompositeComponentchildrenchildrenfor 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
Definición de operaciones relacionadas con niños en el patrón de diseño Composite. [6]

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()):

El patrón de diseño compuesto enfatiza la uniformidad sobre la seguridad del tipo .

Diagrama de clases UML

Patrón compuesto en UML .
Componente
Hoja
Compuesto
Patrón compuesto en LePUS3.

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

  1. ^ 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.
  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 )
  3. ^ "El patrón de diseño compuesto: problema, solución y aplicabilidad". w3sDesign.com . Consultado el 12 de agosto de 2017 .
  4. ^ 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 .
  5. ^ "El patrón de diseño Composite - Estructura y colaboración". w3sDesign.com . Consultado el 12 de agosto de 2017 .
  6. ^ "El patrón de diseño Composite - Implementación". w3sDesign.com . Consultado el 12 de agosto de 2017 .
  7. ^ 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