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:
- Component: Defineix la interfície per als objectes de la composició.
- Leaf (Fulla): Representa objectes individuals de la composició.
- 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
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
- Component: Defineix l'operació comuna que tots els components han d'implementar.
- Leaf: Implementa l'operació definida en el component.
- Composite: Conté una llista de components i implementa l'operació recorrent tots els seus fills.
- 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.
Curs de Patrons de Disseny de Programari
Mòdul 1: Introducció als Patrons de Disseny
- Què són els Patrons de Disseny?
- Història i Origen dels Patrons de Disseny
- Classificació dels Patrons de Disseny
- Avantatges i Desavantatges d'Usar Patrons de Disseny
Mòdul 2: Patrons Creacionals
Mòdul 3: Patrons Estructurals
Mòdul 4: Patrons de Comportament
- Introducció als Patrons de Comportament
- Chain of Responsibility
- Command
- Interpreter
- Iterator
- Mediator
- Memento
- Observer
- State
- Strategy
- Template Method
- Visitor
Mòdul 5: Aplicació de Patrons de Disseny
- Com Seleccionar el Patró Adequat
- Exemples Pràctics d'Ús de Patrons
- Patrons de Disseny en Projectes Reals
- Refactorització Usant Patrons de Disseny
Mòdul 6: Patrons de Disseny Avançats
- Patrons de Disseny en Arquitectures Modernes
- Patrons de Disseny en Microserveis
- Patrons de Disseny en Sistemes Distribuïts
- Patrons de Disseny en Desenvolupament Àgil