google-site-verification: google61fe8ba583a51912.html
Composite Pattern for Tree Structures in Java

The Composite Pattern is a structural design pattern that allows you to compose objects into tree structures to represent part-whole hierarchies. It lets clients treat individual objects and compositions of objects uniformly.

Basic Composite Pattern Implementation

1. Component Interface

// Component interface - defines common operations for both leaf and composite public interface FileSystemComponent { String getName(); long getSize(); void display(String indent); boolean isDirectory(); void addComponent(FileSystemComponent component); void removeComponent(FileSystemComponent component); List<FileSystemComponent> getChildren(); } // Abstract base class providing default implementations public abstract class AbstractFileSystemComponent implements FileSystemComponent { protected String name; public AbstractFileSystemComponent(String name) { this.name = name; } @Override public String getName() { return name; } @Override public boolean isDirectory() { return false; } // Default implementations for leaf nodes @Override public void addComponent(FileSystemComponent component) { throw new UnsupportedOperationException("Cannot add to a leaf node"); } @Override public void removeComponent(FileSystemComponent component) { throw new UnsupportedOperationException("Cannot remove from a leaf node"); } @Override public List<FileSystemComponent> getChildren() { return Collections.emptyList(); } }

2. Leaf Class

// Leaf class - represents individual objects in the composition public class File extends AbstractFileSystemComponent { private long size; private String content; public File(String name, long size) { super(name); this.size = size; } public File(String name, long size, String content) { super(name); this.size = size; this.content = content; } @Override public long getSize() { return size; } @Override public void display(String indent) { System.out.println(indent + "📄 " + name + " (" + size + " bytes)"); } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public void setSize(long size) { this.size = size; } }

3. Composite Class

import java.util.ArrayList; import java.util.List; // Composite class - represents compositions of components public class Directory extends AbstractFileSystemComponent { private List<FileSystemComponent> children; public Directory(String name) { super(name); this.children = new ArrayList<>(); } @Override public long getSize() { long totalSize = 0; for (FileSystemComponent component : children) { totalSize += component.getSize(); } return totalSize; } @Override public boolean isDirectory() { return true; } @Override public void display(String indent) { System.out.println(indent + "📁 " + name + " (" + getSize() + " bytes)"); for (FileSystemComponent component : children) { component.display(indent + " "); } } @Override public void addComponent(FileSystemComponent component) { children.add(component); } @Override public void removeComponent(FileSystemComponent component) { children.remove(component); } @Override public List<FileSystemComponent> getChildren() { return new ArrayList<>(children); } // Additional directory-specific operations public int getFileCount() { int count = 0; for (FileSystemComponent component : children) { if (!component.isDirectory()) { count++; } else { count += ((Directory) component).getFileCount(); } } return count; } public int getDirectoryCount() { int count = 0; for (FileSystemComponent component : children) { if (component.isDirectory()) { count += 1 + ((Directory) component).getDirectoryCount(); } } return count; } public FileSystemComponent findComponent(String name) { if (this.name.equals(name)) { return this; } for (FileSystemComponent component : children) { if (component.getName().equals(name)) { return component; } if (component.isDirectory()) { FileSystemComponent found = ((Directory) component).findComponent(name); if (found != null) { return found; } } } return null; } }

Advanced Composite Pattern Examples

4. Organization Hierarchy Example

// Component interface for organization structure public interface OrganizationComponent { String getName(); String getRole(); double getSalary(); void display(String indent); void add(OrganizationComponent component); void remove(OrganizationComponent component); List<OrganizationComponent> getSubordinates(); } // Employee (Leaf) public class Employee implements OrganizationComponent { private String name; private String role; private double salary; public Employee(String name, String role, double salary) { this.name = name; this.role = role; this.salary = salary; } @Override public String getName() { return name; } @Override public String getRole() { return role; } @Override public double getSalary() { return salary; } @Override public void display(String indent) { System.out.println(indent + "👤 " + name + " - " + role + " ($" + salary + ")"); } @Override public void add(OrganizationComponent component) { throw new UnsupportedOperationException("Cannot add to an employee"); } @Override public void remove(OrganizationComponent component) { throw new UnsupportedOperationException("Cannot remove from an employee"); } @Override public List<OrganizationComponent> getSubordinates() { return Collections.emptyList(); } public void setSalary(double salary) { this.salary = salary; } } // Department (Composite) public class Department implements OrganizationComponent { private String name; private List<OrganizationComponent> members; public Department(String name) { this.name = name; this.members = new ArrayList<>(); } @Override public String getName() { return name; } @Override public String getRole() { return "Department"; } @Override public double getSalary() { double totalSalary = 0; for (OrganizationComponent member : members) { totalSalary += member.getSalary(); } return totalSalary; } @Override public void display(String indent) { System.out.println(indent + "🏢 " + name + " Department (Total Salary: $" + getSalary() + ")"); for (OrganizationComponent member : members) { member.display(indent + " "); } } @Override public void add(OrganizationComponent component) { members.add(component); } @Override public void remove(OrganizationComponent component) { members.remove(component); } @Override public List<OrganizationComponent> getSubordinates() { return new ArrayList<>(members); } // Department-specific operations public int getEmployeeCount() { int count = 0; for (OrganizationComponent member : members) { if (member instanceof Employee) { count++; } else { count += ((Department) member).getEmployeeCount(); } } return count; } public double getAverageSalary() { int employeeCount = getEmployeeCount(); return employeeCount > 0 ? getSalary() / employeeCount : 0; } }

5. UI Components Example

// Component interface for UI elements public interface UIComponent { String getName(); void render(); void add(UIComponent component); void remove(UIComponent component); UIComponent getChild(int index); List<UIComponent> getChildren(); } // Base component class public abstract class BaseUIComponent implements UIComponent { protected String name; protected List<UIComponent> children; public BaseUIComponent(String name) { this.name = name; this.children = new ArrayList<>(); } @Override public String getName() { return name; } @Override public void add(UIComponent component) { children.add(component); } @Override public void remove(UIComponent component) { children.remove(component); } @Override public UIComponent getChild(int index) { return children.get(index); } @Override public List<UIComponent> getChildren() { return new ArrayList<>(children); } } // Leaf components public class Button extends BaseUIComponent { private String label; public Button(String name, String label) { super(name); this.label = label; } @Override public void render() { System.out.println("🔄 Rendering Button: " + name + " [Label: " + label + "]"); } public void click() { System.out.println("✅ Button '" + name + "' clicked!"); } } public class TextField extends BaseUIComponent { private String placeholder; private String value; public TextField(String name, String placeholder) { super(name); this.placeholder = placeholder; } @Override public void render() { System.out.println("📝 Rendering TextField: " + name + " [Value: " + value + ", Placeholder: " + placeholder + "]"); } public void setValue(String value) { this.value = value; } public String getValue() { return value; } } public class Label extends BaseUIComponent { private String text; public Label(String name, String text) { super(name); this.text = text; } @Override public void render() { System.out.println("🏷️ Rendering Label: " + name + " [Text: " + text + "]"); } public String getText() { return text; } public void setText(String text) { this.text = text; } } // Composite components public class Panel extends BaseUIComponent { private String layout; public Panel(String name, String layout) { super(name); this.layout = layout; } @Override public void render() { System.out.println("🎨 Rendering Panel: " + name + " [Layout: " + layout + "]"); for (UIComponent child : getChildren()) { child.render(); } } public void addComponent(UIComponent component) { add(component); } } public class Form extends BaseUIComponent { private String action; public Form(String name, String action) { super(name); this.action = action; } @Override public void render() { System.out.println("📋 Rendering Form: " + name + " [Action: " + action + "]"); for (UIComponent child : getChildren()) { child.render(); } } public void submit() { System.out.println("🚀 Submitting form: " + name); // Collect data from form fields collectFormData(); } private void collectFormData() { for (UIComponent child : getChildren()) { if (child instanceof TextField) { TextField textField = (TextField) child; System.out.println(" 📦 " + textField.getName() + ": " + textField.getValue()); } } } }

Enhanced Composite Pattern with Visitors

6. Visitor Pattern Integration

// Visitor interface for operations on composite structure public interface FileSystemVisitor { void visit(File file); void visit(Directory directory); } // Concrete visitors public class SizeCalculatorVisitor implements FileSystemVisitor { private long totalSize = 0; @Override public void visit(File file) { totalSize += file.getSize(); } @Override public void visit(Directory directory) { // Directory size is calculated from children, so we don't add anything here // But we need to visit all children for (FileSystemComponent child : directory.getChildren()) { if (child instanceof File) { visit((File) child); } else if (child instanceof Directory) { visit((Directory) child); } } } public long getTotalSize() { return totalSize; } } public class SearchVisitor implements FileSystemVisitor { private String searchTerm; private List<FileSystemComponent> results; public SearchVisitor(String searchTerm) { this.searchTerm = searchTerm.toLowerCase(); this.results = new ArrayList<>(); } @Override public void visit(File file) { if (file.getName().toLowerCase().contains(searchTerm)) { results.add(file); } } @Override public void visit(Directory directory) { if (directory.getName().toLowerCase().contains(searchTerm)) { results.add(directory); } // Search in children for (FileSystemComponent child : directory.getChildren()) { if (child instanceof File) { visit((File) child); } else if (child instanceof Directory) { visit((Directory) child); } } } public List<FileSystemComponent> getResults() { return results; } } // Enhanced component interface with accept method public interface VisitableFileSystemComponent extends FileSystemComponent { void accept(FileSystemVisitor visitor); } // Updated File class public class VisitableFile extends File implements VisitableFileSystemComponent { public VisitableFile(String name, long size) { super(name, size); } public VisitableFile(String name, long size, String content) { super(name, size, content); } @Override public void accept(FileSystemVisitor visitor) { visitor.visit(this); } } // Updated Directory class public class VisitableDirectory extends Directory implements VisitableFileSystemComponent { public VisitableDirectory(String name) { super(name); } @Override public void accept(FileSystemVisitor visitor) { visitor.visit(this); } @Override public void addComponent(FileSystemComponent component) { if (!(component instanceof VisitableFileSystemComponent)) { throw new IllegalArgumentException("Component must be visitable"); } super.addComponent(component); } }

Real-World Application: E-commerce Product Catalog

7. Product Catalog System

// Component interface for product catalog public interface CatalogComponent { String getName(); String getDescription(); double getPrice(); void display(); void add(CatalogComponent component); void remove(CatalogComponent component); List<CatalogComponent> getChildren(); } // Product (Leaf) public class Product implements CatalogComponent { private String name; private String description; private double price; private String sku; public Product(String name, String description, double price, String sku) { this.name = name; this.description = description; this.price = price; this.sku = sku; } @Override public String getName() { return name; } @Override public String getDescription() { return description; } @Override public double getPrice() { return price; } @Override public void display() { System.out.println("🛒 " + name + " - $" + price); System.out.println(" 📝 " + description); System.out.println(" 🏷️ SKU: " + sku); } @Override public void add(CatalogComponent component) { throw new UnsupportedOperationException("Cannot add to a product"); } @Override public void remove(CatalogComponent component) { throw new UnsupportedOperationException("Cannot remove from a product"); } @Override public List<CatalogComponent> getChildren() { return Collections.emptyList(); } // Product-specific methods public String getSku() { return sku; } public void setPrice(double price) { this.price = price; } } // Product Bundle (Composite) public class ProductBundle implements CatalogComponent { private String name; private String description; private List<CatalogComponent> products; private double discount; public ProductBundle(String name, String description, double discount) { this.name = name; this.description = description; this.products = new ArrayList<>(); this.discount = discount; } @Override public String getName() { return name; } @Override public String getDescription() { return description; } @Override public double getPrice() { double totalPrice = 0; for (CatalogComponent product : products) { totalPrice += product.getPrice(); } return totalPrice * (1 - discount); } public double getOriginalPrice() { double totalPrice = 0; for (CatalogComponent product : products) { totalPrice += product.getPrice(); } return totalPrice; } @Override public void display() { System.out.println("🎁 " + name + " - $" + getPrice() + " (Original: $" + getOriginalPrice() + ")"); System.out.println(" 📝 " + description); System.out.println(" 💰 Discount: " + (discount * 100) + "%"); System.out.println(" 📦 Contains:"); for (CatalogComponent product : products) { System.out.print(" "); product.display(); } } @Override public void add(CatalogComponent component) { products.add(component); } @Override public void remove(CatalogComponent component) { products.remove(component); } @Override public List<CatalogComponent> getChildren() { return new ArrayList<>(products); } // Bundle-specific methods public double getDiscount() { return discount; } public void setDiscount(double discount) { this.discount = discount; } public int getProductCount() { int count = 0; for (CatalogComponent component : products) { if (component instanceof Product) { count++; } else { count += ((ProductBundle) component).getProductCount(); } } return count; } } // Category (Composite) public class Category implements CatalogComponent { private String name; private String description; private List<CatalogComponent> items; public Category(String name, String description) { this.name = name; this.description = description; this.items = new ArrayList<>(); } @Override public String getName() { return name; } @Override public String getDescription() { return description; } @Override public double getPrice() { throw new UnsupportedOperationException("Category doesn't have a price"); } @Override public void display() { System.out.println("📂 " + name + " Category"); System.out.println(" 📝 " + description); System.out.println(" 📊 Items: " + items.size()); for (CatalogComponent item : items) { item.display(); } } @Override public void add(CatalogComponent component) { items.add(component); } @Override public void remove(CatalogComponent component) { items.remove(component); } @Override public List<CatalogComponent> getChildren() { return new ArrayList<>(items); } // Category-specific methods public int getTotalProductCount() { int count = 0; for (CatalogComponent item : items) { if (item instanceof Product) { count++; } else if (item instanceof ProductBundle) { count += ((ProductBundle) item).getProductCount(); } else if (item instanceof Category) { count += ((Category) item).getTotalProductCount(); } } return count; } }

Demo and Usage Examples

8. Comprehensive Demo Class

public class CompositePatternDemo { public static void main(String[] args) { System.out.println("=== File System Demo ==="); demoFileSystem(); System.out.println("\n=== Organization Structure Demo ==="); demoOrganizationStructure(); System.out.println("\n=== UI Components Demo ==="); demoUIComponents(); System.out.println("\n=== E-commerce Catalog Demo ==="); demoEcommerceCatalog(); System.out.println("\n=== Visitor Pattern Demo ==="); demoVisitorPattern(); } private static void demoFileSystem() { // Create files FileSystemComponent file1 = new File("document.txt", 1024); FileSystemComponent file2 = new File("image.jpg", 2048); FileSystemComponent file3 = new File("data.csv", 512); // Create directories Directory documents = new Directory("Documents"); Directory pictures = new Directory("Pictures"); Directory root = new Directory("Root"); // Build structure documents.addComponent(file1); documents.addComponent(file3); pictures.addComponent(file2); root.addComponent(documents); root.addComponent(pictures); // Display structure root.display(""); // Statistics System.out.println("\n📊 Statistics:"); System.out.println("Total size: " + root.getSize() + " bytes"); System.out.println("File count: " + root.getFileCount()); System.out.println("Directory count: " + root.getDirectoryCount()); } private static void demoOrganizationStructure() { // Create employees OrganizationComponent ceo = new Employee("Alice Johnson", "CEO", 200000); OrganizationComponent cto = new Employee("Bob Smith", "CTO", 180000); OrganizationComponent dev1 = new Employee("Charlie Brown", "Developer", 80000); OrganizationComponent dev2 = new Employee("Diana Prince", "Senior Developer", 120000); // Create departments Department engineering = new Department("Engineering"); Department company = new Department("Company"); // Build structure engineering.add(dev1); engineering.add(dev2); engineering.add(cto); company.add(ceo); company.add(engineering); // Display structure company.display(""); // Statistics System.out.println("\n📊 Department Statistics:"); System.out.println("Total employees: " + company.getEmployeeCount()); System.out.println("Total salary: $" + company.getSalary()); System.out.println("Average salary: $" + company.getAverageSalary()); } private static void demoUIComponents() { // Create UI components UIComponent button1 = new Button("submitBtn", "Submit"); UIComponent textField = new TextField("emailField", "Enter email"); UIComponent label = new Label("welcomeLabel", "Welcome!"); // Create containers Panel mainPanel = new Panel("mainPanel", "Vertical"); Form loginForm = new Form("loginForm", "/login"); // Build UI hierarchy loginForm.add(label); loginForm.add(textField); loginForm.add(button1); mainPanel.add(loginForm); // Render UI mainPanel.render(); // Simulate form submission ((TextField) textField).setValue("[email protected]"); ((Form) loginForm).submit(); } private static void demoEcommerceCatalog() { // Create products CatalogComponent laptop = new Product("MacBook Pro", "High-performance laptop", 1999.99, "MBP-2023"); CatalogComponent mouse = new Product("Wireless Mouse", "Ergonomic wireless mouse", 49.99, "WM-001"); CatalogComponent keyboard = new Product("Mechanical Keyboard", "RGB mechanical keyboard", 129.99, "MK-002"); // Create bundles ProductBundle officeBundle = new ProductBundle("Office Setup", "Complete office setup", 0.1); officeBundle.add(laptop); officeBundle.add(mouse); officeBundle.add(keyboard); // Create categories Category electronics = new Category("Electronics", "Electronic devices and accessories"); electronics.add(laptop); electronics.add(mouse); electronics.add(keyboard); electronics.add(officeBundle); // Display catalog electronics.display(); // Statistics System.out.println("\n📊 Catalog Statistics:"); System.out.println("Total products: " + electronics.getTotalProductCount()); } private static void demoVisitorPattern() { // Create visitable file system VisitableFileSystemComponent file1 = new VisitableFile("doc1.txt", 1024); VisitableFileSystemComponent file2 = new VisitableFile("img1.jpg", 2048); VisitableFileSystemComponent file3 = new VisitableFile("data1.csv", 512); VisitableDirectory dir1 = new VisitableDirectory("Folder1"); VisitableDirectory root = new VisitableDirectory("Root"); dir1.addComponent(file1); dir1.addComponent(file2); root.addComponent(dir1); root.addComponent(file3); // Use SizeCalculatorVisitor SizeCalculatorVisitor sizeVisitor = new SizeCalculatorVisitor(); root.accept(sizeVisitor); System.out.println("Total size: " + sizeVisitor.getTotalSize() + " bytes"); // Use SearchVisitor SearchVisitor searchVisitor = new SearchVisitor("doc"); root.accept(searchVisitor); System.out.println("Search results for 'doc':"); for (FileSystemComponent result : searchVisitor.getResults()) { System.out.println(" - " + result.getName()); } } }

Key Benefits of Composite Pattern

  1. Uniform Treatment: Clients can treat individual objects and compositions uniformly
  2. Simplified Client Code: Clients don't need to know whether they're dealing with leaf or composite nodes
  3. Easy to Add New Components: New component types can be added without changing existing code
  4. Flexible Structure: Tree structures can be built and modified dynamically
  5. Recursive Operations: Operations can be applied recursively throughout the tree structure

Common Use Cases

  • File Systems: Directories and files
  • GUI Components: Containers and individual elements
  • Organization Charts: Departments and employees
  • Product Catalogs: Categories, bundles, and individual products
  • Abstract Syntax Trees: In compilers and interpreters
  • Menu Systems: Nested menus and menu items

The Composite Pattern is particularly useful when you need to represent hierarchical structures and perform operations that need to be applied uniformly across both individual elements and entire compositions.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper