The Facade pattern provides a simplified interface to a larger, more complex system by compositing subsystems.

Components

SOLID Principle

✅ Single Responsibility Principle (SRP)

✅ Open/Closed Principle (OCP)

✅ Liskov Substitution Principle (LSP) / Interface Segregation Principle (ISP)

✅ Dependency Inversion Principle (DIP)

OOP Langs Implementation

Inheritance (Less Common)

Wrapping behaviour is not recommended via inheritance, even though it doesn’t require wrapping multiple subsystems or wrapping at runtime, but introduces unnecessary coupling.

Polymorphism Decision

Composition

// Subsystems
class Lights {
    turnOn() { console.log("Lights are on."); }
    turnOff() { console.log("Lights are off."); }
}
class Music {
    play() { console.log("Playing music."); }
    stop() { console.log("Music stopped."); }
}

// Facade
class HomeAutomation {
    private lights: Lights;
    private music: Music;

    constructor() {
		    // The subsystems can either be encapsulated or use DI injection.
	      //  - Encapsulation: for fixed type cohesion and simplicity
  	    //  - DI: for testability and runtime flexibility
        this.lights = new Lights();
        this.music = new Music();
    }

    public arriveHome() {
        this.lights.turnOn()
        this.music.play();
    }

    public leaveHome() {
        this.lights.turnOff();
        this.music.stop();
    }
}

// Client
const home = new HomeAutomation();
home.arriveHome();

Go Implementation

Explicit Composition

To hide the subsystems’ API from the client and prevent names conflict on multiple subsystems, so it use Explicit Embedding.

Struct Embedding

// Subsystems
type Lights struct{}
func (l *Lights) TurnOn() { fmt.Println("Lights are on.") }
func (l *Lights) TurnOff() { fmt.Println("Lights are off.") }
type Music struct{}
func (m *Music) Play() { fmt.Println("Playing music.") }
func (m *Music) Stop() { fmt.Println("Music stopped.") }

// Facade
type HomeAutomation struct {
	lights *Lights
	music  *Music
}

func NewHomeAutomation() *HomeAutomation {
  // The subsystems can either be encapsulated or use DI injection.
  //  - Encapsulation: for fixed type cohesion and simplicity
  //  - DI: for testability and runtime flexibility
	return &HomeAutomation{
		lights: &Lights{},
		music:  &Music{},
	}
}

func (h *HomeAutomation) ArriveHome() {
	h.lights.TurnOn()
	h.music.Play()
}

func (h *HomeAutomation) LeaveHome() {
	h.lights.TurnOff()
	h.music.Stop()
}

// Client
func main() {
	home := NewHomeAutomation()
	home.ArriveHome()
}