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
| Pattern | Purpose | Real Example |
|---|---|---|
| Adapter | Interface conversion | JDBC drivers |
| Decorator | Add behavior dynamically | Java I/O streams |
| Facade | Simplify complex system | Library API |
| Proxy | Control access | RMI, 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
| Pattern | When to Use |
|---|---|
| Adapter | Incompatible interfaces (legacy integration) |
| Decorator | Add functionality flexibly (I/O streams) |
| Facade | Simplify complex subsystem |
| Proxy | Control 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 behaviorPro-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!