乐闻世界logo
搜索文章和话题

How to implement C++ design patterns

2月18日 17:36

C++ Design Patterns

Design patterns are reusable solutions to common problems in software design. As a powerful object-oriented language, C++ is well-suited for implementing various design patterns.

Creational Patterns

Singleton Pattern:

cpp
class Singleton { private: static Singleton* instance; Singleton() = default; ~Singleton() = default; public: Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; static Singleton& getInstance() { static Singleton instance; return instance; } void doSomething() { std::cout << "Singleton doing something" << std::endl; } }; // Usage Singleton& singleton = Singleton::getInstance(); singleton.doSomething();

Factory Pattern:

cpp
// Abstract product class Product { public: virtual ~Product() = default; virtual void operation() = 0; }; // Concrete products class ConcreteProductA : public Product { public: void operation() override { std::cout << "Product A operation" << std::endl; } }; class ConcreteProductB : public Product { public: void operation() override { std::cout << "Product B operation" << std::endl; } }; // Abstract factory class Factory { public: virtual ~Factory() = default; virtual std::unique_ptr<Product> createProduct() = 0; }; // Concrete factories class FactoryA : public Factory { public: std::unique_ptr<Product> createProduct() override { return std::make_unique<ConcreteProductA>(); } }; class FactoryB : public Factory { public: std::unique_ptr<Product> createProduct() override { return std::make_unique<ConcreteProductB>(); } }; // Usage std::unique_ptr<Factory> factory = std::make_unique<FactoryA>(); auto product = factory->createProduct(); product->operation();

Builder Pattern:

cpp
class Computer { private: std::string cpu; std::string gpu; int ram; public: void setCPU(const std::string& cpu) { this->cpu = cpu; } void setGPU(const std::string& gpu) { this->gpu = gpu; } void setRAM(int ram) { this->ram = ram; } void show() { std::cout << "CPU: " << cpu << ", GPU: " << gpu << ", RAM: " << ram << "GB" << std::endl; } }; class ComputerBuilder { private: Computer computer; public: ComputerBuilder& setCPU(const std::string& cpu) { computer.setCPU(cpu); return *this; } ComputerBuilder& setGPU(const std::string& gpu) { computer.setGPU(gpu); return *this; } ComputerBuilder& setRAM(int ram) { computer.setRAM(ram); return *this; } Computer build() { return computer; } }; // Usage Computer computer = ComputerBuilder() .setCPU("Intel i9") .setGPU("NVIDIA RTX 4090") .setRAM(32) .build(); computer.show();

Structural Patterns

Adapter Pattern:

cpp
// Target interface class Target { public: virtual ~Target() = default; virtual void request() = 0; }; // Adaptee class Adaptee { public: void specificRequest() { std::cout << "Adaptee specific request" << std::endl; } }; // Adapter class Adapter : public Target { private: std::unique_ptr<Adaptee> adaptee; public: Adapter() : adaptee(std::make_unique<Adaptee>()) {} void request() override { adaptee->specificRequest(); } }; // Usage std::unique_ptr<Target> target = std::make_unique<Adapter>(); target->request();

Decorator Pattern:

cpp
// Component interface class Component { public: virtual ~Component() = default; virtual void operation() = 0; }; // Concrete component class ConcreteComponent : public Component { public: void operation() override { std::cout << "ConcreteComponent operation" << std::endl; } }; // Decorator base class class Decorator : public Component { private: std::unique_ptr<Component> component; public: Decorator(std::unique_ptr<Component> comp) : component(std::move(comp)) {} void operation() override { component->operation(); } }; // Concrete decorator class ConcreteDecoratorA : public Decorator { public: ConcreteDecoratorA(std::unique_ptr<Component> comp) : Decorator(std::move(comp)) {} void operation() override { Decorator::operation(); addedBehavior(); } void addedBehavior() { std::cout << "Added behavior A" << std::endl; } }; // Usage std::unique_ptr<Component> component = std::make_unique<ConcreteComponent>(); component = std::make_unique<ConcreteDecoratorA>(std::move(component)); component->operation();

Facade Pattern:

cpp
class SubsystemA { public: void operationA() { std::cout << "Subsystem A operation" << std::endl; } }; class SubsystemB { public: void operationB() { std::cout << "Subsystem B operation" << std::endl; } }; class SubsystemC { public: void operationC() { std::cout << "Subsystem C operation" << std::endl; } }; // Facade class Facade { private: std::unique_ptr<SubsystemA> subsystemA; std::unique_ptr<SubsystemB> subsystemB; std::unique_ptr<SubsystemC> subsystemC; public: Facade() : subsystemA(std::make_unique<SubsystemA>()), subsystemB(std::make_unique<SubsystemB>()), subsystemC(std::make_unique<SubsystemC>()) {} void operation() { subsystemA->operationA(); subsystemB->operationB(); subsystemC->operationC(); } }; // Usage Facade facade; facade.operation();

Behavioral Patterns

Observer Pattern:

cpp
#include <vector> #include <functional> // Observer interface class Observer { public: virtual ~Observer() = default; virtual void update(const std::string& message) = 0; }; // Subject class Subject { private: std::vector<std::reference_wrapper<Observer>> observers; public: void attach(Observer& observer) { observers.push_back(observer); } void detach(Observer& observer) { observers.erase( std::remove_if(observers.begin(), observers.end(), [&observer](auto& obs) { return &obs.get() == &observer; }), observers.end()); } void notify(const std::string& message) { for (auto& observer : observers) { observer.get().update(message); } } }; // Concrete observer class ConcreteObserver : public Observer { private: std::string name; public: ConcreteObserver(const std::string& n) : name(n) {} void update(const std::string& message) override { std::cout << name << " received: " << message << std::endl; } }; // Usage Subject subject; ConcreteObserver observer1("Observer 1"); ConcreteObserver observer2("Observer 2"); subject.attach(observer1); subject.attach(observer2); subject.notify("Hello observers!");

Strategy Pattern:

cpp
// Strategy interface class Strategy { public: virtual ~Strategy() = default; virtual int execute(int a, int b) = 0; }; // Concrete strategies class AddStrategy : public Strategy { public: int execute(int a, int b) override { return a + b; } }; class MultiplyStrategy : public Strategy { public: int execute(int a, int b) override { return a * b; } }; // Context class Context { private: std::unique_ptr<Strategy> strategy; public: void setStrategy(std::unique_ptr<Strategy> s) { strategy = std::move(s); } int executeStrategy(int a, int b) { return strategy->execute(a, b); } }; // Usage Context context; context.setStrategy(std::make_unique<AddStrategy>()); std::cout << context.executeStrategy(10, 20) << std::endl; context.setStrategy(std::make_unique<MultiplyStrategy>()); std::cout << context.executeStrategy(10, 20) << std::endl;

Command Pattern:

cpp
// Command interface class Command { public: virtual ~Command() = default; virtual void execute() = 0; virtual void undo() = 0; }; // Receiver class Receiver { public: void action() { std::cout << "Receiver action" << std::endl; } void reverseAction() { std::cout << "Receiver reverse action" << std::endl; } }; // Concrete command class ConcreteCommand : public Command { private: Receiver& receiver; public: ConcreteCommand(Receiver& r) : receiver(r) {} void execute() override { receiver.action(); } void undo() override { receiver.reverseAction(); } }; // Invoker class Invoker { private: std::vector<std::unique_ptr<Command>> commands; public: void setCommand(std::unique_ptr<Command> command) { commands.push_back(std::move(command)); } void executeCommands() { for (auto& command : commands) { command->execute(); } } void undoCommands() { for (auto it = commands.rbegin(); it != commands.rend(); ++it) { (*it)->undo(); } } }; // Usage Receiver receiver; Invoker invoker; invoker.setCommand(std::make_unique<ConcreteCommand>(receiver)); invoker.setCommand(std::make_unique<ConcreteCommand>(receiver)); invoker.executeCommands(); invoker.undoCommands();

State Pattern:

cpp
// State interface class State { public: virtual ~State() = default; virtual void handle() = 0; }; // Context class Context { private: std::unique_ptr<State> state; public: void setState(std::unique_ptr<State> s) { state = std::move(s); } void request() { state->handle(); } }; // Concrete states class ConcreteStateA : public State { private: Context& context; public: ConcreteStateA(Context& ctx) : context(ctx) {} void handle() override { std::cout << "State A handling" << std::endl; context.setState(std::make_unique<ConcreteStateB>(context)); } }; class ConcreteStateB : public State { private: Context& context; public: ConcreteStateB(Context& ctx) : context(ctx) {} void handle() override { std::cout << "State B handling" << std::endl; context.setState(std::make_unique<ConcreteStateA>(context)); } }; // Usage Context context; context.setState(std::make_unique<ConcreteStateA>(context)); context.request(); // State A context.request(); // State B context.request(); // State A

Template Method Pattern

cpp
// Abstract class class AbstractClass { public: virtual ~AbstractClass() = default; void templateMethod() { primitiveOperation1(); primitiveOperation2(); hook(); } protected: virtual void primitiveOperation1() = 0; virtual void primitiveOperation2() = 0; virtual void hook() {} // Hook method, optional implementation }; // Concrete class class ConcreteClass : public AbstractClass { protected: void primitiveOperation1() override { std::cout << "Primitive operation 1" << std::endl; } void primitiveOperation2() override { std::cout << "Primitive operation 2" << std::endl; } void hook() override { std::cout << "Hook called" << std::endl; } }; // Usage std::unique_ptr<AbstractClass> obj = std::make_unique<ConcreteClass>(); obj->templateMethod();

Chain of Responsibility Pattern

cpp
// Handler interface class Handler { protected: std::unique_ptr<Handler> next; public: virtual ~Handler() = default; void setNext(std::unique_ptr<Handler> h) { next = std::move(h); } virtual void handleRequest(int request) { if (next) { next->handleRequest(request); } } }; // Concrete handlers class ConcreteHandlerA : public Handler { public: void handleRequest(int request) override { if (request >= 0 && request < 10) { std::cout << "Handler A handles request " << request << std::endl; } else { Handler::handleRequest(request); } } }; class ConcreteHandlerB : public Handler { public: void handleRequest(int request) override { if (request >= 10 && request < 20) { std::cout << "Handler B handles request " << request << std::endl; } else { Handler::handleRequest(request); } } }; // Usage auto handlerA = std::make_unique<ConcreteHandlerA>(); auto handlerB = std::make_unique<ConcreteHandlerB>(); handlerA->setNext(std::move(handlerB)); handlerA->handleRequest(5); // Handler A handlerA->handleRequest(15); // Handler B

Best Practices

1. Prefer composition over inheritance

cpp
// Recommended: composition class Engine { public: void start() { std::cout << "Engine started" << std::endl; } }; class Car { private: Engine engine; public: void start() { engine.start(); } }; // Not recommended: excessive inheritance class Vehicle { public: virtual void start() = 0; }; class Car : public Vehicle { public: void start() override { std::cout << "Car started" << std::endl; } };

2. Use smart pointers to manage object lifetimes

cpp
// Recommended std::unique_ptr<Factory> factory = std::make_unique<FactoryA>(); auto product = factory->createProduct(); // Not recommended Factory* factory = new FactoryA(); auto product = factory->createProduct(); delete factory;

3. Follow SOLID principles

  • Single Responsibility Principle (SRP)
  • Open/Closed Principle (OCP)
  • Liskov Substitution Principle (LSP)
  • Interface Segregation Principle (ISP)
  • Dependency Inversion Principle (DIP)

4. Use RAII for resource management

cpp
class ResourceManager { private: std::unique_ptr<Resource> resource; public: ResourceManager() : resource(std::make_unique<Resource>()) {} // Destructor automatically releases resources };

5. Avoid over-engineering

cpp
// Simple scenarios don't need complex patterns int add(int a, int b) { return a + b; } // Complex scenarios benefit from design patterns class Calculator { public: virtual int calculate(int a, int b) = 0; };
标签:C++