Lesson Completion
Back to course

Structural Patterns Part 1: Adapters, Decorators, Facades, and Proxies

Beginner
12 minutes4.9Java

1. The Hook (The "Byte-Sized" Intro)

  • In a Nutshell: Structural patterns organize class/object relationships. Adapter = convert interface (power adapter for foreign plug), Decorator = add responsibilities dynamically (coffee + milk + sugar), Facade = simplified interface (TV remote vs individual controls), Proxy = surrogate object (placeholder, security check).
  • Benefits: Flexibility, decoupling, simplification.
  • Use: Adapter for incompatible interfaces, Decorator for flexible extensions, Facade for complex subsystems, Proxy for access control!

Think of home gadgets. Adapter = power adapter (US plug → EU socket). Decorator = pizza toppings (base + cheese + pepperoni). Facade = smart home app (control lights/AC/TV from one app). Proxy = security guard (checks ID before entry)!


2. Conceptual Clarity (The "Simple" Tier)

💡 The Analogy

  • Adapter: Translator (English ↔ Spanish)
  • Decorator: Gift wrapping (box + paper + ribbon)
  • Facade: Hotel concierge (single contact for all services)
  • Proxy: Bouncer (controls club access)

3. Technical Mastery (The "Deep Dive")

Structural Patterns Overview

PatternPurposeReal Example
AdapterInterface conversionJDBC drivers
DecoratorAdd behavior dynamicallyJava I/O streams
FacadeSimplify complex systemLibrary API
ProxyControl accessRMI, lazy loading

4. Interactive & Applied Code

java
// =========================================== // 1. ADAPTER PATTERN // =========================================== // Old interface (legacy code) interface OldPrinter { void printOldFormat(String text); } class LegacyPrinter implements OldPrinter { public void printOldFormat(String text) { System.out.println("[OLD] " + text); } } // New interface (modern code) interface ModernPrinter { void print(String text); } // ✅ Adapter: Makes legacy printer work with modern interface class PrinterAdapter implements ModernPrinter { private OldPrinter oldPrinter; PrinterAdapter(OldPrinter oldPrinter) { this.oldPrinter = oldPrinter; } public void print(String text) { oldPrinter.printOldFormat(text); // Adapt! } } // Usage: ModernPrinter printer = new PrinterAdapter(new LegacyPrinter()); printer.print("Hello"); // Works with modern interface! // =========================================== // 2. DECORATOR PATTERN // =========================================== // Component interface interface Coffee { double cost(); String description(); } // Concrete component class SimpleCoffee implements Coffee { public double cost() { return 2.0; } public String description() { return "Simple coffee"; } } // Decorator base abstract class CoffeeDecorator implements Coffee { protected Coffee coffee; CoffeeDecorator(Coffee coffee) { this.coffee = coffee; } } // Concrete decorators class Milk extends CoffeeDecorator { Milk(Coffee coffee) { super(coffee); } public double cost() { return coffee.cost() + 0.5; } public String description() { return coffee.description() + ", milk"; } } class Sugar extends CoffeeDecorator { Sugar(Coffee coffee) { super(coffee); } public double cost() { return coffee.cost() + 0.2; } public String description() { return coffee.description() + ", sugar"; } } // Usage (wrap decorators): Coffee coffee = new SimpleCoffee(); coffee = new Milk(coffee); coffee = new Sugar(coffee); System.out.println(coffee.description() + " = $" + coffee.cost()); // Output: Simple coffee, milk, sugar = $2.7 // Real-world: Java I/O InputStream is = new FileInputStream("file.txt"); is = new BufferedInputStream(is); // Decorator! is = new GZIPInputStream(is); // Another decorator! // =========================================== // 3. FACADE PATTERN // =========================================== // Complex subsystem class CPU { void start() { System.out.println("CPU starting"); } } class Memory { void load() { System.out.println("Memory loading"); } } class HardDrive { void read() { System.out.println("HDD reading"); } } // ✅ Facade: Simplified interface class ComputerFacade { private CPU cpu; private Memory memory; private HardDrive hdd; ComputerFacade() { cpu = new CPU(); memory = new Memory(); hdd = new HardDrive(); } void start() { cpu.start(); memory.load(); hdd.read(); System.out.println("Computer started!"); } } // Usage: ComputerFacade computer = new ComputerFacade(); computer.start(); // ✅ Simple! (hides complexity) // =========================================== // 4. PROXY PATTERN // =========================================== // Subject interface interface Image { void display(); } // Real subject (expensive) class RealImage implements Image { private String filename; RealImage(String filename) { this.filename = filename; loadFromDisk(); // Expensive! } void loadFromDisk() { System.out.println("Loading " + filename); } public void display() { System.out.println("Displaying " + filename); } } // ✅ Proxy: Lazy loading class ProxyImage implements Image { private String filename; private RealImage realImage; ProxyImage(String filename) { this.filename = filename; } public void display() { if (realImage == null) { realImage = new RealImage(filename); // Load on demand! } realImage.display(); } } // Usage: Image image = new ProxyImage("photo.jpg"); // Not loaded yet! image.display(); // NOW loaded and displayed image.display(); // Already loaded, just display // Protection Proxy interface BankAccount { void withdraw(int amount); } class RealBankAccount implements BankAccount { public void withdraw(int amount) { System.out.println("Withdrew $" + amount); } } class ProxyBankAccount implements BankAccount { private RealBankAccount account; private String userRole; ProxyBankAccount(String userRole) { this.account = new RealBankAccount(); this.userRole = userRole; } public void withdraw(int amount) { if ("ADMIN".equals(userRole)) { account.withdraw(amount); // ✅ Allowed } else { System.out.println("Access denied!"); // ❌ Blocked } } }

5. The Comparison & Decision Layer

PatternWhen to Use
AdapterIncompatible interfaces (legacy integration)
DecoratorAdd functionality flexibly (I/O streams)
FacadeSimplify complex subsystem
ProxyControl access, lazy loading, security

6. The "Interview Corner" (The Edge)

The "Killer" Interview Question: "Adapter vs Decorator?" Answer:

  • Adapter: Changes interface (makes incompatible things work together)
  • Decorator: Adds behavior (keeps same interface)
java
// Adapter: Interface conversion ModernPrinter adapter = new PrinterAdapter(oldPrinter); adapter.print("Hi"); // Different interface! // Decorator: Add behavior Coffee coffee = new SimpleCoffee(); coffee = new Milk(coffee); // Same Coffee interface, added behavior

Pro-Tip: Java I/O uses Decorator:

java
// Each decorator adds functionality InputStream is = new FileInputStream("file"); // Base is = new BufferedInputStream(is); // + Buffering is = new DataInputStream(is); // + Read primitives // All implement InputStream interface!

Topics Covered

Java Fundamentals

Tags

#java#programming#beginner-friendly

Last Updated

2025-02-01