En ingeniería de software , el patrón compuesto es un patrón de diseño de particiones . 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 parte y todo. La implementación del patrón compuesto permite a los clientes tratar objetos y composiciones individuales de manera uniforme. [1]
Descripción general
El patrón de diseño compuesto [2] es uno de los veintitrés patrones de diseño GoF bien 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.
¿Qué problemas puede resolver el patrón de diseño compuesto? [3]
- Se debe representar una jerarquía de parte y todo para que los clientes puedan tratar partes y objetos de manera uniforme.
- Una jerarquía de parte y todo debe representarse como una estructura de árbol.
Al definir (1) Part
objetos y (2) Whole
objetos que actúan como contenedores de Part
objetos, los clientes deben tratarlos por separado, lo que complica el código del cliente.
¿Qué solución describe el patrón de diseño compuesto?
- Defina una
Component
interfaz unificada paraLeaf
objetos part ( ) y objetos completos (Composite
). - Los
Leaf
objetos individuales implementan laComponent
interfaz directamente y losComposite
objetos envían solicitudes a sus componentes secundarios.
Esto permite a los clientes a trabajar a través de la Component
interfaz de tratar Leaf
y Composite
objetos de manera uniforme: Leaf
objetos realizan una solicitud directamente, y Composite
objetos hacia adelante la solicitud a sus componentes hijos de forma recursiva hacia abajo la estructura de árbol. Esto hace que las clases de cliente sean más fáciles de implementar, cambiar, probar y reutilizar.
Consulte también el diagrama de objetos y clases UML a continuación.
Motivación
Cuando se trata de datos estructurados en árbol, los programadores a menudo tienen que discriminar entre un nodo 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 permite 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 exhibiendo una funcionalidad similar. Esto se conoce como una relación " tiene-a " entre objetos. [4] El concepto clave es que puedes manipular una sola instancia del objeto del mismo modo que manipularías un grupo de ellos. Las operaciones que puede realizar en todos los objetos compuestos suelen tener 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 tener el mismo efecto (en cierto sentido) que cambiar el tamaño de una sola forma.
Cuándo usar
El compuesto debe usarse cuando los clientes ignoran la diferencia entre la composición de los objetos y los objetos individuales. [1] Si los programadores descubren que están usando varios objetos de la misma manera y, a menudo, tienen un código casi idéntico para manejar cada uno de ellos, entonces el compuesto es una buena opción; En esta situación, es menos complejo tratar las primitivas y los compuestos como homogéneos.
Estructura
Diagrama de objetos y clases UML
En el diagrama de clases de UML anterior , la Client
clase no se refiere a las clases Leaf
y Composite
directamente (por separado). En cambio, el Client
se refiere a la común Component
interfaz y puede tratar Leaf
y Composite
de manera uniforme.
La Leaf
clase no tiene hijos e implementa la Component
interfaz directamente.
La Composite
clase mantiene un contenedor de Component
objetos secundarios ( children
) y reenvía solicitudes a estos children
( for 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 Client
objeto envía una solicitud al Composite
objeto de nivel superior (de tipo Component
) en la estructura de árbol. La solicitud se reenvía a (se realiza en) todos los Component
objetos secundarios ( Leaf
y Composite
objetos) hacia abajo en la estructura del árbol.
- Definición de operaciones relacionadas con niños
Hay dos variantes de diseño para definir e implementar operaciones relacionadas con los niños, como agregar / 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 niños se definen en la
Component
interfaz. Esto permite a los clientes a tratarLeaf
yComposite
objetos de manera uniforme. Pero la seguridad de tipos se pierde porque los clientes pueden realizar operaciones relacionadas con niños enLeaf
objetos. - Diseño para seguridad de tipos: las operaciones relacionadas con niños se definen solo en la
Composite
clase. Los clientes deben tratarLeaf
y losComposite
objetos de manera diferente. Pero la seguridad de tipos se obtiene porque los clientes no pueden realizar operaciones relacionadas con niños enLeaf
objetos.
El patrón de diseño compuesto enfatiza la uniformidad sobre la seguridad del tipo .
Diagrama de clases UML
- Componente
- es la abstracción de todos los componentes, incluidos los compuestos
- declara la interfaz para los objetos en la composición
- (opcional) define una interfaz para acceder al padre de un componente en la estructura recursiva y la implementa si es apropiado
- Hoja
- representa objetos de hoja en la composición
- implementa todos los métodos de componentes
- Compuesto
- representa un componente compuesto (componente que tiene hijos)
- implementa métodos para manipular a los niños
- implementa todos los métodos de Component, 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 niños en la interfaz del componente principal, no solo en la subclase compuesta. Las descripciones más recientes a veces omiten estos métodos. [7]
Ejemplo
El siguiente ejemplo, escrito en Java , implementa una clase gráfica, que puede ser una elipse o una composición de varios gráficos. Todos los gráficos se pueden imprimir. En forma Backus-Naur ,
Gráfico :: = elipse | GraphicList GraphicList :: = vacío | Gráfico GráficoLista
Podría ampliarse para implementar varias otras formas (rectángulo, etc.) y métodos ( traducir , etc.).
Java
import java.util.ArrayList ;/ ** "Componente" * / interface Graphic { // Imprime el gráfico. print vacío público (); } / ** "Composite" * / class CompositeGraphic implementa Graphic { // Colección de gráficos secundarios. Lista final privada < Gráfico > childGraphics = new ArrayList <> (); // Agrega el gráfico a la composición. public void add ( Gráfico gráfico ) { childGraphics . agregar ( gráfico ); } // Esto es solo shallowCopy public void add ( List < Graphic > compositGraphic ) { childGraphics . addAll ( compositGraphic ); } // Imprime el gráfico. @ Anular public void print () { para ( Gráfico gráfico : childGraphics ) { gráfico . imprimir (); // Delegación } } }/ ** "Hoja" * / clase Ellipse implementa Gráfico { // Imprime el gráfico. @Override public void print () { System . fuera . println ( "Elipse" ); } }/ ** Cliente * / class CompositeDemo { public static void main ( String [] args ) { // Inicializar cuatro elipses Ellipse ellipse1 = new Ellipse (); Elipse elipse2 = nueva Elipse (); Elipse elipse3 = nueva Elipse (); Elipse elipse4 = nueva Elipse (); // Crea dos compuestos que contienen las elipses CompositeGraphic compositGraphic2 = new CompositeGraphic (); compositGraphic2 . agregar ( elipse1 ); compositGraphic2 . agregar ( elipse2 ); compositGraphic2 . agregar ( elipse3 ); CompositeGraphic compositGraphic3 = new CompositeGraphic (); compositGraphic3 . agregar ( elipse4 ); // Cree otro gráfico que contenga dos gráficos CompositeGraphic compositGraphic = new CompositeGraphic (); compositGraphic . agregar ( compositGraphic2 ); compositGraphic . agregar ( compositGraphic3 ); // Imprime el gráfico completo (Cuatro veces la cadena "Elipse"). compositGraphic . imprimir (); } }
Ver también
Referencias
- ^ a b Gamma, Erich; Richard Helm; Ralph Johnson; John M. Vlissides (1995). Patrones de diseño: elementos de software orientado a objetos reutilizable . Addison-Wesley. págs. 395 . ISBN 0-201-63361-2.
- ^ Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1994). Patrones de diseño: elementos de software orientado a objetos reutilizable . Addison Wesley. págs. 163 y siguientes . ISBN 0-201-63361-2.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 compuesto - Estructura y colaboración" . w3sDesign.com . Consultado el 12 de agosto de 2017 .
- ^ "El patrón de diseño compuesto - 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 compuesto" . 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: construcción automática de interfaces en tiempo de ejecución para objetos agregados" por Paul Baranowski
- Proyecto de código abierto PerfectJPattern , proporciona una implementación en componentes del patrón compuesto en Java
- [1] Una implementación persistente basada en Java
- Patrón de diseño compuesto