Structural Design Patterns

Once your kitchen is set up with the right appliances (thanks to Creational Design Patterns), the next step is to organize everything efficiently. Structural Design Patterns focus on how objects are arranged and related to create flexible, well-structured software.

Just as a well-organized kitchen ensures seamless cooking, Structural Design Patterns ensure that different parts of a system work together efficiently.


1. Adapter Pattern β†’ Universal Plug Adapter πŸ”Œ

Imagine traveling to a country with different electrical outlets. Instead of replacing all your appliances, you use a plug adapter to make them compatible.

πŸ”Ή In software: The Adapter Pattern allows incompatible interfaces to work together by acting as a bridge.

Java Example: Adapter Pattern

javaCopyEdit// Step 1: Define an incompatible interface
interface EuropeanPlug {
    void provideElectricity();
}

// Step 2: Implement a class with that interface
class EuropeanSocket implements EuropeanPlug {
    public void provideElectricity() {
        System.out.println("Providing 220V power ⚑");
    }
}

// Step 3: Create an adapter to make it compatible with the US system
interface USPlug {
    void connectToUSOutlet();
}

class PlugAdapter implements USPlug {
    private EuropeanPlug euroPlug;

    public PlugAdapter(EuropeanPlug euroPlug) {
        this.euroPlug = euroPlug;
    }

    public void connectToUSOutlet() {
        System.out.println("Adapting voltage...");
        euroPlug.provideElectricity();
    }
}

// Step 4: Usage
public class AdapterPatternDemo {
    public static void main(String[] args) {
        EuropeanPlug euroPlug = new EuropeanSocket();
        USPlug adapter = new PlugAdapter(euroPlug);

        adapter.connectToUSOutlet(); // Output: Adapting voltage... Providing 220V power ⚑
    }
}

πŸ›  Why use the Adapter Pattern?
βœ… Allows incompatible systems to work together
βœ… Helps integrate old and new systems
βœ… Improves code reusability


2. Decorator Pattern β†’ Pizza Toppings πŸ•

A plain pizza is good, but adding toppings like cheese, mushrooms, or pepperoni enhances the experienceβ€”without modifying the base pizza.

πŸ”Ή In software: The Decorator Pattern allows you to dynamically add new functionality to objects without changing their core structure.

Java Example: Decorator Pattern

javaCopyEdit// Step 1: Define a base component
interface Pizza {
    String getDescription();
    double getCost();
}

// Step 2: Create a basic pizza
class PlainPizza implements Pizza {
    public String getDescription() {
        return "Plain Pizza";
    }

    public double getCost() {
        return 5.0;
    }
}

// Step 3: Create a decorator class
abstract class PizzaDecorator implements Pizza {
    protected Pizza pizza;

    public PizzaDecorator(Pizza pizza) {
        this.pizza = pizza;
    }

    public String getDescription() {
        return pizza.getDescription();
    }

    public double getCost() {
        return pizza.getCost();
    }
}

// Step 4: Add specific decorators
class Cheese extends PizzaDecorator {
    public Cheese(Pizza pizza) {
        super(pizza);
    }

    public String getDescription() {
        return pizza.getDescription() + ", Cheese";
    }

    public double getCost() {
        return pizza.getCost() + 1.5;
    }
}

class Pepperoni extends PizzaDecorator {
    public Pepperoni(Pizza pizza) {
        super(pizza);
    }

    public String getDescription() {
        return pizza.getDescription() + ", Pepperoni";
    }

    public double getCost() {
        return pizza.getCost() + 2.0;
    }
}

// Step 5: Usage
public class DecoratorPatternDemo {
    public static void main(String[] args) {
        Pizza myPizza = new PlainPizza();
        myPizza = new Cheese(myPizza);
        myPizza = new Pepperoni(myPizza);

        System.out.println("Order: " + myPizza.getDescription()); // Output: Plain Pizza, Cheese, Pepperoni
        System.out.println("Total Cost: $" + myPizza.getCost()); // Output: $8.5
    }
}

πŸ›  Why use the Decorator Pattern?
βœ… Adds new functionality without modifying existing classes
βœ… Supports dynamic behavior modification
βœ… Promotes the Open-Closed Principle (open for extension, closed for modification)


3. Composite Pattern β†’ Food Combo πŸ”+🍟+πŸ₯€

When you order a combo meal, it includes multiple items (burger, fries, and a drink), but you can still treat the entire combo as a single order.

πŸ”Ή In software: The Composite Pattern treats both individual objects and groups of objects uniformly, making it easy to work with hierarchical structures.

Java Example: Composite Pattern

javaCopyEdit// Step 1: Define a component interface
interface FoodItem {
    void showDetails();
}

// Step 2: Create Leaf Components
class Burger implements FoodItem {
    public void showDetails() {
        System.out.println("Burger πŸ”");
    }
}

class Fries implements FoodItem {
    public void showDetails() {
        System.out.println("Fries 🍟");
    }
}

// Step 3: Create Composite Class
class ComboMeal implements FoodItem {
    private List<FoodItem> items = new ArrayList<>();

    public void addItem(FoodItem item) {
        items.add(item);
    }

    public void showDetails() {
        System.out.println("Combo Meal Includes:");
        for (FoodItem item : items) {
            item.showDetails();
        }
    }
}

// Step 4: Usage
public class CompositePatternDemo {
    public static void main(String[] args) {
        // Individual Items
        FoodItem burger = new Burger();
        FoodItem fries = new Fries();

        // Creating a combo meal
        ComboMeal combo = new ComboMeal();
        combo.addItem(burger);
        combo.addItem(fries);

        // Display the combo details
        combo.showDetails();
        // Output:
        // Combo Meal Includes:
        // Burger πŸ”
        // Fries 🍟
    }
}

πŸ›  Why use the Composite Pattern?
βœ… Makes it easy to work with hierarchical data structures
βœ… Treats individual and group objects uniformly
βœ… Simplifies complex structures


Why Structural Patterns Matter?

Just like arranging a kitchen for efficiency, Structural Design Patterns ensure that different parts of your system interact effectively. They help organize code, improve maintainability, and make systems scalable.

When to use Structural Design Patterns?

βœ… When you need to bridge incompatible interfaces (Adapter Pattern)
βœ… When you want to add functionality without modifying existing classes (Decorator Pattern)
βœ… When dealing with hierarchical structures (Composite Pattern)


Final Thoughts

Think of Structural Design Patterns as the organization of your kitchenβ€”ensuring everything is structured and works together efficiently. By using these patterns wisely, you can write scalable, flexible, and maintainable software.

Would you like to dive deeper into more patterns? Drop your thoughts in the comments! πŸš€

0
Subscribe to my newsletter

Read articles from Rohith Reddy Seelam directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Rohith Reddy Seelam
Rohith Reddy Seelam