Skip to content

Behavioral_patterns

Behavioral design patterns define how objects communicate and assign responsibilities between each other. They help manage complex control flow and reduce coupling.

One-to-many dependency where subjects notify observers of state changes.

  • Event handling systems
  • Model-View-Controller (MVC)
  • Pub/Sub messaging
  • UI updates based on data changes
#include <iostream>
#include <vector>
#include <memory>
#include <string>
// Observer interface
class Observer {
public:
virtual ~Observer() = default;
virtual void update(const std::string& message) = 0;
};
// Concrete observers
class ConcreteObserverA : public Observer {
std::string name_;
public:
explicit ConcreteObserverA(const std::string& name) : name_(name) {}
void update(const std::string& message) override {
std::cout << "Observer " << name_ << " received: " << message << "\n";
}
};
// Subject (Observable)
class Subject {
std::vector<Observer*> observers_;
std::string state_;
public:
void attach(Observer* observer) {
observers_.push_back(observer);
}
void detach(Observer* observer) {
auto it = std::find(observers_.begin(), observers_.end(), observer);
if (it != observers_.end()) {
observers_.erase(it);
}
}
void notify() {
for (auto* obs : observers_) {
obs->update(state_);
}
}
void setState(const std::string& state) {
state_ = state;
notify();
}
std::string getState() const { return state_; }
};
// Usage
int main() {
Subject subject;
ConcreteObserverA obs1("A");
ConcreteObserverA obs2("B");
subject.attach(&obs1);
subject.attach(&obs2);
subject.setState("Hello"); // Both observers notified
subject.detach(&obs1);
subject.setState("World"); // Only obs2 notified
return 0;
}

Modern C++ Implementation (using std::function)

Section titled “Modern C++ Implementation (using std::function)”
#include <iostream>
#include <vector>
#include <functional>
class Event {
std::vector<std::function<void()>> listeners_;
public:
void subscribe(std::function<void()> callback) {
listeners_.push_back(std::move(callback));
}
void emit() {
for (auto& listener : listeners_) {
listener();
}
}
};

Defines a family of algorithms, encapsulates each one, and makes them interchangeable.

  • Sorting algorithms
  • Payment processing
  • Compression algorithms
  • Pathfinding algorithms
#include <iostream>
#include <vector>
#include <memory>
// Strategy interface
class SortStrategy {
public:
virtual ~SortStrategy() = default;
virtual void sort(std::vector<int>& data) = 0;
};
// Concrete strategies
class BubbleSort : public SortStrategy {
public:
void sort(std::vector<int>& data) override {
std::cout << "Sorting using bubble sort\n";
for (size_t i = 0; i < data.size(); i++) {
for (size_t j = 0; j < data.size() - i - 1; j++) {
if (data[j] > data[j + 1]) {
std::swap(data[j], data[j + 1]);
}
}
}
}
};
class QuickSort : public SortStrategy {
public:
void sort(std::vector<int>& data) override {
std::cout << "Sorting using quick sort\n";
quickSort(data, 0, data.size() - 1);
}
private:
void quickSort(std::vector<int>& data, int low, int high) {
if (low < high) {
int pi = partition(data, low, high);
quickSort(data, low, pi - 1);
quickSort(data, pi + 1, high);
}
}
int partition(std::vector<int>& data, int low, int high) {
int pivot = data[high];
int i = low - 1;
for (int j = low; j < high; j++) {
if (data[j] <= pivot) {
i++;
std::swap(data[i], data[j]);
}
}
std::swap(data[i + 1], data[high]);
return i + 1;
}
};
// Context
class Sorter {
std::unique_ptr<SortStrategy> strategy_;
public:
void setStrategy(std::unique_ptr<SortStrategy> strategy) {
strategy_ = std::move(strategy);
}
void sortData(std::vector<int>& data) {
if (strategy_) {
strategy_->sort(data);
}
}
};
// Usage
int main() {
std::vector<int> data = {5, 2, 8, 1, 9};
Sorter sorter;
sorter.setStrategy(std::make_unique<BubbleSort>());
sorter.sortData(data);
sorter.setStrategy(std::make_unique<QuickSort>());
sorter.sortData(data);
return 0;
}

Encapsulates a request as an object, allowing parameterization and queuing.

  • Undo/Redo functionality
  • Transaction management
  • Task scheduling
  • Macro recording
#include <iostream>
#include <stack>
#include <memory>
// Command interface
class Command {
public:
virtual ~Command() = default;
virtual void execute() = 0;
virtual void undo() = 0;
};
// Receiver
class TextEditor {
std::string content_;
public:
void addText(const std::string& text) {
content_ += text;
}
void removeText(size_t count) {
if (count <= content_.size()) {
content_.erase(content_.size() - count);
}
}
std::string getContent() const { return content_; }
};
// Concrete commands
class AddTextCommand : public Command {
TextEditor& editor_;
std::string text_;
public:
AddTextCommand(TextEditor& editor, const std::string& text)
: editor_(editor), text_(text) {}
void execute() override {
editor_.addText(text_);
}
void undo() override {
editor_.removeText(text_.size());
}
};
class RemoveTextCommand : public Command {
TextEditor& editor_;
size_t count_;
public:
RemoveTextCommand(TextEditor& editor, size_t count)
: editor_(editor), count_(count) {}
void execute() override {
editor_.removeText(count_);
}
void undo() override {
// Would need to store removed text for proper undo
}
};
// Invoker with undo/redo
class EditorManager {
std::stack<std::unique_ptr<Command>> history_;
std::stack<std::unique_ptr<Command>> redoStack_;
public:
void executeCommand(std::unique_ptr<Command> cmd) {
cmd->execute();
history_.push(std::move(cmd));
redoStack_ = std::stack<std::unique_ptr<Command>>(); // Clear redo
}
void undo() {
if (!history_.empty()) {
auto& cmd = history_.top();
cmd->undo();
redoStack_.push(std::move(history_.top()));
history_.pop();
}
}
void redo() {
if (!redoStack_.empty()) {
auto& cmd = redoStack_.top();
cmd->execute();
history_.push(std::move(redoStack_.top()));
redoStack_.pop();
}
}
};

Defines the skeleton of an algorithm, deferring some steps to subclasses.

  • Framework hooks
  • Data processing pipelines
  • Build processes
#include <iostream>
#include <string>
#include <vector>
// Abstract base class
class DataProcessor {
protected:
// Template method
void process() {
loadData();
validateData();
transformData();
saveResults();
}
// Steps to be implemented by subclasses
virtual void loadData() = 0;
virtual void saveResults() = 0;
// Default implementation
virtual void validateData() {
std::cout << "Validating data...\n";
}
// Required step
virtual void transformData() = 0;
public:
void run() {
process();
}
};
class CSVProcessor : public DataProcessor {
protected:
void loadData() override {
std::cout << "Loading CSV file...\n";
}
void transformData() override {
std::cout << "Processing CSV rows...\n";
}
void saveResults() override {
std::cout << "Saving CSV output...\n";
}
};
class JSONProcessor : public DataProcessor {
protected:
void loadData() override {
std::cout << "Loading JSON file...\n";
}
void transformData() override {
std::cout << "Processing JSON objects...\n";
}
void saveResults() override {
std::cout << "Saving JSON output...\n";
}
};
// Usage
int main() {
CSVProcessor csv;
csv.run();
std::cout << "\n";
JSONProcessor json;
json.run();
return 0;
}

Allows an object to alter its behavior when its internal state changes.

  • State machines
  • Workflow engines
  • Game states
  • Order processing
#include <iostream>
#include <memory>
#include <string>
// Forward declaration
class TCPState;
// Context
class TCPConnection {
std::unique_ptr<TCPState> state_;
public:
TCPConnection();
void open();
void close();
void acknowledge();
void changeState(std::unique_ptr<TCPState> state);
};
// State interface
class TCPState {
public:
virtual void open(TCPConnection* conn) {}
virtual void close(TCPConnection* conn) {}
virtual void acknowledge(TCPConnection* conn) {}
virtual std::string getName() const = 0;
virtual ~TCPState() = default;
};
// Concrete states
class TCPClosed : public TCPState {
public:
void open(TCPConnection* conn) override;
std::string getName() const override { return "Closed"; }
};
class TCPEstablished : public TCPState {
public:
void close(TCPConnection* conn) override;
void acknowledge(TCPConnection* conn) override;
std::string getName() const override { return "Established"; }
};
class TCPListen : public TCPState {
public:
void acknowledge(TCPConnection* conn) override;
std::string getName() const override { return "Listen"; }
};
// Implementation
TCPConnection::TCPConnection() {
state_ = std::make_unique<TCPClosed>();
}
void TCPConnection::open() {
state_->open(this);
}
void TCPConnection::close() {
state_->close(this);
}
void TCPConnection::acknowledge() {
state_->acknowledge(this);
}
void TCPConnection::changeState(std::unique_ptr<TCPState> state) {
state_ = std::move(state);
}
void TCPClosed::open(TCPConnection* conn) {
std::cout << "Opening connection\n";
conn->changeState(std::make_unique<TCPEstablished>());
}
void TCPEstablished::close(TCPConnection* conn) {
std::cout << "Closing connection\n";
conn->changeState(std::make_unique<TCPListen>());
}
void TCPEstablished::acknowledge(TCPConnection* conn) {
std::cout << "Acknowledging\n";
}
void TCPListen::acknowledge(TCPConnection* conn) {
std::cout << "Listen -> Established\n";
conn->changeState(std::make_unique<TCPEstablished>());
}
// Usage
int main() {
TCPConnection conn;
std::cout << "Initial: " << conn.getState() << "\n";
conn.open();
conn.acknowledge();
conn.close();
return 0;
}

Passes requests along a chain of handlers until one handles it.

  • Event handling
  • Logging with different levels
  • Authentication/Authorization
  • Caching
#include <iostream>
#include <string>
#include <memory>
// Request
struct Request {
std::string message;
int priority;
};
// Handler interface
class Handler {
std::unique_ptr<Handler> next_;
public:
virtual ~Handler() = default;
void setNext(std::unique_ptr<Handler> next) {
next_ = std::move(next);
}
void handle(const Request& req) {
if (canHandle(req)) {
process(req);
} else if (next_) {
next_->handle(req);
} else {
std::cout << "No handler found\n";
}
}
protected:
virtual bool canHandle(const Request& req) = 0;
virtual void process(const Request& req) = 0;
};
// Concrete handlers
class DebugHandler : public Handler {
protected:
bool canHandle(const Request& req) override {
return req.priority <= 1;
}
void process(const Request& req) override {
std::cout << "Debug: " << req.message << "\n";
}
};
class InfoHandler : public Handler {
protected:
bool canHandle(const Request& req) override {
return req.priority <= 2;
}
void process(const Request& req) override {
std::cout << "Info: " << req.message << "\n";
}
};
class ErrorHandler : public Handler {
protected:
bool canHandle(const Request& req) override {
return req.priority <= 3;
}
void process(const Request& req) override {
std::cout << "Error: " << req.message << "\n";
}
};
// Usage
int main() {
auto error = std::make_unique<ErrorHandler>();
auto info = std::make_unique<InfoHandler>();
auto debug = std::make_unique<DebugHandler>();
debug->setNext(std::move(info));
info->setNext(std::move(error));
Handler& chain = *debug;
chain.handle({"Debug message", 1});
chain.handle({"Info message", 2});
chain.handle({"Error message", 3});
return 0;
}

Separates algorithm from object structure.

  • Operating on element collections
  • Compiler AST traversal
  • Document conversion
  • Tax calculation systems
#include <iostream>
#include <memory>
#include <vector>
// Forward declarations
class Circle;
class Rectangle;
class Triangle;
// Visitor interface
class ShapeVisitor {
public:
virtual ~ShapeVisitor() = default;
virtual void visit(Circle& circle) = 0;
virtual void visit(Rectangle& rectangle) = 0;
virtual void visit(Triangle& triangle) = 0;
};
// Element interface
class Shape {
public:
virtual void accept(ShapeVisitor& visitor) = 0;
virtual ~Shape() = default;
};
// Concrete elements
class Circle : public Shape {
double radius_;
public:
explicit Circle(double r) : radius_(r) {}
double radius() const { return radius_; }
void accept(ShapeVisitor& visitor) override {
visitor.visit(*this);
}
};
class Rectangle : public Shape {
double width_, height_;
public:
Rectangle(double w, double h) : width_(w), height_(h) {}
double width() const { return width_; }
double height() const { return height_; }
void accept(ShapeVisitor& visitor) override {
visitor.visit(*this);
}
};
class Triangle : public Shape {
double base_, height_;
public:
Triangle(double b, double h) : base_(b), height_(h) {}
double base() const { return base_; }
double height() const { return height_; }
void accept(ShapeVisitor& visitor) override {
visitor.visit(*this);
}
};
// Concrete visitors
class AreaCalculator : public ShapeVisitor {
double totalArea_ = 0;
public:
void visit(Circle& circle) override {
totalArea_ += 3.14159 * circle.radius() * circle.radius();
}
void visit(Rectangle& rectangle) override {
totalArea_ += rectangle.width() * rectangle.height();
}
void visit(Triangle& triangle) override {
totalArea_ += 0.5 * triangle.base() * triangle.height();
}
double area() const { return totalArea_; }
};
class DrawingVisitor : public ShapeVisitor {
public:
void visit(Circle& circle) override {
std::cout << "Drawing circle with r=" << circle.radius() << "\n";
}
void visit(Rectangle& rectangle) override {
std::cout << "Drawing rectangle " << rectangle.width()
<< "x" << rectangle.height() << "\n";
}
void visit(Triangle& triangle) override {
std::cout << "Drawing triangle b=" << triangle.base()
<< " h=" << triangle.height() << "\n";
}
};
// Usage
int main() {
std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(std::make_unique<Circle>(5));
shapes.push_back(std::make_unique<Rectangle>(4, 6));
shapes.push_back(std::make_unique<Triangle>(3, 4));
AreaCalculator areaCalc;
DrawingVisitor drawer;
for (auto& shape : shapes) {
shape->accept(areaCalc);
shape->accept(drawer);
}
std::cout << "Total area: " << areaCalc.area() << "\n";
return 0;
}
PatternIntentUse Case
ObserverNotify changesEvent systems
StrategySwap algorithmsSorting, payment
CommandEncapsulate requestsUndo/redo
Template MethodDefine skeletonProcessing pipelines
StateChange behaviorState machines
Chain of ResponsibilityPass along chainLogging, auth
VisitorSeparate operationsAST, shapes
  • Behavioral patterns manage object interactions
  • Choose pattern based on the problem:
    • Need loose coupling → Observer
    • Need algorithm flexibility → Strategy
    • Need undo/redo → Command
    • Need state transitions → State
  • Modern C++ can use std::function for simpler implementations
  • Pattern overuse can add unnecessary complexity