Generative Design Pattern Series - Episode 1


Introduction

In this series of posts, we examine how established design patterns from the classical Gang of Four (GoF) patterns [1] combine to produce something greater than the sum of their parts. Where the classic Gang of Four (GoF) literature treats each pattern in isolation, real-world software architects regularly compose patterns into higher-order structures. Here these compositions are referred to as Generative Design Patterns (GDP), configurations where one pattern’s output becomes another pattern’s input, and the combination exhibits emergent structural properties neither pattern alone provides.

Today’s focus: the Strategy pattern (behavioural) composed with the Façade pattern (structural). The vehicle for exploration is a Tic-Tac-Toe game implemented in C++ with Qt6. In the next episode, we will extend this same architecture into an Entity-Component-System (ECS) integration, where the ECS world itself becomes the strategy context, but more on that at the end. Here is a screen shot of the Qt6 app.

The Strategy Pattern

The Strategy pattern [1] defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

More precisely, the pattern separates what a computation does (the algorithm) from who requests it (the client). This is a direct mechanical realisation of two SOLID principles simultaneously:

  • Single Responsibility Principle (SRP): each concrete strategy owns exactly one algorithm
  • Open/Closed Principle (OCP): new algorithms are added by writing new concrete classes, not by modifying the context or existing strategies

The pattern also functions as a structured dependency injection mechanism. The context does not construct its own algorithm. It receives one from outside, through the strategy interface. This makes the context testable in isolation and makes algorithms substitutable at runtime.

Strategy Pattern UML

Strategy Pattern UML

Participating Structure
ParticipantRole
StrategyInterface (or abstract class) declaring the algorithm contract
ConcreteStrategyImplements a specific variant of the algorithm
ContextConfigured with a ConcreteStrategy; delegates algorithm execution to it; may expose a data-access interface back to Strategy

The Context–Strategy relationship is key: Context holds a Strategy reference but does not depend on any concrete implementation. This is the Dependency Inversion Principle at the structural level.

The Façade Pattern

The Façade pattern [1] provides a simplified interface to a complex subsystem. It does not add new behaviour. It curates access. The subsystem’s full API remains available to code that needs it; the façade serves clients that only need the common, high-level operations.

In game programming this is especially valuable. A game loop involves state mutation, win detection, draw detection, turn management, and input validation. A UI client should not be orchestrating all of that directly. A façade collapses it into a single, stable call.

Façade Pattern UML

Façade Pattern UML

The Façade lives outside the subsystem. Clients talk only to the façade. The subsystem classes remain unchanged and remain available for direct use when needed. The façade is a convenience layer, not a wrapper that hides or removes functionality.

Generative Design Pattern: Strategy + Façade

GDP Composition UML

The Generative Composition

When Strategy and Façade are composed, a specific structural relationship emerges:

The ConcreteStrategy class IS the Façade.

Each concrete strategy exposes a minimal, unified interface (gamePlay(), reset()) that hides the internal algorithmic subsystem, state management, win-checking, draw-checking, turn-switching. The context is freed from knowing any of this. This is the generative property: the Strategy’s concreteness requirement generates a natural boundary at which the Façade must sit.

Without the façade aspect, the context would need to call getNextState(), then check isWinState(), then check isBoardFilled(), then toggle the turn. That sequencing logic would leak into TicTacToeWidget. Breaking SRP for the widget and coupling it to the algorithm’s internal protocol.

With the façade aspect built into the concrete strategy, the entire game-loop protocol is encapsulated. The widget issues one call:

m_strategy->gamePlay(index);

And the subsystem handles the rest.

TicTacToeGame C++ Implementation

Interface: IGameStrategy
// IGameStrategy.h
class IGameStrategy
{
public:
    using GameState  = std::array<char, 9>;
    using WinStates  = std::vector<std::array<int, 3>>;

    virtual ~IGameStrategy() = default;

    virtual const GameState&  gameState()     const = 0;
    virtual const WinStates&  winStates()     const = 0;
    virtual char              currentPlayer() const = 0;
    virtual char              winner()        const = 0;
    virtual void              gamePlay(int index)   = 0;
    virtual void              reset()               = 0;
};

IGameStrategy is the Strategy interface. Its two type aliases, GameState and WinStates, define the data contract the context uses to read back state for rendering. This implements the GoF provision that Context “may define an interface that lets Strategy access its data”. Here, Strategy provides the data interface so Context can consume it.

The interface is kept deliberately minimal. Anything the UI does not need to know stays private in the concrete class.

ConcreteStrategy + Façade: TicTacToeStrategy
// TicTacToeStrategy.h
class TicTacToeStrategy : public IGameStrategy
{
public:
    TicTacToeStrategy();

    const GameState&  gameState()     const override;
    const WinStates&  winStates()     const override;
    char              currentPlayer() const override;
    char              winner()        const override;
    void              gamePlay(int index)   override;  // <-- Façade entry point
    void              reset()               override;

private:
    GameState   m_gameState  = {'-','-','-','-','-','-','-','-','-'};
    WinStates   m_winStates  = {{0,1,2},{3,4,5},{6,7,8},
                                {0,3,6},{1,4,7},{2,5,8},
                                {0,4,8},{2,4,6}};
    bool        m_XTurnToPlay = true;
    QString     m_winnerText;

    void getNextState(int v);      // subsystem: state mutation + turn toggle
    bool isWinState()  const;      // subsystem: win detection
    bool isBoardFilled() const;    // subsystem: draw detection
};

gamePlay() is the façade method. Everything below the public line is the subsystem: three private methods that together implement the game-loop protocol. No client ever calls them directly.

// TicTacToeStrategy.cpp - the façade in action
void TicTacToeStrategy::gamePlay(int index)
{
    if (index < 0 || index >= static_cast<int>(m_gameState.size())) return;
    if (winner() != '\0') return;
    if (m_gameState[index] != '-') return;

    getNextState(index);   // orchestrates the subsystem
}

void TicTacToeStrategy::getNextState(int v)
{
    m_gameState[v] = currentPlayer();

    if (isWinState()) {
        m_winnerText = QString(QChar(currentPlayer())) + " wins";
        return;
    }
    if (isBoardFilled()) {
        m_winnerText = QStringLiteral("Draw");
        return;
    }
    m_XTurnToPlay = !m_XTurnToPlay;
}

getNextState() is the internal façade. It orchestrates isWinState() and isBoardFilled() so that gamePlay() itself stays clean. Two levels of encapsulation, both serving the same purpose: the right detail is hidden from the right caller.

Context: TicTacToeWidget
// tictactoewidget.h
class TicTacToeWidget : public QWidget
{
    Q_OBJECT
public:
    explicit TicTacToeWidget(QWidget *parent = nullptr);
    char currentPlayer() const;
    void initNewGame();

signals:
    void currentPlayerChanged(char);
    void gameOver(char);

private slots:
    void handleButtonClick(int index);

private:
    QVector<QPushButton*>           m_board;
    std::unique_ptr<IGameStrategy>  m_strategy;   // Strategy reference
    void updateBoard();
};

TicTacToeWidget is the Strategy Context. It holds the strategy through std::unique_ptr<IGameStrategy>, an owning pointer to the abstract interface. It knows nothing about TicTacToeStrategy’s internals. The only coupling is at construction:

TicTacToeWidget::TicTacToeWidget(QWidget *parent) : QWidget(parent)
{
    m_strategy.reset(new TicTacToeStrategy());
    // ...
}

This is the one point where the concrete type is named. Everything else goes through the interface. In a full dependency injection setup this construction would also be inverted, the strategy would be passed in, which Episode 2 will explore when the ECS world takes over as context.

The click handler shows the pattern operating cleanly:

void TicTacToeWidget::handleButtonClick(int index)
{
    if (!m_strategy || m_strategy->winner() != '\0') return;

    m_strategy->gamePlay(index);    // single façade call
    updateBoard();

    char w = m_strategy->winner();
    if (w != '\0') { emit gameOver(w); return; }

    emit currentPlayerChanged(currentPlayer());
}

One call to gamePlay(). No knowledge of state transitions, win arrays, or turn logic. The façade does its job.

Application-Level Structure: MainWindow
// mainwindow.cpp
void MainWindow::startNewGame()
{
    ui->player1Name->setText(tr("Alice"));
    ui->player2Name->setText(tr("Bob"));
    ui->gameBoard->initNewGame();   // delegates to widget, which delegates to strategy
}

MainWindow sits above TicTacToeWidget. It calls initNewGame(), itself another façade, and names the players. The separation of concerns extends upward: MainWindow owns layout and player metadata; TicTacToeWidget owns game interaction; TicTacToeStrategy owns game logic.

Full Implementation Class diagram

Full System UML

Summary: What Makes This a Generative Pattern?

A simple way to state the generative property:

Strategy aloneFaçade aloneStrategy + Façade (GDP)
Swappable algorithmsYesNoYes
Hidden subsystemNoYesYes
Data access contractPartialNoYes
Context isolationPartialNoComplete
OCP + SRP simultaneouslyPartialNoYes

The composition is generative because placing the façade at the concrete strategy boundary, which the Strategy pattern requires to be well-defined, means you get the subsystem hiding for free. You cannot implement the strategy interface without drawing that line. Drawing that line is building the façade. Neither pattern demands the other; together, they make each other stronger.

Key Design Principles Applied

SRP: TicTacToeStrategy owns algorithm; TicTacToeWidget owns UI interaction; MainWindow owns application structure. No class touches more than one concern.

OCP: To add Connect Four, Snake, or Chess: implement IGameStrategy, inject it into TicTacToeWidget. Zero changes to existing classes.

DIP: TicTacToeWidget depends on IGameStrategy (abstraction), not TicTacToeStrategy (concretion). The unique_ptr<IGameStrategy> member enforces this at the type level.

Law of Demeter: handleButtonClick() calls exactly m_strategy->gamePlay() and m_strategy->winner(). It does not reach into the strategy’s internal subsystem.

Coming in Episode 2: ECS as the Strategy Context

Episode 1 established the GDP: Strategy + Façade working as a unified structure, with TicTacToeWidget as a straightforward Qt widget context. Episode 2 replaces that context with something architecturally richer.

Entity-Component-System (ECS) is a data-oriented architecture widely used in game engines (Unity DOTS, Unreal Mass, EnTT). In ECS, game objects are plain integer entity IDs; all data lives in components; all behaviour lives in systems. There is no object hierarchy. Only composition..

The question Episode 2 answers: what happens when the ECS world itself becomes the Strategy context?

Instead of TicTacToeWidget holding unique_ptr<IGameStrategy>, an ECS GameSystem queries entities that carry a StrategyComponent. The StrategyComponent holds the IGameStrategy pointer. The system drives gamePlay() in response to input events stored in separate InputComponent entities. Board state writes back into BoardStateComponent arrays, which a separate rendering system reads independently.

This has profound consequences. The game strategy is no longer coupled to any rendering code, not even the thin coupling that updateBoard() represents. Adding a second simultaneous game means spawning a second entity with its own StrategyComponent. Replaying a game means replaying the input-event stream. The GDP does not break. It scales..

Episode 2 will show the full ECS–Strategy integration with UML, C++ component and system definitions, and a worked example of swapping strategies at runtime by mutating the StrategyComponent on a live entity.

Tags: design-patterns strategy-pattern facade-pattern generative-design cpp qt6 game-programming SOLID ECS software-architecture

Glossary

Abstract class. A class that cannot be instantiated directly. It defines a partial or empty contract that subclasses must complete. Used as a base when shared default behaviour exists alongside required overrides.

Algorithm. A self-contained sequence of steps that solves a specific problem. In the Strategy pattern, each concrete strategy encapsulates one algorithm.

Concrete class / ConcreteStrategy. A fully implemented class. In the Strategy pattern, a ConcreteStrategy is a specific algorithm that fulfils the Strategy interface contract.

Context. In the Strategy pattern, the object that holds a reference to a Strategy and delegates work to it. The context does not know which concrete strategy it is using, only that it satisfies the interface.

Dependency injection. Supplying an object’s collaborators from outside rather than constructing them internally. Enables swapping implementations without changing the dependent class.

Dependency Inversion Principle (DIP). One of the SOLID principles. High-level modules should depend on abstractions (interfaces), not on concrete implementations. Concrete classes depend on abstractions, not the reverse.

Encapsulation. Bundling data and the operations that act on it into a single unit, and hiding internal details from the outside. Callers interact through a defined interface, not raw internals.

Entity-Component-System (ECS). A data-oriented architecture common in game engines. Entities are plain IDs; data lives in components attached to entities; behaviour lives in systems that query and process components. Replaces object hierarchies with composition.

Façade. A structural design pattern that provides a single, simplified entry point into a complex subsystem. The subsystem’s full detail remains available; the façade serves callers that only need the common path.

Gang of Four (GoF). Refers to the four authors, Gamma, Helm, Johnson, Vlissides, of Design Patterns: Elements of Reusable Object-Oriented Software (1994), the foundational catalogue of 23 object-oriented design patterns.

Generative Design Pattern (GDP). A composition of two or more standard patterns where the structural requirements of one pattern naturally produce or reinforce the structure required by the other, yielding properties neither provides alone.

Interface. A pure contract: a list of method signatures with no implementation. Any class that implements the interface must provide all listed methods. Used to decouple callers from specific implementations.

Law of Demeter. A design guideline that says an object should only talk to its immediate collaborators. In practice: a method should call methods on objects it directly holds, not on objects returned by those objects (“don’t reach through”). This limits how deeply one class depends on the internal structure of another, making code easier to change without cascading breakage.

Open/Closed Principle (OCP). One of the SOLID principles. Software entities should be open for extension (new behaviour can be added) but closed for modification (existing code need not change to accommodate it).

Single Responsibility Principle (SRP). One of the SOLID principles. A class should have exactly one reason to change, meaning it should own one concern, not several unrelated ones.

SOLID. An acronym for five object-oriented design principles: Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, and Dependency Inversion. Collectively they guide towards maintainable, extensible code.

Strategy pattern. A behavioural design pattern that defines a family of algorithms, encapsulates each one behind a common interface, and makes them interchangeable at runtime without altering the client that uses them.

Subsystem. A group of related classes or functions that together implement a coherent piece of functionality. In the Façade pattern, the subsystem is the complex internals that the façade hides from callers.

UML (Unified Modeling Language). A standardised visual language for describing software structure and behaviour. Class diagrams (used throughout this post) show classes, their attributes and methods, and the relationships between them.

References

[1] Gamma, E., Helm, R., Johnson, R., Vlissides, J. (1994). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. Original GoF definitions of Strategy (p. 315) and Façade (p. 185).

[2] Martin, R. C. (2002). Agile Software Development, Principles, Patterns, and Practices. Prentice Hall. SOLID principles, SRP and DIP in depth.

[3] Nystrom, R. (2014). Game Programming Patterns. genever benning. Available free at gameprogrammingpatterns.com. Game-specific treatment of State, Command, and Component patterns; directly relevant to Episode 2.

[4] Martin, R. C. (2018). Clean Architecture: A Craftsman’s Guide to Software Structure and Design. Prentice Hall. Dependency rule and boundary theory underpinning the GDP composition.

[5] Vlissides, J. (1998). Pattern Hatching: Design Patterns Applied. Addison-Wesley. Pattern composition and when combined patterns exhibit emergent properties.

[6] PlantUML documentation: plantuml.com. All UML diagrams in this post can be rendered at planttext.com by pasting the @startuml blocks.