Facade Pattern
Facade Pattern: Simplifying Complex Subsystems
Section titled “Facade Pattern: Simplifying Complex Subsystems”Now let’s explore the Facade Pattern - a powerful structural design pattern that provides a simplified interface to a complex subsystem.
Why Facade Pattern?
Section titled “Why Facade Pattern?”Imagine you’re driving a car. You don’t need to know how the engine, transmission, brakes, and steering work internally. You just use simple controls: steering wheel, pedals, and gear shift. The car’s interface hides all the complexity! The Facade Pattern works the same way - it provides a simple interface that hides the complexity of a subsystem.
The Facade Pattern provides a unified interface to a set of interfaces in a subsystem. It defines a higher-level interface that makes the subsystem easier to use.
What’s the Use of Facade Pattern?
Section titled “What’s the Use of Facade Pattern?”The Facade Pattern is useful when:
- You want to simplify a complex subsystem - Hide complexity behind a simple interface
- You want to decouple clients - From subsystem classes
- You want to provide a unified interface - To multiple subsystems
- You want to reduce dependencies - Clients depend on facade, not subsystem
- You want to make subsystem easier to use - Provide high-level operations
What Happens If We Don’t Use Facade Pattern?
Section titled “What Happens If We Don’t Use Facade Pattern?”Without the Facade Pattern, you might:
- Complex client code - Clients need to know about many subsystem classes
- Tight coupling - Clients directly depend on subsystem classes
- Hard to use - Need to understand entire subsystem to use it
- Code duplication - Similar initialization code repeated everywhere
- Hard to maintain - Changes in subsystem affect all clients
Simple Example: The Home Theater System
Section titled “Simple Example: The Home Theater System”Let’s start with a super simple example that anyone can understand!
Visual Representation
Section titled “Visual Representation”Interaction Flow
Section titled “Interaction Flow”Here’s how the Facade Pattern works in practice - showing how facade simplifies complex subsystem interactions:
sequenceDiagram
participant Client
participant Facade as HomeTheaterFacade
participant Amplifier
participant DVDPlayer
participant Projector
participant Screen
Client->>Facade: watch_movie()
activate Facade
Facade->>Amplifier: on()
activate Amplifier
Amplifier-->>Facade: OK
deactivate Amplifier
Facade->>DVDPlayer: on()
activate DVDPlayer
DVDPlayer-->>Facade: OK
deactivate DVDPlayer
Facade->>Projector: on()
activate Projector
Projector-->>Facade: OK
deactivate Projector
Facade->>Screen: down()
activate Screen
Screen-->>Facade: OK
deactivate Screen
Facade->>Amplifier: set_volume(10)
Facade->>DVDPlayer: play()
Facade-->>Client: Movie started
deactivate Facade
Note over Client,Screen: Facade coordinates multiple<br/>subsystem classes for client!
The Problem
Section titled “The Problem”You’re building a home theater system. You have multiple components (Amplifier, DVD Player, Projector, Screen) that need to be coordinated. Without Facade Pattern:
# ❌ Without Facade Pattern - Complex client code!
class Amplifier: """Amplifier subsystem""" def on(self): print("Amplifier: Turning on...")
def set_volume(self, level: int): print(f"Amplifier: Setting volume to {level}")
def off(self): print("Amplifier: Turning off...")
class DVDPlayer: """DVD Player subsystem""" def on(self): print("DVD Player: Turning on...")
def play(self, movie: str): print(f"DVD Player: Playing {movie}")
def off(self): print("DVD Player: Turning off...")
class Projector: """Projector subsystem""" def on(self): print("Projector: Turning on...")
def wide_screen_mode(self): print("Projector: Setting wide screen mode")
def off(self): print("Projector: Turning off...")
class Screen: """Screen subsystem""" def down(self): print("Screen: Lowering screen...")
def up(self): print("Screen: Raising screen...")
# Problem: Client needs to know about all subsystems and coordinate them!class Client: def watch_movie(self, movie: str): # Client needs to know about all subsystems! amplifier = Amplifier() dvd_player = DVDPlayer() projector = Projector() screen = Screen()
# Complex coordination logic in client! amplifier.on() amplifier.set_volume(10) dvd_player.on() projector.on() projector.wide_screen_mode() screen.down() dvd_player.play(movie)
print("🎬 Movie started!")
def end_movie(self): # Client needs to know shutdown order too! amplifier = Amplifier() dvd_player = DVDPlayer() projector = Projector() screen = Screen()
dvd_player.off() projector.off() amplifier.off() screen.up()
print("✅ Movie ended!")
# Problems:# - Client knows about all subsystem classes# - Complex coordination logic in client# - Hard to change subsystem (affects all clients)# - Code duplication (coordination logic repeated)// ❌ Without Facade Pattern - Complex client code!
public class Amplifier { // Amplifier subsystem public void on() { System.out.println("Amplifier: Turning on..."); }
public void setVolume(int level) { System.out.println("Amplifier: Setting volume to " + level); }
public void off() { System.out.println("Amplifier: Turning off..."); }}
public class DVDPlayer { // DVD Player subsystem public void on() { System.out.println("DVD Player: Turning on..."); }
public void play(String movie) { System.out.println("DVD Player: Playing " + movie); }
public void off() { System.out.println("DVD Player: Turning off..."); }}
public class Projector { // Projector subsystem public void on() { System.out.println("Projector: Turning on..."); }
public void wideScreenMode() { System.out.println("Projector: Setting wide screen mode"); }
public void off() { System.out.println("Projector: Turning off..."); }}
public class Screen { // Screen subsystem public void down() { System.out.println("Screen: Lowering screen..."); }
public void up() { System.out.println("Screen: Raising screen..."); }}
// Problem: Client needs to know about all subsystems and coordinate them!public class Client { public void watchMovie(String movie) { // Client needs to know about all subsystems! Amplifier amplifier = new Amplifier(); DVDPlayer dvdPlayer = new DVDPlayer(); Projector projector = new Projector(); Screen screen = new Screen();
// Complex coordination logic in client! amplifier.on(); amplifier.setVolume(10); dvdPlayer.on(); projector.on(); projector.wideScreenMode(); screen.down(); dvdPlayer.play(movie);
System.out.println("🎬 Movie started!"); }
public void endMovie() { // Client needs to know shutdown order too! Amplifier amplifier = new Amplifier(); DVDPlayer dvdPlayer = new DVDPlayer(); Projector projector = new Projector(); Screen screen = new Screen();
dvdPlayer.off(); projector.off(); amplifier.off(); screen.up();
System.out.println("✅ Movie ended!"); }}
// Problems:// - Client knows about all subsystem classes// - Complex coordination logic in client// - Hard to change subsystem (affects all clients)// - Code duplication (coordination logic repeated)Problems:
- Complex client code - Clients need to know about all subsystems
- Tight coupling - Clients directly depend on subsystem classes
- Hard to maintain - Changes in subsystem affect all clients
- Code duplication - Coordination logic repeated everywhere
The Solution: Facade Pattern
Section titled “The Solution: Facade Pattern”Class Structure
Section titled “Class Structure”classDiagram
class Client {
+operation() void
}
class Facade {
-subsystem1: Subsystem1
-subsystem2: Subsystem2
-subsystem3: Subsystem3
+simple_operation() void
}
class Subsystem1 {
+operation1() void
}
class Subsystem2 {
+operation2() void
}
class Subsystem3 {
+operation3() void
}
Client --> Facade : uses
Facade --> Subsystem1 : coordinates
Facade --> Subsystem2 : coordinates
Facade --> Subsystem3 : coordinates
note for Facade "Simplifies subsystem interactions"
note for Client "Only knows about Facade"
# Step 1: Subsystem classes (unchanged)class Amplifier: """Amplifier subsystem""" def on(self): print("Amplifier: Turning on...")
def set_volume(self, level: int): print(f"Amplifier: Setting volume to {level}")
def off(self): print("Amplifier: Turning off...")
class DVDPlayer: """DVD Player subsystem""" def on(self): print("DVD Player: Turning on...")
def play(self, movie: str): print(f"DVD Player: Playing {movie}")
def off(self): print("DVD Player: Turning off...")
class Projector: """Projector subsystem""" def on(self): print("Projector: Turning on...")
def wide_screen_mode(self): print("Projector: Setting wide screen mode")
def off(self): print("Projector: Turning off...")
class Screen: """Screen subsystem""" def down(self): print("Screen: Lowering screen...")
def up(self): print("Screen: Raising screen...")
# Step 2: Create the Facadeclass HomeTheaterFacade: """Facade - simplifies home theater subsystem"""
def __init__(self): # Facade knows about all subsystems self.amplifier = Amplifier() self.dvd_player = DVDPlayer() self.projector = Projector() self.screen = Screen()
def watch_movie(self, movie: str) -> None: """Simple interface - hides complex coordination""" print("🎬 Getting ready to watch a movie...")
# Facade coordinates all subsystems self.screen.down() self.projector.on() self.projector.wide_screen_mode() self.amplifier.on() self.amplifier.set_volume(10) self.dvd_player.on() self.dvd_player.play(movie)
print("✅ Movie started! Enjoy!\n")
def end_movie(self) -> None: """Simple interface - hides complex shutdown""" print("🛑 Shutting down home theater...")
# Facade coordinates shutdown self.dvd_player.off() self.projector.off() self.amplifier.off() self.screen.up()
print("✅ Home theater shut down!\n")
# Usage - Clean and simple!def main(): # Client only needs to know about Facade! home_theater = HomeTheaterFacade()
# Simple interface - no need to know about subsystems! home_theater.watch_movie("The Matrix")
# Simple shutdown - no need to coordinate subsystems! home_theater.end_movie()
print("✅ Facade Pattern simplifies complex subsystem!")
if __name__ == "__main__": main()// Step 1: Subsystem classes (unchanged)class Amplifier { // Amplifier subsystem public void on() { System.out.println("Amplifier: Turning on..."); }
public void setVolume(int level) { System.out.println("Amplifier: Setting volume to " + level); }
public void off() { System.out.println("Amplifier: Turning off..."); }}
class DVDPlayer { // DVD Player subsystem public void on() { System.out.println("DVD Player: Turning on..."); }
public void play(String movie) { System.out.println("DVD Player: Playing " + movie); }
public void off() { System.out.println("DVD Player: Turning off..."); }}
class Projector { // Projector subsystem public void on() { System.out.println("Projector: Turning on..."); }
public void wideScreenMode() { System.out.println("Projector: Setting wide screen mode"); }
public void off() { System.out.println("Projector: Turning off..."); }}
class Screen { // Screen subsystem public void down() { System.out.println("Screen: Lowering screen..."); }
public void up() { System.out.println("Screen: Raising screen..."); }}
// Step 2: Create the Facadeclass HomeTheaterFacade { // Facade - simplifies home theater subsystem private Amplifier amplifier; private DVDPlayer dvdPlayer; private Projector projector; private Screen screen;
public HomeTheaterFacade() { // Facade knows about all subsystems this.amplifier = new Amplifier(); this.dvdPlayer = new DVDPlayer(); this.projector = new Projector(); this.screen = new Screen(); }
public void watchMovie(String movie) { // Simple interface - hides complex coordination System.out.println("🎬 Getting ready to watch a movie...");
// Facade coordinates all subsystems screen.down(); projector.on(); projector.wideScreenMode(); amplifier.on(); amplifier.setVolume(10); dvdPlayer.on(); dvdPlayer.play(movie);
System.out.println("✅ Movie started! Enjoy!\n"); }
public void endMovie() { // Simple interface - hides complex shutdown System.out.println("🛑 Shutting down home theater...");
// Facade coordinates shutdown dvdPlayer.off(); projector.off(); amplifier.off(); screen.up();
System.out.println("✅ Home theater shut down!\n"); }}
// Usage - Clean and simple!public class Main { public static void main(String[] args) { // Client only needs to know about Facade! HomeTheaterFacade homeTheater = new HomeTheaterFacade();
// Simple interface - no need to know about subsystems! homeTheater.watchMovie("The Matrix");
// Simple shutdown - no need to coordinate subsystems! homeTheater.endMovie();
System.out.println("✅ Facade Pattern simplifies complex subsystem!"); }}Real-World Software Example: Order Processing System
Section titled “Real-World Software Example: Order Processing System”Now let’s see a realistic software example - an e-commerce system that needs to process orders through multiple subsystems.
The Problem
Section titled “The Problem”You’re building an e-commerce system. Order processing involves multiple subsystems (Inventory, Payment, Shipping, Notification). Without Facade Pattern:
# ❌ Without Facade Pattern - Complex client code!
class InventoryService: """Inventory subsystem""" def check_stock(self, product_id: str, quantity: int) -> bool: print(f"Inventory: Checking stock for {product_id}...") return True
def reserve_items(self, product_id: str, quantity: int): print(f"Inventory: Reserving {quantity} items of {product_id}")
class PaymentService: """Payment subsystem""" def process_payment(self, amount: float, card_token: str) -> bool: print(f"Payment: Processing payment of ${amount}...") return True
def refund_payment(self, transaction_id: str): print(f"Payment: Refunding transaction {transaction_id}")
class ShippingService: """Shipping subsystem""" def create_shipment(self, order_id: str, address: dict): print(f"Shipping: Creating shipment for order {order_id}")
def track_shipment(self, shipment_id: str): print(f"Shipping: Tracking shipment {shipment_id}")
class NotificationService: """Notification subsystem""" def send_email(self, email: str, subject: str, body: str): print(f"Notification: Sending email to {email}")
# Problem: Client needs to coordinate all subsystems!class OrderProcessor: def process_order(self, order: dict): # Client needs to know about all subsystems! inventory = InventoryService() payment = PaymentService() shipping = ShippingService() notification = NotificationService()
# Complex coordination logic! if not inventory.check_stock(order["product_id"], order["quantity"]): return False
inventory.reserve_items(order["product_id"], order["quantity"])
if not payment.process_payment(order["amount"], order["card_token"]): inventory.reserve_items(order["product_id"], -order["quantity"]) # Rollback return False
shipping.create_shipment(order["order_id"], order["address"]) notification.send_email( order["email"], "Order Confirmed", f"Your order {order['order_id']} has been confirmed" )
return True
# Problems:# - Client knows about all subsystems# - Complex coordination logic# - Hard to change (affects all clients)// ❌ Without Facade Pattern - Complex client code!
public class InventoryService { // Inventory subsystem public boolean checkStock(String productId, int quantity) { System.out.println("Inventory: Checking stock for " + productId + "..."); return true; }
public void reserveItems(String productId, int quantity) { System.out.println("Inventory: Reserving " + quantity + " items of " + productId); }}
public class PaymentService { // Payment subsystem public boolean processPayment(double amount, String cardToken) { System.out.println("Payment: Processing payment of $" + amount + "..."); return true; }
public void refundPayment(String transactionId) { System.out.println("Payment: Refunding transaction " + transactionId); }}
public class ShippingService { // Shipping subsystem public void createShipment(String orderId, Map<String, String> address) { System.out.println("Shipping: Creating shipment for order " + orderId); }
public void trackShipment(String shipmentId) { System.out.println("Shipping: Tracking shipment " + shipmentId); }}
public class NotificationService { // Notification subsystem public void sendEmail(String email, String subject, String body) { System.out.println("Notification: Sending email to " + email); }}
// Problem: Client needs to coordinate all subsystems!public class OrderProcessor { public boolean processOrder(Map<String, Object> order) { // Client needs to know about all subsystems! InventoryService inventory = new InventoryService(); PaymentService payment = new PaymentService(); ShippingService shipping = new ShippingService(); NotificationService notification = new NotificationService();
// Complex coordination logic! if (!inventory.checkStock((String) order.get("product_id"), (Integer) order.get("quantity"))) { return false; }
inventory.reserveItems((String) order.get("product_id"), (Integer) order.get("quantity"));
if (!payment.processPayment((Double) order.get("amount"), (String) order.get("card_token"))) { inventory.reserveItems((String) order.get("product_id"), -(Integer) order.get("quantity")); // Rollback return false; }
shipping.createShipment((String) order.get("order_id"), (Map<String, String>) order.get("address")); notification.sendEmail( (String) order.get("email"), "Order Confirmed", "Your order " + order.get("order_id") + " has been confirmed" );
return true; }}
// Problems:// - Client knows about all subsystems// - Complex coordination logic// - Hard to change (affects all clients)Problems:
- Complex client code - Clients need to coordinate multiple subsystems
- Tight coupling - Clients directly depend on all subsystem classes
- Hard to maintain - Changes affect all clients
The Solution: Facade Pattern
Section titled “The Solution: Facade Pattern”from typing import Dict, Any
# Step 1: Subsystem classes (unchanged)class InventoryService: """Inventory subsystem""" def check_stock(self, product_id: str, quantity: int) -> bool: print(f"Inventory: Checking stock for {product_id}...") return True
def reserve_items(self, product_id: str, quantity: int): print(f"Inventory: Reserving {quantity} items of {product_id}")
class PaymentService: """Payment subsystem""" def process_payment(self, amount: float, card_token: str) -> bool: print(f"Payment: Processing payment of ${amount}...") return True
def refund_payment(self, transaction_id: str): print(f"Payment: Refunding transaction {transaction_id}")
class ShippingService: """Shipping subsystem""" def create_shipment(self, order_id: str, address: Dict[str, str]): print(f"Shipping: Creating shipment for order {order_id}") return f"SHIP_{order_id}"
def track_shipment(self, shipment_id: str): print(f"Shipping: Tracking shipment {shipment_id}")
class NotificationService: """Notification subsystem""" def send_email(self, email: str, subject: str, body: str): print(f"Notification: Sending email to {email}: {subject}")
# Step 2: Create the Facadeclass OrderProcessingFacade: """Facade - simplifies order processing subsystem"""
def __init__(self): # Facade knows about all subsystems self.inventory = InventoryService() self.payment = PaymentService() self.shipping = ShippingService() self.notification = NotificationService()
def process_order(self, order: Dict[str, Any]) -> bool: """Simple interface - hides complex order processing""" print(f"🛒 Processing order {order['order_id']}...\n")
# Step 1: Check inventory if not self.inventory.check_stock(order["product_id"], order["quantity"]): print("❌ Order failed: Out of stock\n") return False
# Step 2: Reserve items self.inventory.reserve_items(order["product_id"], order["quantity"])
# Step 3: Process payment if not self.payment.process_payment(order["amount"], order["card_token"]): # Rollback inventory self.inventory.reserve_items(order["product_id"], -order["quantity"]) print("❌ Order failed: Payment failed\n") return False
# Step 4: Create shipment shipment_id = self.shipping.create_shipment(order["order_id"], order["address"])
# Step 5: Send notification self.notification.send_email( order["email"], "Order Confirmed", f"Your order {order['order_id']} has been confirmed. Shipment ID: {shipment_id}" )
print(f"✅ Order {order['order_id']} processed successfully!\n") return True
def cancel_order(self, order_id: str, transaction_id: str, product_id: str, quantity: int): """Simple interface - hides complex cancellation""" print(f"🛑 Cancelling order {order_id}...\n")
# Facade coordinates cancellation across subsystems self.payment.refund_payment(transaction_id) self.inventory.reserve_items(product_id, -quantity)
print(f"✅ Order {order_id} cancelled successfully!\n")
# Usage - Clean and simple!def main(): # Client only needs to know about Facade! order_facade = OrderProcessingFacade()
# Simple interface - no need to know about subsystems! order = { "order_id": "ORD123", "product_id": "PROD456", "quantity": 2, "amount": 99.99, "card_token": "tok_123456", "email": "customer@example.com", "address": {"street": "123 Main St", "city": "New York"} }
order_facade.process_order(order)
print("✅ Facade Pattern simplifies complex order processing!")
if __name__ == "__main__": main()import java.util.*;
// Step 1: Subsystem classes (unchanged)class InventoryService { // Inventory subsystem public boolean checkStock(String productId, int quantity) { System.out.println("Inventory: Checking stock for " + productId + "..."); return true; }
public void reserveItems(String productId, int quantity) { System.out.println("Inventory: Reserving " + quantity + " items of " + productId); }}
class PaymentService { // Payment subsystem public boolean processPayment(double amount, String cardToken) { System.out.println("Payment: Processing payment of $" + amount + "..."); return true; }
public void refundPayment(String transactionId) { System.out.println("Payment: Refunding transaction " + transactionId); }}
class ShippingService { // Shipping subsystem public String createShipment(String orderId, Map<String, String> address) { System.out.println("Shipping: Creating shipment for order " + orderId); return "SHIP_" + orderId; }
public void trackShipment(String shipmentId) { System.out.println("Shipping: Tracking shipment " + shipmentId); }}
class NotificationService { // Notification subsystem public void sendEmail(String email, String subject, String body) { System.out.println("Notification: Sending email to " + email + ": " + subject); }}
// Step 2: Create the Facadeclass OrderProcessingFacade { // Facade - simplifies order processing subsystem private InventoryService inventory; private PaymentService payment; private ShippingService shipping; private NotificationService notification;
public OrderProcessingFacade() { // Facade knows about all subsystems this.inventory = new InventoryService(); this.payment = new PaymentService(); this.shipping = new ShippingService(); this.notification = new NotificationService(); }
public boolean processOrder(Map<String, Object> order) { // Simple interface - hides complex order processing System.out.println("🛒 Processing order " + order.get("order_id") + "...\n");
// Step 1: Check inventory if (!inventory.checkStock((String) order.get("product_id"), (Integer) order.get("quantity"))) { System.out.println("❌ Order failed: Out of stock\n"); return false; }
// Step 2: Reserve items inventory.reserveItems((String) order.get("product_id"), (Integer) order.get("quantity"));
// Step 3: Process payment if (!payment.processPayment((Double) order.get("amount"), (String) order.get("card_token"))) { // Rollback inventory inventory.reserveItems((String) order.get("product_id"), -(Integer) order.get("quantity")); System.out.println("❌ Order failed: Payment failed\n"); return false; }
// Step 4: Create shipment String shipmentId = shipping.createShipment((String) order.get("order_id"), (Map<String, String>) order.get("address"));
// Step 5: Send notification notification.sendEmail( (String) order.get("email"), "Order Confirmed", "Your order " + order.get("order_id") + " has been confirmed. Shipment ID: " + shipmentId );
System.out.println("✅ Order " + order.get("order_id") + " processed successfully!\n"); return true; }
public void cancelOrder(String orderId, String transactionId, String productId, int quantity) { // Simple interface - hides complex cancellation System.out.println("🛑 Cancelling order " + orderId + "...\n");
// Facade coordinates cancellation across subsystems payment.refundPayment(transactionId); inventory.reserveItems(productId, -quantity);
System.out.println("✅ Order " + orderId + " cancelled successfully!\n"); }}
// Usage - Clean and simple!public class Main { public static void main(String[] args) { // Client only needs to know about Facade! OrderProcessingFacade orderFacade = new OrderProcessingFacade();
// Simple interface - no need to know about subsystems! Map<String, Object> order = new HashMap<>(); order.put("order_id", "ORD123"); order.put("product_id", "PROD456"); order.put("quantity", 2); order.put("amount", 99.99); order.put("card_token", "tok_123456"); order.put("email", "customer@example.com"); Map<String, String> address = new HashMap<>(); address.put("street", "123 Main St"); address.put("city", "New York"); order.put("address", address);
orderFacade.processOrder(order);
System.out.println("✅ Facade Pattern simplifies complex order processing!"); }}Facade Pattern Variants
Section titled “Facade Pattern Variants”There are different ways to implement the Facade Pattern:
1. Simple Facade
Section titled “1. Simple Facade”Single facade class that coordinates subsystems:
# Simple Facade - single facade classclass Facade: def __init__(self): self.subsystem1 = Subsystem1() self.subsystem2 = Subsystem2()
def operation(self): self.subsystem1.do_something() self.subsystem2.do_something()// Simple Facade - single facade classclass Facade { private Subsystem1 subsystem1; private Subsystem2 subsystem2;
public Facade() { this.subsystem1 = new Subsystem1(); this.subsystem2 = new Subsystem2(); }
public void operation() { subsystem1.doSomething(); subsystem2.doSomething(); }}Pros: Simple, easy to understand
Cons: Can become large if subsystem is complex
2. Facade with Multiple Facades
Section titled “2. Facade with Multiple Facades”Multiple facades for different use cases:
# Multiple Facades - different facades for different use casesclass OrderFacade: """Facade for order operations""" def process_order(self): pass
class ShippingFacade: """Facade for shipping operations""" def create_shipment(self): pass// Multiple Facades - different facades for different use casesclass OrderFacade { // Facade for order operations public void processOrder() { }}
class ShippingFacade { // Facade for shipping operations public void createShipment() { }}Pros: More focused, easier to maintain
Cons: More classes to manage
When to Use Facade Pattern?
Section titled “When to Use Facade Pattern?”Use Facade Pattern when:
✅ You want to simplify a complex subsystem - Hide complexity behind simple interface
✅ You want to decouple clients - From subsystem classes
✅ You want to provide a unified interface - To multiple subsystems
✅ You want to reduce dependencies - Clients depend on facade, not subsystem
✅ You want to make subsystem easier to use - Provide high-level operations
When NOT to Use Facade Pattern?
Section titled “When NOT to Use Facade Pattern?”Don’t use Facade Pattern when:
❌ Simple subsystem - If subsystem is simple, facade adds unnecessary abstraction
❌ Clients need subsystem details - If clients need fine-grained control
❌ Over-engineering - Don’t add complexity for simple cases
❌ Performance critical - Facade adds a layer (usually negligible)
Common Mistakes to Avoid
Section titled “Common Mistakes to Avoid”Mistake 1: Facade Becoming a God Object
Section titled “Mistake 1: Facade Becoming a God Object”# ❌ Bad: Facade does too muchclass Facade: def __init__(self): self.subsystem1 = Subsystem1() self.subsystem2 = Subsystem2() # ... 20 more subsystems
def operation1(self): pass def operation2(self): pass # ... 50 more operations
# Bad: Facade becomes too large and does everything!
# ✅ Good: Focused facadeclass OrderFacade: """Focused facade for orders""" def process_order(self): pass def cancel_order(self): pass
class ShippingFacade: """Focused facade for shipping""" def create_shipment(self): pass def track_shipment(self): pass// ❌ Bad: Facade does too muchclass Facade { private Subsystem1 subsystem1; private Subsystem2 subsystem2; // ... 20 more subsystems
public void operation1() { } public void operation2() { } // ... 50 more operations
// Bad: Facade becomes too large and does everything!}
// ✅ Good: Focused facadeclass OrderFacade { // Focused facade for orders public void processOrder() { } public void cancelOrder() { }}
class ShippingFacade { // Focused facade for shipping public void createShipment() { } public void trackShipment() { }}Mistake 2: Facade Exposing Subsystem Details
Section titled “Mistake 2: Facade Exposing Subsystem Details”# ❌ Bad: Facade exposes subsystem detailsclass Facade: def get_inventory_service(self): # Bad: Exposes subsystem return self.inventory
def get_payment_service(self): # Bad: Exposes subsystem return self.payment
# ✅ Good: Facade hides subsystem detailsclass Facade: def process_order(self): # Good: High-level operation # Uses subsystems internally, doesn't expose them self.inventory.check_stock() self.payment.process_payment()// ❌ Bad: Facade exposes subsystem detailsclass Facade { public InventoryService getInventoryService() { // Bad: Exposes subsystem return inventory; }
public PaymentService getPaymentService() { // Bad: Exposes subsystem return payment; }}
// ✅ Good: Facade hides subsystem detailsclass Facade { public void processOrder() { // Good: High-level operation // Uses subsystems internally, doesn't expose them inventory.checkStock(); payment.processPayment(); }}Mistake 3: Not Handling Errors Properly
Section titled “Mistake 3: Not Handling Errors Properly”# ❌ Bad: No error handlingclass Facade: def process_order(self): self.inventory.reserve_items() # What if this fails? self.payment.process_payment() # What if this fails? # No rollback!
# ✅ Good: Proper error handling with rollbackclass Facade: def process_order(self): try: self.inventory.reserve_items() if not self.payment.process_payment(): self.inventory.reserve_items(-quantity) # Rollback return False except Exception as e: # Handle error and rollback self.inventory.reserve_items(-quantity) raise// ❌ Bad: No error handlingclass Facade { public void processOrder() { inventory.reserveItems(); // What if this fails? payment.processPayment(); // What if this fails? // No rollback! }}
// ✅ Good: Proper error handling with rollbackclass Facade { public boolean processOrder() { try { inventory.reserveItems(); if (!payment.processPayment()) { inventory.reserveItems(-quantity); // Rollback return false; } } catch (Exception e) { // Handle error and rollback inventory.reserveItems(-quantity); throw e; } return true; }}Benefits of Facade Pattern
Section titled “Benefits of Facade Pattern”- Simplified Interface - Clients only need to know about Facade
- Decoupling - Clients don’t depend on subsystem classes
- Easy to Use - Simple high-level operations
- Centralized Logic - Coordination logic in one place
- Easy to Maintain - Changes in subsystem don’t affect clients
- Reduced Dependencies - Clients depend on facade, not subsystems
Revision: Quick Catch-Up
Section titled “Revision: Quick Catch-Up”What is Facade Pattern?
Section titled “What is Facade Pattern?”Facade Pattern is a structural design pattern that provides a unified interface to a set of interfaces in a subsystem. It defines a higher-level interface that makes the subsystem easier to use.
Why Use It?
Section titled “Why Use It?”- ✅ Simplify complex subsystem - Hide complexity behind simple interface
- ✅ Decouple clients - Clients don’t depend on subsystem classes
- ✅ Easy to use - Simple high-level operations
- ✅ Centralized logic - Coordination logic in one place
- ✅ Easy to maintain - Changes don’t affect clients
How It Works?
Section titled “How It Works?”- Identify complex subsystem - Multiple classes that need coordination
- Create facade - Single class that coordinates subsystems
- Provide simple interface - High-level operations that hide complexity
- Client uses facade - Client only knows about facade, not subsystems
- Facade coordinates - Facade coordinates all subsystem interactions
Key Components
Section titled “Key Components”Client → Facade → Subsystem1, Subsystem2, Subsystem3- Client - Uses the Facade
- Facade - Simplifies subsystem, provides unified interface
- Subsystem - Complex classes that Facade coordinates
Simple Example
Section titled “Simple Example”class Facade: def __init__(self): self.subsystem1 = Subsystem1() self.subsystem2 = Subsystem2()
def simple_operation(self): self.subsystem1.operation() self.subsystem2.operation()When to Use?
Section titled “When to Use?”✅ Simplify complex subsystem
✅ Decouple clients from subsystems
✅ Provide unified interface
✅ Reduce dependencies
✅ Make subsystem easier to use
When NOT to Use?
Section titled “When NOT to Use?”❌ Simple subsystem
❌ Clients need subsystem details
❌ Over-engineering
❌ Performance critical
Key Takeaways
Section titled “Key Takeaways”- Facade Pattern = Simplifies complex subsystem
- Facade = Single interface to multiple subsystems
- Subsystem = Complex classes that need coordination
- Benefit = Simplified interface, decoupling
- Use Case = Complex APIs, libraries, subsystems
Common Pattern Structure
Section titled “Common Pattern Structure”class Facade: def __init__(self): self.subsystem1 = Subsystem1() self.subsystem2 = Subsystem2()
def simple_operation(self): # Coordinate subsystems self.subsystem1.operation() self.subsystem2.operation()Remember
Section titled “Remember”- Facade Pattern simplifies complex subsystems
- It provides a unified interface
- Clients only know about Facade, not subsystems
- It’s about simplification, not just wrapping!
- Keep it focused - don’t let it become a god object!
Interview Focus: Facade Pattern
Section titled “Interview Focus: Facade Pattern”Key Points to Remember
Section titled “Key Points to Remember”1. Core Concept
Section titled “1. Core Concept”What to say:
“Facade Pattern is a structural design pattern that provides a unified interface to a set of interfaces in a subsystem. It simplifies complex subsystems by providing a single, easy-to-use interface that hides the complexity of multiple subsystem classes.”
Why it matters:
- Shows you understand the fundamental purpose
- Demonstrates knowledge of when to use it
- Indicates you can explain concepts clearly
2. When to Use Facade Pattern
Section titled “2. When to Use Facade Pattern”Must mention:
- ✅ Simplify complex subsystem - Hide complexity behind simple interface
- ✅ Decouple clients - Clients don’t depend on subsystem classes
- ✅ Provide unified interface - Single interface to multiple subsystems
- ✅ Reduce dependencies - Clients depend on facade, not subsystems
Example scenario to give:
“I’d use Facade Pattern when building an e-commerce order processing system. Order processing involves multiple subsystems: Inventory, Payment, Shipping, and Notification. Without Facade, clients need to coordinate all these subsystems. With Facade, clients just call
process_order()and the facade handles all the coordination internally.”
3. Facade vs Adapter Pattern
Section titled “3. Facade vs Adapter Pattern”Must discuss:
- Facade: Simplifies complex subsystem, provides unified interface
- Adapter: Makes incompatible interfaces compatible
- Key difference: Facade simplifies, Adapter adapts
Example to give:
“Facade Pattern simplifies a complex subsystem by providing a unified interface. Adapter Pattern makes incompatible interfaces compatible. Facade is about simplification and coordination, Adapter is about compatibility. Facade coordinates multiple classes, Adapter adapts one interface to another.”
4. Benefits and Trade-offs
Section titled “4. Benefits and Trade-offs”Benefits to mention:
- Simplified interface - Clients only need to know about Facade
- Decoupling - Clients don’t depend on subsystem classes
- Easy to use - Simple high-level operations
- Centralized logic - Coordination logic in one place
- Easy to maintain - Changes in subsystem don’t affect clients
Trade-offs to acknowledge:
- Abstraction layer - Adds a layer of indirection
- Limited flexibility - Clients can’t access subsystem details directly
- Over-engineering risk - Can be overkill for simple subsystems
5. Common Interview Questions
Section titled “5. Common Interview Questions”Q: “What’s the difference between Facade Pattern and Adapter Pattern?”
A:
“Facade Pattern simplifies a complex subsystem by providing a unified interface to multiple subsystem classes. Adapter Pattern makes incompatible interfaces compatible. Facade is about simplification and coordination, Adapter is about compatibility. Facade coordinates multiple classes, Adapter adapts one interface to another.”
Q: “When would you use Facade vs directly accessing subsystems?”
A:
“I use Facade when the subsystem is complex and clients need to coordinate multiple classes. If clients need fine-grained control over individual subsystems, direct access might be better. However, if coordination logic is complex and repeated, Facade provides better encapsulation and maintainability.”
Q: “How does Facade Pattern relate to SOLID principles?”
A:
“Facade Pattern supports the Single Responsibility Principle by separating coordination logic into the Facade. It supports the Dependency Inversion Principle by having clients depend on the Facade abstraction rather than concrete subsystem classes. It also supports the Open/Closed Principle - you can modify subsystems without affecting clients.”
Interview Checklist
Section titled “Interview Checklist”Before your interview, make sure you can:
- Define Facade Pattern clearly in one sentence
- Explain when to use it (with examples showing simplification)
- Describe Facade vs Adapter Pattern
- Implement Facade Pattern from scratch
- Compare with other structural patterns (Adapter, Proxy)
- List benefits and trade-offs
- Identify common mistakes (god object, exposing details)
- Give 2-3 real-world examples
- Connect to SOLID principles
- Discuss when NOT to use it
Remember: Facade Pattern is about simplifying complex subsystems - providing a unified interface that makes the subsystem easier to use! 🎭