Skip to content

Facade Pattern

Intent

The Facade pattern provides a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.

Problem

Working with complex subsystems can be challenging:

  • The subsystem contains many classes with complex interdependencies
  • Clients need to understand and interact with multiple classes
  • Each client must manage the initialization and coordination of subsystem components
  • Changes in the subsystem can affect many clients

For example, a home theater system might have components like an amplifier, DVD player, projector, screen, and lights. To watch a movie, a client would need to: 1. Turn on the amplifier and set the volume 2. Turn on the DVD player and load the movie 3. Turn on the projector and set the input 4. Lower the screen 5. Dim the lights

This is complex and error-prone. If you forget a step or do them in the wrong order, it won't work correctly.

Solution

The Facade pattern suggests creating a facade class that provides a simple interface to the complex subsystem. The facade:

  • Knows which subsystem classes are responsible for a request
  • Delegates client requests to appropriate subsystem objects
  • Provides simple methods that combine multiple subsystem operations
  • Doesn't prevent clients from accessing subsystem classes directly if needed

Structure

Facade Pattern Diagram 0

Participants

  • Facade - Knows which subsystem classes are responsible for a request - Delegates client requests to appropriate subsystem objects - Provides convenient methods that combine subsystem operations - In our example: Facade class with operation() and anotherOperation()
  • Subsystem Classes - Implement subsystem functionality - Handle work assigned by the Facade - Have no knowledge of the facade (no reference to it) - In our example: SubsystemA, SubsystemB, SubsystemC
  • Client - Uses the Facade instead of subsystem objects directly - Can still access subsystem classes if needed - Simplified interaction with complex subsystem

Implementation

Subsystem Classes

class SubsystemA {
public:
    std::string operationA1() const {
        return "SubsystemA: Ready!\n";
    }

    std::string operationA2() const {
        return "SubsystemA: Go!\n";
    }
};

class SubsystemB {
public:
    std::string operationB1() const {
        return "SubsystemB: Get ready!\n";
    }

    std::string operationB2() const {
        return "SubsystemB: Fire!\n";
    }
};

class SubsystemC {
public:
    std::string operationC1() const {
        return "SubsystemC: Ready!\n";
    }

    std::string operationC2() const {
        return "SubsystemC: Done!\n";
    }
};

Facade Class

class Facade {
private:
    std::unique_ptr<SubsystemA> subsystemA_;
    std::unique_ptr<SubsystemB> subsystemB_;
    std::unique_ptr<SubsystemC> subsystemC_;

public:
    Facade(std::unique_ptr<SubsystemA> subsystemA = nullptr,
           std::unique_ptr<SubsystemB> subsystemB = nullptr,
           std::unique_ptr<SubsystemC> subsystemC = nullptr)
        : subsystemA_(subsystemA ? std::move(subsystemA) 
                                  : std::make_unique<SubsystemA>()),
          subsystemB_(subsystemB ? std::move(subsystemB) 
                                  : std::make_unique<SubsystemB>()),
          subsystemC_(subsystemC ? std::move(subsystemC) 
                                  : std::make_unique<SubsystemC>()) {}

    std::string operation() const {
        std::string result = "Facade initializes subsystems:\n";
        result += this->subsystemA_->operationA1();
        result += this->subsystemB_->operationB1();
        result += this->subsystemC_->operationC1();
        result += "Facade orders subsystems to perform the action:\n";
        result += this->subsystemA_->operationA2();
        result += this->subsystemB_->operationB2();
        result += this->subsystemC_->operationC2();
        return result;
    }
};

Usage Example

int main() {
    // Working directly with subsystems (complex)
    SubsystemA subsystemA;
    SubsystemB subsystemB;
    SubsystemC subsystemC;

    std::cout << subsystemA.operationA1();
    std::cout << subsystemB.operationB1();
    std::cout << subsystemC.operationC1();
    std::cout << subsystemA.operationA2();
    std::cout << subsystemB.operationB2();
    std::cout << subsystemC.operationC2();

    // Working through Facade (simple)
    Facade facade;
    std::cout << facade.operation();

    return 0;
}

Applicability

Use the Facade pattern when:

  • You want to provide a simple interface to a complex subsystem - Most clients need only a subset of subsystem features - Facade provides simple defaults for common use cases - Example: Compiler facade hiding scanner, parser, code generator
  • There are many dependencies between clients and implementation classes - Facade decouples subsystem from clients - Changes to subsystem don't affect clients - Example: Database access layer hiding connection pooling, caching
  • You want to layer your subsystems - Use facades to define entry points to each subsystem level - Subsystems communicate only through facades - Example: Network protocol layers, each with a facade
  • You want to wrap a poorly designed collection of APIs with a single well-designed API - Legacy code integration - Third-party library wrapper - Example: Wrapping complex graphics libraries

Consequences

Benefits

  1. Simplified interface - Hides complexity of subsystem from clients - Provides convenient methods for common operations - Reduces learning curve for subsystem usage

  2. Decoupling - Shields clients from subsystem components - Reduces dependencies between clients and subsystem - Changes to subsystem don't affect clients using the facade

  3. Layering - Helps organize code into layers - Each layer has a facade defining its interface - Promotes good architecture

  4. Flexibility - Clients can still access subsystem classes directly if needed - Facade doesn't prevent advanced usage - Can create multiple facades for different use cases

Drawbacks

  1. God object risk - Facade can become too large and complex - May try to do too much - Should focus on simplifying, not adding business logic

  2. Limited functionality - Facade may not expose all subsystem features - Advanced users may need to bypass facade - Need to balance simplicity with completeness

  3. Additional layer - Adds one more layer of abstraction - May impact performance slightly - Usually negligible overhead

Facade vs Similar Patterns

Facade vs Adapter

Aspect Facade Adapter
Intent Simplify interface (optional simpler interface) Make incompatible interfaces compatible
Interface New, simplified interface Adapts to specific target interface
Subsystems Works with multiple subsystems Typically wraps single class
Knowledge Knows about all subsystems Knows about adaptee

Facade vs Mediator

Aspect Facade Mediator
Intent Simplify subsystem interface Coordinate communication between objects
Direction Unidirectional (client → facade → subsystems) Bidirectional (colleagues ↔ mediator)
Awareness Subsystems don't know about facade Colleagues know about mediator
Purpose Simplification Decoupling communication

Facade vs Abstract Factory

Aspect Facade Abstract Factory
Intent Simplify subsystem usage Create families of related objects
Focus Simplifying interface Creating objects
Use together Facade can use Abstract Factory to create subsystem objects -

Real-world Examples

  1. Home Theater System - Subsystems: Amplifier, DVDPlayer, Projector, Screen, Lights - Facade methods: watchMovie(), endMovie() - Simplifies: Turn on all components in correct order with correct settings

  2. Compiler - Subsystems: Scanner, Parser, Optimizer, CodeGenerator - Facade method: compile(sourceFile, outputFile) - Simplifies: Orchestrates entire compilation pipeline

  3. Online Shopping - Subsystems: Inventory, Payment, Shipping, Notification - Facade method: placeOrder(product, paymentInfo, address) - Simplifies: Coordinates all steps of order processing

  4. Database Access - Subsystems: Connection pool, Query builder, Cache, Transaction manager - Facade methods: query(), save(), delete() - Simplifies: Hides connection management, caching, transactions

  5. Operating System APIs - Subsystems: File system, Memory management, Process management - Facade: High-level OS API - Simplifies: System calls, resource management

  6. Graphics Libraries - Subsystems: Renderer, Texture manager, Shader compiler, Buffer manager - Facade methods: drawSprite(), drawText() - Simplifies: Complex GPU operations

Implementation Considerations

1. Reducing Client-Subsystem Coupling

Minimize what clients need to know:

1
2
3
4
5
6
7
8
// Good - client only knows about Facade
Facade facade;
facade.operation();

// Less ideal - client knows about subsystems
SubsystemA a;
SubsystemB b;
// ... complex coordination

2. Public vs Private Subsystem Classes

Consider making subsystem classes private or in an internal namespace:

namespace internal {
    class SubsystemA { /*...*/ };
    class SubsystemB { /*...*/ };
}

class Facade {
    internal::SubsystemA subsystemA_;
    internal::SubsystemB subsystemB_;
    // ...
};

3. Optional Facade

Facade is optional - clients can still access subsystems:

1
2
3
4
5
6
// Simple case: use facade
facade.operation();

// Advanced case: direct access
SubsystemA subsystem;
subsystem.advancedOperation();

4. Creating Subsystem Objects

Facade can: - Create subsystem objects itself (default) - Accept subsystem objects from client (dependency injection) - Use Abstract Factory to create subsystems

1
2
3
4
Facade() : subsystemA_(std::make_unique<SubsystemA>()) {}

Facade(std::unique_ptr<SubsystemA> subsystemA) 
    : subsystemA_(std::move(subsystemA)) {}

5. One or More Facades

Can have multiple facades for: - Different client needs (simple vs advanced) - Different use cases - Different abstraction levels

6. Abstract Facade

For multiple implementations:

1
2
3
4
5
6
7
class AbstractFacade {
public:
    virtual void operation() = 0;
};

class ConcreteFacadeA : public AbstractFacade { /*...*/ };
class ConcreteFacadeB : public AbstractFacade { /*...*/ };

Sample Output

=== Facade Pattern Demo ===

Client: Working with subsystems directly (complex):
SubsystemA: Ready!
SubsystemB: Get ready!
SubsystemC: Ready!
SubsystemA: Go!
SubsystemB: Fire!
SubsystemC: Done!

Client: Working through Facade (simple):
Facade initializes subsystems:
SubsystemA: Ready!
SubsystemB: Get ready!
SubsystemC: Ready!
Facade orders subsystems to perform the action:
SubsystemA: Go!
SubsystemB: Fire!
SubsystemC: Done!

Client: Using another Facade operation:
Facade performs alternative sequence:
SubsystemC: Ready!
SubsystemA: Go!
SubsystemB: Get ready!
SubsystemC: Done!

=== Real-world Scenarios ===

Scenario 1: Home Theater System
- Without Facade: Client must manage all components
- With Facade: homeTheater->watchMovie(movie)

Scenario 2: Compiler System
- Without Facade: Client must orchestrate compilation steps
- With Facade: compiler->compile(sourceFile, outputFile)

Scenario 3: Online Shopping
- Without Facade: Client must coordinate all systems
- With Facade: order->placeOrder(product, card, address)

Benefits:
- Simplifies complex subsystems
- Reduces coupling between client and subsystems
- Provides a higher-level interface
- Clients can still access subsystems directly if needed

Key Takeaways

  1. Facade provides a simplified interface to a complex subsystem
  2. Reduces dependencies between clients and subsystem implementation
  3. Doesn't prevent clients from accessing subsystem classes directly
  4. Helps organize code into layers with clear boundaries
  5. Commonly used to wrap complex libraries or legacy code
  6. Can have multiple facades for different purposes
  7. Subsystems are unaware of the facade
  8. Focuses on simplifying usage, not adding business logic
  9. Different from Adapter (which makes incompatible interfaces work) and Mediator (which coordinates communication)
  10. Essential for maintaining clean architecture and reducing coupling