Το Composite Pattern (Σύνθετο Πρότυπο Σχεδίασης) ανήκει στα δομικά πρότυπα (structural patterns) και χρησιμοποιείται όταν θέλουμε να διαχειριστούμε μια ομάδα αντικειμένων με τον ίδιο ακριβώς τρόπο που θα διαχειριζόμασταν ένα μεμονωμένο αντικείμενο. Το μοτίβο αυτό οργανώνει τα αντικείμενα σε δενδρικές δομές (tree structures) για να αναπαραστήσει ιεραρχίες.
Πότε χρησιμοποιείται;
Τα Βασικά Συστατικά του Composite Pattern
Το παρακάτω παράδειγμα περιγράφει ένα σύστημα αρχείων που έχουμε Αρχεία (Files) και Φακέλους (Folders). Ένας φάκελος μπορεί να περιέχει αρχεία και άλλους φακέλους.
Το interface FileSystemComponent ορίζει τη μέθοδο showDetails.
// Common interface for both files and folders public interface FileSystemComponent { void showDetails(String indent); }
Ένα οποιοδήποτε αρχείο αποτελεί φύλλο της ιεραρχίας, καθώς δεν περιέχει άλλα στοιχεία. Η παρακάτω κλάση File δεν πρέπει να συγχέεται με την κλάση java.io.File του JDK.
// Leaf class public class File implements FileSystemComponent { private String name; private long size; // σε KB public File(String name, long size) { this.name = name; this.size = size; } @Override public void showDetails(String indent) { System.out.println(indent + "📄 Αρχείο: " + name + " (" + size + " KB)"); } }
Ο φάκελος είναι το “σύνθετο” αντικείμενο. Περιέχει μια λίστα από FileSystemComponent (άρα μπορεί να περιέχει αρχεία και άλλους φακέλους).
import java.util.ArrayList; import java.util.List; // Composite class public class Folder implements FileSystemComponent { private String name; private List<FileSystemComponent> components = new ArrayList<>(); public Folder(String name) { this.name = name; } public void addComponent(FileSystemComponent component) { components.add(component); } public void removeComponent(FileSystemComponent component) { components.remove(component); } @Override public void showDetails(String indent) { System.out.println(indent + "Folder: [" + name + "]"); // Αναδρομική κλήση της μεθόδου για όλα τα παιδιά του φακέλου for (FileSystemComponent component : components) { component.showDetails(indent + " "); } } }
Ο κώδικας προς εκτέλεση ενός συστήματος αρχείων θα μπορούσε να είναι ο εξής:
public class FileSystem { public static void main(String[] args) { // Δημιουργία απλών αρχείων (Leafs) FileSystemComponent file1 = new File("resume.pdf", 120); FileSystemComponent file2 = new File("photo.jpg", 1500); FileSystemComponent file3 = new File("script.py", 15); FileSystemComponent file4 = new File("notes.txt", 12); // Δημιουργία υποφακέλου και προσθήκη αρχείων σε αυτόν Folder subFolder = new Folder("Document Folder"); subFolder.addComponent(file1); subFolder.addComponent(file3); // Δημιουργ`ία κεντρικού φακέλου (Root Composite) Folder homeFolder = new Folder("Home Filder"); homeFolder.addComponent(file2); // Προσθήκη αρχείου απευθείας στο root homeFolder.addComponent(file4); // Προσθήκη αρχείου απευθείας στο root homeFolder.addComponent(subFolder); // Προσθήκη του υποφακέλου στο root // Εμφάνιση όλης της δομής με μία κλήση! System.out.println("--- File System Display ---"); homeFolder.showDetails(""); } }
// Leaf class representing a Symbolic Link (Συμβολικός Σύνδεσμος) public class SymbolicLink implements FileSystemComponent { // Κρατάμε αναφορά στο αντικείμενο-στόχο (target) που δείχνει το symlink private FileSystemComponent target; public SymbolicLink(FileSystemComponent target) { this.target = target; } @Override public void showDetails(String indent) { if (target != null) { target.showDetails(indent); } else { System.out.println("Broken Link!"); } } }