11.设计模式实战:从创建型到结构型的全面解析

设计模式实战:从创建型到结构型的全面解析

前言

设计模式是软件开发中解决常见问题的经典方案,掌握设计模式不仅能提升代码质量,还能让我们与团队更好地沟通设计思想。本文将从创建型模式结构型模式两大类别出发,结合实际C++代码,详解10个最常用的设计模式。

工厂模式、建造者模式、原型模式、桥接模式、适配器模式、装饰模式、组合模式、享元模式、外观模式、代理模式


一、创建型模式

创建型模式关注对象创建机制,使系统在创建对象时更加灵活、解耦。

1. 工厂模式

工厂模式的核心思想是将对象的创建和使用分离,由专门的"工厂"负责创建对象。
工厂方法
客户端
工厂接口
具体工厂A
具体工厂B
产品A
产品B
简单工厂
客户端
简单工厂
产品A
产品B

三种工厂模式对比
模式 核心思想 何时使用
简单工厂 一个工厂根据参数创建不同产品 产品种类少,不想为每个类建工厂
工厂方法 每个产品对应一个工厂,工厂也抽象化 产品会不断扩展,需要符合开闭原则
抽象工厂 创建一组相关或依赖的对象(产品族) 需要切换整套产品系列
代码示例:工厂方法
cpp 复制代码
// 抽象产品
class Car {
public:
    virtual void run() = 0;
    virtual ~Car() = default;
};

// 具体产品
class Bmw : public Car {
public:
    void run() override { std::cout << "宝马在飞驰" << std::endl; }
};

// 抽象工厂
class CarFactory {
public:
    virtual std::unique_ptr<Car> createCar() = 0;
    virtual ~CarFactory() = default;
};

// 具体工厂
class BmwFactory : public CarFactory {
public:
    std::unique_ptr<Car> createCar() override {
        return std::make_unique<Bmw>();
    }
};

// 使用
int main() {
    std::unique_ptr<CarFactory> factory = std::make_unique<BmwFactory>();
    auto car = factory->createCar();
    car->run(); // 输出:宝马在飞驰
}

2. 建造者模式

建造者模式将复杂对象的构建与表示分离,使同样的构建过程可以创建不同的表示。
Director
+construct()
<<interface>>
Builder
+buildPartA()
+buildPartB()
+getResult()
ConcreteBuilder
+buildPartA()
+buildPartB()
+getResult()
Product
+setPart()

经典实现 vs 链式调用
对比维度 经典模式 链式调用
角色完整性 完整(Director+Builder+Product) 简化(Builder+Product)
构建流程控制 Director统一控制 客户端直接控制
构建顺序复用 Director可复用 需要客户端重新写调用链
代码示例:链式调用
cpp 复制代码
class Pizza {
public:
    class Builder {
        Pizza pizza_;
    public:
        Builder& setSize(const std::string& size) {
            pizza_.size_ = size;
            return *this;
        }
        Builder& addCheese() {
            pizza_.cheese_ = true;
            return *this;
        }
        Pizza build() { return pizza_; }
    };
    
private:
    std::string size_;
    bool cheese_ = false;
};

// 使用
Pizza pizza = Pizza::Builder()
    .setSize("大份")
    .addCheese()
    .build();

3. 原型模式

原型模式通过复制现有对象来创建新对象,避免昂贵的初始化操作。
clone
clone
clone
原型对象
新对象1
新对象2
新对象3

深拷贝 vs 浅拷贝
拷贝方式 说明 适用场景
浅拷贝 只复制对象本身,引用成员共享 成员都是值类型
深拷贝 递归复制所有引用成员 对象包含动态资源
代码示例
cpp 复制代码
// 抽象原型
class Shape {
public:
    virtual std::unique_ptr<Shape> clone() const = 0;
    virtual void draw() const = 0;
    virtual ~Shape() = default;
};

// 具体原型
class Circle : public Shape {
private:
    int radius;
    std::string color;
    
public:
    Circle(int r, const std::string& c) : radius(r), color(c) {}
    
    // 拷贝构造函数(深拷贝)
    Circle(const Circle& other) = default;
    
    std::unique_ptr<Shape> clone() const override {
        return std::make_unique<Circle>(*this);
    }
    
    void draw() const override {
        std::cout << "Circle: radius=" << radius << ", color=" << color << std::endl;
    }
};

// 使用
int main() {
    auto original = std::make_unique<Circle>(10, "red");
    auto cloned = original->clone();  // 多态克隆
    cloned->draw();
}

二、结构型模式

结构型模式关注如何将类和对象组合成更大的结构。

4. 桥接模式

桥接模式将抽象部分与实现部分分离,使它们可以独立变化。
Shape
#color: Color
+draw()
<<interface>>
Color
+applyColor()
Circle
+draw()
Red
+applyColor()
Blue
+applyColor()

解决什么问题?

问题:有M种形状和N种颜色,使用继承需要创建 M×N 个类。

解决:桥接模式只需 M + N 个类。

代码示例
cpp 复制代码
// 实现部分:颜色接口
class Color {
public:
    virtual void applyColor() const = 0;
};

class Red : public Color {
public:
    void applyColor() const override {
        std::cout << "应用红色" << std::endl;
    }
};

// 抽象部分:形状
class Shape {
protected:
    std::shared_ptr<Color> color_;
public:
    Shape(std::shared_ptr<Color> color) : color_(color) {}
    virtual void draw() const = 0;
};

class Circle : public Shape {
public:
    Circle(std::shared_ptr<Color> color) : Shape(color) {}
    void draw() const override {
        std::cout << "绘制圆形,";
        color_->applyColor();
    }
};

// 使用
int main() {
    auto red = std::make_shared<Red>();
    auto circle = Circle(red);
    circle.draw();  // 绘制圆形,应用红色
}

5. 适配器模式

适配器模式让不兼容的接口能够一起工作
调用
实现
转换调用
客户端
目标接口
适配器
适配者

类适配器 vs 对象适配器
对比维度 类适配器 对象适配器
实现方式 多重继承 对象组合
耦合度 高(编译时绑定) 低(运行时绑定)
灵活性 高(可动态切换)
推荐度 ⭐⭐ ⭐⭐⭐⭐⭐
代码示例:对象适配器
cpp 复制代码
// 目标接口(客户端期望)
class ChineseSocket {
public:
    virtual void charge_220v() const {
        std::cout << "使用220V电压充电" << std::endl;
    }
};

// 适配者(需要适配的类)
class USASocket {
public:
    void charge_110v() const {
        std::cout << "使用110V电压充电" << std::endl;
    }
};

// 适配器
class USAAdapter : public ChineseSocket {
private:
    std::unique_ptr<USASocket> usa_socket_;
public:
    USAAdapter(std::unique_ptr<USASocket> socket) 
        : usa_socket_(std::move(socket)) {}
    
    void charge_220v() const override {
        std::cout << "[适配器] 将110V转换为220V: ";
        usa_socket_->charge_110v();
    }
};

// 使用
int main() {
    auto usaSocket = std::make_unique<USASocket>();
    USAAdapter adapter(std::move(usaSocket));
    adapter.charge_220v();  // 美国插座也能在中国使用
}

6. 装饰模式

装饰模式动态地给对象添加新功能,比继承更灵活。
核心组件
装饰器1
装饰器2
装饰器3

装饰模式 vs 继承

装饰模式
核心
装饰器A
装饰器B
装饰器C
继承方式
父类
子类1
子类2
孙类1-1
孙类1-2

代码示例:咖啡订单系统
cpp 复制代码
// 抽象组件
class Beverage {
public:
    virtual std::string getDescription() const = 0;
    virtual double cost() const = 0;
    virtual ~Beverage() = default;
};

// 具体组件
class Espresso : public Beverage {
public:
    std::string getDescription() const override { return "浓缩咖啡"; }
    double cost() const override { return 25.0; }
};

// 抽象装饰类
class CondimentDecorator : public Beverage {
protected:
    std::unique_ptr<Beverage> beverage_;
public:
    CondimentDecorator(std::unique_ptr<Beverage> bev) 
        : beverage_(std::move(bev)) {}
};

// 具体装饰:牛奶
class Milk : public CondimentDecorator {
public:
    Milk(std::unique_ptr<Beverage> bev) 
        : CondimentDecorator(std::move(bev)) {}
    
    std::string getDescription() const override {
        return beverage_->getDescription() + " + 牛奶";
    }
    double cost() const override { return beverage_->cost() + 5.0; }
};

// 使用
int main() {
    auto coffee = std::make_unique<Espresso>();
    coffee = std::make_unique<Milk>(std::move(coffee));
    coffee = std::make_unique<Milk>(std::move(coffee));  // 双份牛奶
    
    std::cout << coffee->getDescription() << ": ¥" << coffee->cost() << std::endl;
    // 输出:浓缩咖啡 + 牛奶 + 牛奶: ¥35
}

7. 组合模式

组合模式将对象组合成树形结构 ,使客户端对单个对象和组合对象的使用具有一致性
根节点 Composite
叶子 Leaf
子节点 Composite
叶子 Leaf
叶子 Leaf

代码示例:公司组织架构
cpp 复制代码
// 抽象组件
class Employee {
public:
    virtual void showInfo(int depth = 0) = 0;
    virtual void add(std::shared_ptr<Employee>) {}
    virtual ~Employee() = default;
};

// 叶子:普通员工
class OrdinaryEmployee : public Employee {
private:
    std::string name_;
    std::string position_;
public:
    OrdinaryEmployee(std::string name, std::string pos) 
        : name_(name), position_(pos) {}
    
    void showInfo(int depth = 0) override {
        std::string indent(depth * 2, ' ');
        std::cout << indent << "- " << name_ << " (" << position_ << ")" << std::endl;
    }
};

// 组合:经理
class Manager : public Employee {
private:
    std::string name_;
    std::string position_;
    std::vector<std::shared_ptr<Employee>> subordinates_;
public:
    Manager(std::string name, std::string pos) 
        : name_(name), position_(pos) {}
    
    void add(std::shared_ptr<Employee> emp) override {
        subordinates_.push_back(emp);
    }
    
    void showInfo(int depth = 0) override {
        std::string indent(depth * 2, ' ');
        std::cout << indent << "+ " << name_ << " (经理 - " << position_ << ")" << std::endl;
        for (auto& emp : subordinates_) {
            emp->showInfo(depth + 1);
        }
    }
};

// 使用
int main() {
    auto ceo = std::make_shared<Manager>("张三", "CEO");
    auto techManager = std::make_shared<Manager>("李四", "技术总监");
    auto dev = std::make_shared<OrdinaryEmployee>("王五", "工程师");
    
    ceo->add(techManager);
    techManager->add(dev);
    ceo->showInfo();
}

8. 享元模式

享元模式通过共享大量细粒度对象来减少内存占用。
享元模式
共享对象A
引用1
引用2
共享对象B
引用3
传统方式
对象1
对象2
对象3
对象4

内在状态 vs 外在状态
状态类型 说明 示例
内在状态 可共享,存储在享元对象内部 字符的ASCII码
外在状态 不可共享,由客户端传递 字体大小、颜色
代码示例:字符共享
cpp 复制代码
// 享元接口
class Character {
public:
    virtual void display(int fontSize, const std::string& color) = 0;
    virtual ~Character() = default;
};

// 具体享元
class ConcreteCharacter : public Character {
private:
    char symbol_;  // 内在状态(可共享)
public:
    ConcreteCharacter(char c) : symbol_(c) {}
    
    void display(int fontSize, const std::string& color) override {
        std::cout << "字符: " << symbol_ 
                  << ", 字号: " << fontSize 
                  << ", 颜色: " << color << std::endl;
    }
};

// 享元工厂
class CharacterFactory {
private:
    std::unordered_map<char, std::shared_ptr<Character>> pool_;
public:
    std::shared_ptr<Character> getCharacter(char key) {
        if (pool_.find(key) == pool_.end()) {
            pool_[key] = std::make_shared<ConcreteCharacter>(key);
            std::cout << "创建新字符: " << key << std::endl;
        }
        return pool_[key];
    }
    
    size_t size() const { return pool_.size(); }
};

// 使用
int main() {
    CharacterFactory factory;
    std::string text = "Hello";
    
    for (char c : text) {
        auto ch = factory.getCharacter(c);
        ch->display(12, "Black");
    }
    // 输出:只创建了4个字符对象(H,e,l,o),l被重用
    std::cout << "实际创建对象数: " << factory.size() << std::endl;
}

9. 外观模式

外观模式为子系统提供统一的简化接口
SubC SubB SubA Facade Client SubC SubB SubA Facade Client operation() doA() doB() doC() 完成

代码示例:家庭影院
cpp 复制代码
// 子系统类
class Projector {
public:
    void on() { std::cout << "投影仪打开" << std::endl; }
    void off() { std::cout << "投影仪关闭" << std::endl; }
};

class SoundSystem {
public:
    void on() { std::cout << "音响打开" << std::endl; }
    void setVolume(int level) { std::cout << "音量: " << level << std::endl; }
};

class BluRayPlayer {
public:
    void on() { std::cout << "播放器打开" << std::endl; }
    void play(const std::string& movie) { std::cout << "播放: " << movie << std::endl; }
};

// 外观类
class HomeTheaterFacade {
private:
    Projector* projector_;
    SoundSystem* sound_;
    BluRayPlayer* player_;
    
public:
    HomeTheaterFacade(Projector* p, SoundSystem* s, BluRayPlayer* b)
        : projector_(p), sound_(s), player_(b) {}
    
    void watchMovie(const std::string& movie) {
        std::cout << "=== 开始观影 ===" << std::endl;
        projector_->on();
        sound_->on();
        sound_->setVolume(30);
        player_->on();
        player_->play(movie);
        std::cout << "=== 观影中 ===" << std::endl;
    }
    
    void endMovie() {
        std::cout << "=== 结束观影 ===" << std::endl;
        projector_->off();
        sound_->off();
        player_->off();
    }
};

// 使用
int main() {
    Projector proj;
    SoundSystem sound;
    BluRayPlayer player;
    
    HomeTheaterFacade theater(&proj, &sound, &player);
    theater.watchMovie("盗梦空间");
    theater.endMovie();
}

10. 代理模式

代理模式为另一个对象提供替身以控制访问
<<interface>>
Subject
+request()
RealSubject
+request()
Proxy
-realSubject: RealSubject
+request()

代理模式分类
类型 用途 示例
虚拟代理 延迟加载 图片懒加载
保护代理 访问控制 权限检查
远程代理 隐藏地址空间 RPC调用
智能引用 附加操作 日志、缓存
代码示例:保护代理
cpp 复制代码
// 抽象主题
class Document {
public:
    virtual void read() = 0;
    virtual void write(const std::string& content) = 0;
    virtual ~Document() = default;
};

// 真实主题
class RealDocument : public Document {
private:
    std::string content_;
public:
    void read() override { std::cout << "阅读: " << content_ << std::endl; }
    void write(const std::string& c) override { content_ = c; }
};

// 代理:带权限控制
class DocumentProxy : public Document {
private:
    std::unique_ptr<RealDocument> realDoc_;
    std::string userRole_;
    
    bool hasWritePermission() const {
        return userRole_ == "admin" || userRole_ == "editor";
    }
    
public:
    DocumentProxy(const std::string& role) 
        : realDoc_(std::make_unique<RealDocument>()), userRole_(role) {}
    
    void read() override {
        realDoc_->read();  // 所有人都可读
    }
    
    void write(const std::string& content) override {
        if (hasWritePermission()) {
            realDoc_->write(content);
            std::cout << "写入成功" << std::endl;
        } else {
            std::cout << "权限不足,无法写入" << std::endl;
        }
    }
};

// 使用
int main() {
    auto userDoc = DocumentProxy("user");
    userDoc.write("新内容");  // 失败
    
    auto adminDoc = DocumentProxy("admin");
    adminDoc.write("重要内容");  // 成功
}

三、模式对比总结

创建型模式对比

模式 核心问题 解决方案
工厂方法 创建单个对象 将创建延迟到子类
抽象工厂 创建产品族 用工厂创建相关对象
建造者 创建复杂对象 分步构建
原型模式 创建相似对象 克隆现有对象

结构型模式对比

模式 核心思想 关键特征
桥接 分离抽象与实现 两个独立维度变化
适配器 接口转换 让不兼容接口协作
装饰 动态添加功能 比继承更灵活
组合 树形结构 统一对待叶子与容器
享元 共享对象 减少内存占用
外观 简化接口 封装复杂子系统
代理 控制访问 延迟加载/权限控制

四、选择建议





















遇到设计问题
需要创建对象?
对象创建复杂?
建造者模式
需要克隆?
原型模式
产品会扩展?
工厂方法
简单工厂
需要接口转换?
适配器模式
需要控制访问?
代理模式
需要减少内存?
享元模式
有树形结构?
组合模式
需要简化接口?
外观模式
多维度变化?
桥接模式
装饰模式

核心原则

别一开始就上最复杂的设计。先用简单方案解决问题,当发现需要扩展时,再平滑地重构为更合适的设计模式。


五、设计模式应用场景速查表

1.创建型模式

模式 核心问题 典型应用场景 何时使用 何时避免
简单工厂 根据参数创建不同产品 解析配置文件创建对象、日志记录器创建、数据库驱动加载 产品种类少(<5种),调用者只需传参,不想关心创建细节 产品频繁增加,需要经常修改工厂类
工厂方法 创建单个对象,支持扩展 跨平台UI组件创建、文档编辑器支持多种格式、游戏角色生成器 产品会不断扩展,需要符合开闭原则,每个产品有专属创建逻辑 产品类型固定不变,或创建逻辑极其简单
抽象工厂 创建一系列相关对象(产品族) 跨平台UI工具包(Windows/Mac/Linux风格)、数据库连接族(连接/命令/事务)、游戏皮肤系统 需要切换整套产品系列,需要保证产品族内对象兼容性 产品族不会变化,或产品间没有关联关系
建造者 分步创建复杂对象 SQL查询构建器、HTTP请求构建器、PDF文档生成器、披萨订单系统 构造参数多且部分可选(>5个),需要控制构建顺序,需要生成不同表示形式 对象构造简单(参数<3个),或不需要多种表示形式
原型模式 通过克隆创建对象 游戏中的怪物复制、图形编辑器的复制粘贴、单元格样式克隆、大对象缓存 对象创建成本高(复杂计算/数据库查询),需要运行时动态配置对象类型 对象有循环引用,或克隆实现极其复杂

2.结构型模式

模式 核心问题 典型应用场景 何时使用 何时避免
适配器 让不兼容接口协作 不同日志库统一接口、新旧系统API对接、第三方支付集成、电源适配器 需要集成第三方库但接口不匹配,需要兼容旧版API,需要统一多个相似接口 可以修改原始类接口,或系统设计阶段就能统一接口
桥接 分离抽象与实现 跨平台绘图(形状+渲染引擎)、消息系统(消息类型+发送方式)、遥控器(设备+控制) 有两个以上独立变化的维度,需要避免M×N的类爆炸,需要在运行时切换实现 只有一个变化维度,或维度间没有独立变化的需求
组合 树形结构统一处理 文件系统(目录+文件)、组织架构(部门+员工)、GUI控件(容器+控件)、菜单系统 有部分-整体的层次结构,需要统一对待叶子节点和容器节点 结构是扁平的,或不需要递归操作
装饰 动态添加功能 咖啡配料系统、数据流加密/压缩、GUI控件添加边框/滚动条、权限动态增强 需要在不修改原类的情况下动态添加功能,功能组合数量大(避免类爆炸),需要运行时撤销功能 功能层级固定且少,或继承结构已经足够清晰
外观 简化复杂子系统 家庭影院系统、编译器前端、视频转换工具、微服务聚合层 需要为复杂子系统提供简单入口,需要分层解耦,需要为遗留系统提供新接口 子系统本身很简单,或客户端需要直接访问底层功能
享元 共享细粒度对象 文本编辑器字符渲染、游戏中的子弹/粒子系统、数据库连接池、字符串常量池 存在大量重复对象(>1000个),对象大部分状态可外部化,内存是瓶颈 对象数量少,或状态无法分离为内/外在状态
代理 控制对象访问 图片懒加载、权限控制(不同角色不同权限)、远程服务调用(RPC)、日志/性能监控 需要延迟加载(虚拟代理),需要访问控制(保护代理),需要添加透明操作(智能引用) 不需要任何控制或附加操作,或代理类变得过于复杂

3.快速决策矩阵

遇到的情况 推荐模式 一句话理由
创建对象时有一堆if-else 工厂方法 把创建逻辑封装起来
构造函数参数太多(>5个) 建造者 链式调用,清晰优雅
需要复制一个复杂对象 原型模式 克隆比重新创建更高效
两个维度都在变化 桥接模式 避免类爆炸
第三方库接口不匹配 适配器 加一层转换
需要动态给对象加功能 装饰模式 比继承灵活
有树形结构需要处理 组合模式 统一对待叶子与容器
大量相似对象内存爆炸 享元模式 共享内在状态
复杂子系统难用 外观模式 给个简化入口
需要控制对象访问 代理模式 加个中间人
相关推荐
蜡笔小马1 小时前
09.C++设计模式-外观模式
c++·设计模式·外观模式
workflower2 小时前
AI能源智慧生产与绿色开发核心场景
大数据·人工智能·设计模式·机器人·软件工程·能源
蜡笔小马2 小时前
10.C++设计模式-代理模式
c++·设计模式·代理模式
雪度娃娃3 小时前
行为型设计模式——职责链模式
c++·设计模式·责任链模式
多加点辣也没关系13 小时前
设计模式-观察者模式
观察者模式·设计模式
hssfscv14 小时前
软件设计师下午题训练1-3题+2019上上午题错题解析 练习真题训练13
笔记·设计模式·uml
拾-光15 小时前
【Git】命令大全:从入门到高手,100 个最常用命令速查(2026 版)
java·大数据·人工智能·git·python·elasticsearch·设计模式
多加点辣也没关系18 小时前
设计模式-模板方法模式
设计模式·模板方法模式
Autumn_ing1 天前
2026实测:这5款AI生成UI工具支持Shadcn UI/Ant Design组件库
人工智能·ui·设计模式·aigc·设计规范