面向对象设计原则

发布于:2025-09-13 ⋅ 阅读:(17) ⋅ 点赞:(0)

类和类之间的关系

关系类型 核心语义 C++ 实现方式 生命周期依赖 典型示例
继承 is-a(是一种) class 子类 : 访问控制 父类 子类依赖父类 狗→动物
实现 is-a(符合规范) 子类继承「纯虚函数抽象类」 类依赖接口 圆形→可绘制接口
关联 has-a(关联) 成员变量存储对方指针 / 引用 + 容器 老师→学生
聚合 has-a(整体 - 部分) 整体通过外部传入部分指针 学校→老师
组合 has-a(整体 - 部分) 整体内部定义部分为「成员对象」 有(同生共死) 汽车→发动机
依赖 use-a(依赖功能) 调用静态方法 / 方法参数传入依赖对象 临时依赖 计算器→数学工具类

类和类之间的关系

1、继承()

基类会成为派生类的一部分。在语义层面:A is B。在类图的画法上:从派生类指向基类的空心三角箭头。

继承:先有基类,然后派生出新的类,也就是派生类。

泛化(一般化):先有派生类,将相同的属性抽出来,然后形成基类。

2、关联
2.1、双向的关联关系

双向的关联关系,彼此知道对方的存在,但是彼此不负责对方的生命周期。在语义层面:A has B。在代码层面上:使用的是指针或者引用。在类图的画法上:使用的是实心直线。

2.2、单向的关联关系

单向的关联关系,A知道对方的存在,但是B不知道A的存在。A不负责B的生命周期。在语义层面:A has B。在代码层面上:使用的是指针或者引用。在类图的画法上:从A指向B的实线箭头。

3、聚合

是一种稍强的关联关系,表现为整体与局部的关系,但是整体并不负责局部的销毁。在语义层面上:A has B。在代码层面上:使用的是指针或者引用。在类图的画法上:从局部指向整体的空心菱形箭头。

4、组合

是一种更强的关联关系,表现为整体与局部的关系,并且整体会负责局部的销毁。在语义层面上:A has B。在代码层面上:使用的是子对象。在类图的画法上:从局部指向整体的实心菱形箭头。

5、依赖

是两个类之间的一种不确定的关系,语义上是一种:A use B。这种关系是偶然的,临时的,并非固定的。在代码上表现为三种形式:

  • B作为A的成员函数参数;

  • B作为A的成员函数的局部变量或者B作为A的成员函数的返回值;

  • A的成员函数调用B的静态方法。

类图的画法上:使用虚线的箭头从A指向B

6、总结
  • 继承是垂直关系,其它的四种是水平关系;

  • 从语义划分:继承 is;依赖:use;关联、聚合、组合:has;

  • 从耦合强弱:依赖 < 关联 < 聚合 < 组合 < 继承;

  • 从代码层面:依赖:主要体现在成员函数上; 关联、聚合、组合:主要体现在数据成员;继承:既有数据成员,也有成员函数;

面向对象的设计原则

原则名称 核心思想(通俗解释) 设计目的(解决的问题)
单一职责原则(SRP) 一个类 / 模块只干 “一件明确的事”,比如 “计算器只算数学题”“日志工具只记录操作”,不把无关功能堆在一起。 1. 降低复杂度:避免一个类又管计算又管日志,逻辑混乱;
2. 减少修改风险:改日志功能时,不会影响计算器的计算逻辑。
开放 - 封闭原则(OCP) 对 “新增功能” 开放(能通过加新代码实现),对 “修改旧代码” 封闭(不用改已稳定的老代码)。比如 “图形面积计算”,新增 “三角形” 时,不用改原来 “圆形、矩形” 的计算逻辑,只加个三角形类。可扩展性 1. 保护旧代码:避免改老代码时引入新 bug;
2. 适应需求变化:新增功能时,不用推翻原有设计。
里氏替换原则(LSP) 子类能 “完全代替父类” 干活,且不破坏父类的 “行为约定”。比如父类 “鸟” 约定 “会飞”,子类 “麻雀” 能飞(符合),但子类 “鸵鸟” 不会飞(违反)—— 因为用鸵鸟代替鸟时,“飞” 的功能就失效了。 可替代性 1. 保证继承的正确性:避免子类 “篡改” 父类逻辑,比如父类 “排序” 是正序,子类改成逆序,就会让调用者出错;
2. 确保代码兼容性:调用父类的地方,换子类也能正常运行。
依赖倒置原则(DIP) 1. 高层模块(比如 “手机”)不依赖低层模块(比如 “安卓充电器、苹果充电器”),而是依赖 “抽象接口”(比如 “充电接口”);面向接口编程,依赖于抽象
2. 低层模块(充电器)反过来依赖抽象接口(符合充电接口的规则)。
1. 解耦:比如换充电器时,不用改手机的逻辑(只要新充电器符合充电接口);
2. 灵活替换:低层模块变了,高层模块不用动。
接口隔离原则(ISP) 接口要 “小而专”,别搞 “大而全的 “万能接口”。比如 “工作者接口” 不该包含 “编程”“做饭”“开车” 所有方法,而是拆成 “程序员接口”(只有编程)、“厨师接口”(只有做饭),需要哪个就实现哪个。 1. 避免 “被迫实现无用方法”:比如让程序员实现 “做饭” 方法,明明用不上却必须写空逻辑;
2. 减少冗余:接口只包含必要的功能,逻辑更清晰。
迪米特法则(LOD) 一个对象只和自己的 “直接朋友” 打交道,别管 “陌生人” 的事。“朋友” 指:自己的成员变量、方法参数、返回值;“陌生人” 指:朋友的朋友。比如 A 依赖 B,B 依赖 C,A 不该直接调用 C 的方法,要通过 B 中转 1. 降低耦合:避免 A 和 C 直接关联,C 变了 A 不用改;
2. 简化依赖关系:对象只关注身边的朋友,不用管深层逻辑。
合成复用原则(CRP) 优先用 “组合 / 聚合”(比如 “汽车有发动机”)代替 “继承”(比如 “汽车继承发动机”)来复用代码。比如想让汽车有 “发动” 功能,不用让汽车继承发动机(继承耦合太高),而是给汽车加个 “发动机” 成员(组合),调用发动机的发动方法。 1. 降低耦合:继承是 “强绑定”(改发动机要改汽车类),组合是 “弱绑定”(换发动机只需换成员变量);
2. 提高灵活性:能动态替换复用的功能(比如给汽车换涡轮增压发动机)。

三、设计模式

1、概念

设计模式是面向对象编程(OOP)中解决通用问题的成熟方案,核心目标是提升代码的复用性、可维护性、可扩展性,避免重复造轮子。它不是语法规则,而是基于大量实践总结的 “设计思想模板”,通常分为创建型、结构型、行为型三大类,每类对应不同场景的问题(如 “如何创建对象”“如何组织类结构”“如何交互通信”)。

设计模式的分类与核心目标

类别 核心目标 典型模式举例
创建型 控制对象的创建过程,隐藏创建细节,降低耦合(如 “确保一个类只有一个实例”) 单例模式、工厂方法模式、建造者模式
结构型 优化类或对象的组合关系,实现灵活的结构扩展(如 “适配不同接口”“动态增强功能”) 适配器模式、装饰器模式、组合模式
行为型 定义对象间的交互逻辑,规范通信方式,减少依赖(如 “一个对象变了通知其他对象”) 观察者模式、策略模式、迭代器模式

高频常用设计模式详解(C++ 实现)

一、创建型模式:解决 “对象如何创建” 的问题

 单例模式(Singleton):创建型

1. 简单工厂模式(静态工厂方法)

核心定义

提供一个静态工厂类,通过传入参数判断创建哪种具体产品,实现 “对象创建与使用分离”。
⚠️ 本质是 “工厂集中判断”,非 23 种 GOF 模式,但是工厂模式的基础。

类图关系(文字描述)

核心代码
#include <iostream>
#include <memory>
using namespace std;

// 1. 抽象产品:定义产品统一接口
class Product {
public:
    virtual void show() = 0; // 纯虚函数,强制子类实现
    virtual ~Product() {}
};

// 2. 具体产品:实现抽象接口
class ProductA : public Product {
public:
    void show() override { cout << "ProductA::show()" << endl; }
};
class ProductB : public Product {
public:
    void show() override { cout << "ProductB::show()" << endl; }
};

// 3. 静态工厂:集中创建产品(核心)
class ProductFactory {
public:
    // 静态方法:根据类型创建产品,返回智能指针避免内存泄漏
    static unique_ptr<Product> createProduct(int type) {
        switch (type) {
            case 1: return make_unique<ProductA>();
            case 2: return make_unique<ProductB>();
            default: return nullptr;
        }
    }
};

// 使用示例
void test() {
    // 客户端只需传参数,无需关心创建细节
    auto pa = ProductFactory::createProduct(1);
    auto pb = ProductFactory::createProduct(2);
    if (pa) pa->show();
    if (pb) pb->show();
}
优缺点
优点 缺点
解耦创建与使用,客户端无需知产品类名 工厂职责过重,新增产品需修改工厂(违反开闭原则)
简化客户端代码,仅需传参数 静态方法无法继承,工厂类无法扩展
便于统一管理产品创建 产品过多时,switch 逻辑臃肿
使用场景
  • 需创建的产品类型少(避免 switch 臃肿);
  • 客户端仅知产品参数,不关心创建细节(如工具类、简单组件)。

2. 工厂方法模式

核心定义

对简单工厂的工厂类抽象化:核心工厂变为抽象类,每个具体产品对应一个具体工厂,由子类决定创建哪种产品(“延迟到子类创建”)。

类图关系(文字描述)

核心代码
#include <iostream>
#include <memory>
using namespace std;

// 1. 抽象产品
class Product {
public:
    virtual void show() = 0;
    virtual ~Product() {}
};
class ProductA : public Product {
public:
    void show() override { cout << "ProductA::show()" << endl; }
};
class ProductB : public Product {
public:
    void show() override { cout << "ProductB::show()" << endl; }
};

// 2. 抽象工厂(核心:定义工厂接口)
class Factory {
public:
    virtual unique_ptr<Product> createProduct() = 0; // 纯虚方法
    virtual ~Factory() {}
};

// 3. 具体工厂:一个产品对应一个工厂
class FactoryA : public Factory {
public:
    unique_ptr<Product> createProduct() override {
        return make_unique<ProductA>();
    }
};
class FactoryB : public Factory {
public:
    unique_ptr<Product> createProduct() override {
        return make_unique<ProductB>();
    }
};

// 使用示例
void test() {
    // 客户端需指定工厂类型,工厂创建对应产品
    unique_ptr<Factory> fa = make_unique<FactoryA>();
    unique_ptr<Product> pa = fa->createProduct();
    pa->show();

    unique_ptr<Factory> fb = make_unique<FactoryB>();
    unique_ptr<Product> pb = fb->createProduct();
    pb->show();
}
优缺点
优点 缺点
新增产品仅需加 “具体产品 + 具体工厂”(符合开闭原则) 类数量爆炸(N 产品需 N 工厂 + 1 抽象工厂)
工厂可继承扩展,支持多态 客户端需知工厂与产品的对应关系,增加理解成本
解耦更彻底,工厂职责单一 仅支持单产品等级结构(无法同时创多个关联产品)
使用场景
  • 产品类型多变,需频繁新增产品(如插件、扩展组件);
  • 客户端知工厂名,不知产品创建细节(如框架中的插件工厂)。

3. 抽象工厂模式

核心定义

解决 “产品族” 创建问题:抽象工厂定义多个产品的创建接口,具体工厂对应一个产品族(同一品牌 / 系列的多类产品),实现 “一族产品统一创建”。

关键概念
  • 产品等级结构:同一类产品的继承树(如 “电视机”→“海尔电视”“TCL 电视”);
  • 产品族:同一工厂生产的多等级产品(如 “海尔工厂”→“海尔电视”“海尔冰箱”)。

类图关系(文字描述)

核心代码
#include <iostream>
#include <memory>
using namespace std;

// 1. 抽象产品族(多等级产品)
class AbstractProductA { // 产品A等级(如“电视”)
public:
    virtual void show() = 0;
    virtual ~AbstractProductA() {}
};
class AbstractProductB { // 产品B等级(如“冰箱”)
public:
    virtual void show() = 0;
    virtual ~AbstractProductB() {}
};

// 2. 具体产品(分产品族)
class ProductA1 : public AbstractProductA { // 族1的A产品(海尔电视)
public:
    void show() override { cout << "ProductA1::show()" << endl; }
};
class ProductB1 : public AbstractProductB { // 族1的B产品(海尔冰箱)
public:
    void show() override { cout << "ProductB1::show()" << endl; }
};
class ProductA2 : public AbstractProductA { // 族2的A产品(TCL电视)
public:
    void show() override { cout << "ProductA2::show()" << endl; }
};
class ProductB2 : public AbstractProductB { // 族2的B产品(TCL冰箱)
public:
    void show() override { cout << "ProductB2::show()" << endl; }
};

// 3. 抽象工厂(定义产品族的创建接口)
class AbstractFactory {
public:
    virtual unique_ptr<AbstractProductA> createProductA() = 0;
    virtual unique_ptr<AbstractProductB> createProductB() = 0;
    virtual ~AbstractFactory() {}
};

// 4. 具体工厂(对应产品族)
class Factory1 : public AbstractFactory { // 族1工厂(海尔工厂)
public:
    unique_ptr<AbstractProductA> createProductA() override {
        return make_unique<ProductA1>();
    }
    unique_ptr<AbstractProductB> createProductB() override {
        return make_unique<ProductB1>();
    }
};
class Factory2 : public AbstractFactory { // 族2工厂(TCL工厂)
public:
    unique_ptr<AbstractProductA> createProductA() override {
        return make_unique<ProductA2>();
    }
    unique_ptr<AbstractProductB> createProductB() override {
        return make_unique<ProductB2>();
    }
};

// 使用示例
void test() {
    // 创族1产品(海尔电视+冰箱)
    unique_ptr<AbstractFactory> f1 = make_unique<Factory1>();
    auto pa1 = f1->createProductA();
    auto pb1 = f1->createProductB();
    pa1->show();
    pb1->show();

    // 创族2产品(TCL电视+冰箱)
    unique_ptr<AbstractFactory> f2 = make_unique<Factory2>();
    auto pa2 = f2->createProductA();
    auto pb2 = f2->createProductB();
    pa2->show();
    pb2->show();
}
优缺点
优点 缺点
保证同一产品族的一致性(避免混用餐品牌) 新增产品等级(如加 “洗衣机”)需修改所有工厂(违反开闭)
解耦产品族创建与使用,客户端无需知具体产品 类结构复杂,工厂与产品多对多,理解成本高
切换产品族仅需换工厂,灵活性高 产品族过多时,类数量急剧增加
使用场景
  • 系统需使用多个关联产品(产品族),且需保证族内一致性(如 UI 组件库、品牌套件);
  • 产品族稳定,极少新增产品等级(如家电、汽车系列)。

二、结构型模式:解决 “对象如何组合” 的问题

核心目标:通过组合优化对象结构,实现功能扩展或解耦。

4. 装饰器模式

核心定义

动态给对象添加新功能:不修改原对象代码,通过 “装饰器类包装原对象”,递归叠加多个装饰器实现功能组合(“套娃式扩展”)。

类图关系(文字描述)

核心代码
#include <iostream>
#include <memory>
using namespace std;

// 1. 抽象组件:定义核心功能接口
class Component {
public:
    virtual void operation() = 0;
    virtual ~Component() {}
};

// 2. 具体组件:原始对象(被装饰者)
class ConcreteComponent : public Component {
public:
    void operation() override {
        cout << "原始组件:基础功能" << endl;
    }
};

// 3. 抽象装饰器:包装组件,保持接口一致
class Decorator : public Component {
public:
    Decorator(Component* comp) : _comp(comp) {} // 注入被装饰组件
    void operation() override {
        if (_comp) _comp->operation(); // 调用被装饰者的基础功能
    }
    virtual ~Decorator() {}
protected:
    Component* _comp; // 指向被装饰的组件(多态)
};

// 4. 具体装饰器:扩展功能
class DecoratorA : public Decorator {
public:
    DecoratorA(Component* comp) : Decorator(comp) {}
    void operation() override {
        Decorator::operation(); // 先执行基础功能
        addFuncA(); // 新增功能A
    }
private:
    void addFuncA() { cout << "装饰器A:新增功能A" << endl; }
};
class DecoratorB : public Decorator {
public:
    DecoratorB(Component* comp) : Decorator(comp) {}
    void operation() override {
        Decorator::operation(); // 先执行基础功能(含A的扩展)
        addFuncB(); // 新增功能B
    }
private:
    void addFuncB() { cout << "装饰器B:新增功能B" << endl; }
};

// 使用示例
void test() {
    // 1. 原始组件
    unique_ptr<Component> comp = make_unique<ConcreteComponent>();
    comp->operation(); // 仅基础功能

    // 2. 装饰器A包装(基础+A)
    unique_ptr<Decorator> decA = make_unique<DecoratorA>(comp.get());
    decA->operation();

    // 3. 装饰器B包装A(基础+A+B)
    unique_ptr<Decorator> decB = make_unique<DecoratorB>(decA.get());
    decB->operation();
}
优缺点
优点 缺点
动态扩展功能,无需修改原代码(符合开闭) 多层装饰后,调试困难(套娃结构)
功能可组合(多个装饰器叠加) 类数量增加,结构稍复杂
比继承灵活(继承是静态扩展,装饰是动态) 需维护装饰器与组件的接口一致性
使用场景
  • 需动态给对象加 / 删功能(如 UI 组件加边框、阴影、滚动条);
  • 避免用继承产生大量子类(如 1 个组件 + 3 功能 = 3 装饰器,而非 8 个子类);
  • 功能可组合(如日志系统加 “时间戳 + 格式化 + 加密”)。

三、行为型模式:解决 “对象如何交互” 的问题

核心目标:优化对象间的通信、职责分配与行为复用。

5. 观察者模式

核心定义

定义 “一对多” 依赖:当被观察者(主题) 状态变化时,自动通知所有观察者,并触发其更新(“发布 - 订阅” 模式)。

类图关系(文字描述)

核心代码
#include <iostream>
#include <list>
#include <memory>
#include <string>
using namespace std;

// 前向声明:观察者需知道被观察者
class Subject;

// 1. 观察者抽象类
class Observer {
public:
    virtual void update(const Subject& sub) = 0; // 接收被观察者通知
    virtual ~Observer() {}
};

// 2. 被观察者抽象类
class Subject {
public:
    void attach(Observer* obs) { // 注册观察者
        if (obs) _observers.push_back(obs);
    }
    void detach(Observer* obs) { // 移除观察者
        _observers.remove(obs);
    }
    void notify() { // 通知所有观察者
        for (auto obs : _observers) {
            obs->update(*this);
        }
    }
    virtual int getStatus() const = 0; // 供观察者获取状态
    virtual void setStatus(int s) = 0;
    virtual ~Subject() {}
private:
    list<Observer*> _observers; // 观察者列表
};

// 3. 具体被观察者(如“天气预报主题”)
class ConcreteSubject : public Subject {
public:
    int getStatus() const override { return _status; }
    void setStatus(int s) override {
        _status = s;
        notify(); // 状态变化时自动通知
    }
private:
    int _status; // 状态(如温度)
};

// 4. 具体观察者(如“手机APP”“电视端”)
class ConcreteObserver : public Observer {
public:
    ConcreteObserver(string name) : _name(name) {}
    void update(const Subject& sub) override {
        // 响应通知:获取被观察者状态并处理
        cout << _name << " 收到更新,当前状态:" << sub.getStatus() << endl;
    }
private:
    string _name;
};

// 使用示例
void test() {
    unique_ptr<Subject> sub = make_unique<ConcreteSubject>();
    unique_ptr<Observer> obs1 = make_unique<ConcreteObserver>("手机APP");
    unique_ptr<Observer> obs2 = make_unique<ConcreteObserver>("电视端");

    sub->attach(obs1.get());
    sub->attach(obs2.get());

    sub->setStatus(25); // 状态变化→自动通知
    sub->detach(obs2.get()); // 移除电视端
    sub->setStatus(30); // 仅手机APP收到
}
优缺点
优点 缺点
解耦主题与观察者(主题无需知观察者具体类型) 观察者过多时,通知耗时(性能瓶颈)
支持动态注册 / 移除观察者(灵活性高) 可能产生循环依赖(如 A 观察 B,B 观察 A)
符合开闭原则(新增观察者无需改主题) 观察者仅知 “状态变了”,不知 “怎么变的”
使用场景
  • 一个对象变化需触发多个对象更新(如 GUI 事件、日志系统、消息通知);
  • 主题与观察者松耦合(如微信公众号、天气预报推送)。

6. 策略模式

核心定义

定义 “一系列算法”,将每个算法封装为独立类,使算法可动态切换(算法族与使用算法的客户端解耦)。

类图关系(文字描述)

核心代码
#include <iostream>
#include <memory>
using namespace std;

// 1. 策略抽象类(算法接口)
class Strategy {
public:
    virtual void execute() = 0; // 算法执行接口
    virtual ~Strategy() {}
};

// 2. 具体策略(不同算法实现)
class StrategyA : public Strategy { // 算法A(如支付宝支付)
public:
    void execute() override {
        cout << "策略A:执行支付宝支付" << endl;
    }
};
class StrategyB : public Strategy { // 算法B(如微信支付)
public:
    void execute() override {
        cout << "策略B:执行微信支付" << endl;
    }
};

// 3. 上下文(使用算法的客户端)
class Context {
public:
    Context(Strategy* stra) : _strategy(stra) {}
    void setStrategy(Strategy* stra) { // 动态切换策略
        _strategy = stra;
    }
    void doOperation() { // 调用策略的算法
        if (_strategy) _strategy->execute();
    }
private:
    Strategy* _strategy; // 策略指针(多态)
};

// 使用示例
void test() {
    // 初始用策略A
    unique_ptr<Strategy> sa = make_unique<StrategyA>();
    Context ctx(sa.get());
    ctx.doOperation();

    // 动态切换为策略B
    unique_ptr<Strategy> sb = make_unique<StrategyB>();
    ctx.setStrategy(sb.get());
    ctx.doOperation();
}
优缺点
优点 缺点
算法可动态切换(无需改上下文代码) 算法过多时,策略类数量爆炸
避免 if-else 判断(算法选择由上下文决定) 客户端需知所有策略(才能选择合适的)
算法独立封装,便于维护与复用 策略间无依赖,无法共享数据
使用场景
  • 多种同类算法需切换(如排序算法、支付方式、压缩算法);
  • 避免用 if-else 堆砌算法(如 “if 支付宝→xxx else if 微信→xxx”);
  • 算法需独立扩展(如新增 “银行卡支付” 仅需加 StrategyC)。

7. 模板方法模式

核心定义

定义 “算法骨架”:在抽象基类中固定算法的步骤流程,将可变步骤延迟到子类实现(整体流程稳定,局部细节可变)。

类图关系(文字描述)

核心代码
#include <iostream>
using namespace std;

// 1. 抽象基类:定义算法骨架
class AbstractReport {
public:
    // 模板方法(核心:固定算法流程,不可重写)
    void generateReport() final {
        collectData();   // 步骤1:固定实现
        processData();   // 步骤2:可变实现(纯虚)
        formatReport();  // 步骤3:固定实现
        saveReport();    // 步骤4:默认实现(可重写)
    }
    virtual ~AbstractReport() {}
protected:
    // 固定步骤:基类实现,子类无需改
    void collectData() { cout << "1. 收集数据" << endl; }
    void formatReport() { cout << "3. 格式化报告为PDF" << endl; }

    // 可变步骤1:纯虚函数,子类必须实现
    virtual void processData() = 0;

    // 可变步骤2:默认实现,子类可重写
    virtual void saveReport() { cout << "4. 保存到服务器" << endl; }
};

// 2. 具体子类1:实现可变步骤
class SalesReport : public AbstractReport {
protected:
    void processData() override { // 实现纯虚步骤
        cout << "2. 处理销售数据:计算销售额" << endl;
    }
    // 不重写saveReport,用默认实现
};

// 3. 具体子类2:实现可变步骤+重写默认步骤
class InventoryReport : public AbstractReport {
protected:
    void processData() override { // 实现纯虚步骤
        cout << "2. 处理库存数据:分析缺货商品" << endl;
    }
    void saveReport() override { // 重写默认步骤
        cout << "4. 保存到本地数据库" << endl;
    }
};

// 使用示例
void test() {
    SalesReport sales;
    cout << "=== 生成销售报告 ===" << endl;
    sales.generateReport();

    InventoryReport inventory;
    cout << "\n=== 生成库存报告 ===" << endl;
    inventory.generateReport();
}
优缺点
优点 缺点
代码复用:固定步骤在基类,避免重复 算法骨架固定,子类无法修改流程(如调整步骤顺序)
控制反转:基类控流程,子类填细节 子类过多时,维护成本高
符合开闭原则:新增算法仅需加子类 简单场景用模板方法,会增加抽象层次(过度设计)
使用场景
  • 算法流程稳定,仅部分步骤可变(如报告生成、流程审批、游戏角色技能释放);
  • 多个子类有相似流程,仅细节不同(如不同报表的生成、不同支付的校验)。

8. 职责链模式

核心定义

将请求的 “处理者” 连成链,请求沿链传递,直到有处理者能处理(解耦请求发送者与接收者,无需知谁处理)。

类图关系(文字描述)

核心代码
#include <iostream>
#include <string>
#include <memory>
using namespace std;

// 1. 请求类:封装请求信息
class Request {
public:
    Request(string name, int amount) : _name(name), _amount(amount) {}
    string getName() const { return _name; }
    int getAmount() const { return _amount; }
private:
    string _name;   // 请求人
    int _amount;    // 请求金额(如加薪金额)
};

// 2. 抽象处理者
class Approver {
public:
    Approver() : _next(nullptr) {}
    void setNext(Approver* next) { // 设置下一个处理者
        _next = next;
    }
    virtual void handleRequest(const Request& req) = 0; // 处理请求
    virtual ~Approver() {}
protected:
    Approver* _next; // 下一个处理者(链的节点)
};

// 3. 具体处理者1:部门经理(处理≤1000)
class DeptManager : public Approver {
public:
    void handleRequest(const Request& req) override {
        if (req.getAmount() <= 1000) {
            cout << "部门经理审批:" << req.getName() << " 加薪" << req.getAmount() << "元" << endl;
        } else if (_next) {
            _next->handleRequest(req); // 无法处理,传下家
        } else {
            cout << "无人审批:" << req.getName() << " 加薪" << req.getAmount() << "元" << endl;
        }
    }
};

// 4. 具体处理者2:总监(处理1001~5000)
class Director : public Approver {
public:
    void handleRequest(const Request& req) override {
        if (req.getAmount() > 1000 && req.getAmount() <= 5000) {
            cout << "总监审批:" << req.getName() << " 加薪" << req.getAmount() << "元" << endl;
        } else if (_next) {
            _next->handleRequest(req);
        } else {
            cout << "无人审批:" << req.getName() << " 加薪" << req.getAmount() << "元" << endl;
        }
    }
};

// 5. 具体处理者3:总经理(处理>5000)
class GM : public Approver {
public:
    void handleRequest(const Request& req) override {
        if (req.getAmount() > 5000) {
            cout << "总经理审批:" << req.getName() << " 加薪" << req.getAmount() << "元" << endl;
        } else if (_next) {
            _next->handleRequest(req);
        } else {
            cout << "无人审批:" << req.getName() << " 加薪" << req.getAmount() << "元" << endl;
        }
    }
};

// 使用示例
void test() {
    // 创建处理者并连成链:部门经理→总监→总经理
    unique_ptr<Approver> dept = make_unique<DeptManager>();
    unique_ptr<Approver> dir = make_unique<Director>();
    unique_ptr<Approver> gm = make_unique<GM>();
    dept->setNext(dir.get());
    dir->setNext(gm.get());

    // 发送请求
    Request req1("张三", 800);   // 部门经理处理
    Request req2("李四", 3000);  // 总监处理
    Request req3("王五", 6000);  // 总经理处理
    Request req4("赵六", 10000); // 无人处理(链尾无下家)

    dept->handleRequest(req1);
    dept->handleRequest(req2);
    dept->handleRequest(req3);
    dept->handleRequest(req4);
}
优缺点
优点 缺点
解耦请求发送者与接收者(发送者不知谁处理) 请求可能沿链传递后无人处理(需处理边界)
可动态调整链的结构(增删处理者) 链过长时,请求传递耗时(性能问题)
处理者职责单一(仅处理自己能处理的请求) 调试困难(需跟踪请求传递路径)
使用场景
  • 请求需多个处理者分级处理(如审批流程、日志分级打印、异常处理);
  • 不确定谁处理请求(如 HTTP 请求路由、事件冒泡);
  • 需动态调整处理者(如插件化处理链)。

9. 命令模式

核心定义

将 “请求” 封装为对象,使请求的发送者(调用者)与接收者解耦:调用者只需调用命令对象的 “执行” 方法,无需知接收者如何处理。

类图关系(文字描述)

核心代码
#include <iostream>
#include <list>
#include <memory>
using namespace std;

// 1. 接收者:实现具体业务(如“厨师”)
class Cook {
public:
    void cookFish() { cout << "厨师:做红烧鱼" << endl; }
    void cookMeat() { cout << "厨师:做锅包肉" << endl; }
};

// 2. 命令抽象类
class Command {
public:
    Command(Cook* cook) : _cook(cook) {}
    virtual void execute() = 0; // 命令执行接口
    virtual ~Command() {}
protected:
    Cook* _cook; // 接收者(命令需知谁执行)
};

// 3. 具体命令:绑定“命令→接收者方法”
class FishCommand : public Command {
public:
    FishCommand(Cook* cook) : Command(cook) {}
    void execute() override {
        _cook->cookFish(); // 命令对应接收者的“做鱼”方法
    }
};
class MeatCommand : public Command {
public:
    MeatCommand(Cook* cook) : Command(cook) {}
    void execute() override {
        _cook->cookMeat(); // 命令对应接收者的“做肉”方法
    }
};

// 4. 调用者:发起命令(如“服务员”)
class Waiter {
public:
    void addCommand(Command* cmd) { // 添加命令到列表
        if (cmd) _commands.push_back(cmd);
    }
    void notify() { // 通知执行所有命令
        for (auto cmd : _commands) {
            cmd->execute();
        }
    }
private:
    list<Command*> _commands; // 命令列表(支持批量执行)
};

// 使用示例
void test() {
    // 1. 创建接收者(厨师)
    unique_ptr<Cook> cook = make_unique<Cook>();

    // 2. 创建命令(订单),绑定接收者
    unique_ptr<Command> cmd1 = make_unique<FishCommand>(cook.get());
    unique_ptr<Command> cmd2 = make_unique<MeatCommand>(cook.get());

    // 3. 调用者(服务员)添加命令并执行
    unique_ptr<Waiter> waiter = make_unique<Waiter>();
    waiter->addCommand(cmd1.get());
    waiter->addCommand(cmd2.get());
    waiter->notify(); // 服务员通知厨师执行订单
}
优缺点
优点 缺点
解耦发送者与接收者(发送者不知接收者) 类数量增加(N 命令需 N 类 + 1 抽象命令)
支持撤销 / 重做(保存命令历史,反向执行) 命令对象多,内存开销大
支持批量命令(如事务、队列执行) 简单场景用命令模式,会增加抽象层次
支持延迟执行(如定时任务、异步命令) 命令与接收者绑定,复用性低
使用场景
  • 需解耦请求发送与接收(如 GUI 按钮→业务逻辑、遥控器→家电);
  • 需支持撤销 / 重做(如文本编辑器、游戏存档);
  • 需批量 / 延迟执行请求(如任务队列、事务提交);
  • 需日志记录请求(如审计日志、命令回放)。

总结:设计模式选择指南

场景需求 推荐模式
简单对象创建,类型少 简单工厂
频繁新增产品,单产品等级 工厂方法
需创建关联产品族 抽象工厂
动态扩展对象功能 装饰器
一对多通知,状态变化 观察者
多算法切换,避免 if-else 策略
固定流程,局部可变 模板方法
分级处理请求,解耦发送者 职责链
封装请求,解耦发送与接收 命令

设计模式的核心不是 “套用模板”,而是理解其 “解耦、复用、扩展” 的设计思想,根据实际场景灵活选择。