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:
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)// ❌ Without Facade Pattern - Complex client code!
class Amplifier { /** Amplifier subsystem */ on(): void { console.log("Amplifier: Turning on..."); }
setVolume(level: number): void { console.log(`Amplifier: Setting volume to ${level}`); }
off(): void { console.log("Amplifier: Turning off..."); }}
class DVDPlayer { /** DVD Player subsystem */ on(): void { console.log("DVD Player: Turning on..."); }
play(movie: string): void { console.log(`DVD Player: Playing ${movie}`); }
off(): void { console.log("DVD Player: Turning off..."); }}
class Projector { /** Projector subsystem */ on(): void { console.log("Projector: Turning on..."); }
wideScreenMode(): void { console.log("Projector: Setting wide screen mode"); }
off(): void { console.log("Projector: Turning off..."); }}
class Screen { /** Screen subsystem */ down(): void { console.log("Screen: Lowering screen..."); }
up(): void { console.log("Screen: Raising screen..."); }}
// Problem: Client needs to know about all subsystems and coordinate them!class Client { watchMovie(movie: string): void { // Client needs to know about all subsystems! const amplifier = new Amplifier(); const dvdPlayer = new DVDPlayer(); const projector = new Projector(); const screen = new Screen();
// Complex coordination logic in client! amplifier.on(); amplifier.setVolume(10); dvdPlayer.on(); projector.on(); projector.wideScreenMode(); screen.down(); dvdPlayer.play(movie);
console.log("🎬 Movie started!"); }
endMovie(): void { // Client needs to know shutdown order too! const amplifier = new Amplifier(); const dvdPlayer = new DVDPlayer(); const projector = new Projector(); const screen = new Screen();
dvdPlayer.off(); projector.off(); amplifier.off(); screen.up();
console.log("✅ 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!
#include <iostream>#include <string>
class Amplifier {public: // Amplifier subsystem void on() { std::cout << "Amplifier: Turning on..." << std::endl; }
void setVolume(int level) { std::cout << "Amplifier: Setting volume to " << level << std::endl; }
void off() { std::cout << "Amplifier: Turning off..." << std::endl; }};
class DVDPlayer {public: // DVD Player subsystem void on() { std::cout << "DVD Player: Turning on..." << std::endl; }
void play(const std::string& movie) { std::cout << "DVD Player: Playing " << movie << std::endl; }
void off() { std::cout << "DVD Player: Turning off..." << std::endl; }};
class Projector {public: // Projector subsystem void on() { std::cout << "Projector: Turning on..." << std::endl; }
void wideScreenMode() { std::cout << "Projector: Setting wide screen mode" << std::endl; }
void off() { std::cout << "Projector: Turning off..." << std::endl; }};
class Screen {public: // Screen subsystem void down() { std::cout << "Screen: Lowering screen..." << std::endl; }
void up() { std::cout << "Screen: Raising screen..." << std::endl; }};
// Problem: Client needs to know about all subsystems and coordinate them!class Client {public: void watchMovie(const std::string& movie) { // Client needs to know about all subsystems! Amplifier amplifier; DVDPlayer dvdPlayer; Projector projector; Screen screen;
// Complex coordination logic in client! amplifier.on(); amplifier.setVolume(10); dvdPlayer.on(); projector.on(); projector.wideScreenMode(); screen.down(); dvdPlayer.play(movie);
std::cout << "🎬 Movie started!" << std::endl; }
void endMovie() { // Client needs to know shutdown order too! Amplifier amplifier; DVDPlayer dvdPlayer; Projector projector; Screen screen;
dvdPlayer.off(); projector.off(); amplifier.off(); screen.up();
std::cout << "✅ Movie ended!" << std::endl; }};
// 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!
using System;
public class Amplifier{ /** Amplifier subsystem */ public void On() { Console.WriteLine("Amplifier: Turning on..."); }
public void SetVolume(int level) { Console.WriteLine($"Amplifier: Setting volume to {level}"); }
public void Off() { Console.WriteLine("Amplifier: Turning off..."); }}
public class DVDPlayer{ /** DVD Player subsystem */ public void On() { Console.WriteLine("DVD Player: Turning on..."); }
public void Play(string movie) { Console.WriteLine($"DVD Player: Playing {movie}"); }
public void Off() { Console.WriteLine("DVD Player: Turning off..."); }}
public class Projector{ /** Projector subsystem */ public void On() { Console.WriteLine("Projector: Turning on..."); }
public void WideScreenMode() { Console.WriteLine("Projector: Setting wide screen mode"); }
public void Off() { Console.WriteLine("Projector: Turning off..."); }}
public class Screen{ /** Screen subsystem */ public void Down() { Console.WriteLine("Screen: Lowering screen..."); }
public void Up() { Console.WriteLine("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);
Console.WriteLine("🎬 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();
Console.WriteLine("✅ 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!
package main
import "fmt"
type Amplifier struct{}func (a *Amplifier) On() { fmt.Println("Amplifier: Turning on...") }func (a *Amplifier) SetVolume(l int) { fmt.Printf("Amplifier: Setting volume to %d\n", l) }func (a *Amplifier) Off() { fmt.Println("Amplifier: Turning off...") }
type DVDPlayer struct{}func (d *DVDPlayer) On() { fmt.Println("DVD Player: Turning on...") }func (d *DVDPlayer) Play(movie string) { fmt.Printf("DVD Player: Playing %s\n", movie) }func (d *DVDPlayer) Off() { fmt.Println("DVD Player: Turning off...") }
type Projector struct{}func (p *Projector) On() { fmt.Println("Projector: Turning on...") }func (p *Projector) WideScreenMode() { fmt.Println("Projector: Setting wide screen mode") }func (p *Projector) Off() { fmt.Println("Projector: Turning off...") }
type Screen struct{}func (s *Screen) Down() { fmt.Println("Screen: Lowering screen...") }func (s *Screen) Up() { fmt.Println("Screen: Raising screen...") }
// Problem: Client needs to know about all subsystems and coordinate them!type Client struct{}
func (c *Client) WatchMovie(movie string) { amp := &Amplifier{} dvd := &DVDPlayer{} proj := &Projector{} screen := &Screen{}
amp.On() amp.SetVolume(10) dvd.On() proj.On() proj.WideScreenMode() screen.Down() dvd.Play(movie) fmt.Println("Movie started!")}
// Problems:// - Client knows about all subsystem structs// - Complex coordination logic in client// - Hard to change subsystem (affects all clients)// - Code duplication (coordination logic repeated)// The Problemstruct VideoConverter;impl VideoConverter { fn convert(&self) { println!("codec"); println!("frames"); println!("audio"); println!("output"); }}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”# 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!"); }}// Step 1: Subsystem classes (unchanged)class Amplifier { /** Amplifier subsystem */ on(): void { console.log("Amplifier: Turning on..."); }
setVolume(level: number): void { console.log(`Amplifier: Setting volume to ${level}`); }
off(): void { console.log("Amplifier: Turning off..."); }}
class DVDPlayer { /** DVD Player subsystem */ on(): void { console.log("DVD Player: Turning on..."); }
play(movie: string): void { console.log(`DVD Player: Playing ${movie}`); }
off(): void { console.log("DVD Player: Turning off..."); }}
class Projector { /** Projector subsystem */ on(): void { console.log("Projector: Turning on..."); }
wideScreenMode(): void { console.log("Projector: Setting wide screen mode"); }
off(): void { console.log("Projector: Turning off..."); }}
class Screen { /** Screen subsystem */ down(): void { console.log("Screen: Lowering screen..."); }
up(): void { console.log("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;
constructor() { // Facade knows about all subsystems this.amplifier = new Amplifier(); this.dvdPlayer = new DVDPlayer(); this.projector = new Projector(); this.screen = new Screen(); }
watchMovie(movie: string): void { // Simple interface - hides complex coordination console.log("🎬 Getting ready to watch a movie...");
// Facade coordinates all subsystems this.screen.down(); this.projector.on(); this.projector.wideScreenMode(); this.amplifier.on(); this.amplifier.setVolume(10); this.dvdPlayer.on(); this.dvdPlayer.play(movie);
console.log("✅ Movie started! Enjoy!\n"); }
endMovie(): void { // Simple interface - hides complex shutdown console.log("🛑 Shutting down home theater...");
// Facade coordinates shutdown this.dvdPlayer.off(); this.projector.off(); this.amplifier.off(); this.screen.up();
console.log("✅ Home theater shut down!\n"); }}
// Usage - Clean and simple!function main(): void { // Client only needs to know about Facade! const homeTheater = new HomeTheaterFacade();
// Simple interface - no need to know about subsystems! homeTheater.watchMovie("The Matrix");
// Simple shutdown - no need to coordinate subsystems! homeTheater.endMovie();
console.log("✅ Facade Pattern simplifies complex subsystem!");}
main();#include <iostream>#include <string>
// Step 1: Subsystem classes (unchanged)class Amplifier {public: // Amplifier subsystem void on() { std::cout << "Amplifier: Turning on..." << std::endl; }
void setVolume(int level) { std::cout << "Amplifier: Setting volume to " << level << std::endl; }
void off() { std::cout << "Amplifier: Turning off..." << std::endl; }};
class DVDPlayer {public: // DVD Player subsystem void on() { std::cout << "DVD Player: Turning on..." << std::endl; }
void play(const std::string& movie) { std::cout << "DVD Player: Playing " << movie << std::endl; }
void off() { std::cout << "DVD Player: Turning off..." << std::endl; }};
class Projector {public: // Projector subsystem void on() { std::cout << "Projector: Turning on..." << std::endl; }
void wideScreenMode() { std::cout << "Projector: Setting wide screen mode" << std::endl; }
void off() { std::cout << "Projector: Turning off..." << std::endl; }};
class Screen {public: // Screen subsystem void down() { std::cout << "Screen: Lowering screen..." << std::endl; }
void up() { std::cout << "Screen: Raising screen..." << std::endl; }};
// Step 2: Create the Facadeclass HomeTheaterFacade {private: Amplifier amplifier; DVDPlayer dvdPlayer; Projector projector; Screen screen;
public: // Facade - simplifies home theater subsystem HomeTheaterFacade() = default;
void watchMovie(const std::string& movie) { // Simple interface - hides complex coordination std::cout << "🎬 Getting ready to watch a movie..." << std::endl;
// Facade coordinates all subsystems screen.down(); projector.on(); projector.wideScreenMode(); amplifier.on(); amplifier.setVolume(10); dvdPlayer.on(); dvdPlayer.play(movie);
std::cout << "✅ Movie started! Enjoy!\n" << std::endl; }
void endMovie() { // Simple interface - hides complex shutdown std::cout << "🛑 Shutting down home theater..." << std::endl;
// Facade coordinates shutdown dvdPlayer.off(); projector.off(); amplifier.off(); screen.up();
std::cout << "✅ Home theater shut down!\n" << std::endl; }};
// Usage - Clean and simple!int main() { // Client only needs to know about Facade! HomeTheaterFacade homeTheater;
// Simple interface - no need to know about subsystems! homeTheater.watchMovie("The Matrix");
// Simple shutdown - no need to coordinate subsystems! homeTheater.endMovie();
std::cout << "✅ Facade Pattern simplifies complex subsystem!" << std::endl;
return 0;}using System;
// Step 1: Subsystem classes (unchanged)class Amplifier{ /** Amplifier subsystem */ public void On() { Console.WriteLine("Amplifier: Turning on..."); }
public void SetVolume(int level) { Console.WriteLine($"Amplifier: Setting volume to {level}"); }
public void Off() { Console.WriteLine("Amplifier: Turning off..."); }}
class DVDPlayer{ /** DVD Player subsystem */ public void On() { Console.WriteLine("DVD Player: Turning on..."); }
public void Play(string movie) { Console.WriteLine($"DVD Player: Playing {movie}"); }
public void Off() { Console.WriteLine("DVD Player: Turning off..."); }}
class Projector{ /** Projector subsystem */ public void On() { Console.WriteLine("Projector: Turning on..."); }
public void WideScreenMode() { Console.WriteLine("Projector: Setting wide screen mode"); }
public void Off() { Console.WriteLine("Projector: Turning off..."); }}
class Screen{ /** Screen subsystem */ public void Down() { Console.WriteLine("Screen: Lowering screen..."); }
public void Up() { Console.WriteLine("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 Console.WriteLine("🎬 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);
Console.WriteLine("✅ Movie started! Enjoy!\n"); }
public void EndMovie() { // Simple interface - hides complex shutdown Console.WriteLine("🛑 Shutting down home theater...");
// Facade coordinates shutdown dvdPlayer.Off(); projector.Off(); amplifier.Off(); screen.Up();
Console.WriteLine("✅ Home theater shut down!\n"); }}
// Usage - Clean and simple!class Program{ static void Main() { // 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();
Console.WriteLine("✅ Facade Pattern simplifies complex subsystem!"); }}package main
import "fmt"
// Subsystem structs (unchanged from bad example)type HomeAmplifier struct{}func (a *HomeAmplifier) On() { fmt.Println("Amplifier: Turning on...") }func (a *HomeAmplifier) SetVolume(l int) { fmt.Printf("Amplifier: Setting volume to %d\n", l) }func (a *HomeAmplifier) Off() { fmt.Println("Amplifier: Turning off...") }
type HomeDVDPlayer struct{}func (d *HomeDVDPlayer) On() { fmt.Println("DVD Player: Turning on...") }func (d *HomeDVDPlayer) Play(movie string) { fmt.Printf("DVD Player: Playing %s\n", movie) }func (d *HomeDVDPlayer) Off() { fmt.Println("DVD Player: Turning off...") }
type HomeProjector struct{}func (p *HomeProjector) On() { fmt.Println("Projector: Turning on...") }func (p *HomeProjector) WideScreenMode() { fmt.Println("Projector: Setting wide screen mode") }func (p *HomeProjector) Off() { fmt.Println("Projector: Turning off...") }
type HomeScreen struct{}func (s *HomeScreen) Down() { fmt.Println("Screen: Lowering screen...") }func (s *HomeScreen) Up() { fmt.Println("Screen: Raising screen...") }
// Facade - simplifies home theater subsystemtype HomeTheaterFacade struct { amplifier *HomeAmplifier dvdPlayer *HomeDVDPlayer projector *HomeProjector screen *HomeScreen}
func NewHomeTheaterFacade() *HomeTheaterFacade { return &HomeTheaterFacade{ amplifier: &HomeAmplifier{}, dvdPlayer: &HomeDVDPlayer{}, projector: &HomeProjector{}, screen: &HomeScreen{}, }}
func (h *HomeTheaterFacade) WatchMovie(movie string) { fmt.Println("Getting ready to watch a movie...") h.screen.Down() h.projector.On() h.projector.WideScreenMode() h.amplifier.On() h.amplifier.SetVolume(10) h.dvdPlayer.On() h.dvdPlayer.Play(movie) fmt.Println("Movie started! Enjoy!\n")}
func (h *HomeTheaterFacade) EndMovie() { fmt.Println("Shutting down home theater...") h.dvdPlayer.Off() h.projector.Off() h.amplifier.Off() h.screen.Up() fmt.Println("Home theater shut down!\n")}
func main() { homeTheater := NewHomeTheaterFacade() homeTheater.WatchMovie("The Matrix") homeTheater.EndMovie() fmt.Println("✅ Facade Pattern simplifies complex subsystem!")}// Class Structurestruct Codec;struct AudioMixer;struct FileWriter;struct VideoFacade { codec: Codec, mixer: AudioMixer, writer: FileWriter,}impl VideoFacade { fn convert(&self, file: &str) { println!("prepare {}", file); println!("decode"); println!("mix audio"); println!("write output"); }}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)// ❌ Without Facade Pattern - Complex client code!
class InventoryService { /** Inventory subsystem */ checkStock(productId: string, quantity: number): boolean { console.log(`Inventory: Checking stock for ${productId}...`); return true; }
reserveItems(productId: string, quantity: number): void { console.log(`Inventory: Reserving ${quantity} items of ${productId}`); }}
class PaymentService { /** Payment subsystem */ processPayment(amount: number, cardToken: string): boolean { console.log(`Payment: Processing payment of $${amount}...`); return true; }
refundPayment(transactionId: string): void { console.log(`Payment: Refunding transaction ${transactionId}`); }}
class ShippingService { /** Shipping subsystem */ createShipment(orderId: string, address: Record<string, string>): void { console.log(`Shipping: Creating shipment for order ${orderId}`); }
trackShipment(shipmentId: string): void { console.log(`Shipping: Tracking shipment ${shipmentId}`); }}
class NotificationService { /** Notification subsystem */ sendEmail(email: string, subject: string, body: string): void { console.log(`Notification: Sending email to ${email}`); }}
// Problem: Client needs to coordinate all subsystems!class OrderProcessor { processOrder(order: Record<string, any>): boolean { // Client needs to know about all subsystems! const inventory = new InventoryService(); const payment = new PaymentService(); const shipping = new ShippingService(); const notification = new NotificationService();
// Complex coordination logic! if (!inventory.checkStock(order["product_id"], order["quantity"])) { return false; }
inventory.reserveItems(order["product_id"], order["quantity"]);
if (!payment.processPayment(order["amount"], order["card_token"])) { inventory.reserveItems(order["product_id"], -order["quantity"]); // Rollback return false; }
shipping.createShipment(order["order_id"], order["address"]); notification.sendEmail( order["email"], "Order Confirmed", `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!
#include <iostream>#include <string>#include <map>
class InventoryService {public: // Inventory subsystem bool checkStock(const std::string& productId, int quantity) { std::cout << "Inventory: Checking stock for " << productId << "..." << std::endl; return true; }
void reserveItems(const std::string& productId, int quantity) { std::cout << "Inventory: Reserving " << quantity << " items of " << productId << std::endl; }};
class PaymentService {public: // Payment subsystem bool processPayment(double amount, const std::string& cardToken) { std::cout << "Payment: Processing payment of $" << amount << "..." << std::endl; return true; }
void refundPayment(const std::string& transactionId) { std::cout << "Payment: Refunding transaction " << transactionId << std::endl; }};
class ShippingService {public: // Shipping subsystem void createShipment(const std::string& orderId, const std::map<std::string, std::string>& address) { std::cout << "Shipping: Creating shipment for order " << orderId << std::endl; }
void trackShipment(const std::string& shipmentId) { std::cout << "Shipping: Tracking shipment " << shipmentId << std::endl; }};
class NotificationService {public: // Notification subsystem void sendEmail(const std::string& email, const std::string& subject, const std::string& body) { std::cout << "Notification: Sending email to " << email << std::endl; }};
// Problem: Client needs to coordinate all subsystems!class OrderProcessor {public: bool processOrder(const std::map<std::string, std::string>& order) { // Client needs to know about all subsystems! InventoryService inventory; PaymentService payment; ShippingService shipping; NotificationService notification;
// Complex coordination logic! std::string productId = order.at("product_id"); int quantity = std::stoi(order.at("quantity"));
if (!inventory.checkStock(productId, quantity)) { return false; }
inventory.reserveItems(productId, quantity);
double amount = std::stod(order.at("amount")); if (!payment.processPayment(amount, order.at("card_token"))) { inventory.reserveItems(productId, -quantity); // Rollback return false; }
std::map<std::string, std::string> address; // Simplified shipping.createShipment(order.at("order_id"), address); notification.sendEmail( order.at("email"), "Order Confirmed", "Your order " + order.at("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!
using System;using System.Collections.Generic;
public class InventoryService{ /** Inventory subsystem */ public bool CheckStock(string productId, int quantity) { Console.WriteLine($"Inventory: Checking stock for {productId}..."); return true; }
public void ReserveItems(string productId, int quantity) { Console.WriteLine($"Inventory: Reserving {quantity} items of {productId}"); }}
public class PaymentService{ /** Payment subsystem */ public bool ProcessPayment(double amount, string cardToken) { Console.WriteLine($"Payment: Processing payment of ${amount}..."); return true; }
public void RefundPayment(string transactionId) { Console.WriteLine($"Payment: Refunding transaction {transactionId}"); }}
public class ShippingService{ /** Shipping subsystem */ public void CreateShipment(string orderId, Dictionary<string, string> address) { Console.WriteLine($"Shipping: Creating shipment for order {orderId}"); }
public void TrackShipment(string shipmentId) { Console.WriteLine($"Shipping: Tracking shipment {shipmentId}"); }}
public class NotificationService{ /** Notification subsystem */ public void SendEmail(string email, string subject, string body) { Console.WriteLine($"Notification: Sending email to {email}"); }}
// Problem: Client needs to coordinate all subsystems!public class OrderProcessor{ public bool ProcessOrder(Dictionary<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["product_id"], (int)order["quantity"])) { return false; }
inventory.ReserveItems((string)order["product_id"], (int)order["quantity"]);
if (!payment.ProcessPayment((double)order["amount"], (string)order["card_token"])) { inventory.ReserveItems((string)order["product_id"], -(int)order["quantity"]); // Rollback return false; }
shipping.CreateShipment((string)order["order_id"], (Dictionary<string, string>)order["address"]); notification.SendEmail( (string)order["email"], "Order Confirmed", $"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!
package main
import "fmt"
type InventoryService struct{}func (s *InventoryService) CheckStock(productId string, qty int) bool { fmt.Printf("Inventory: Checking stock for %s...\n", productId) return true}func (s *InventoryService) ReserveItems(productId string, qty int) { fmt.Printf("Inventory: Reserving %d items of %s\n", qty, productId)}
type PaymentService struct{}func (s *PaymentService) ProcessPayment(amount float64, token string) bool { fmt.Printf("Payment: Processing payment of $%.2f...\n", amount) return true}
type ShippingService struct{}func (s *ShippingService) CreateShipment(orderId string, address map[string]string) { fmt.Printf("Shipping: Creating shipment for order %s\n", orderId)}
type NotificationService struct{}func (s *NotificationService) SendEmail(email, subject, body string) { fmt.Printf("Notification: Sending email to %s\n", email)}
// Problem: Client needs to coordinate all subsystems!type OrderProcessor struct{}
func (p *OrderProcessor) ProcessOrder(order map[string]interface{}) bool { inventory := &InventoryService{} payment := &PaymentService{} shipping := &ShippingService{} notification := &NotificationService{}
if !inventory.CheckStock(order["product_id"].(string), order["quantity"].(int)) { return false } inventory.ReserveItems(order["product_id"].(string), order["quantity"].(int))
if !payment.ProcessPayment(order["amount"].(float64), order["card_token"].(string)) { inventory.ReserveItems(order["product_id"].(string), -order["quantity"].(int)) return false } shipping.CreateShipment(order["order_id"].(string), nil) notification.SendEmail(order["email"].(string), "Order Confirmed", "Your order is confirmed") return true}
// Problems:// - Client knows about all subsystems// - Complex coordination logic// - Hard to change (affects all clients)// The Problemstruct VideoConverter;impl VideoConverter { fn convert(&self) { println!("codec"); println!("frames"); println!("audio"); println!("output"); }}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", "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"); 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!"); }}// Step 1: Subsystem classes (unchanged)class InventoryService { /** Inventory subsystem */ checkStock(productId: string, quantity: number): boolean { console.log(`Inventory: Checking stock for ${productId}...`); return true; }
reserveItems(productId: string, quantity: number): void { console.log(`Inventory: Reserving ${quantity} items of ${productId}`); }}
class PaymentService { /** Payment subsystem */ processPayment(amount: number, cardToken: string): boolean { console.log(`Payment: Processing payment of $${amount}...`); return true; }
refundPayment(transactionId: string): void { console.log(`Payment: Refunding transaction ${transactionId}`); }}
class ShippingService { /** Shipping subsystem */ createShipment(orderId: string, address: Record<string, string>): string { console.log(`Shipping: Creating shipment for order ${orderId}`); return `SHIP_${orderId}`; }
trackShipment(shipmentId: string): void { console.log(`Shipping: Tracking shipment ${shipmentId}`); }}
class NotificationService { /** Notification subsystem */ sendEmail(email: string, subject: string, body: string): void { console.log(`Notification: Sending email to ${email}: ${subject}`); }}
// Step 2: Create the Facadeclass OrderProcessingFacade { /** Facade - simplifies order processing subsystem */ private inventory: InventoryService; private payment: PaymentService; private shipping: ShippingService; private notification: NotificationService;
constructor() { // Facade knows about all subsystems this.inventory = new InventoryService(); this.payment = new PaymentService(); this.shipping = new ShippingService(); this.notification = new NotificationService(); }
processOrder(order: Record<string, any>): boolean { // Simple interface - hides complex order processing console.log(`🛒 Processing order ${order["order_id"]}...\n`);
// Step 1: Check inventory if (!this.inventory.checkStock(order["product_id"], order["quantity"])) { console.log("❌ Order failed: Out of stock\n"); return false; }
// Step 2: Reserve items this.inventory.reserveItems(order["product_id"], order["quantity"]);
// Step 3: Process payment if (!this.payment.processPayment(order["amount"], order["card_token"])) { // Rollback inventory this.inventory.reserveItems(order["product_id"], -order["quantity"]); console.log("❌ Order failed: Payment failed\n"); return false; }
// Step 4: Create shipment const shipmentId = this.shipping.createShipment(order["order_id"], order["address"]);
// Step 5: Send notification this.notification.sendEmail( order["email"], "Order Confirmed", `Your order ${order["order_id"]} has been confirmed. Shipment ID: ${shipmentId}` );
console.log(`✅ Order ${order["order_id"]} processed successfully!\n`); return true; }
cancelOrder(orderId: string, transactionId: string, productId: string, quantity: number): void { // Simple interface - hides complex cancellation console.log(`🛑 Cancelling order ${orderId}...\n`);
// Facade coordinates cancellation across subsystems this.payment.refundPayment(transactionId); this.inventory.reserveItems(productId, -quantity);
console.log(`✅ Order ${orderId} cancelled successfully!\n`); }}
// Usage - Clean and simple!function main(): void { // Client only needs to know about Facade! const orderFacade = new OrderProcessingFacade();
// Simple interface - no need to know about subsystems! const order = { "order_id": "ORD123", "product_id": "PROD456", "quantity": 2, "amount": 99.99, "card_token": "tok_123456", "address": { "street": "123 Main St", "city": "New York" } };
orderFacade.processOrder(order);
console.log("✅ Facade Pattern simplifies complex order processing!");}
main();#include <iostream>#include <string>#include <map>
// Step 1: Subsystem classes (unchanged)class InventoryService {public: // Inventory subsystem bool checkStock(const std::string& productId, int quantity) { std::cout << "Inventory: Checking stock for " << productId << "..." << std::endl; return true; }
void reserveItems(const std::string& productId, int quantity) { std::cout << "Inventory: Reserving " << quantity << " items of " << productId << std::endl; }};
class PaymentService {public: // Payment subsystem bool processPayment(double amount, const std::string& cardToken) { std::cout << "Payment: Processing payment of $" << amount << "..." << std::endl; return true; }
void refundPayment(const std::string& transactionId) { std::cout << "Payment: Refunding transaction " << transactionId << std::endl; }};
class ShippingService {public: // Shipping subsystem std::string createShipment(const std::string& orderId, const std::map<std::string, std::string>& address) { std::cout << "Shipping: Creating shipment for order " << orderId << std::endl; return "SHIP_" + orderId; }
void trackShipment(const std::string& shipmentId) { std::cout << "Shipping: Tracking shipment " << shipmentId << std::endl; }};
class NotificationService {public: // Notification subsystem void sendEmail(const std::string& email, const std::string& subject, const std::string& body) { std::cout << "Notification: Sending email to " << email << ": " << subject << std::endl; }};
// Step 2: Create the Facadeclass OrderProcessingFacade {private: InventoryService inventory; PaymentService payment; ShippingService shipping; NotificationService notification;
public: // Facade - simplifies order processing subsystem OrderProcessingFacade() = default;
bool processOrder(const std::map<std::string, std::string>& order) { // Simple interface - hides complex order processing std::cout << "🛒 Processing order " << order.at("order_id") << "...\n" << std::endl;
std::string productId = order.at("product_id"); int quantity = std::stoi(order.at("quantity"));
// Step 1: Check inventory if (!inventory.checkStock(productId, quantity)) { std::cout << "❌ Order failed: Out of stock\n" << std::endl; return false; }
// Step 2: Reserve items inventory.reserveItems(productId, quantity);
// Step 3: Process payment double amount = std::stod(order.at("amount")); if (!payment.processPayment(amount, order.at("card_token"))) { // Rollback inventory inventory.reserveItems(productId, -quantity); std::cout << "❌ Order failed: Payment failed\n" << std::endl; return false; }
// Step 4: Create shipment std::map<std::string, std::string> address; // Simplified std::string shipmentId = shipping.createShipment(order.at("order_id"), address);
// Step 5: Send notification notification.sendEmail( order.at("email"), "Order Confirmed", "Your order " + order.at("order_id") + " has been confirmed. Shipment ID: " + shipmentId );
std::cout << "✅ Order " << order.at("order_id") << " processed successfully!\n" << std::endl; return true; }
void cancelOrder(const std::string& orderId, const std::string& transactionId, const std::string& productId, int quantity) { // Simple interface - hides complex cancellation std::cout << "🛑 Cancelling order " << orderId << "...\n" << std::endl;
// Facade coordinates cancellation across subsystems payment.refundPayment(transactionId); inventory.reserveItems(productId, -quantity);
std::cout << "✅ Order " << orderId << " cancelled successfully!\n" << std::endl; }};
// Usage - Clean and simple!int main() { // Client only needs to know about Facade! OrderProcessingFacade orderFacade;
// Simple interface - no need to know about subsystems! std::map<std::string, std::string> order; order["order_id"] = "ORD123"; order["product_id"] = "PROD456"; order["quantity"] = "2"; order["amount"] = "99.99"; order["card_token"] = "tok_123456";
orderFacade.processOrder(order);
std::cout << "✅ Facade Pattern simplifies complex order processing!" << std::endl;
return 0;}using System;using System.Collections.Generic;
// Step 1: Subsystem classes (unchanged)class InventoryService{ /** Inventory subsystem */ public bool CheckStock(string productId, int quantity) { Console.WriteLine($"Inventory: Checking stock for {productId}..."); return true; }
public void ReserveItems(string productId, int quantity) { Console.WriteLine($"Inventory: Reserving {quantity} items of {productId}"); }}
class PaymentService{ /** Payment subsystem */ public bool ProcessPayment(double amount, string cardToken) { Console.WriteLine($"Payment: Processing payment of ${amount}..."); return true; }
public void RefundPayment(string transactionId) { Console.WriteLine($"Payment: Refunding transaction {transactionId}"); }}
class ShippingService{ /** Shipping subsystem */ public string CreateShipment(string orderId, Dictionary<string, string> address) { Console.WriteLine($"Shipping: Creating shipment for order {orderId}"); return $"SHIP_{orderId}"; }
public void TrackShipment(string shipmentId) { Console.WriteLine($"Shipping: Tracking shipment {shipmentId}"); }}
class NotificationService{ /** Notification subsystem */ public void SendEmail(string email, string subject, string body) { Console.WriteLine($"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 bool ProcessOrder(Dictionary<string, object> order) { // Simple interface - hides complex order processing Console.WriteLine($"🛒 Processing order {order["order_id"]}...\n");
// Step 1: Check inventory if (!inventory.CheckStock((string)order["product_id"], (int)order["quantity"])) { Console.WriteLine("❌ Order failed: Out of stock\n"); return false; }
// Step 2: Reserve items inventory.ReserveItems((string)order["product_id"], (int)order["quantity"]);
// Step 3: Process payment if (!payment.ProcessPayment((double)order["amount"], (string)order["card_token"])) { // Rollback inventory inventory.ReserveItems((string)order["product_id"], -(int)order["quantity"]); Console.WriteLine("❌ Order failed: Payment failed\n"); return false; }
// Step 4: Create shipment string shipmentId = shipping.CreateShipment((string)order["order_id"], (Dictionary<string, string>)order["address"]);
// Step 5: Send notification notification.SendEmail( (string)order["email"], "Order Confirmed", $"Your order {order["order_id"]} has been confirmed. Shipment ID: {shipmentId}" );
Console.WriteLine($"✅ Order {order["order_id"]} processed successfully!\n"); return true; }
public void CancelOrder(string orderId, string transactionId, string productId, int quantity) { // Simple interface - hides complex cancellation Console.WriteLine($"🛑 Cancelling order {orderId}...\n");
// Facade coordinates cancellation across subsystems payment.RefundPayment(transactionId); inventory.ReserveItems(productId, -quantity);
Console.WriteLine($"✅ Order {orderId} cancelled successfully!\n"); }}
// Usage - Clean and simple!class Program{ static void Main() { // Client only needs to know about Facade! OrderProcessingFacade orderFacade = new OrderProcessingFacade();
// Simple interface - no need to know about subsystems! Dictionary<string, object> order = new Dictionary<string, object> { ["order_id"] = "ORD123", ["product_id"] = "PROD456", ["quantity"] = 2, ["amount"] = 99.99, ["card_token"] = "tok_123456", ["address"] = new Dictionary<string, string> { ["street"] = "123 Main St", ["city"] = "New York" } };
orderFacade.ProcessOrder(order);
Console.WriteLine("✅ Facade Pattern simplifies complex order processing!"); }}package main
import "fmt"
// Subsystem structstype OrderInventory struct{}func (s *OrderInventory) CheckStock(pid string, qty int) bool { fmt.Printf("Inventory: Checking stock for %s...\n", pid); return true}func (s *OrderInventory) ReserveItems(pid string, qty int) { fmt.Printf("Inventory: Reserving %d items of %s\n", qty, pid)}
type OrderPayment struct{}func (s *OrderPayment) ProcessPayment(amount float64, token string) bool { fmt.Printf("Payment: Processing payment of $%.2f...\n", amount); return true}func (s *OrderPayment) RefundPayment(txId string) { fmt.Printf("Payment: Refunding transaction %s\n", txId)}
type OrderShipping struct{}func (s *OrderShipping) CreateShipment(orderId string) string { fmt.Printf("Shipping: Creating shipment for order %s\n", orderId) return "SHIP_" + orderId}
type OrderNotification struct{}func (s *OrderNotification) SendEmail(email, subject, body string) { fmt.Printf("Notification: Sending email to %s: %s\n", email, subject)}
// Facadetype OrderProcessingFacade struct { inventory *OrderInventory payment *OrderPayment shipping *OrderShipping notification *OrderNotification}
func NewOrderProcessingFacade() *OrderProcessingFacade { return &OrderProcessingFacade{ inventory: &OrderInventory{}, payment: &OrderPayment{}, shipping: &OrderShipping{}, notification: &OrderNotification{}, }}
func (f *OrderProcessingFacade) ProcessOrder(order map[string]interface{}) bool { fmt.Printf("Processing order %s...\n\n", order["order_id"])
if !f.inventory.CheckStock(order["product_id"].(string), order["quantity"].(int)) { fmt.Println("Order failed: Out of stock") return false } f.inventory.ReserveItems(order["product_id"].(string), order["quantity"].(int))
if !f.payment.ProcessPayment(order["amount"].(float64), order["card_token"].(string)) { f.inventory.ReserveItems(order["product_id"].(string), -order["quantity"].(int)) fmt.Println("Order failed: Payment failed") return false }
shipId := f.shipping.CreateShipment(order["order_id"].(string)) f.notification.SendEmail( order["email"].(string), "Order Confirmed", fmt.Sprintf("Order %s confirmed. Shipment: %s", order["order_id"], shipId), ) fmt.Printf("Order %s processed successfully!\n\n", order["order_id"]) return true}
func main() { facade := NewOrderProcessingFacade() order := map[string]interface{}{ "order_id": "ORD123", "product_id": "PROD456", "quantity": 2, "amount": 99.99, "card_token": "tok_123456", } facade.ProcessOrder(order) fmt.Println("✅ Facade Pattern simplifies complex order processing!")}// The Solution: Facade Patternstruct Codec;struct AudioMixer;struct FileWriter;struct VideoFacade { codec: Codec, mixer: AudioMixer, writer: FileWriter,}impl VideoFacade { fn convert(&self, file: &str) { println!("prepare {}", file); println!("decode"); println!("mix audio"); println!("write output"); }}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(); }}// Simple Facade - single facade classclass Facade { private subsystem1: Subsystem1; private subsystem2: Subsystem2;
constructor() { this.subsystem1 = new Subsystem1(); this.subsystem2 = new Subsystem2(); }
operation(): void { this.subsystem1.doSomething(); this.subsystem2.doSomething(); }}// Simple Facade - single facade classclass Facade {private: Subsystem1 subsystem1; Subsystem2 subsystem2;
public: Facade() = default;
void operation() { subsystem1.doSomething(); subsystem2.doSomething(); }};// 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(); }}package main
// Simple Facade - single facade structtype Subsystem1 struct{}func (s *Subsystem1) DoSomething() {}
type Subsystem2 struct{}func (s *Subsystem2) DoSomething() {}
type Facade struct { subsystem1 *Subsystem1 subsystem2 *Subsystem2}
func NewFacade() *Facade { return &Facade{subsystem1: &Subsystem1{}, subsystem2: &Subsystem2{}}}
func (f *Facade) Operation() { f.subsystem1.DoSomething() f.subsystem2.DoSomething()}// 1. Simple Facadestruct Codec;struct AudioMixer;struct FileWriter;struct VideoFacade { codec: Codec, mixer: AudioMixer, writer: FileWriter,}impl VideoFacade { fn convert(&self, file: &str) { println!("prepare {}", file); println!("decode"); println!("mix audio"); println!("write output"); }}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() { }}// Multiple Facades - different facades for different use casesclass OrderFacade { /** Facade for order operations */ processOrder(): void { }}
class ShippingFacade { /** Facade for shipping operations */ createShipment(): void { }}// Multiple Facades - different facades for different use casesclass OrderFacade {public: // Facade for order operations void processOrder() { }};
class ShippingFacade {public: // Facade for shipping operations void createShipment() { }};// 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() { }}package main
// Multiple Facades - different facades for different use casestype FacadeOrderFacade struct{}func (f *FacadeOrderFacade) ProcessOrder() {}func (f *FacadeOrderFacade) CancelOrder() {}
type FacadeShippingFacade struct{}func (f *FacadeShippingFacade) CreateShipment() {}func (f *FacadeShippingFacade) TrackShipment() {}// 2. Facade with Multiple Facadesstruct Codec;struct AudioMixer;struct FileWriter;struct VideoFacade { codec: Codec, mixer: AudioMixer, writer: FileWriter,}impl VideoFacade { fn convert(&self, file: &str) { println!("prepare {}", file); println!("decode"); println!("mix audio"); println!("write output"); }}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() { }}// ❌ Bad: Facade does too muchclass BadFacade { private subsystem1: Subsystem1; private subsystem2: Subsystem2; // ... 20 more subsystems
operation1(): void { } operation2(): void { } // ... 50 more operations
// Bad: Facade becomes too large and does everything!}
// ✅ Good: Focused facadeclass OrderFacade { /** Focused facade for orders */ processOrder(): void { } cancelOrder(): void { }}
class ShippingFacade { /** Focused facade for shipping */ createShipment(): void { } trackShipment(): void { }}// ❌ Bad: Facade does too muchclass BadFacade {private: Subsystem1 subsystem1; Subsystem2 subsystem2; // ... 20 more subsystems
public: void operation1() { } void operation2() { } // ... 50 more operations
// Bad: Facade becomes too large and does everything!};
// ✅ Good: Focused facadeclass OrderFacade {public: // Focused facade for orders void processOrder() { } void cancelOrder() { }};
class ShippingFacade {public: // Focused facade for shipping void createShipment() { } void trackShipment() { }};// ❌ Bad: Facade does too muchclass BadFacade{ 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() { }}package main
// ❌ Bad: Facade does too muchtype BadFacade struct { // ... many subsystems}func (f *BadFacade) Operation1() {}func (f *BadFacade) Operation2() {}// ... 50 more operations — Facade becomes a god object!
// ✅ Good: Focused facadestype GoodOrderFacade struct{}func (f *GoodOrderFacade) ProcessOrder() {}func (f *GoodOrderFacade) CancelOrder() {}
type GoodShippingFacade struct{}func (f *GoodShippingFacade) CreateShipment() {}func (f *GoodShippingFacade) TrackShipment() {}// Mistake 1: Facade Becoming a God Objectstruct VideoConverter;impl VideoConverter { fn convert(&self) { println!("codec"); println!("frames"); println!("audio"); println!("output"); }}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(); }}// ❌ Bad: Facade exposes subsystem detailsclass BadFacade { getInventoryService(): InventoryService { // Bad: Exposes subsystem return this.inventory; }
getPaymentService(): PaymentService { // Bad: Exposes subsystem return this.payment; }}
// ✅ Good: Facade hides subsystem detailsclass GoodFacade { processOrder(): void { // Good: High-level operation // Uses subsystems internally, doesn't expose them this.inventory.checkStock(); this.payment.processPayment(); }}// ❌ Bad: Facade exposes subsystem detailsclass BadFacade {public: InventoryService* getInventoryService() { // Bad: Exposes subsystem return &inventory; }
PaymentService* getPaymentService() { // Bad: Exposes subsystem return &payment; }};
// ✅ Good: Facade hides subsystem detailsclass GoodFacade {public: void processOrder() { // Good: High-level operation // Uses subsystems internally, doesn't expose them inventory.checkStock(); payment.processPayment(); }};// ❌ Bad: Facade exposes subsystem detailsclass BadFacade{ public InventoryService GetInventoryService() // Bad: Exposes subsystem { return inventory; }
public PaymentService GetPaymentService() // Bad: Exposes subsystem { return payment; }}
// ✅ Good: Facade hides subsystem detailsclass GoodFacade{ public void ProcessOrder() // Good: High-level operation { // Uses subsystems internally, doesn't expose them inventory.CheckStock(); payment.ProcessPayment(); }}package main
type InvService struct{}func (s *InvService) CheckStock() {}type PayService struct{}func (s *PayService) ProcessPayment() {}
// ❌ Bad: Facade exposes subsystem detailstype ExposingFacade struct { inventory *InvService payment *PayService}func (f *ExposingFacade) GetInventoryService() *InvService { return f.inventory } // Bad: Exposes subsystemfunc (f *ExposingFacade) GetPaymentService() *PayService { return f.payment } // Bad: Exposes subsystem
// ✅ Good: Facade hides subsystem detailstype HidingFacade struct { inventory *InvService payment *PayService}func (f *HidingFacade) ProcessOrder() { // Good: High-level operation f.inventory.CheckStock() f.payment.ProcessPayment()}// Mistake 2: Facade Exposing Subsystem Detailsstruct VideoConverter;impl VideoConverter { fn convert(&self) { println!("codec"); println!("frames"); println!("audio"); println!("output"); }}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; }}// ❌ Bad: No error handlingclass BadFacade { processOrder(): void { this.inventory.reserveItems(); // What if this fails? this.payment.processPayment(); // What if this fails? // No rollback! }}
// ✅ Good: Proper error handling with rollbackclass GoodFacade { processOrder(): boolean { try { this.inventory.reserveItems(); if (!this.payment.processPayment()) { this.inventory.reserveItems(-quantity); // Rollback return false; } } catch (e) { // Handle error and rollback this.inventory.reserveItems(-quantity); throw e; } return true; }}// ❌ Bad: No error handlingclass BadFacade {public: void processOrder() { inventory.reserveItems(); // What if this fails? payment.processPayment(); // What if this fails? // No rollback! }};
// ✅ Good: Proper error handling with rollbackclass GoodFacade {public: bool processOrder() { try { inventory.reserveItems(); if (!payment.processPayment()) { inventory.reserveItems(-quantity); // Rollback return false; } } catch (const std::exception& e) { // Handle error and rollback inventory.reserveItems(-quantity); throw; } return true; }};// ❌ Bad: No error handlingclass BadFacade{ public void ProcessOrder() { inventory.ReserveItems(); // What if this fails? payment.ProcessPayment(); // What if this fails? // No rollback! }}
// ✅ Good: Proper error handling with rollbackclass GoodFacade{ public bool ProcessOrder() { try { inventory.ReserveItems(); if (!payment.ProcessPayment()) { inventory.ReserveItems(-quantity); // Rollback return false; } } catch (Exception e) { // Handle error and rollback inventory.ReserveItems(-quantity); throw; } return true; }}package main
import "errors"
type ErrInventory struct{}func (s *ErrInventory) ReserveItems(qty int) error { return nil }type ErrPayment struct{}func (s *ErrPayment) ProcessPayment() bool { return true }
// ❌ Bad: No error handlingtype BadErrFacade struct { inventory *ErrInventory payment *ErrPayment}func (f *BadErrFacade) ProcessOrder() { f.inventory.ReserveItems(1) // What if this fails? f.payment.ProcessPayment() // What if this fails? // No rollback!}
// ✅ Good: Proper error handling with rollbacktype GoodErrFacade struct { inventory *ErrInventory payment *ErrPayment}func (f *GoodErrFacade) ProcessOrder() error { if err := f.inventory.ReserveItems(1); err != nil { return err } if !f.payment.ProcessPayment() { f.inventory.ReserveItems(-1) // Rollback return errors.New("payment failed") } return nil}// Mistake 3: Not Handling Errors Properlystruct VideoConverter;impl VideoConverter { fn convert(&self) { println!("codec"); println!("frames"); println!("audio"); println!("output"); }}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()class Facade { private Subsystem1 subsystem1 = new Subsystem1(); private Subsystem2 subsystem2 = new Subsystem2();
void simpleOperation() { subsystem1.operation(); subsystem2.operation(); }}class Facade { private subsystem1 = new Subsystem1(); private subsystem2 = new Subsystem2();
simpleOperation(): void { this.subsystem1.operation(); this.subsystem2.operation(); }}class Facade { Subsystem1 subsystem1; Subsystem2 subsystem2;public: void simpleOperation() { subsystem1.operation(); subsystem2.operation(); }};class Facade { private Subsystem1 subsystem1 = new(); private Subsystem2 subsystem2 = new();
public void SimpleOperation() { subsystem1.Operation(); subsystem2.Operation(); }}type FacSub1 struct{}func (s *FacSub1) Operation() {}type FacSub2 struct{}func (s *FacSub2) Operation() {}
type SimpleFacade struct { sub1 *FacSub1 sub2 *FacSub2}func NewSimpleFacade() *SimpleFacade { return &SimpleFacade{sub1: &FacSub1{}, sub2: &FacSub2{}} }func (f *SimpleFacade) SimpleOperation() { f.sub1.Operation() f.sub2.Operation()}// Simple Examplestruct Codec;struct AudioMixer;struct FileWriter;struct VideoFacade { codec: Codec, mixer: AudioMixer, writer: FileWriter,}impl VideoFacade { fn convert(&self, file: &str) { println!("prepare {}", file); println!("decode"); println!("mix audio"); println!("write output"); }}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()class Facade { private Subsystem1 subsystem1 = new Subsystem1(); private Subsystem2 subsystem2 = new Subsystem2();
void simpleOperation() { // Coordinate subsystems subsystem1.operation(); subsystem2.operation(); }}class Facade { private subsystem1 = new Subsystem1(); private subsystem2 = new Subsystem2();
simpleOperation(): void { // Coordinate subsystems this.subsystem1.operation(); this.subsystem2.operation(); }}class Facade { Subsystem1 subsystem1; Subsystem2 subsystem2;public: void simpleOperation() { // Coordinate subsystems subsystem1.operation(); subsystem2.operation(); }};class Facade { private Subsystem1 subsystem1 = new(); private Subsystem2 subsystem2 = new();
public void SimpleOperation() { // Coordinate subsystems subsystem1.Operation(); subsystem2.Operation(); }}type RevSub1 struct{}func (s *RevSub1) Operation() {}type RevSub2 struct{}func (s *RevSub2) Operation() {}
type RevFacade struct { sub1 *RevSub1 sub2 *RevSub2}func NewRevFacade() *RevFacade { return &RevFacade{sub1: &RevSub1{}, sub2: &RevSub2{}} }func (f *RevFacade) SimpleOperation() { // Coordinate subsystems f.sub1.Operation() f.sub2.Operation()}// Common Pattern Structurestruct Codec;struct AudioMixer;struct FileWriter;struct VideoFacade { codec: Codec, mixer: AudioMixer, writer: FileWriter,}impl VideoFacade { fn convert(&self, file: &str) { println!("prepare {}", file); println!("decode"); println!("mix audio"); println!("write output"); }}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.”
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! 🎭