Introducció al Patró Composite

El patró Composite és un patró estructural que permet tractar objectes individuals i composicions d'objectes de manera uniforme. Aquest patró és especialment útil quan es treballa amb estructures d'arbres, com ara jerarquies d'objectes.

Objectius del Patró Composite

  • Uniformitat: Permet tractar objectes individuals i grups d'objectes de la mateixa manera.
  • Flexibilitat: Facilita l'addició de nous tipus de components sense canviar el codi existent.
  • Simplicitat: Redueix la complexitat del codi client que treballa amb estructures jeràrquiques.

Components del Patró Composite

El patró Composite consta de tres components principals:

  1. Component: Defineix la interfície per als objectes de la composició.
  2. Leaf (Fulla): Representa objectes individuals de la composició.
  3. Composite: Representa composicions d'objectes (pot contenir altres fulles o composicions).

Diagrama UML del Patró Composite

Component
+ operation()

Leaf
+ operation()

Composite
+ add(Component)
+ remove(Component)
+ getChild(int)
+ operation()

Implementació del Patró Composite

A continuació es mostra una implementació del patró Composite en Java:

Component

public abstract class Component {
    public abstract void operation();
}

Leaf

public class Leaf extends Component {
    @Override
    public void operation() {
        System.out.println("Leaf operation");
    }
}

Composite

import java.util.ArrayList;
import java.util.List;

public class Composite extends Component {
    private List<Component> children = new ArrayList<>();

    public void add(Component component) {
        children.add(component);
    }

    public void remove(Component component) {
        children.remove(component);
    }

    public Component getChild(int index) {
        return children.get(index);
    }

    @Override
    public void operation() {
        for (Component child : children) {
            child.operation();
        }
    }
}

Exemple d'Ús

public class Client {
    public static void main(String[] args) {
        Composite root = new Composite();
        Composite branch1 = new Composite();
        Composite branch2 = new Composite();

        Leaf leaf1 = new Leaf();
        Leaf leaf2 = new Leaf();
        Leaf leaf3 = new Leaf();

        root.add(branch1);
        root.add(branch2);
        branch1.add(leaf1);
        branch1.add(leaf2);
        branch2.add(leaf3);

        root.operation();
    }
}

Explicació del Codi

  1. Component: Defineix l'operació comuna que tots els components han d'implementar.
  2. Leaf: Implementa l'operació definida en el component.
  3. Composite: Conté una llista de components i implementa l'operació recorrent tots els seus fills.
  4. Client: Crea una estructura jeràrquica de components i invoca l'operació en la composició arrel.

Exercicis Pràctics

Exercici 1: Implementació Bàsica

Implementa una estructura jeràrquica utilitzant el patró Composite per representar un sistema de fitxers amb carpetes i arxius. Cada carpeta pot contenir altres carpetes o arxius, i cada arxiu ha de tenir una operació per mostrar el seu nom.

Solució

public abstract class FileSystemComponent {
    public abstract void showDetails();
}

public class File extends FileSystemComponent {
    private String name;

    public File(String name) {
        this.name = name;
    }

    @Override
    public void showDetails() {
        System.out.println("File: " + name);
    }
}

public class Directory extends FileSystemComponent {
    private String name;
    private List<FileSystemComponent> components = new ArrayList<>();

    public Directory(String name) {
        this.name = name;
    }

    public void add(FileSystemComponent component) {
        components.add(component);
    }

    public void remove(FileSystemComponent component) {
        components.remove(component);
    }

    @Override
    public void showDetails() {
        System.out.println("Directory: " + name);
        for (FileSystemComponent component : components) {
            component.showDetails();
        }
    }
}

public class Client {
    public static void main(String[] args) {
        Directory root = new Directory("root");
        Directory home = new Directory("home");
        Directory user = new Directory("user");

        File file1 = new File("file1.txt");
        File file2 = new File("file2.txt");
        File file3 = new File("file3.txt");

        root.add(home);
        home.add(user);
        user.add(file1);
        user.add(file2);
        root.add(file3);

        root.showDetails();
    }
}

Exercici 2: Extensió del Patró

Extén l'exemple anterior per afegir permisos de lectura i escriptura als arxius i carpetes. Implementa mètodes per verificar aquests permisos abans de mostrar els detalls.

Solució

public abstract class FileSystemComponent {
    protected boolean canRead;
    protected boolean canWrite;

    public FileSystemComponent(boolean canRead, boolean canWrite) {
        this.canRead = canRead;
        this.canWrite = canWrite;
    }

    public abstract void showDetails();
}

public class File extends FileSystemComponent {
    private String name;

    public File(String name, boolean canRead, boolean canWrite) {
        super(canRead, canWrite);
        this.name = name;
    }

    @Override
    public void showDetails() {
        if (canRead) {
            System.out.println("File: " + name);
        } else {
            System.out.println("File: " + name + " (No read permission)");
        }
    }
}

public class Directory extends FileSystemComponent {
    private String name;
    private List<FileSystemComponent> components = new ArrayList<>();

    public Directory(String name, boolean canRead, boolean canWrite) {
        super(canRead, canWrite);
        this.name = name;
    }

    public void add(FileSystemComponent component) {
        components.add(component);
    }

    public void remove(FileSystemComponent component) {
        components.remove(component);
    }

    @Override
    public void showDetails() {
        if (canRead) {
            System.out.println("Directory: " + name);
            for (FileSystemComponent component : components) {
                component.showDetails();
            }
        } else {
            System.out.println("Directory: " + name + " (No read permission)");
        }
    }
}

public class Client {
    public static void main(String[] args) {
        Directory root = new Directory("root", true, true);
        Directory home = new Directory("home", true, false);
        Directory user = new Directory("user", false, true);

        File file1 = new File("file1.txt", true, true);
        File file2 = new File("file2.txt", true, false);
        File file3 = new File("file3.txt", false, true);

        root.add(home);
        home.add(user);
        user.add(file1);
        user.add(file2);
        root.add(file3);

        root.showDetails();
    }
}

Resum

El patró Composite és una solució poderosa per treballar amb estructures jeràrquiques, permetent tractar objectes individuals i composicions d'objectes de manera uniforme. Aquest patró és especialment útil en aplicacions que requereixen treballar amb estructures d'arbres, com ara sistemes de fitxers, menús i organitzacions jeràrquiques.

© Copyright 2024. Tots els drets reservats