Patrón de Diseño Composite en Java:

El patrón Composite permite que los clientes traten a objetos individuales y composiciones de objetos de manera uniforme. Esto se logra mediante la creación de una interfaz común para componentes individuales y compuestos.

Ejemplo de Composite en Java:

Supongamos que tienes una interfaz Componente que define operaciones comunes para componentes individuales y compuestos:

// Interfaz Componente
interface Componente {
    void operacion();
}

Y tienes dos implementaciones: Hoja (componente individual) y Compuesto (componente que contiene otros componentes):

// Hoja (Componente individual)
class Hoja implements Componente {
    private String nombre;

    public Hoja(String nombre) {
        this.nombre = nombre;
    }

    @Override
    public void operacion() {
        System.out.println("Operación en Hoja " + nombre);
    }
}

// Compuesto (Componente que contiene otros componentes)
class Compuesto implements Componente {
    private List<Componente> componentes = new ArrayList<>();

    public void agregar(Componente componente) {
        componentes.add(componente);
    }

    public void eliminar(Componente componente) {
        componentes.remove(componente);
    }

    @Override
    public void operacion() {
        System.out.println("Operación en Compuesto");
        for (Componente componente : componentes) {
            componente.operacion();
        }
    }
}

Ahora puedes utilizar estas clases para crear estructuras complejas:

public class Principal {
    public static void main(String[] args) {
        // Crear hojas individuales
        Hoja hoja1 = new Hoja("Hoja 1");
        Hoja hoja2 = new Hoja("Hoja 2");

        // Crear un compuesto y agregar hojas
        Compuesto compuesto = new Compuesto();
        compuesto.agregar(hoja1);
        compuesto.agregar(hoja2);

        // Crear otra hoja individual
        Hoja hoja3 = new Hoja("Hoja 3");

        // Crear un compuesto y agregar la hoja individual y el compuesto anterior
        Compuesto compuestoPrincipal = new Compuesto();
        compuestoPrincipal.agregar(hoja3);
        compuestoPrincipal.agregar(compuesto);

        // Llamar a operaciones
        compuestoPrincipal.operacion();
    }
}

Cuándo usar el patrón Composite:

  1. Tratar Componentes Individuales y Compuestos de Forma Uniforme:
    • Cuando necesitas tratar tanto componentes individuales como compuestos de manera uniforme.
  2. Composición Recursiva:
    • Cuando la estructura de tus objetos puede ser representada como una composición anidada de objetos más pequeños.
  3. Clientes Independientes de la Estructura de la Jerarquía:
    • Para permitir que los clientes manipulen tanto hojas como compuestos sin conocer la estructura interna de la jerarquía.
  4. Simplificar la Interfaz del Cliente:
    • Cuando deseas simplificar la interfaz del cliente al no tener que distinguir entre componentes individuales y compuestos.

El patrón Composite es especialmente útil en situaciones donde necesitas tratar colecciones de objetos de manera uniforme, independientemente de si son objetos individuales o compuestos. Proporciona una estructura jerárquica que simplifica la manipulación de estos elementos.