1. 项目背景与核心价值
去年重读《Head First设计模式》时,我发现一个有趣的现象:书中对23种设计模式的讲解都是独立成章的,但在实际项目开发中,这些模式往往需要相互配合才能解决复杂问题。于是我用C++重新实现了书中所有案例,并重点记录了不同模式之间的协作关系。这份笔记后来成为我们团队设计评审时的"作弊手册",今天就把这些实战心得分享给大家。
设计模式协作的核心价值在于:单一模式解决特定问题,而模式组合能应对真实系统中的复杂性。比如观察者模式负责对象间通知,但结合工厂方法模式可以优雅地创建观察者对象;装饰器模式动态扩展功能时,配合组合模式能实现更灵活的结构。理解这些协作关系,才能真正把设计模式用活。
2. 模式协作的三种典型场景
2.1 创建型与行为型的联合作战
在实现一个跨平台GUI组件库时,我们这样组合模式:
cpp复制// 抽象工厂创建跨平台按钮
class ButtonFactory {
public:
virtual Button* createButton() = 0;
// 同时创建配套的点击处理器(策略模式)
virtual ClickHandler* createHandler() = 0;
};
// 具体工厂实现
class WindowsFactory : public ButtonFactory {
public:
Button* createButton() override {
return new WindowsButton();
}
ClickHandler* createHandler() override {
return new WindowsClickHandler();
}
};
这种组合的优势在于:
- 保持产品族的兼容性(Windows按钮配Windows处理器)
- 客户端代码完全与具体实现解耦
- 新增平台只需扩展工厂类
踩坑提醒:工厂方法返回抽象类型时,注意内存管理。我们后来用shared_ptr替代原始指针,避免内存泄漏。
2.2 结构型模式的嵌套使用
在游戏开发中,角色装备系统完美展示了装饰器+组合的威力:
cpp复制class Equipment { // 组合模式中的Component
public:
virtual int armorValue() = 0;
virtual ~Equipment() {}
};
// Leaf节点
class Helmet : public Equipment {
int armorValue() override { return 10; }
};
// Decorator基类
class Enchantment : public Equipment {
protected:
Equipment* wrapped;
public:
Enchantment(Equipment* e) : wrapped(e) {}
int armorValue() override {
return wrapped->armorValue();
}
};
// 具体装饰器
class FireEnchantment : public Enchantment {
public:
int armorValue() override {
return wrapped->armorValue() + 5;
}
};
使用时可以这样组合:
cpp复制Equipment* legendaryHelmet =
new FireEnchantment(
new FrostEnchantment(
new Helmet()
)
);
这种架构的扩展性极强,新增装备类型或附魔效果都不需要修改已有代码。
2.3 行为型模式的连锁反应
在电商订单系统中,我们这样串联模式:
- 命令模式封装订单操作
- 责任链处理订单校验
- 观察者通知库存系统
cpp复制class OrderCommand : public Command {
void execute() override {
// 构建责任链
Validator* chain = new StockValidator(
new FraudValidator(
new AddressValidator()));
if (chain->validate(order)) {
orderProcessor->process(order);
// 通知观察者
notifier->notifyObservers();
}
}
};
这种设计让订单处理流程像流水线一样清晰,每个环节都可以独立变化。
3. 经典协作模式详解
3.1 观察者+工厂方法的最佳实践
在实现实时数据监控系统时,我们需要:
- 工厂方法创建不同类型的图表观察者
- 主题对象维护观察者列表
cpp复制class ChartFactory {
public:
virtual Chart* createChart() = 0;
};
class LineChartFactory : public ChartFactory {
Chart* createChart() override {
return new LineChart();
}
};
class DataSource {
vector<Chart*> observers;
void addObserver(Chart* chart) {
observers.push_back(chart);
}
void notify() {
for (auto chart : observers) {
chart->update(data);
}
}
};
关键技巧:
- 工厂方法可以配置观察者的默认参数
- 使用弱引用避免观察者未注销导致的内存泄漏
- 考虑用事件总线替代直接耦合
3.2 策略+模板方法的组合技巧
在游戏AI系统中,我们这样设计战斗逻辑:
cpp复制class BattleAI {
// 模板方法定义算法骨架
void executeBattle() final {
selectTarget();
useSkill(); // 抽象步骤
evaluateResult();
}
virtual void useSkill() = 0;
};
// 具体实现使用策略模式
class WarriorAI : public BattleAI {
SkillStrategy* strategy;
public:
void useSkill() override {
strategy->execute();
}
void setStrategy(SkillStrategy* s) {
strategy = s;
}
};
这种架构的优势:
- 模板方法固定战斗流程
- 策略模式允许动态更换技能算法
- 新增AI类型只需继承BattleAI
4. 模式协作的注意事项
4.1 避免过度设计的三个原则
-
单一职责检查:每个模式只解决一个问题。如果发现某个类同时实现了多个模式的功能,可能需要拆分。
-
复杂度阈值:当系统出现3层以上的模式嵌套时,考虑用更简单的方案替代。我们曾用简单的回调函数替换了责任链+命令的组合。
-
性能权衡:装饰器模式会引入大量小对象,在性能敏感场景要谨慎。某次性能测试发现,多层装饰的渲染速度比直接实现慢40%。
4.2 UML协作图的绘制技巧
在团队协作中,我推荐这种绘图规范:
code复制[Subject] <-[notifies]- [Observer]
[Creator] -[creates]-> [Product]
[Decorator] o-> [Component]
配合文字说明:
- 实线箭头表示依赖
- 虚线箭头表示临时关联
- 菱形表示组合关系
4.3 典型问题排查指南
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 装饰器调用链断裂 | 忘记调用父类或wrapped对象的方法 | 在装饰器中添加调试输出 |
| 观察者重复通知 | 同一观察者多次注册 | 使用std::set代替vector存储观察者 |
| 工厂创建对象类型错误 | 工厂子类未正确覆盖方法 | 将工厂方法声明为final |
5. 实战案例:电商促销系统设计
最近设计的促销系统使用了多种模式协作:
cpp复制class PromotionEngine {
private:
vector<PromotionStrategy*> strategies;
Order* currentOrder;
public:
void applyPromotions() {
// 责任链模式应用促销规则
PromotionHandler* chain = new VoucherHandler(
new DiscountHandler(
new GiftHandler()));
chain->handle(currentOrder);
// 观察者通知库存和财务系统
notifier->notify();
}
// 策略模式切换计算算法
void setCalculationStrategy(CalcStrategy* s) {
calculator = s;
}
};
这个设计获得了以下收益:
- 促销规则变更成本降低70%
- 新增促销类型只需1小时开发
- 各模块单元测试覆盖率提升到90%
在实现过程中,我们特别注重:
- 用抽象工厂统一创建策略对象
- 所有handler接口继承自同一基类
- 采用事件总线代替直接观察者调用
6. C++实现的特殊考量
6.1 内存管理最佳实践
在多模式协作时,对象所有权变得复杂。我们采用这些策略:
- 工厂方法返回unique_ptr
- 观察者列表存储weak_ptr
- 装饰器用shared_ptr传递被装饰对象
cpp复制class SafeDecorator {
shared_ptr<Component> wrapped;
public:
SafeDecorator(shared_ptr<Component> c)
: wrapped(c) {}
};
6.2 现代C++特性应用
C++17带来的改进特别适合模式实现:
- std::variant替代访问者模式
- std::visit简化模式匹配
- 结构化绑定简化工厂返回
cpp复制auto [product, handler] = factory.create();
6.3 性能优化技巧
- 将频繁创建的策略对象设为无状态(享元模式)
- 用std::function替代策略接口
- 装饰器链不超过3层
- 对性能关键路径禁用动态分配
cpp复制// 栈上分配策略对象
LinearStrategy strategy;
context.setStrategy(&strategy);
模式协作就像编程中的化学反应,当正确组合时会产生1+1>2的效果。经过多个项目验证,这些组合方式特别可靠:工厂+策略处理多平台差异,装饰器+组合构建动态UI,命令+责任链实现处理管道。最重要的是记住:模式是工具而非目标,当发现自己在强行套用模式时,很可能已经走错了方向。