El patrón Composite
Descripción
El objetivo del patrón Composite es ofrecer un marco de diseño de una composición de objetos de profundidad variable, diseño que estará basado en un árbol.
Por otro lado, esta composición está encapsulada respecto a los clientes de los objetos que pueden interactuar sin tener que conocer la profundidad de la composición.
Ejemplo
En nuestro sistema de venta de vehículos, queremos representar las empresas cliente, en especial para conocer el número de vehículos de los que disponen y proporcionarles ofertas de mantenimiento para su parque de vehículos.
Las empresas que posean filiales solicitan ofertas de mantenimiento que tengan en cuenta el parque de vehículos de sus filiales.
Una solución inmediata consiste en procesar de forma diferente las empresas sin filiales y las que posean filiales. No obstante esta diferencia en el procesado entre ambos tipos de empresa vuelve a la aplicación más compleja y dependiente de la composición interna de las empresas cliente.
El patrón Composite resuelve este problema unificando ambos tipos de empresa y utilizando la composición recursiva. Esta composición recursiva es necesaria puesto que una empresa puede tener filiales que posean, ellas mismas, otras filiales. Se trata de una composición en árbol (tomamos la hipótesis de la ausencia de una filial común entre dos empresas) tal y como se ilustra en la figura 3-4.1 donde las empresas madre se sitúan sobre sus filiales.
Figura 3-4.1 - Árbol de empresas madres y de sus filiales
La figura 3-4.2 presenta el diagrama de clases correspondiente. La clase abstracta Empresa contiene la interfaz destinada a los clientes. Posee dos subclases concretas, a saber EmpresaSinFilial...
Estructura
1. Diagrama de clases
La figura 3-4.3 detalla la estructura genérica del patrón.
Figura 3-4.3 - Estructura del patrón Composite
2. Participantes
Los participantes del patrón son los siguientes:
-
Componente (Empresa) es la clase abstracta que contiene la interfaz de los objetos de la composición, implementa los métodos comunes e introduce la firma de los métodos que gestionan la composición agregando o suprimiendo componentes.
-
Hoja (EmpresasSinFilial) es la clase concreta que describe las hojas de la composición (una hoja no posee componentes).
-
Compuesto (EmpresaMadre) es la clase concreta que describe los objetos compuestos de la jerarquía. Esta clase posee una asociación de agregación con la clase Componente.
-
Cliente es la clase de los objetos que acceden a los objetos de la composición y que los manipulan.
3. Colaboraciones
Los clientes envían sus peticiones a los componentes a través de la interfaz de la clase Componente.
Cuando un componente recibe una petición, reacciona en función de su clase. Si el componente es una hoja, procesa la petición tal y como se ilustra en la figura 3-4.4.
Figura 3-4.4 - Procesado de un mensaje por parte de una hoja
Si el componente es una instancia de la clase Compuesto, realiza un procesado previo, generalmente envía un mensaje a cada uno de sus componentes y realiza un procesamiento posterior....
Dominios de aplicación
El patrón se utiliza en los siguientes casos:
-
Es necesario representar jerarquías de composición en un sistema.
-
Los clientes de una composición deben ignorar si se comunican con objetos compuestos o no.
Ejemplo en Java
Retomemos el ejemplo de las empresas y la gestión de su parque de vehículos.
El código fuente en Java de la clase abstracta Empresa aparece a continuación. Conviene observar que el método agregaFilial reenvía un resultado booleano que indica si ha sido posible realizar o no la agregación.
public abstract class Empresa
{
protected static double costeUnitarioVehiculo = 5.0;
protected int nVehiculos;
public void agregaVehiculo()
{
nVehiculos = nVehiculos + 1;
}
public abstract double calculaCosteMantenimiento();
public abstract boolean agregaFilial(Empresa filial);
}
El código fuente de la clase EmpresaSinFilial aparece a continuación. Las instancias de esta clase no pueden agregar filiales.
public class EmpresaSinFilial extends Empresa
{
public boolean agregaFilial(Empresa filial)
{
return false;
}
...