Composite

RECU-0184 (Recurso Patrón)

Descripción

El patrón Composite sirve para construir objetos complejos a partir de otros más simples y similares entre sí, gracias a la composición recursiva y a una estructura en forma de árbol. Esto simplifica el tratamiento de los objetos creados, ya que al poseer todos ellos una interfaz común, se tratan todos de la misma manera.

Nombre

También denominado Compositor

Clasificación

Patrón estructural

Motivación

Si pensamos en una aplicación gráfica de componentes sencillos (lineas, texto, etc..) Imaginemos que necesitamos crear una serie de clases para guardar información acerca de una serie de figuras que serán círculos, cuadrados y triángulos. Además necesitamos poder tratar también grupos de imágenes porque nuestro programa permite seleccionar varias de estas figuras a la vez para moverlas por la pantalla. Lo lógico es crear una clase abstracta que represente componentes y contenedores de la cual todas heredan, y que define sus operaciones

En cuanto a los grupos de Figuras podríamos caer en la tentación de crear una clase particular separada de las anteriores llamada GrupoDeImágenes, también con un método pintar().Podríamos pensar la idea de separar en clases privadas componentes (figuras) y contenedores (grupos) pero tiene el problema de que, para cada uno de los dos atributos, el método pintar() tendrá una implementación diferente, aumentando la complejidad del sistema.El patrón Composite da una solución elegante a este problema, de la que además resulta en una implementación más sencilla.

Aplicabildidad

Se recomienda el uso del patrón si:

  • Se quiere realizar jerarquías de objetos del tipo "todo-parte"
  • Quieres ser capaz de ignorar la diferencia entre los objetos individuales y composiciones de objetos. Los clientes tratarán a todos los objetos de la estructura compuesta uniformemente.

Estructura

El patrón se representa gráficamente de la siguiente manera

168

Participantes

  • Componente : Define la interfaz común para los objetos de la composición. Ademas, define la interfaz para acceder y gestionar los hijos. Implemente un comportamiento por defecto común a las subclases
  • Hoja: Representa los objetos sin hijos de la composición. Define el comportamiento de los mismos.
  • Composite: Define el comportamiento para los componentes que tienen hijos. Almacena los componentes con hijos e implemente las operaciones para la gestión de hijos
  • Cliente: Manipulas los objetos de la composición a través de la interfaz

Colaboraciones

Cliente usa el interfaz de Componente para interactuar con objetos en la estructura de Composite. Si el receptor es una Hoja, entonces la petición es manejada directamente. Si el receptor es un Composite, lanza la petición a sus hijos

Consecuencias

  • Define jerarquías de clases hechas de objetos primitivos y compuestos. Si el código cliente espera un objeto simple, puede recibir también uno compuesto.
  • Simplifica el cliente, objetos simples y compuestos se tratan homogéneamente
  • Facilita la incorporación de nuevos tipos de componentes
  • Puede hacer el diseño demasiado general

Implementación

Algunas recomendaciones para la implementación del patrón son las siguientes.

  • Referencias explicitas a los padres. Con ello se simplifica algunas operaciones de la estructura compuesta. Lo mejor es definirlas en la clase Componente. Gestionarlas al añadir/eliminar elementos de unComposite.
  • Compartir Componentes: Es muy útil por el ahorro de memoria que supone. LA gestión del componente con varios padres puede llegar a sser compleja
  • Maximizar la interfaz del componente: Dar un comportamiento por defecto que sobrescribirán las subclases.
  • Orden de los hijos: En ocasiones los hijos presentan un orden , y hay que tenerlo en cuenta para la implementación
  • Declaración de las operaciones de manejo de hijos: Definirlas en la raíz Componente y darle una implementación por defecto permite aumentar la transparencia, pero se pierde seguridad (¿como evitar añada/elimine objetos a una hoja?). Si las definimos en la clase Composite se obtiene mas seguridad pero se pierde transparencia al no existir interfaz uniforme

Código de ejemplo

public abstract class Componente
{
    protected String nombre;
    public Componente (String nombre)
    {
        this.nombre = nombre;
    }
    abstract public void Agregar(Componente c);
    abstract public void Remover(Componente c);
    abstract public void Mostrar(int profundidad);
}
class Composite extends Componente
{
    private ArrayList<Componente> hijo = new ArrayList<Componente>();
    public Composite (String name)
    {
        super(name);
    }
    public void Agregar(Componente componente)
    {
        hijo.add(componente);
    }
    public void Remover(Componente componente)
    {
        hijo.remove(componente);
    }
    public void Mostrar(int profundidad)
    {
        System.out.println(nombre + " nivel: " + profundidad);
        for (int i = 0; i < hijo.size(); i++)
            hijo.get(i).Mostrar(profundidad + 1);
    }
}
class Hoja extends Componente
{
    public Hoja (String nombre)
    {
        super(nombre);
    }
    public void Agregar(Componente c)
    {
        System.out.println("no se puede agregar la hoja");
    }
    public void Remover(Componente c)
    {
        System.out.println("no se puede quitar la hoja");
    }
    public void Mostrar(int depth)
    {
        System.out.println('-' + "" + nombre);
    }
}
public class Client
{
    public static void main(String[] args)
    {
        Composite raiz = new Composite("root");
        raiz.Agregar(new Hoja("hoja A"));
        raiz.Agregar(new Hoja("hoja B"));
        Composite comp = new Composite("compuesto X");
        comp.Agregar(new Hoja("hoja XA"));
        comp.Agregar(new Hoja("hoja XB"));
        raiz.Agregar(comp);
        raiz.Agregar(new Hoja("hoja C"));
        Hoja l = new Hoja("hoja D");
        raiz.Agregar(l);
        raiz.Remover(l);
        raiz.Mostrar(1);
    }
}

Usos conocidos

Las clases java.awt.Component (Componente), java.awt.Container (Contenedor), java.awt.Panel (Contenedor concreto), java.awt.Button (Boton).