10个最常用 设计模式 详解

优缺点、使用场景与 C++ 实现

什么是设计模式?

设计模式是软件设计中常见问题的经典解决方案。它们是经过实践检验的代码设计经验,是解决特定场景问题的模板,不是具体的代码,而是用于设计软件的准则和范式。

设计模式最早由"四人帮"(Gang of Four,即 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides)在他们的著作《设计模式:可复用面向对象软件的基础》中提出,该书记录了23种设计模式,成为软件工程的里程碑。

可复用性

设计模式提供了经过测试的开发范式,帮助你避免重复发明轮子,提高代码可复用性和可维护性。

通用语言

设计模式为开发团队提供共通的词汇和沟通基础,使架构和设计讨论更加清晰高效。

最佳实践

设计模式代表了专业软件开发的最佳实践,可以帮助你编写更加健壮、灵活和可维护的代码。

目录 Navigation

设计模式 详解 Design Pattern Details

01

单例模式

Singleton Pattern

确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。单例模式是最简单但也是使用最广泛的设计模式之一。

优点

  • 保证一个类只有一个实例,避免资源冲突
  • 全局访问点,方便控制对实例的访问方式
  • 延迟初始化实例,减少不必要的资源开销

缺点

  • 在多线程环境中实现复杂
  • 违反单一职责原则
  • 难以进行单元测试

使用场景

  • 需要频繁创建和销毁的对象,如数据库连接池
  • 多个组件需要共享资源,如配置信息、线程池或缓存
  • 当需要控制资源的并发访问时

C++ 实现

// 线程安全的懒汉式单例模式
class Singleton {
private:
    // 私有构造函数
    Singleton() {
        std::cout << "单例被创建" << std::endl;
    }
    
    // 禁止复制和赋值
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    
    // 静态实例指针
    static std::unique_ptr<Singleton> instance_;
    static std::mutex mutex_;

public:
    // 析构函数
    ~Singleton() {
        std::cout << "单例被销毁" << std::endl;
    }
    
    // 获取单例实例的全局访问点
    static Singleton& getInstance() {
        // 双重检查锁定
        if (instance_ == nullptr) {
            std::lock_guard<std::mutex> lock(mutex_);
            if (instance_ == nullptr) {
                instance_.reset(new Singleton());
            }
        }
        return *instance_;
    }
    
    // 业务方法示例
    void doSomething() {
        std::cout << "单例执行某项任务" << std::endl;
    }
};

// 静态成员初始化
std::unique_ptr<Singleton> Singleton::instance_ = nullptr;
std::mutex Singleton::mutex_;

// 使用示例
int main() {
    // 获取单例实例
    Singleton& s1 = Singleton::getInstance();
    s1.doSomething();
    
    // 再次获取实例(实际上是同一个实例)
    Singleton& s2 = Singleton::getInstance();
    
    // 验证是否是同一实例
    std::cout << "s1和s2是否是同一个实例: " 
              << (&s1 == &s2 ? "是" : "否") << std::endl;
    
    return 0;
}
02

工厂方法模式

Factory Method Pattern

定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。这种模式处理对象的创建,同时不暴露创建逻辑。

优点

  • 遵循开闭原则,便于扩展
  • 将创建与业务逻辑分离,降低耦合性
  • 客户端代码与具体产品解耦

缺点

  • 增加新产品时需要编写新的具体工厂类和产品类
  • 代码结构较为复杂
  • 客户端必须了解不同工厂对应不同产品

使用场景

  • 当无法预知要创建对象的确切类型时
  • 当一个类希望由其子类来指定创建对象时
  • 用于框架和库,允许用户扩展系统的内部对象

C++ 实现

// 产品抽象类
class Product {
public:
    virtual ~Product() {}
    virtual std::string operation() const = 0;
};

// 具体产品A
class ConcreteProductA : public Product {
public:
    std::string operation() const override {
        return "这是产品A的结果";
    }
};

// 具体产品B
class ConcreteProductB : public Product {
public:
    std::string operation() const override {
        return "这是产品B的结果";
    }
};

// 工厂抽象类
class Creator {
public:
    virtual ~Creator() {}
    
    // 工厂方法
    virtual std::unique_ptr<Product> createProduct() const = 0;
    
    // 使用工厂方法的基础业务逻辑
    std::string someOperation() const {
        // 调用工厂方法创建产品
        std::unique_ptr<Product> product = this->createProduct();
        
        // 使用产品
        return "创建者: " + product->operation();
    }
};

// 具体工厂A,创建产品A
class ConcreteCreatorA : public Creator {
public:
    std::unique_ptr<Product> createProduct() const override {
        return std::make_unique<ConcreteProductA>();
    }
};

// 具体工厂B,创建产品B
class ConcreteCreatorB : public Creator {
public:
    std::unique_ptr<Product> createProduct() const override {
        return std::make_unique<ConcreteProductB>();
    }
};

// 客户端代码
void clientCode(const Creator& creator) {
    std::cout << "客户端代码使用工厂: " << creator.someOperation() << std::endl;
}

int main() {
    std::cout << "使用具体创建者A:" << std::endl;
    ConcreteCreatorA creatorA;
    clientCode(creatorA);
    
    std::cout << std::endl;
    
    std::cout << "使用具体创建者B:" << std::endl;
    ConcreteCreatorB creatorB;
    clientCode(creatorB);
    
    return 0;
}
03

观察者模式

Observer Pattern

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式常用于实现分布式事件处理系统。

优点

  • 支持对象间松耦合设计
  • 遵循开闭原则,方便添加新观察者
  • 支持广播通信和一对多通知

缺点

  • 观察者过多可能导致性能问题
  • 可能导致循环通知和意外的副作用
  • 通知顺序不可控,可能导致复杂度提高

使用场景

  • 当一个对象状态改变需要通知其他对象,但不知道这些对象具体是谁
  • 事件驱动系统和GUI开发(如按钮点击事件处理)
  • 实时消息通知,如邮件、消息订阅和日志系统

C++ 实现

// 观察者抽象类
class Observer {
public:
    virtual ~Observer() = default;
    virtual void update(const std::string& message) = 0;
};

// 主题/被观察者抽象类
class Subject {
public:
    virtual ~Subject() = default;
    virtual void attach(Observer* observer) = 0;
    virtual void detach(Observer* observer) = 0;
    virtual void notify() = 0;
};

// 具体主题
class ConcreteSubject : public Subject {
private:
    std::list<Observer*> observers_;
    std::string message_;

public:
    void attach(Observer* observer) override {
        observers_.push_back(observer);
    }

    void detach(Observer* observer) override {
        observers_.remove(observer);
    }

    void notify() override {
        for (Observer* observer : observers_) {
            observer->update(message_);
        }
    }

    // 设置状态并通知所有观察者
    void createMessage(const std::string& message) {
        message_ = message;
        notify();
    }

    void someBusinessLogic() {
        message_ = "状态已更改";
        notify();
        std::cout << "我执行了一些重要的业务逻辑\n";
    }
};

// 具体观察者A
class ConcreteObserverA : public Observer {
private:
    std::string name_;

public:
    explicit ConcreteObserverA(const std::string& name) : name_(name) {}

    void update(const std::string& message) override {
        std::cout << name_ << " 收到通知: " << message << std::endl;
    }
};

// 具体观察者B
class ConcreteObserverB : public Observer {
private:
    std::string name_;

public:
    explicit ConcreteObserverB(const std::string& name) : name_(name) {}

    void update(const std::string& message) override {
        std::cout << name_ << " 收到更新: " << message << std::endl;
        // 具体观察者B对通知有特殊处理
        std::cout << name_ << " 执行特殊反应\n";
    }
};

// 客户端代码
int main() {
    ConcreteSubject subject;

    ConcreteObserverA observerA("观察者A");
    ConcreteObserverB observerB("观察者B");
    
    subject.attach(&observerA);
    subject.attach(&observerB);
    
    subject.createMessage("重要通知: 新事件发生!");
    
    subject.detach(&observerB);
    
    std::cout << "\n观察者B已移除\n\n";
    
    subject.createMessage("第二条通知: 状态更新!");
    
    return 0;
}
04

策略模式

Strategy Pattern

定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。策略模式让算法独立于使用它的客户而变化,支持不同行为的动态切换。

优点

  • 支持算法的动态切换
  • 避免使用多重条件判断
  • 将算法实现和使用分离,易于扩展

缺点

  • 客户端必须了解所有的策略类
  • 策略类数量可能增多
  • 增加了对象的数量

使用场景

  • 需要在运行时选择算法的场景,如排序算法
  • 避免使用多重条件语句处理不同的行为
  • 有一系列可互换的算法,如压缩算法、验证策略或支付方式

C++ 实现

// 策略接口
class PaymentStrategy {
public:
    virtual ~PaymentStrategy() = default;
    virtual bool pay(double amount) = 0;
    virtual std::string getName() const = 0;
};

// 具体策略 - 信用卡支付
class CreditCardPayment : public PaymentStrategy {
private:
    std::string name_;
    std::string cardNumber_;
    std::string cvv_;
    std::string dateOfExpiry_;
    
public:
    CreditCardPayment(const std::string& name, 
                     const std::string& cardNumber, 
                     const std::string& cvv, 
                     const std::string& expiryDate)
        : name_(name), cardNumber_(cardNumber), cvv_(cvv), dateOfExpiry_(expiryDate) {}
        
    bool pay(double amount) override {
        std::cout << amount << "元已通过信用卡支付\n";
        return true;
    }
    
    std::string getName() const override {
        return "信用卡支付";
    }
};

// 具体策略 - 支付宝支付
class AlipayPayment : public PaymentStrategy {
private:
    std::string emailId_;
    std::string password_;
    
public:
    AlipayPayment(const std::string& email, const std::string& pwd)
        : emailId_(email), password_(pwd) {}
        
    bool pay(double amount) override {
        std::cout << amount << "元已通过支付宝支付\n";
        return true;
    }
    
    std::string getName() const override {
        return "支付宝支付";
    }
};

// 具体策略 - 微信支付
class WeChatPayment : public PaymentStrategy {
private:
    std::string id_;
    
public:
    explicit WeChatPayment(const std::string& id) : id_(id) {}
    
    bool pay(double amount) override {
        std::cout << amount << "元已通过微信支付\n";
        return true;
    }
    
    std::string getName() const override {
        return "微信支付";
    }
};

// 上下文类
class ShoppingCart {
private:
    std::vector<double> prices_;
    PaymentStrategy* paymentStrategy_;

public:
    ShoppingCart() : paymentStrategy_(nullptr) {}
    
    void addItem(double price) {
        prices_.push_back(price);
    }
    
    double calculateTotal() const {
        double sum = 0;
        for (double price : prices_) {
            sum += price;
        }
        return sum;
    }
    
    void setPaymentStrategy(PaymentStrategy* strategy) {
        paymentStrategy_ = strategy;
    }
    
    bool checkout() {
        if (!paymentStrategy_) {
            std::cout << "请先设置支付方式!\n";
            return false;
        }
        
        double amount = calculateTotal();
        std::cout << "购物车总金额: " << amount 
                 << "元, 使用" << paymentStrategy_->getName() 
                 << "支付\n";
                 
        return paymentStrategy_->pay(amount);
    }
};

// 客户端代码
int main() {
    ShoppingCart cart;
    
    cart.addItem(100);
    cart.addItem(50);
    cart.addItem(200);
    
    std::cout << "总金额: " << cart.calculateTotal() << "元\n\n";
    
    // 使用信用卡支付
    CreditCardPayment creditCard("张三", "1234 5678 9012 3456", "123", "12/25");
    cart.setPaymentStrategy(&creditCard);
    cart.checkout();
    
    std::cout << "\n更换支付方式\n\n";
    
    // 使用支付宝支付
    AlipayPayment alipay("example@mail.com", "password");
    cart.setPaymentStrategy(&alipay);
    cart.checkout();
    
    std::cout << "\n更换支付方式\n\n";
    
    // 使用微信支付
    WeChatPayment wechat("wx123456");
    cart.setPaymentStrategy(&wechat);
    cart.checkout();
    
    return 0;
}
05

装饰器模式

Decorator Pattern

动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式比生成子类更为灵活。在不修改原对象代码的情况下,能够为对象动态添加功能。

优点

  • 比继承更灵活,可以动态地增强功能
  • 遵循开闭原则,易于维护和扩展
  • 支持功能的排列组合,可以任意组合功能

缺点

  • 会产生很多小对象,增加系统复杂性
  • 装饰器链可能变得过于复杂
  • 难以更改特定装饰器的顺序

使用场景

  • 在不修改对象代码的前提下动态增强对象功能时
  • 需要组合多种功能,且不希望产生大量子类的情况
  • I/O流处理、UI组件、数据缓冲和日志功能增强等场景

C++ 实现

// 组件接口
class Coffee {
public:
    virtual ~Coffee() = default;
    virtual double getCost() const = 0;
    virtual std::string getDescription() const = 0;
};

// 具体组件
class SimpleCoffee : public Coffee {
public:
    double getCost() const override {
        return 10.0; // 基础咖啡价格
    }
    
    std::string getDescription() const override {
        return "简单咖啡";
    }
};

// 装饰器基类
class CoffeeDecorator : public Coffee {
protected:
    Coffee* coffee_;
    
public:
    explicit CoffeeDecorator(Coffee* coffee) : coffee_(coffee) {}
    
    virtual ~CoffeeDecorator() {
        delete coffee_;  // 装饰器负责释放被装饰对象
    }
    
    double getCost() const override {
        return coffee_->getCost();
    }
    
    std::string getDescription() const override {
        return coffee_->getDescription();
    }
};

// 具体装饰器 - 牛奶
class MilkDecorator : public CoffeeDecorator {
public:
    explicit MilkDecorator(Coffee* coffee) : CoffeeDecorator(coffee) {}
    
    double getCost() const override {
        return coffee_->getCost() + 2.0; // 牛奶额外加2元
    }
    
    std::string getDescription() const override {
        return coffee_->getDescription() + ", 加牛奶";
    }
};

// 具体装饰器 - 糖
class SugarDecorator : public CoffeeDecorator {
public:
    explicit SugarDecorator(Coffee* coffee) : CoffeeDecorator(coffee) {}
    
    double getCost() const override {
        return coffee_->getCost() + 1.0; // 糖额外加1元
    }
    
    std::string getDescription() const override {
        return coffee_->getDescription() + ", 加糖";
    }
};

// 具体装饰器 - 巧克力
class ChocolateDecorator : public CoffeeDecorator {
public:
    explicit ChocolateDecorator(Coffee* coffee) : CoffeeDecorator(coffee) {}
    
    double getCost() const override {
        return coffee_->getCost() + 5.0; // 巧克力额外加5元
    }
    
    std::string getDescription() const override {
        return coffee_->getDescription() + ", 加巧克力";
    }
};

// 客户端代码
int main() {
    // 制作一杯简单咖啡
    Coffee* simpleCoffee = new SimpleCoffee();
    std::cout << "饮品: " << simpleCoffee->getDescription() 
              << ", 价格: ¥" << simpleCoffee->getCost() << std::endl;
    
    // 加牛奶的咖啡
    Coffee* milkCoffee = new MilkDecorator(new SimpleCoffee());
    std::cout << "饮品: " << milkCoffee->getDescription() 
              << ", 价格: ¥" << milkCoffee->getCost() << std::endl;
    
    // 加糖和牛奶的咖啡
    Coffee* sweetMilkCoffee = new SugarDecorator(new MilkDecorator(new SimpleCoffee()));
    std::cout << "饮品: " << sweetMilkCoffee->getDescription() 
              << ", 价格: ¥" << sweetMilkCoffee->getCost() << std::endl;
    
    // 加巧克力、糖和牛奶的咖啡
    Coffee* deluxeCoffee = new ChocolateDecorator(
                            new SugarDecorator(
                                new MilkDecorator(
                                    new SimpleCoffee())));
    std::cout << "饮品: " << deluxeCoffee->getDescription() 
              << ", 价格: ¥" << deluxeCoffee->getCost() << std::endl;
    
    // 别忘了释放内存
    delete simpleCoffee;
    delete milkCoffee;
    delete sweetMilkCoffee;
    delete deluxeCoffee;
    
    return 0;
}
06

适配器模式

Adapter Pattern

将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以一起工作。适配器模式实现了代码复用和系统集成。

优点

  • 可以让不兼容的接口一起工作
  • 提高了类的复用性和透明度
  • 不需要修改现有代码就能集成新的系统和类库

缺点

  • 增加了系统的复杂性
  • 有时过度使用会导致代码难以理解和维护
  • 在某些情况下可能不如直接修改现有代码更简单

使用场景

  • 需要使用现有的类,但其接口不符合你的要求
  • 需要集成第三方库或遗留系统,但无法修改其源代码
  • 需要统一多个类的接口设计时

C++ 实现

// 目标接口(客户端期望的接口)
class MediaPlayer {
public:
    virtual ~MediaPlayer() = default;
    virtual void play(const std::string& audioType, const std::string& fileName) = 0;
};

// 被适配的类(已存在的接口)
class AdvancedMediaPlayer {
public:
    virtual ~AdvancedMediaPlayer() = default;
    virtual void playVlc(const std::string& fileName) = 0;
    virtual void playMp4(const std::string& fileName) = 0;
};

// 具体的高级播放器实现
class VlcPlayer : public AdvancedMediaPlayer {
public:
    void playVlc(const std::string& fileName) override {
        std::cout << "播放VLC文件: " << fileName << std::endl;
    }
    
    void playMp4(const std::string& fileName) override {
        // 什么都不做,VlcPlayer不支持mp4
    }
};

class Mp4Player : public AdvancedMediaPlayer {
public:
    void playVlc(const std::string& fileName) override {
        // 什么都不做,Mp4Player不支持vlc
    }
    
    void playMp4(const std::string& fileName) override {
        std::cout << "播放MP4文件: " << fileName << std::endl;
    }
};

// 适配器类,将AdvancedMediaPlayer适配为MediaPlayer
class MediaAdapter : public MediaPlayer {
private:
    std::unique_ptr<AdvancedMediaPlayer> advancedMusicPlayer_;
    
public:
    explicit MediaAdapter(const std::string& audioType) {
        if (audioType == "vlc") {
            advancedMusicPlayer_ = std::make_unique<VlcPlayer>();
        } else if (audioType == "mp4") {
            advancedMusicPlayer_ = std::make_unique<Mp4Player>();
        }
    }
    
    void play(const std::string& audioType, const std::string& fileName) override {
        if (audioType == "vlc") {
            advancedMusicPlayer_->playVlc(fileName);
        } else if (audioType == "mp4") {
            advancedMusicPlayer_->playMp4(fileName);
        }
    }
};

// 具体的播放器实现,使用适配器
class AudioPlayer : public MediaPlayer {
private:
    std::unique_ptr<MediaAdapter> mediaAdapter_;
    
public:
    void play(const std::string& audioType, const std::string& fileName) override {
        // 内置支持mp3格式
        if (audioType == "mp3") {
            std::cout << "播放MP3文件: " << fileName << std::endl;
        } 
        // 使用适配器支持其他格式
        else if (audioType == "vlc" || audioType == "mp4") {
            mediaAdapter_ = std::make_unique<MediaAdapter>(audioType);
            mediaAdapter_->play(audioType, fileName);
        } else {
            std::cout << "不支持的音频类型: " << audioType << std::endl;
        }
    }
};

// 客户端代码
int main() {
    AudioPlayer audioPlayer;
    
    audioPlayer.play("mp3", "beyond_the_horizon.mp3");
    audioPlayer.play("mp4", "alone.mp4");
    audioPlayer.play("vlc", "far_far_away.vlc");
    audioPlayer.play("avi", "mind_me.avi"); // 不支持的格式
    
    return 0;
}
07

命令模式

Command Pattern

将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。命令模式实现了请求发送者与接收者解耦。

优点

  • 将调用操作的对象与实现操作的对象解耦
  • 可以实现命令的排队和日志
  • 支持撤销和重做操作

缺点

  • 可能导致系统中存在大量具体命令类
  • 命令模式实现复杂命令时可能变得困难
  • 增加了系统的复杂性

使用场景

  • 需要将请求发送者和接收者解耦的场景
  • 需要支持命令排队、命令日志或撤销功能时
  • GUI应用中的菜单功能、宏命令或事务操作

C++ 实现

// 接收者类 - 执行命令相关的操作
class Light {
private:
    std::string location_;
    
public:
    explicit Light(const std::string& location) : location_(location) {}
    
    void on() {
        std::cout << location_ << " 灯已打开" << std::endl;
    }
    
    void off() {
        std::cout << location_ << " 灯已关闭" << std::endl;
    }
};

// 命令接口
class Command {
public:
    virtual ~Command() = default;
    virtual void execute() = 0;
    virtual void undo() = 0;
};

// 具体命令 - 开灯命令
class LightOnCommand : public Command {
private:
    Light* light_;
    
public:
    explicit LightOnCommand(Light* light) : light_(light) {}
    
    void execute() override {
        light_->on();
    }
    
    void undo() override {
        light_->off();
    }
};

// 具体命令 - 关灯命令
class LightOffCommand : public Command {
private:
    Light* light_;
    
public:
    explicit LightOffCommand(Light* light) : light_(light) {}
    
    void execute() override {
        light_->off();
    }
    
    void undo() override {
        light_->on();
    }
};

// 调用者类 - 简单遥控器
class SimpleRemoteControl {
private:
    Command* slot_;
    Command* lastCommand_;
    
public:
    SimpleRemoteControl() : slot_(nullptr), lastCommand_(nullptr) {}
    
    void setCommand(Command* command) {
        slot_ = command;
    }
    
    void buttonPressed() {
        if (slot_) {
            slot_->execute();
            lastCommand_ = slot_;
        }
    }
    
    void undoButtonPressed() {
        if (lastCommand_) {
            lastCommand_->undo();
        }
    }
};

// 宏命令 - 组合多个命令
class MacroCommand : public Command {
private:
    std::vector<Command*> commands_;
    
public:
    explicit MacroCommand(const std::vector<Command*>& commands) : commands_(commands) {}
    
    void execute() override {
        std::cout << "执行宏命令 -- 开始" << std::endl;
        for (auto command : commands_) {
            command->execute();
        }
        std::cout << "执行宏命令 -- 结束" << std::endl;
    }
    
    void undo() override {
        std::cout << "撤销宏命令 -- 开始" << std::endl;
        // 反向撤销命令
        for (auto it = commands_.rbegin(); it != commands_.rend(); ++it) {
            (*it)->undo();
        }
        std::cout << "撤销宏命令 -- 结束" << std::endl;
    }
};

// 客户端代码
int main() {
    // 创建接收者
    Light* livingRoomLight = new Light("客厅");
    Light* kitchenLight = new Light("厨房");
    
    // 创建具体命令
    Command* livingRoomLightOn = new LightOnCommand(livingRoomLight);
    Command* livingRoomLightOff = new LightOffCommand(livingRoomLight);
    Command* kitchenLightOn = new LightOnCommand(kitchenLight);
    Command* kitchenLightOff = new LightOffCommand(kitchenLight);
    
    // 创建调用者
    SimpleRemoteControl remote;
    
    // 测试单个命令
    std::cout << "=== 测试单个命令 ===" << std::endl;
    remote.setCommand(livingRoomLightOn);
    remote.buttonPressed();
    remote.undoButtonPressed();
    
    // 创建宏命令
    std::vector<Command*> partyOnCommands = {livingRoomLightOn, kitchenLightOn};
    std::vector<Command*> partyOffCommands = {livingRoomLightOff, kitchenLightOff};
    
    Command* partyOnMacro = new MacroCommand(partyOnCommands);
    Command* partyOffMacro = new MacroCommand(partyOffCommands);
    
    // 测试宏命令
    std::cout << "\n=== 测试宏命令 ===" << std::endl;
    std::cout << "--- 派对开始 ---" << std::endl;
    remote.setCommand(partyOnMacro);
    remote.buttonPressed();
    
    std::cout << "\n--- 派对结束 ---" << std::endl;
    remote.setCommand(partyOffMacro);
    remote.buttonPressed();
    
    std::cout << "\n--- 撤销派对结束 ---" << std::endl;
    remote.undoButtonPressed();
    
    // 清理资源
    delete livingRoomLight;
    delete kitchenLight;
    delete livingRoomLightOn;
    delete livingRoomLightOff;
    delete kitchenLightOn;
    delete kitchenLightOff;
    delete partyOnMacro;
    delete partyOffMacro;
    
    return 0;
}
08

代理模式

Proxy Pattern

为其他对象提供一种代理以控制对这个对象的访问。代理模式可以在不改变原始对象的代码的情况下,增强对象的功能或控制对对象的访问。

优点

  • 在不修改原对象的情况下增强功能
  • 控制对原对象的访问,提供保护
  • 可以延迟加载,提高性能

缺点

  • 增加系统复杂度
  • 可能引入性能延迟
  • 实现远程代理可能会很复杂

使用场景

  • 延迟加载(虚拟代理):在需要时才创建开销很大的对象
  • 访问控制(保护代理):控制对原始对象的访问权限
  • 远程调用(远程代理):为远程对象提供本地代理
  • 记录日志(智能引用):在访问对象前后进行额外操作

C++ 实现

// 主题接口
class Subject {
public:
    virtual ~Subject() = default;
    virtual void request() const = 0;
};

// 真实主题
class RealSubject : public Subject {
public:
    void request() const override {
        std::cout << "RealSubject: 处理请求" << std::endl;
    }
};

// 代理
class Proxy : public Subject {
private:
    RealSubject* realSubject_;
    bool ownRealSubject_;
    
    bool checkAccess() const {
        // 这里进行访问控制检查
        std::cout << "Proxy: 检查访问权限" << std::endl;
        return true;
    }
    
    void logAccess() const {
        std::cout << "Proxy: 记录访问时间: " 
                  << std::time(nullptr) << std::endl;
    }
    
public:
    // 构造函数,可以接受已有实例或创建新实例
    explicit Proxy(RealSubject* realSubject = nullptr)
        : realSubject_(realSubject), ownRealSubject_(realSubject == nullptr) {
        
        if (ownRealSubject_) {
            realSubject_ = new RealSubject();
        }
    }
    
    ~Proxy() {
        if (ownRealSubject_) {
            delete realSubject_;
        }
    }
    
    // 代理方法,在调用实际对象前后添加额外操作
    void request() const override {
        if (checkAccess()) {
            std::cout << "Proxy: 预处理" << std::endl;
            
            realSubject_->request();
            
            std::cout << "Proxy: 后处理" << std::endl;
            logAccess();
        } else {
            std::cout << "Proxy: 请求被拒绝" << std::endl;
        }
    }
};

// 延迟加载的虚拟代理
class VirtualProxy : public Subject {
private:
    RealSubject* realSubject_;
    mutable bool initialized_;
    
    void initialize() const {
        if (!initialized_) {
            std::cout << "VirtualProxy: 延迟加载实际对象" << std::endl;
            realSubject_ = new RealSubject();
            initialized_ = true;
        }
    }
    
public:
    VirtualProxy() : realSubject_(nullptr), initialized_(false) {}
    
    ~VirtualProxy() {
        delete realSubject_;
    }
    
    void request() const override {
        initialize();
        realSubject_->request();
    }
};

// 缓存代理示例
class CachingProxy : public Subject {
private:
    RealSubject* realSubject_;
    mutable std::map<std::string, std::string> cache_;
    
public:
    explicit CachingProxy(RealSubject* realSubject) 
        : realSubject_(realSubject) {}
    
    void request() const override {
        std::string key = "request_key"; // 实际应用中,这可能是一个参数
        
        // 检查缓存
        if (cache_.find(key) != cache_.end()) {
            std::cout << "CachingProxy: 返回缓存结果: " 
                      << cache_[key] << std::endl;
            return;
        }
        
        // 没有缓存,调用实际对象
        realSubject_->request();
        
        // 缓存结果
        cache_[key] = "result"; // 实际应用中,应该缓存实际结果
        std::cout << "CachingProxy: 结果已缓存" << std::endl;
    }
};

// 客户端代码
void clientCode(const Subject& subject) {
    subject.request();
}

int main() {
    std::cout << "客户端: 直接调用真实主题:" << std::endl;
    RealSubject realSubject;
    clientCode(realSubject);
    std::cout << std::endl;
    
    std::cout << "客户端: 通过代理调用真实主题:" << std::endl;
    Proxy proxy(&realSubject);
    clientCode(proxy);
    std::cout << std::endl;
    
    std::cout << "客户端: 通过虚拟代理调用真实主题:" << std::endl;
    VirtualProxy virtualProxy;
    std::cout << "第一次调用:" << std::endl;
    clientCode(virtualProxy);
    std::cout << "第二次调用:" << std::endl;
    clientCode(virtualProxy);
    std::cout << std::endl;
    
    std::cout << "客户端: 通过缓存代理调用真实主题:" << std::endl;
    CachingProxy cachingProxy(&realSubject);
    std::cout << "第一次调用:" << std::endl;
    clientCode(cachingProxy);
    std::cout << "第二次调用:" << std::endl;
    clientCode(cachingProxy);
    
    return 0;
}
09

模板方法模式

Template Method Pattern

定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。

优点

  • 封装不变部分,扩展可变部分
  • 提取公共代码,便于维护
  • 通过钩子方法灵活控制算法

缺点

  • 每个不同的实现都需要一个子类
  • 增加了系统复杂度
  • 继承关系自身缺点,如果父类增加新的抽象方法,所有子类都要改

使用场景

  • 多个类有相似的算法骨架,但具体步骤不同
  • 将算法的不变部分和变化部分分离,以提高代码复用性
  • 控制子类扩展的方式和范围

C++ 实现

// 抽象类,定义模板方法和原语操作
class AbstractClass {
public:
    // 模板方法,定义了算法骨架
    void templateMethod() const {
        // 算法的固定步骤
        baseOperation1();
        requiredOperation1();
        baseOperation2();
        hook1();
        requiredOperation2();
        baseOperation3();
        hook2();
    }
    
    virtual ~AbstractClass() = default;
    
protected:
    // 基本操作,已在抽象类中实现
    void baseOperation1() const {
        std::cout << "AbstractClass: 执行基本操作1\n";
    }
    
    void baseOperation2() const {
        std::cout << "AbstractClass: 执行基本操作2\n";
    }
    
    void baseOperation3() const {
        std::cout << "AbstractClass: 执行基本操作3\n";
    }
    
    // 必须由子类实现的抽象操作
    virtual void requiredOperation1() const = 0;
    virtual void requiredOperation2() const = 0;
    
    // 钩子方法,子类可以选择性重写
    virtual void hook1() const {}
    virtual void hook2() const {}
};

// 具体类A
class ConcreteClassA : public AbstractClass {
protected:
    void requiredOperation1() const override {
        std::cout << "ConcreteClassA: 实现操作1\n";
    }
    
    void requiredOperation2() const override {
        std::cout << "ConcreteClassA: 实现操作2\n";
    }
    
    void hook1() const override {
        std::cout << "ConcreteClassA: 重写钩子1\n";
    }
};

// 具体类B
class ConcreteClassB : public AbstractClass {
protected:
    void requiredOperation1() const override {
        std::cout << "ConcreteClassB: 实现操作1\n";
    }
    
    void requiredOperation2() const override {
        std::cout << "ConcreteClassB: 实现操作2\n";
    }
    
    void hook2() const override {
        std::cout << "ConcreteClassB: 重写钩子2\n";
    }
};

// 数据处理实例
class DataProcessor {
public:
    virtual ~DataProcessor() = default;
    
    // 模板方法定义了数据处理算法
    void processData() {
        openFile();
        extractData();
        transformData();
        analyze();
        sendReport();
        closeFile();
    }
    
protected:
    void openFile() {
        std::cout << "打开数据文件\n";
    }
    
    void closeFile() {
        std::cout << "关闭数据文件\n";
    }
    
    // 需要子类实现的抽象方法
    virtual void extractData() = 0;
    virtual void transformData() = 0;
    virtual void analyze() = 0;
    virtual void sendReport() = 0;
};

// CSV数据处理器
class CSVProcessor : public DataProcessor {
protected:
    void extractData() override {
        std::cout << "从CSV文件中提取数据\n";
    }
    
    void transformData() override {
        std::cout << "转换CSV数据格式\n";
    }
    
    void analyze() override {
        std::cout << "分析CSV数据\n";
    }
    
    void sendReport() override {
        std::cout << "生成CSV分析报告\n";
    }
};

// JSON数据处理器
class JSONProcessor : public DataProcessor {
protected:
    void extractData() override {
        std::cout << "从JSON文件中提取数据\n";
    }
    
    void transformData() override {
        std::cout << "转换JSON数据格式\n";
    }
    
    void analyze() override {
        std::cout << "分析JSON数据\n";
    }
    
    void sendReport() override {
        std::cout << "生成JSON分析报告\n";
    }
};

// 客户端代码
void clientCode(AbstractClass* abstractClass) {
    // 调用模板方法,执行算法
    abstractClass->templateMethod();
}

int main() {
    std::cout << "基础示例演示:\n";
    ConcreteClassA* concreteA = new ConcreteClassA();
    clientCode(concreteA);
    std::cout << "\n";
    
    ConcreteClassB* concreteB = new ConcreteClassB();
    clientCode(concreteB);
    
    std::cout << "\n数据处理示例演示:\n";
    std::cout << "处理CSV数据:\n";
    CSVProcessor csvProcessor;
    csvProcessor.processData();
    
    std::cout << "\n处理JSON数据:\n";
    JSONProcessor jsonProcessor;
    jsonProcessor.processData();
    
    delete concreteA;
    delete concreteB;
    
    return 0;
}
10

组合模式

Composite Pattern

将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性。组合模式使得客户端可以统一对待单个对象和对象组合。

优点

  • 定义了包含基本对象和复合对象的类层次结构
  • 使客户端代码可以统一处理单个对象和组合对象
  • 使添加新组件变得容易,符合开闭原则

缺点

  • 在特定情况下很难限制组件的类型
  • 接口设计可能会变得过于一般化
  • 可能会使设计变得过于复杂

使用场景

  • 需要表示对象的部分-整体层次结构时
  • 希望客户端忽略单个对象和组合对象的差别
  • GUI组件树、文件系统管理、组织架构等

C++ 实现

// 组件抽象类
class Component {
protected:
    Component* parent_;
    
public:
    Component() : parent_(nullptr) {}
    virtual ~Component() = default;
    
    void setParent(Component* parent) {
        parent_ = parent;
    }
    
    Component* getParent() const {
        return parent_;
    }
    
    // 在组件中添加子组件
    virtual void add(Component* component) {
        throw std::runtime_error("无法添加子组件");
    }
    
    // 移除子组件
    virtual void remove(Component* component) {
        throw std::runtime_error("无法移除子组件");
    }
    
    // 检查是否是复合对象
    virtual bool isComposite() const {
        return false;
    }
    
    // 执行组件操作
    virtual std::string operation() const = 0;
};

// 叶子节点 - 不包含子组件的基本对象
class Leaf : public Component {
public:
    explicit Leaf(const std::string& name) : name_(name) {}
    
    std::string operation() const override {
        return name_;
    }
    
private:
    std::string name_;
};

// 复合节点 - 可以包含叶子节点或其他复合节点
class Composite : public Component {
private:
    std::vector<Component*> children_;
    std::string name_;
    
public:
    explicit Composite(const std::string& name) : name_(name) {}
    
    ~Composite() override {
        for (auto child : children_) {
            delete child;
        }
    }
    
    void add(Component* component) override {
        children_.push_back(component);
        component->setParent(this);
    }
    
    void remove(Component* component) override {
        auto it = std::find(children_.begin(), children_.end(), component);
        if (it != children_.end()) {
            (*it)->setParent(nullptr);
            children_.erase(it);
        }
    }
    
    bool isComposite() const override {
        return true;
    }
    
    // 递归执行所有子组件的操作
    std::string operation() const override {
        std::string result = name_ + " [ ";
        
        for (size_t i = 0; i < children_.size(); ++i) {
            result += children_[i]->operation();
            if (i != children_.size() - 1) {
                result += ", ";
            }
        }
        
        return result + " ]";
    }
};

// 文件系统示例
class FileSystemComponent {
public:
    virtual ~FileSystemComponent() = default;
    virtual std::string getName() const = 0;
    virtual int getSize() const = 0;
    virtual void display(int depth = 0) const = 0;
    virtual void add(FileSystemComponent* component) {
        throw std::runtime_error("不支持添加操作");
    }
    virtual void remove(FileSystemComponent* component) {
        throw std::runtime_error("不支持移除操作");
    }
};

// 文件类
class File : public FileSystemComponent {
private:
    std::string name_;
    int size_;
    
public:
    File(const std::string& name, int size) : name_(name), size_(size) {}
    
    std::string getName() const override {
        return name_;
    }
    
    int getSize() const override {
        return size_;
    }
    
    void display(int depth = 0) const override {
        std::string indent(depth * 2, ' ');
        std::cout << indent << "- " << name_ << " (" << size_ << "KB)" << std::endl;
    }
};

// 文件夹类
class Directory : public FileSystemComponent {
private:
    std::string name_;
    std::vector<FileSystemComponent*> children_;
    
public:
    explicit Directory(const std::string& name) : name_(name) {}
    
    ~Directory() {
        for (auto child : children_) {
            delete child;
        }
    }
    
    std::string getName() const override {
        return name_;
    }
    
    int getSize() const override {
        int totalSize = 0;
        for (auto child : children_) {
            totalSize += child->getSize();
        }
        return totalSize;
    }
    
    void add(FileSystemComponent* component) override {
        children_.push_back(component);
    }
    
    void remove(FileSystemComponent* component) override {
        auto it = std::find(children_.begin(), children_.end(), component);
        if (it != children_.end()) {
            children_.erase(it);
        }
    }
    
    void display(int depth = 0) const override {
        std::string indent(depth * 2, ' ');
        std::cout << indent << "+ " << name_ 
                  << " (" << getSize() << "KB)" << std::endl;
        
        for (const auto& child : children_) {
            child->display(depth + 1);
        }
    }
};

// 客户端代码
int main() {
    // 基本示例
    std::cout << "基本组合模式示例:\n";
    
    Leaf* leaf1 = new Leaf("Leaf 1");
    Leaf* leaf2 = new Leaf("Leaf 2");
    Leaf* leaf3 = new Leaf("Leaf 3");
    
    Composite* branch1 = new Composite("Branch 1");
    branch1->add(leaf1);
    branch1->add(leaf2);
    
    Composite* branch2 = new Composite("Branch 2");
    branch2->add(leaf3);
    
    Composite* tree = new Composite("Tree");
    tree->add(branch1);
    tree->add(branch2);
    
    std::cout << "树结构: " << tree->operation() << std::endl;
    
    delete tree;
    
    // 文件系统示例
    std::cout << "\n文件系统示例:\n";
    
    Directory* root = new Directory("root");
    
    Directory* documents = new Directory("documents");
    Directory* pictures = new Directory("pictures");
    
    File* file1 = new File("report.doc", 100);
    File* file2 = new File("presentation.ppt", 2500);
    File* pic1 = new File("vacation.jpg", 3000);
    File* pic2 = new File("family.png", 2000);
    
    documents->add(file1);
    documents->add(file2);
    
    pictures->add(pic1);
    pictures->add(pic2);
    
    root->add(documents);
    root->add(pictures);
    
    root->display();
    
    std::cout << "\n根目录总大小: " << root->getSize() << "KB" << std::endl;
    std::cout << "文档目录总大小: " << documents->getSize() << "KB" << std::endl;
    std::cout << "图片目录总大小: " << pictures->getSize() << "KB" << std::endl;
    
    delete root;
    
    return 0;
}

如何选择合适的设计模式

设计模式是解决软件设计中特定问题的模板,然而每种模式都有其适用场景。以下是选择合适设计模式的指导原则:

基于问题识别

  • 明确你要解决的是什么问题
  • 查找该问题对应的设计模式
  • 参考设计模式的意图和适用性部分

考虑系统上下文

  • 评估模式是否与现有架构兼容
  • 考虑团队对该模式的熟悉度
  • 思考模式的长期维护成本

权衡利弊

  • 每种模式都有优缺点,避免过度设计
  • 权衡设计的灵活性与复杂性
  • 有时简单直接的解决方案比模式更适合

组合使用模式

  • 设计模式通常可以组合使用
  • 有些模式自然地相互补充
  • 学习常见的模式组合方式

常见设计问题与推荐模式

设计问题 推荐模式 原因
需要确保一个类只有一个实例 单例模式 控制资源访问,提供全局访问点
对象创建逻辑复杂或需要解耦 工厂方法模式 分离对象创建和使用,支持扩展
需要通知多个对象状态变化 观察者模式 实现松耦合的对象通知机制
需要动态选择算法实现 策略模式 封装算法族,支持运行时切换
需要动态添加功能且不修改原类 装饰器模式 灵活组合功能,避免类爆炸