文章目录
-
-
- [建造者模式 & 代理模式(C++ 详解)](#建造者模式 & 代理模式(C++ 详解))
- [一、建造者模式(Builder Pattern)](#一、建造者模式(Builder Pattern))
- [二、代理模式(Proxy Pattern)](#二、代理模式(Proxy Pattern))
-
建造者模式 & 代理模式(C++ 详解)
建造者模式和代理模式都属于设计模式的核心范畴,但解决的问题完全不同:
- 建造者模式 :聚焦复杂对象的分步创建,将"对象构建"与"对象表示"解耦;
- 代理模式 :聚焦对象访问控制,为目标对象提供一个代理,控制对它的访问(如权限、延迟加载、日志等)。
下面分别讲解两种模式的核心思想、结构、代码示例和应用场景。
一、建造者模式(Builder Pattern)
1. 核心概念
(1)解决的问题
当一个对象的创建需要多个步骤 、多个部件,且不同组合会生成不同"表示"时(比如:创建一个电脑,需要选CPU、内存、硬盘、显卡,不同配置组合出游戏本/办公本/服务器),直接在构造函数中创建会导致代码臃肿、可读性差,建造者模式将"分步构建"和"最终组装"分离。
(2)核心结构
| 角色 | 作用 |
|---|---|
| 产品(Product) | 最终要创建的复杂对象(如电脑) |
| 抽象建造者(Builder) | 定义构建产品的所有步骤接口(如setCPU、setMemory、setDisk等) |
| 具体建造者(ConcreteBuilder) | 实现抽象建造者的接口,完成具体部件的构建(如游戏本建造者、办公本建造者) |
| 指挥者(Director) | 控制构建流程(如"先装CPU→再装内存→再装硬盘"),调用建造者的步骤完成构建 |
(3)核心优势
- 分步构建复杂对象,代码清晰;
- 不同建造者可生成不同配置的产品,扩展灵活;
- 指挥者与建造者解耦,修改构建流程只需改指挥者。
2. C++ 代码示例(构建电脑)
cpp
#include <iostream>
#include <string>
// 1. 产品:电脑(复杂对象)
class Computer {
private:
std::string cpu; // CPU
std::string memory; // 内存
std::string disk; // 硬盘
std::string gpu; // 显卡
public:
// 设置部件
void setCPU(const std::string& cpu) { this->cpu = cpu; }
void setMemory(const std::string& memory) { this->memory = memory; }
void setDisk(const std::string& disk) { this->disk = disk; }
void setGPU(const std::string& gpu) { this->gpu = gpu; }
// 展示配置
void showConfig() const {
std::cout << "电脑配置:" << std::endl;
std::cout << "CPU: " << cpu << std::endl;
std::cout << "内存: " << memory << std::endl;
std::cout << "硬盘: " << disk << std::endl;
std::cout << "显卡: " << gpu << std::endl;
}
};
// 2. 抽象建造者:定义构建电脑的所有步骤
class ComputerBuilder {
protected:
Computer* computer; // 持有产品对象
public:
ComputerBuilder() { computer = new Computer(); }
virtual ~ComputerBuilder() { delete computer; }
// 纯虚函数:构建各个部件(子类必须实现)
virtual void buildCPU() = 0;
virtual void buildMemory() = 0;
virtual void buildDisk() = 0;
virtual void buildGPU() = 0;
// 返回构建好的产品
Computer* getComputer() { return computer; }
};
// 3. 具体建造者1:游戏本建造者(高性能配置)
class GameComputerBuilder : public ComputerBuilder {
public:
void buildCPU() override { computer->setCPU("Intel i9-14900K"); }
void buildMemory() override { computer->setMemory("32GB DDR5 6400"); }
void buildDisk() override { computer->setDisk("2TB NVMe 4.0"); }
void buildGPU() override { computer->setGPU("NVIDIA RTX 4090"); }
};
// 4. 具体建造者2:办公本建造者(低功耗、轻便配置)
class OfficeComputerBuilder : public ComputerBuilder {
public:
void buildCPU() override { computer->setCPU("Intel i5-1340P"); }
void buildMemory() override { computer->setMemory("16GB DDR4 3200"); }
void buildDisk() override { computer->setDisk("1TB NVMe 3.0"); }
void buildGPU() override { computer->setGPU("Intel UHD Graphics"); }
};
// 5. 指挥者:控制构建流程(固定步骤,无需关心具体配置)
class Director {
public:
// 指挥构建流程:CPU → 内存 → 硬盘 → 显卡
Computer* construct(ComputerBuilder* builder) {
builder->buildCPU();
builder->buildMemory();
builder->buildDisk();
builder->buildGPU();
return builder->getComputer();
}
};
// 客户端调用
int main() {
// 1. 创建指挥者
Director director;
// 2. 构建游戏本
ComputerBuilder* gameBuilder = new GameComputerBuilder();
Computer* gameComputer = director.construct(gameBuilder);
std::cout << "=== 游戏本配置 ===" << std::endl;
gameComputer->showConfig();
// 3. 构建办公本
ComputerBuilder* officeBuilder = new OfficeComputerBuilder();
Computer* officeComputer = director.construct(officeBuilder);
std::cout << "\n=== 办公本配置 ===" << std::endl;
officeComputer->showConfig();
// 释放资源
delete gameBuilder; // 内部会删除computer
delete officeBuilder;
return 0;
}
3. 输出结果
=== 游戏本配置 ===
电脑配置:
CPU: Intel i9-14900K
内存: 32GB DDR5 6400
硬盘: 2TB NVMe 4.0
显卡: NVIDIA RTX 4090
=== 办公本配置 ===
电脑配置:
CPU: Intel i5-1340P
内存: 16GB DDR4 3200
硬盘: 1TB NVMe 3.0
显卡: Intel UHD Graphics
4. 应用场景
- 复杂对象的创建(如:汽车、电脑、文档、报表);
- 对象创建需要多个步骤,且步骤顺序固定/可调整;
- 需生成不同配置的同一类产品(如:不同配置的手机、不同风格的蛋糕)。
二、代理模式(Proxy Pattern)
1. 核心概念
(1)解决的问题
为目标对象提供一个"代理",控制对目标对象的访问,代理可以在访问目标对象前后添加额外逻辑(如:权限校验、日志记录、延迟加载、远程调用),而客户端无需感知目标对象的存在。
(2)核心结构
| 角色 | 作用 |
|---|---|
| 抽象主题(Subject) | 定义目标对象和代理对象的共同接口(保证代理和目标对象可替换) |
| 真实主题(RealSubject) | 实际执行业务逻辑的目标对象 |
| 代理(Proxy) | 实现抽象主题接口,持有真实主题的引用,控制对真实主题的访问 |
(3)常见类型
- 静态代理:编译期确定代理类(如下方示例);
- 动态代理:运行时生成代理类(C++ 需手动实现,Java 有 JDK 动态代理/CGLIB);
- 按功能分类:远程代理(访问远程对象)、虚拟代理(延迟加载)、保护代理(权限控制)、日志代理(记录访问日志)。
2. C++ 代码示例(保护代理 + 日志代理)
场景:模拟"文件下载",代理先校验用户权限,再记录下载日志,最后调用真实下载逻辑。
cpp
#include <iostream>
#include <string>
// 1. 抽象主题:下载接口
class Downloader {
public:
virtual ~Downloader() = default;
virtual void download(const std::string& url, const std::string& user) = 0;
};
// 2. 真实主题:真实的下载器(核心业务逻辑)
class RealDownloader : public Downloader {
public:
void download(const std::string& url, const std::string& user) override {
// 真实的下载逻辑
std::cout << "[真实下载] 用户" << user << "下载文件:" << url << std::endl;
}
};
// 3. 代理:下载代理(权限校验 + 日志记录)
class DownloadProxy : public Downloader {
private:
RealDownloader* realDownloader; // 持有真实主题的引用
// 额外逻辑1:权限校验
bool checkPermission(const std::string& user) {
std::cout << "[权限校验] 检查用户" << user << "的下载权限..." << std::endl;
// 模拟:只有admin和user1有权限
return (user == "admin" || user == "user1");
}
// 额外逻辑2:记录日志
void logDownload(const std::string& url, const std::string& user) {
std::cout << "[日志记录] 用户" << user << "下载了" << url << ",时间:2026-03-17" << std::endl;
}
public:
DownloadProxy() {
realDownloader = new RealDownloader(); // 创建真实主题
}
~DownloadProxy() {
delete realDownloader;
}
// 代理的核心方法:控制访问
void download(const std::string& url, const std::string& user) override {
// 步骤1:权限校验
if (!checkPermission(user)) {
std::cout << "[权限拒绝] 用户" << user << "无下载权限!" << std::endl;
return;
}
// 步骤2:调用真实下载逻辑
realDownloader->download(url, user);
// 步骤3:记录日志
logDownload(url, user);
}
};
// 客户端调用
int main() {
// 客户端只和代理交互,无需知道真实下载器的存在
Downloader* downloader = new DownloadProxy();
// 测试1:有权限的用户
std::cout << "=== 测试1:admin下载 ===" << std::endl;
downloader->download("https://example.com/file.zip", "admin");
// 测试2:无权限的用户
std::cout << "\n=== 测试2:guest下载 ===" << std::endl;
downloader->download("https://example.com/file.zip", "guest");
// 释放资源
delete downloader;
return 0;
}
3. 输出结果
=== 测试1:admin下载 ===
[权限校验] 检查用户admin的下载权限...
[真实下载] 用户admin下载文件:https://example.com/file.zip
[日志记录] 用户admin下载了https://example.com/file.zip,时间:2026-03-17
=== 测试2:guest下载 ===
[权限校验] 检查用户guest的下载权限...
[权限拒绝] 用户guest无下载权限!
4. 应用场景
- 权限控制:如接口访问权限、文件操作权限;
- 日志/监控:记录接口调用的入参、出参、耗时;
- 延迟加载:对象创建成本高时,代理先不创建,直到真正使用时再初始化;
- 远程代理:访问远程服务器的对象(如 RPC 框架的代理);
- 缓存代理:缓存频繁访问的结果,避免重复计算/请求。
总结
1. 建造者模式
- 核心:分步构建复杂对象,将"构建步骤"与"产品配置"分离;
- 关键:指挥者控制流程,建造者实现具体部件,产品是最终结果;
- 适用:复杂对象、多步骤创建、多配置变体的场景。
2. 代理模式
- 核心:控制目标对象的访问,在访问前后添加额外逻辑;
- 关键:代理和目标对象实现同一接口,客户端无感知;
- 适用:权限控制、日志、延迟加载、远程调用等需"增强访问逻辑"的场景。
3. 核心区别
| 维度 | 建造者模式 | 代理模式 |
|---|---|---|
| 解决的问题 | 复杂对象的分步创建 | 目标对象的访问控制 |
| 核心关系 | 指挥者 → 建造者 → 产品 | 客户端 → 代理 → 真实对象 |
| 核心目的 | 生成不同配置的对象 | 增强/控制对象的访问逻辑 |