Lecture 14: Facade Pattern - Concealing Complexity

๐Ÿ›๏ธ Facade Pattern

Concealing Complexity with a Facade

Game Programming - CSCI 3213

Spring 2026 - Lecture 14

Oklahoma City University

๐Ÿ“š Learning Objectives

  • Understand the Facade design pattern
  • Learn to simplify complex subsystem interfaces
  • Design and implement a bike engine system
  • Recognize when to use Facade vs Adapter patterns
  • Avoid common pitfalls (hiding messy code, too many facades)
  • Create maintainable, simplified interfaces for complex systems

๐ŸŒ Real-World Analogy

Starting Your Car

Think about what happens when you turn the ignition key:

Behind the Scenes (Complex):

  • Battery sends electrical current to starter motor
  • Starter motor cranks the engine
  • Fuel pump activates, sending gas to cylinders
  • Spark plugs ignite fuel-air mixture
  • Pistons start moving, crankshaft rotates
  • Cooling system activates to prevent overheating
  • Oil pump circulates lubricant

What You Do (Simple):

Turn the key. ๐Ÿ”‘

The Pattern: The ignition system is a facade that hides complex engine subsystems behind a simple interface

๐Ÿ” Understanding the Facade Pattern

Core Concept

Provide a unified, simplified interface to a set of interfaces in a subsystem, making the subsystem easier to use.

Key Characteristics:

  • Single class acts as simplified interface
  • Wraps complex subsystem interactions
  • Client doesn't know about subsystem details
  • Reduces coupling between client and subsystem
  • Doesn't prevent direct subsystem access (optional)

The Problem It Solves

Your game has multiple complex, interacting systems. Clients need to use these systems but shouldn't need to understand their intricate interactions.

Pattern Type: Structural - concerned with composing classes into larger structures

โš–๏ธ Facade vs Adapter Pattern

Aspect Facade Pattern Adapter Pattern
Intent Simplify complex interface Convert incompatible interface
Interface Creates NEW simplified interface Adapts EXISTING interface to match target
Number of Classes Wraps multiple subsystems Usually adapts one class
Goal Ease of use Compatibility
Key Difference: Facade establishes a new interface; Adapter adapts an old interface

๐Ÿ—๏ธ Facade Pattern Structure

Key Participants:

  • Facade: Simplified interface to subsystems
    • In our case: BikeEngine
  • Subsystems: Complex components that do the work
    • FuelPump
    • CoolingSystem
    • TurboCharger
  • Client: Uses the Facade
    • ClientFacade - doesn't know about subsystems

Flow:

Client โ†’ Facade.TurnOn() โ†’ FuelPump.Start()
                         โ†’ CoolingSystem.Start()
                         โ†’ Internal orchestration
                    

โœ… Benefits of Facade Pattern

  • Simplified Interface:
    • Hides complexity from client code
    • Easier to understand and use
    • Reduces cognitive load
  • Loose Coupling:
    • Client depends on facade, not subsystems
    • Subsystems can change without affecting client
    • Easier to swap implementations
  • Easy Refactoring:
    • Modify subsystems behind the facade
    • Interface remains stable for clients
    • Isolates changes to one layer
  • Layered Architecture:
    • Clear separation of concerns
    • Promotes organized code structure

โš ๏ธ Potential Drawbacks

  • Hiding the Mess:
    • Easy to use facade to mask poorly designed code
    • Temporary fix becomes permanent technical debt
    • Defeats pattern's core benefits
  • Too Many Facades (God Object):
    • Unity developers love Singleton + Facade = Manager classes
    • Leads to tightly coupled "manager hell"
    • Difficult to debug, refactor, unit test
    • Example: GameManager, InputManager, UIManager, SoundManager...
  • Over-Simplification:
    • Facade might not expose all needed functionality
    • Forces workarounds or direct subsystem access
Warning: Use facades to simplify, not to hide bad design!

๐ŸŽฏ When to Use Facade Pattern

Use Facade When:

  • โœ… Subsystem is complex and has many dependencies
  • โœ… Want to provide simple interface for common tasks
  • โœ… Need to decouple client from subsystem implementation
  • โœ… Want to layer your application architecture
  • โœ… Subsystem has poor or confusing API

Don't Use Facade When:

  • โŒ Subsystem is already simple (adds unnecessary layer)
  • โŒ Trying to hide messy code instead of refactoring
  • โŒ All clients need full subsystem functionality

Common Game Use Cases:

  • Engine systems (physics, audio, rendering)
  • Input handling (keyboard, gamepad, touch unified)
  • Save/load systems with multiple backends
  • Network communication abstraction

๐Ÿ๏ธ Use Case: High-Speed Bike Engine

Design Goal:

Simulate a motorcycle engine with realistic component interactions, but expose a simple interface for gameplay.

Engine Components (Subsystems):

  • Fuel Pump:
    • Manages fuel consumption
    • Tracks remaining fuel
    • Stops engine when fuel depleted
  • Cooling System:
    • Prevents engine overheating
    • Shuts down during turbo (power relay)
    • Stops engine if temperature exceeds max
  • Turbo Charger:
    • Temporarily increases top speed
    • Disables cooling system when active
    • Limited duration
Risk vs Reward: Turbo makes you faster but risks overheating!

๐Ÿ’ป FuelPump Subsystem (1/2)

using UnityEngine;
using System.Collections;

namespace Chapter.Facade
{
    public class FuelPump : MonoBehaviour
    {
        public BikeEngine engine;
        public IEnumerator burnFuel;

        void Start()
        {
            burnFuel = BurnFuel();
        }

Key Points:

  • References the engine facade
  • Stores coroutine reference for stopping
  • Initialized on Start()

๐Ÿ’ป FuelPump Subsystem (2/2)

        IEnumerator BurnFuel()
        {
            while (true)
            {
                yield return new WaitForSeconds(1);
                engine.fuelAmount -= engine.burnRate;

                if (engine.fuelAmount <= 0.0f)
                {
                    engine.TurnOff();
                    yield return 0;
                }
            }
        }

        void OnGUI()
        {
            GUI.color = Color.green;
            GUI.Label(new Rect(100, 40, 500, 20),
                "Fuel: " + engine.fuelAmount);
        }
    }
}
Behavior: Burns fuel every second, stops engine when depleted

๐Ÿ’ป CoolingSystem Subsystem (1/2)

using UnityEngine;
using System.Collections;

namespace Chapter.Facade
{
    public class CoolingSystem : MonoBehaviour
    {
        public BikeEngine engine;
        public IEnumerator coolEngine;
        private bool _isPaused;

        void Start()
        {
            coolEngine = CoolEngine();
        }

        public void PauseCooling()
        {
            _isPaused = !_isPaused;
        }

        public void ResetTemperature()
        {
            engine.currentTemp = 0.0f;
        }

๐Ÿ’ป CoolingSystem Subsystem (2/2)

        IEnumerator CoolEngine()
        {
            while (true)
            {
                yield return new WaitForSeconds(1);

                if (!_isPaused)
                {
                    // Cool toward ideal temperature
                    if (engine.currentTemp > engine.minTemp)
                        engine.currentTemp -= engine.tempRate;
                    if (engine.currentTemp < engine.minTemp)
                        engine.currentTemp += engine.tempRate;
                }
                else
                {
                    // Paused = heating up!
                    engine.currentTemp += engine.tempRate;
                }

                // Overheat protection
                if (engine.currentTemp > engine.maxTemp)
                    engine.TurnOff();
            }
        }

๐Ÿ’ป TurboCharger Subsystem

using UnityEngine;
using System.Collections;

namespace Chapter.Facade
{
    public class TurboCharger : MonoBehaviour
    {
        public BikeEngine engine;
        private bool _isTurboOn;
        private CoolingSystem _coolingSystem;

        public void ToggleTurbo(CoolingSystem coolingSystem)
        {
            _coolingSystem = coolingSystem;

            if (!_isTurboOn)
                StartCoroutine(TurboCharge());
        }

        IEnumerator TurboCharge()
        {
            _isTurboOn = true;
            _coolingSystem.PauseCooling(); // DISABLE COOLING!

            yield return new WaitForSeconds(engine.turboDuration);

            _isTurboOn = false;
            _coolingSystem.PauseCooling(); // RE-ENABLE COOLING
        }
    }
}

๐Ÿ’ป BikeEngine Facade (1/4)

using UnityEngine;

namespace Chapter.Facade
{
    public class BikeEngine : MonoBehaviour
    {
        // Engine parameters (exposed to subsystems)
        public float burnRate = 1.0f;
        public float fuelAmount = 100.0f;
        public float tempRate = 5.0f;
        public float minTemp = 50.0f;
        public float maxTemp = 65.0f;
        public float currentTemp;
        public float turboDuration = 2.0f;

        private bool _isEngineOn;
        private FuelPump _fuelPump;
        private TurboCharger _turboCharger;
        private CoolingSystem _coolingSystem;
Design: Facade owns and configures all subsystems

๐Ÿ’ป BikeEngine Facade (2/4)

        void Awake()
        {
            // Create subsystem components
            _fuelPump = gameObject.AddComponent<FuelPump>();
            _turboCharger = gameObject.AddComponent<TurboCharger>();
            _coolingSystem = gameObject.AddComponent<CoolingSystem>();
        }

        void Start()
        {
            // Wire up subsystem references
            _fuelPump.engine = this;
            _turboCharger.engine = this;
            _coolingSystem.engine = this;
        }

Initialization:

  • Awake(): Create subsystem components
  • Start(): Configure subsystem dependencies
  • All subsystems reference the facade

๐Ÿ’ป BikeEngine Facade (3/4) - The Interface!

        // SIMPLIFIED PUBLIC INTERFACE
        public void TurnOn()
        {
            _isEngineOn = true;
            StartCoroutine(_fuelPump.burnFuel);
            StartCoroutine(_coolingSystem.coolEngine);
        }

        public void TurnOff()
        {
            _isEngineOn = false;
            _coolingSystem.ResetTemperature();
            StopCoroutine(_fuelPump.burnFuel);
            StopCoroutine(_coolingSystem.coolEngine);
        }

        public void ToggleTurbo()
        {
            if (_isEngineOn)
                _turboCharger.ToggleTurbo(_coolingSystem);
        }
Key: Three simple methods hide all subsystem complexity!

๐Ÿ’ป BikeEngine Facade (4/4)

        void OnGUI()
        {
            GUI.color = Color.green;
            GUI.Label(
                new Rect(100, 0, 500, 20),
                "Engine Running: " + _isEngineOn);
        }
    }
}

What the Facade Hides:

  • Component initialization and configuration
  • Coroutine management
  • Inter-subsystem communication (turbo โ†” cooling)
  • Parameter passing between systems
  • Startup/shutdown sequences

๐Ÿ’ป ClientFacade - Simple Usage

using UnityEngine;

namespace Chapter.Facade
{
    public class ClientFacade : MonoBehaviour
    {
        private BikeEngine _bikeEngine;

        void Start()
        {
            _bikeEngine = gameObject.AddComponent<BikeEngine>();
        }

        void OnGUI()
        {
            if (GUILayout.Button("Turn On"))
                _bikeEngine.TurnOn();

            if (GUILayout.Button("Turn Off"))
                _bikeEngine.TurnOff();

            if (GUILayout.Button("Toggle Turbo"))
                _bikeEngine.ToggleTurbo();
        }
    }
}
Beauty: Client has NO knowledge of FuelPump, CoolingSystem, or TurboCharger!

๐ŸŽฏ The Power of Abstraction

Without Facade:

// Client needs to know EVERYTHING
var fuelPump = gameObject.AddComponent<FuelPump>();
var coolingSystem = gameObject.AddComponent<CoolingSystem>();
var turboCharger = gameObject.AddComponent<TurboCharger>();

fuelPump.engine = this;
coolingSystem.engine = this;
turboCharger.engine = this;

StartCoroutine(fuelPump.burnFuel);
StartCoroutine(coolingSystem.coolEngine);
// ... and on and on ...

With Facade:

// Client just uses the engine
_bikeEngine.TurnOn();
Result: ~20 lines reduced to 1 line!

๐Ÿงช Testing the Implementation

Steps:

  1. Create new Unity scene
  2. Create empty GameObject named "BikeEngineTest"
  3. Attach ClientFacade script
  4. Press Play
  5. Click "Turn On" button
  6. Watch fuel decrease and temperature regulate
  7. Click "Toggle Turbo" - see cooling pause
  8. Wait for fuel to run out or engine to overheat
Expected Behavior:
  • Fuel decreases over time
  • Temperature regulates around minTemp
  • Turbo causes temperature to spike
  • Engine stops when fuel = 0 or temp > maxTemp

๐Ÿ”ง Adding New Components

Example: Add Nitro Injector

// 1. Create new subsystem
public class NitroInjector : MonoBehaviour
{
    public BikeEngine engine;

    public void InjectNitro()
    {
        // Massive speed boost!
        // Huge fuel consumption!
        // Temperature spike!
    }
}

// 2. Update facade
public class BikeEngine : MonoBehaviour
{
    private NitroInjector _nitroInjector;

    void Awake()
    {
        // ... existing code ...
        _nitroInjector = gameObject.AddComponent<NitroInjector>();
    }

    public void ActivateNitro()
    {
        if (_isEngineOn)
            _nitroInjector.InjectNitro();
    }
}
Key: Client code doesn't change - just exposes new method!

๐ŸŽฎ Connecting to Bike Movement

public class BikeController : MonoBehaviour
{
    private BikeEngine _engine;
    private Rigidbody _rb;

    public float normalSpeed = 10f;
    public float turboMultiplier = 2f;

    void Start()
    {
        _engine = GetComponent<BikeEngine>();
        _rb = GetComponent<Rigidbody>();
        _engine.TurnOn();
    }

    void Update()
    {
        // Can't move if engine is off
        if (!_engine.IsEngineOn)
        {
            _rb.velocity = Vector3.zero;
            return;
        }

        // Normal movement
        float speed = normalSpeed;

        // Turbo boost!
        if (Input.GetKeyDown(KeyCode.Space))
            _engine.ToggleTurbo();

        if (_engine.IsTurboActive)
            speed *= turboMultiplier;

        // Move bike...
        _rb.velocity = transform.forward * speed;
    }
}

๐Ÿ“Š Engine Status UI

using UnityEngine;
using UnityEngine.UI;

public class EngineUI : MonoBehaviour
{
    public BikeEngine engine;

    public Slider fuelGauge;
    public Slider tempGauge;
    public Text statusText;
    public Image turboIndicator;

    void Update()
    {
        // Update fuel gauge
        fuelGauge.value = engine.fuelAmount / 100f;

        // Update temperature gauge
        tempGauge.value = (engine.currentTemp - engine.minTemp) /
                          (engine.maxTemp - engine.minTemp);

        // Color temperature gauge based on danger
        if (tempGauge.value > 0.8f)
            tempGauge.fillRect.GetComponent<Image>().color = Color.red;
        else if (tempGauge.value > 0.5f)
            tempGauge.fillRect.GetComponent<Image>().color = Color.yellow;
        else
            tempGauge.fillRect.GetComponent<Image>().color = Color.green;

        // Update status
        statusText.text = engine.IsEngineOn ? "ENGINE ON" : "ENGINE OFF";

        // Turbo indicator
        turboIndicator.enabled = engine.IsTurboActive;
    }
}

โš™๏ธ Enhanced Engine System

Additional Subsystems to Consider:

  • Transmission/Gearbox:
    • Calculate RPM (revolutions per minute)
    • Manual or automatic shifting
    • Gear ratios affect speed and acceleration
  • Damage System:
    • Engine wear over time
    • Crash damage affects performance
    • Repair mechanics
  • Audio System:
    • Engine sound pitch based on RPM
    • Turbo whoosh sound effect
    • Backfire on overheat
  • Particle Effects:
    • Exhaust smoke
    • Turbo flames
    • Overheat steam
Facade handles them all: Client still just calls TurnOn(), TurnOff(), ToggleTurbo()

๐Ÿ”„ Alternative Patterns to Consider

Before Using Facade, Consider:

  • Abstract Factory Pattern:
    • Use if you only need to hide object creation
    • Facade does more than just creation
  • Adapter Pattern:
    • Use if bridging incompatible interfaces
    • Facade creates new simplified interface
  • Mediator Pattern:
    • Use if subsystems need to communicate
    • Facade is one-way: client โ†’ subsystems
    • Mediator is multi-way: subsystems โ†” mediator โ†” subsystems
  • Service Locator Pattern:
    • Use for global service access
    • Facade is instance-specific

๐Ÿšซ Common Mistakes to Avoid

  1. Using Facade to Hide Bad Code:
    // BAD - Facade hiding spaghetti code
    public void DoEverything()
    {
        // 500 lines of messy logic
    }
    
    // GOOD - Facade coordinating clean subsystems
    public void StartGame()
    {
        _levelLoader.Load();
        _playerSpawner.Spawn();
        _uiManager.ShowHUD();
    }
  2. Too Many Manager Classes:
    • โŒ GameManager, LevelManager, PlayerManager, EnemyManager...
    • โœ… Use facades sparingly, only where needed
  3. Leaky Abstraction:
    // BAD - Exposing subsystem details
    public FuelPump GetFuelPump() { return _fuelPump; }
    
    // GOOD - Exposing only needed info
    public float GetFuelPercent() { return fuelAmount / maxFuel; }
  4. Overly Complex Facade:
    • If facade has 50+ methods, reconsider design
    • Might need multiple smaller facades

๐Ÿงช Unit Testing Strategies

using NUnit.Framework;
using UnityEngine;

[Test]
public void BikeEngine_TurnOn_StartsFuelBurning()
{
    // Arrange
    var go = new GameObject();
    var engine = go.AddComponent<BikeEngine>();
    float initialFuel = engine.fuelAmount;

    // Act
    engine.TurnOn();
    yield return new WaitForSeconds(2f);

    // Assert
    Assert.Less(engine.fuelAmount, initialFuel);
}

[Test]
public void BikeEngine_ToggleTurbo_IncreasesTemperature()
{
    // Arrange
    var go = new GameObject();
    var engine = go.AddComponent<BikeEngine>();
    engine.TurnOn();
    float initialTemp = engine.currentTemp;

    // Act
    engine.ToggleTurbo();
    yield return new WaitForSeconds(1f);

    // Assert
    Assert.Greater(engine.currentTemp, initialTemp);
}

[Test]
public void BikeEngine_OutOfFuel_StopsEngine()
{
    // Arrange
    var go = new GameObject();
    var engine = go.AddComponent<BikeEngine>();
    engine.fuelAmount = 1f;
    engine.TurnOn();

    // Act
    yield return new WaitForSeconds(2f);

    // Assert
    Assert.IsFalse(engine.IsEngineOn);
}

๐Ÿ› Debugging Tips

Add Logging to Facade:

public void TurnOn()
{
    Debug.Log("[BikeEngine] TurnOn() called");
    _isEngineOn = true;

    Debug.Log("[BikeEngine] Starting FuelPump coroutine");
    StartCoroutine(_fuelPump.burnFuel);

    Debug.Log("[BikeEngine] Starting CoolingSystem coroutine");
    StartCoroutine(_coolingSystem.coolEngine);

    Debug.Log("[BikeEngine] Engine is now running");
}

Visual Debugging:

  • Gizmos to show component states
  • Color-coded status indicators
  • Debug UI overlay
void OnDrawGizmos()
{
    if (_isEngineOn)
        Gizmos.color = Color.green;
    else
        Gizmos.color = Color.red;

    Gizmos.DrawWireSphere(transform.position, 1f);
}

โšก Performance Tips

Optimization Strategies:

  • Lazy Initialization:
    // Only create subsystems when needed
    private FuelPump _fuelPump;
    public FuelPump FuelPump
    {
        get
        {
            if (_fuelPump == null)
                _fuelPump = gameObject.AddComponent<FuelPump>();
            return _fuelPump;
        }
    }
  • Update Rate Control:
    // Don't update every frame
    private float _updateInterval = 0.1f;
    private float _nextUpdate = 0f;
    
    void Update()
    {
        if (Time.time >= _nextUpdate)
        {
            UpdateEngine();
            _nextUpdate = Time.time + _updateInterval;
        }
    }
  • Object Pooling for Effects:
    • Pool exhaust particles
    • Pool audio sources

๐ŸŽฏ Facade + Singleton (Use Carefully!)

Common Unity Pattern:

public class GameManager : MonoBehaviour
{
    public static GameManager Instance { get; private set; }

    private AudioSystem _audio;
    private InputSystem _input;
    private SaveSystem _save;

    void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }

        Instance = this;
        DontDestroyOnLoad(gameObject);

        // Initialize subsystems
        _audio = new AudioSystem();
        _input = new InputSystem();
        _save = new SaveSystem();
    }

    // Simplified facade methods
    public void PlaySound(string name) => _audio.Play(name);
    public float GetAxis(string name) => _input.GetAxis(name);
    public void SaveGame() => _save.Save();
}
Warning: Don't abuse this pattern - creates global dependencies!

๐ŸŽฎ Real-World Game Applications

Facade Pattern in Production Games:

  • Input System Facade:
    • Unify keyboard, gamepad, touch, VR inputs
    • Client calls GetMoveDirection(), not Input.GetAxis()
    • Easy to rebind controls
  • Audio System Facade:
    • Wrap Unity's AudioSource, AudioMixer, etc.
    • Simple PlaySFX(), PlayMusic(), SetVolume()
    • Handle pooling, fade in/out, priority
  • Save System Facade:
    • Hide PlayerPrefs, file I/O, cloud saves
    • Client calls Save(key, value), Load(key)
    • Automatic serialization, encryption
  • UI Manager Facade:
    • Manage canvas hierarchy, panels, popups
    • ShowMenu(), HideMenu(), ShowDialog()
    • Handle transitions, stacking
  • Network Facade:
    • Wrap Netcode, Mirror, Photon
    • SendMessage(), Connect(), Disconnect()
    • Easy to swap networking solutions

๐Ÿ—๏ธ Facade in Layered Architecture

Organizing Your Game:

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚    Presentation Layer (UI)          โ”‚
โ”‚    - Buttons, Menus, HUD             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
               โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚    Facade Layer (Managers)          โ”‚ โ† Simplified Interface
โ”‚    - GameManager                     โ”‚
โ”‚    - LevelManager                    โ”‚
โ”‚    - PlayerManager                   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
               โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚    Business Logic Layer              โ”‚
โ”‚    - Game rules, systems             โ”‚
โ”‚    - Physics, AI, progression        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
               โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚    Data Layer                        โ”‚
โ”‚    - Save files, databases           โ”‚
โ”‚    - Assets, configuration           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                    
Benefit: Each layer only knows about the layer below through facades

๐Ÿ”ง Using Facades for Refactoring

Gradual Improvement Strategy:

Step 1: Create Facade for Messy Code

// Wrap existing messy system
public class LegacyInventoryFacade
{
    private OldMessyInventory _legacy = new OldMessyInventory();

    public void AddItem(string item)
    {
        // Simple interface hides complexity
        _legacy.DoComplicatedThingToAddItem(item, true, null, 0);
    }
}

Step 2: Replace Client Dependencies

// Change all client code to use facade
// Before: OldMessyInventory inventory;
// After:  LegacyInventoryFacade inventory;

Step 3: Refactor Behind Facade

// Replace implementation, keep interface
public class LegacyInventoryFacade
{
    private NewCleanInventory _new = new NewCleanInventory();

    public void AddItem(string item)
    {
        _new.Add(item); // Much better!
    }
}

Step 4: Remove "Legacy" Prefix

๐Ÿ“ Summary

What We Learned:

  • โœ… Facade pattern provides simplified interface to complex subsystems
  • โœ… Hides complexity, not functionality
  • โœ… Different from Adapter (simplify vs adapt)
  • โœ… Implemented realistic bike engine with 3 subsystems
  • โœ… Client code reduced from 20+ lines to 1 line
  • โœ… Easy to extend with new subsystems

Key Takeaways:

  • Facade creates NEW simplified interface
  • Subsystems remain accessible if needed
  • Don't use to hide bad code - use to organize good code
  • Beware of too many manager classes (Singleton + Facade)
  • Perfect for layered architecture
  • Excellent for refactoring legacy systems

๐Ÿ’ช Practice Exercise

Build a Character Combat System Facade

Requirements:

  1. Create subsystem classes:
    • HealthSystem: Track HP, handle damage, death
    • StaminaSystem: Track stamina, regeneration, depletion
    • WeaponSystem: Manage equipped weapon, durability
    • DefenseSystem: Armor, blocking, dodge
  2. Create CombatFacade with methods:
    • Attack() - costs stamina, uses weapon
    • Defend() - costs stamina, reduces damage
    • TakeDamage(float amount) - applies armor, affects health
    • Rest() - restore stamina faster
  3. Client should only interact with facade, not subsystems
  4. Add UI to display health, stamina, weapon durability
Bonus: Add special move that costs both health and stamina

๐Ÿ“š Additional Resources

Further Reading:

  • Design Patterns (GoF): Original Facade pattern definition
  • Game Development Patterns with Unity 2021:
    • Chapter 15: Concealing Complexity with a Facade
  • Head First Design Patterns: Facade chapter with home theater example
  • Refactoring.Guru:
    • https://refactoring.guru/design-patterns/facade

Unity Resources:

  • Unity Manual: Coroutines
  • Unity Scripting Reference: MonoBehaviour lifecycle
  • Unity Learn: Intermediate Scripting

Related Patterns:

  • Adapter Pattern (Lecture 13)
  • Mediator Pattern
  • Service Locator Pattern

๐Ÿš€ Looking Ahead

Course Progress:

  • โœ… Lecture 13: Adapter Pattern
  • โœ… Lecture 14: Facade Pattern (current)
  • โญ๏ธ Lecture 15: Final Project Workshop

Design Patterns Covered:

  • Creational: Singleton, Object Pool, Abstract Factory
  • Structural: Adapter, Facade, Decorator
  • Behavioral: Observer, Visitor, Strategy, Command
  • Optimization: Spatial Partition

Final Project Ideas Using Facade:

  • Racing game with complex vehicle systems
  • RPG with combat, inventory, quest systems
  • Strategy game with resource management
  • Simulation game with interconnected systems

โ“ Questions?

Common Questions:

  • Q: Should I always use facades for complex systems?
    • A: Only if the complexity is worth abstracting. Don't over-engineer simple systems.
  • Q: Can clients access subsystems directly?
    • A: Yes! Facade doesn't prevent it, just provides simpler alternative.
  • Q: Difference between Facade and God Object anti-pattern?
    • A: Facade delegates to subsystems. God Object does everything itself.
  • Q: Should facade be a Singleton?
    • A: Only if truly needed globally. Often better as instance or dependency injection.
  • Q: How many methods should a facade have?
    • A: As few as possible. If 20+, consider splitting into multiple facades.
  • Q: Can subsystems use the facade?
    • A: Generally no - creates circular dependencies. Use Mediator instead.

Thank you! ๐ŸŽฎ