Facade Pattern

Provide a unified, simplified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.


Problem

You have a complex subsystem with many classes that clients need to interact with. This complexity makes the subsystem hard to use and maintain.

Solution

The Facade pattern provides a single unified interface to a set of classes/components.


Implementation

# ============ COMPLEX SUBSYSTEM: Movie Theater ============

class AudioSystem:
    def __init__(self):
        self.volume = 5

    def set_volume(self, level: int):
        self.volume = level
        print(f"šŸ”Š Audio volume set to {level}")

    def turn_on(self):
        print("šŸ”Š Audio system ON")

    def turn_off(self):
        print("šŸ”Š Audio system OFF")

class Projector:
    def __init__(self):
        self.is_on = False

    def turn_on(self):
        self.is_on = True
        print("šŸŽ¬ Projector ON")

    def turn_off(self):
        self.is_on = False
        print("šŸŽ¬ Projector OFF")

    def set_resolution(self, width: int, height: int):
        if self.is_on:
            print(f"šŸŽ¬ Projector resolution: {width}x{height}")

class Screen:
    def __init__(self):
        self.position = "up"

    def down(self):
        self.position = "down"
        print("šŸŽ„ Screen DOWN")

    def up(self):
        self.position = "up"
        print("šŸŽ„ Screen UP")

class DVDPlayer:
    def __init__(self):
        self.is_playing = False

    def turn_on(self):
        print("šŸ“€ DVD Player ON")

    def turn_off(self):
        print("šŸ“€ DVD Player OFF")

    def play(self, movie: str):
        self.is_playing = True
        print(f"šŸ“€ Playing: {movie}")

    def stop(self):
        self.is_playing = False
        print("šŸ“€ DVD stopped")

class Lights:
    def __init__(self):
        self.brightness = 100

    def dim(self, level: int):
        self.brightness = level
        print(f"šŸ’” Lights dimmed to {level}%")

    def turn_on(self):
        self.brightness = 100
        print("šŸ’” Lights ON")

# ============ FACADE: Simplifies subsystem ============

class HomeTheaterFacade:
    """Facade - provides simple interface to complex subsystem"""

    def __init__(self):
        self.audio = AudioSystem()
        self.projector = Projector()
        self.screen = Screen()
        self.dvd_player = DVDPlayer()
        self.lights = Lights()

    def watch_movie(self, movie: str):
        """Simple interface to watch a movie"""
        print(f"\nšŸŽ­ Starting movie night: {movie}")
        self.lights.dim(10)
        self.screen.down()
        self.projector.turn_on()
        self.projector.set_resolution(1920, 1080)
        self.audio.turn_on()
        self.audio.set_volume(8)
        self.dvd_player.turn_on()
        self.dvd_player.play(movie)

    def end_movie(self):
        """Simple interface to end movie"""
        print("\nšŸŽ­ Ending movie night")
        self.dvd_player.stop()
        self.dvd_player.turn_off()
        self.projector.turn_off()
        self.screen.up()
        self.audio.turn_off()
        self.lights.turn_on()

# ============ ANOTHER EXAMPLE: Restaurant Ordering System ============

class Menu:
    def get_items(self):
        return ["Pizza", "Pasta", "Salad", "Burger"]

class Kitchen:
    def prepare_pizza(self):
        print("šŸ• Preparing pizza...")
        return "Pizza"

    def prepare_pasta(self):
        print("šŸ Preparing pasta...")
        return "Pasta"

    def prepare_salad(self):
        print("šŸ„— Preparing salad...")
        return "Salad"

class Waiter:
    def take_order(self, order: str):
        print(f"āœļø Waiter taking order: {order}")

    def serve_order(self, item: str):
        print(f"🚶 Waiter serving: {item}")

class Cashier:
    def calculate_bill(self, items: list) -> float:
        prices = {"Pizza": 10, "Pasta": 8, "Salad": 6, "Burger": 7}
        total = sum(prices.get(item, 0) for item in items)
        print(f"šŸ’° Total bill: ${total}")
        return total

    def process_payment(self, amount: float):
        print(f"šŸ’³ Payment of ${amount} processed")

class RestaurantFacade:
    """Facade - simplifies restaurant operations"""

    def __init__(self):
        self.menu = Menu()
        self.kitchen = Kitchen()
        self.waiter = Waiter()
        self.cashier = Cashier()

    def order_meal(self, items: list):
        print(f"\nšŸ½ļø Ordering meal: {items}")

        # Show menu
        available = self.menu.get_items()
        print(f"Available: {available}")

        # Take order
        for item in items:
            self.waiter.take_order(item)

        # Prepare food
        for item in items:
            if item == "Pizza":
                self.kitchen.prepare_pizza()
            elif item == "Pasta":
                self.kitchen.prepare_pasta()
            elif item == "Salad":
                self.kitchen.prepare_salad()

        # Serve food
        for item in items:
            self.waiter.serve_order(item)

        # Calculate and pay bill
        total = self.cashier.calculate_bill(items)
        self.cashier.process_payment(total)

# ============ LIBRARY SYSTEM FACADE ============

class BookDatabase:
    def search_by_title(self, title: str):
        print(f"šŸ“š Searching for: {title}")
        return ["Book 1", "Book 2"]

class InventorySystem:
    def check_availability(self, book_id: str):
        print(f"āœ… Checking inventory for {book_id}")
        return True

class CheckoutSystem:
    def checkout_book(self, book_id: str, member_id: str):
        print(f"šŸ“‹ Checkout: {book_id} to member {member_id}")

class NotificationSystem:
    def send_confirmation(self, member_id: str, book: str):
        print(f"šŸ“§ Confirmation email sent to member {member_id}")

class LibraryFacade:
    """Facade - simplifies library operations"""

    def __init__(self):
        self.database = BookDatabase()
        self.inventory = InventorySystem()
        self.checkout = CheckoutSystem()
        self.notifications = NotificationSystem()

    def borrow_book(self, title: str, member_id: str):
        print(f"\nšŸ“– Borrowing book: {title}")

        # Search
        results = self.database.search_by_title(title)
        if not results:
            print("Book not found")
            return

        # Check availability
        book_id = results[0]
        if not self.inventory.check_availability(book_id):
            print("Book not available")
            return

        # Checkout
        self.checkout.checkout_book(book_id, member_id)

        # Send confirmation
        self.notifications.send_confirmation(member_id, title)

# Demo
if __name__ == "__main__":
    print("=" * 50)
    print("=== HOME THEATER FACADE ===")
    print("=" * 50)
    theater = HomeTheaterFacade()

    # Without facade, clients would need to do all this manually
    theater.watch_movie("The Matrix")
    theater.end_movie()

    print("\n" + "=" * 50)
    print("=== RESTAURANT FACADE ===")
    print("=" * 50)
    restaurant = RestaurantFacade()
    restaurant.order_meal(["Pizza", "Pasta", "Salad"])

    print("\n" + "=" * 50)
    print("=== LIBRARY FACADE ===")
    print("=" * 50)
    library = LibraryFacade()
    library.borrow_book("Clean Code", "M001")

Key Concepts

  • Facade: Single unified interface to complex subsystem
  • Subsystem: Complex set of classes/components
  • Simplification: Clients don't see complexity
  • Decoupling: Clients depend only on facade, not subsystem
  • Convenience: Common operations wrapped in simple methods

When to Use

āœ… Complex subsystem needs simplified interface
āœ… Reduce coupling between clients and subsystem
āœ… Provide entry point to layered subsystem
āœ… Simplify client code and dependencies


Interview Q&A

Q1: What's the difference between Facade and Adapter?

A: - Facade: Simplifies complex subsystem. Multiple classes. - Adapter: Makes incompatible interfaces work. Usually wraps one class.

# Facade: Simplifies many classes
class HomeTheaterFacade:
    def watch_movie(self):
        self.projector.turn_on()
        self.audio.turn_on()
        self.screen.down()  # Multiple components

# Adapter: Converts one interface
class PaymentAdapter:
    def pay(self, amount):
        return legacy_payment.process(amount)  # One component

Q2: Can a facade itself be decorated or wrapped?

A: Yes, facades can be composed:

class AdvancedTheaterFacade(HomeTheaterFacade):
    def watch_4k_movie(self, movie):
        # Uses parent facade + adds new functionality
        self.watch_movie(movie)
        self.projector.set_resolution(4096, 2160)

Q3: How would you handle exceptions in a facade?

A:

class RobustTheaterFacade:
    def watch_movie(self, movie: str):
        try:
            self.lights.dim(10)
            self.projector.turn_on()
            self.dvd_player.play(movie)
        except Exception as e:
            print(f"Error: {e}")
            self.cleanup()

    def cleanup(self):
        # Ensure system is in safe state
        self.lights.turn_on()
        self.projector.turn_off()

Q4: How would you make a facade for multiple providers?

A:

class FlexibleRestaurantFacade:
    def __init__(self, provider: str):
        if provider == "italian":
            self.kitchen = ItalianKitchen()
        elif provider == "chinese":
            self.kitchen = ChineseKitchen()
        else:
            self.kitchen = StandardKitchen()

    def order_meal(self, items):
        for item in items:
            self.kitchen.prepare(item)

Q5: How would you test code using a facade?

A:

from unittest.mock import Mock

class MockHomeTheaterFacade:
    def __init__(self):
        self.projector = Mock()
        self.audio = Mock()
        self.lights = Mock()

    def watch_movie(self, movie):
        self.lights.dim(10)
        self.projector.turn_on()

# Test
facade = MockHomeTheaterFacade()
facade.watch_movie("The Matrix")
facade.lights.dim.assert_called_with(10)

Trade-offs

āœ… Pros: Simplifies complex subsystems, loose coupling, easier to use
āŒ Cons: Facade becomes too large, hides subsystem details, may be overkill for simple systems


Real-World Examples

  • Framework APIs (Django, React provide facades)
  • Database ORMs (hide SQL complexity)
  • Logging frameworks (hide configuration complexity)
  • Cloud SDKs (AWS SDK facades, Azure SDK)
  • Java I/O (various stream classes with simplified facades)