C++装饰器设计模式中抽象装饰器基类的存在必要性问询
C++装饰器模式中装饰器基类的核心作用与合规性说明
装饰器基类的实际作用
独立的MilkShakeDecorator基类不是语法要求的冗余代码,它的核心价值有四点:
- 抽离通用逻辑,消除重复代码
所有装饰器都有固定的通用逻辑:持有被包装的组件对象指针、通过构造函数初始化指针、对不需要扩展的接口做默认转发(直接调用被包装对象的对应方法)。如果没有基类,每新增一个装饰器(比如草莓味、奥利奥碎味奶昔),你都要把这部分逻辑重复写一遍。当装饰器数量增多后,不仅编码效率低,修改时也容易漏——比如你要把裸指针改成智能指针,有基类只需要改1处,没基类就得改N处。 - 明确类型语义边界
继承体系加入装饰器基类后,类型归属非常清晰:直接继承MilkShake的是基础组件(不加任何配料的纯奶昔),继承MilkShakeDecorator的一定是用来包装其他奶昔的装饰器(配料),读代码、做类型判断时不会把两类逻辑混为一谈。 - 支撑全局装饰逻辑扩展
如果后续需要给所有装饰器加统一能力,比如嵌套装饰层数统计、统一的配料溯源标记、析构时自动释放持有的对象逻辑,只需要在装饰器基类里实现一次,所有具体装饰器自动继承这个能力,不需要挨个修改每个装饰器的代码。 - 降低新装饰器的开发成本
写新装饰器的时候,只需要继承基类,覆写你要扩展的那部分方法就行,不用关心指针初始化、默认接口转发这些固定逻辑,写出来的代码更短,出错概率更低。
省略装饰器基类的实现是否合规
你写的去掉基类的版本,在当前这个只有2个装饰器的极简Demo里运行效果完全一致,但严格来说不符合装饰器模式的标准结构规范,属于极小场景下的简化写法:
装饰器模式的标准结构定义明确要求存在独立的抽象装饰器层,这一层的固定职责就是持有组件引用、实现接口默认转发,具体装饰器仅负责新增扩展逻辑。你把抽象装饰器的职责拆分到每个具体装饰器里重复实现,本质是把模式里的一层结构打散了,在简单场景下能跑,但扩展性很差。
这种简化写法的具体问题包括:
- 无法做通用的装饰器类型判断:比如你想在运行时检查一个
MilkShake*是不是加了配料的装饰器实例,有基类的时候直接用dynamic_cast<MilkShakeDecorator*>一次就能判断,没有基类的话你得挨个判断它是不是芒果味、香草味、草莓味……每加一个新装饰器就得改判断逻辑,根本没法维护。 - 代码维护成本随装饰器数量线性上升:前面提到的通用逻辑修改、新功能添加,都要重复改N份代码,很容易出现逻辑不一致的问题。
当然也不是说这种写法完全不能用:如果你确定整个项目里这个组件的装饰器永远不会超过2-3个,也不会加任何装饰器通用逻辑,少写一个基类确实能省几行代码,不会有运行时错误。但只要项目有后续迭代的可能,保留独立装饰器基类是投入极低、长期收益很高的做法。
参考代码
标准装饰器模式实现(带装饰器基类)
// CPP program to demonstrate // Decorator pattern #include <iostream> #include <string> using namespace std; // Component class MilkShake { public: virtual string Serve() = 0; virtual float price() = 0; }; // Concrete Component class BaseMilkShake : public MilkShake { public: string Serve() { return "MilkShake"; } float price() { return 30; } }; // Decorator class MilkShakeDecorator: public MilkShake { protected: MilkShake *m_MilkShake; public: MilkShakeDecorator(MilkShake *baseMilkShake): m_MilkShake(baseMilkShake){} string Serve() { return m_MilkShake->Serve(); } float price() { return m_MilkShake->price(); } }; // Concrete Decorator class MangoMilkShake: public MilkShakeDecorator { public: MangoMilkShake(MilkShake *baseMilkShake): MilkShakeDecorator(baseMilkShake){} string Serve() { return m_MilkShake->Serve() + " decorated with Mango "; } float price() { return m_MilkShake->price() + 40; } }; class VanillaMilkShake: public MilkShakeDecorator { public: VanillaMilkShake(MilkShake *baseMilkShake): MilkShakeDecorator(baseMilkShake){} string Serve() { return m_MilkShake->Serve() + " decorated with Vanilla "; } float price() { return m_MilkShake->price() + 80; } }; int main() { MilkShake *baseMilkShake = new BaseMilkShake(); cout << "Basic Milk shake \n"; cout << baseMilkShake -> Serve() << endl; cout << baseMilkShake -> price() << endl; MilkShake *decoratedMilkShake = new MangoMilkShake(baseMilkShake); cout << "Mango decorated Milk shake \n"; cout << decoratedMilkShake -> Serve() << endl; cout << decoratedMilkShake -> price() << endl; delete decoratedMilkShake; decoratedMilkShake = new VanillaMilkShake(baseMilkShake); cout << "Vanilla decorated Milk shake \n"; cout << decoratedMilkShake -> Serve() << endl; cout << decoratedMilkShake -> price() << endl; delete decoratedMilkShake; delete baseMilkShake; return 0; }
简化实现(无装饰器基类)
// CPP program to demonstrate // Decorator pattern #include <iostream> #include <string> using namespace std; // Component class MilkShake { public: virtual string Serve() = 0; virtual float price() = 0; }; // Concrete Component class BaseMilkShake : public MilkShake { public: string Serve() { return "MilkShake"; } float price() { return 30; } }; class MangoMilkShake: public MilkShake { private: MilkShake *m_MilkShake; public: MangoMilkShake(MilkShake *baseMilkShake): m_MilkShake(baseMilkShake){} string Serve() { return m_MilkShake->Serve() + " decorated with Mango "; } float price() { return m_MilkShake->price() + 40; } }; class VanillaMilkShake: public MilkShake { private: MilkShake *m_MilkShake; public: VanillaMilkShake(MilkShake *baseMilkShake): m_MilkShake(baseMilkShake){} string Serve() { return m_MilkShake->Serve() + " decorated with Vanilla "; } float price() { return m_MilkShake->price() + 80; } }; int main() { MilkShake *baseMilkShake = new BaseMilkShake(); cout << "Basic Milk shake \n"; cout << baseMilkShake -> Serve() << endl; cout << baseMilkShake -> price() << endl; MilkShake *decoratedMilkShake = new MangoMilkShake(baseMilkShake); cout << "Mango decorated Milk shake \n"; cout << decoratedMilkShake -> Serve() << endl; cout << decoratedMilkShake -> price() << endl; delete decoratedMilkShake; decoratedMilkShake = new VanillaMilkShake(baseMilkShake); cout << "Vanilla decorated Milk shake \n"; cout << decoratedMilkShake -> Serve() << endl; cout << decoratedMilkShake -> price() << endl; delete decoratedMilkShake; delete baseMilkShake; return 0; }
内容的提问来源于stack exchange,提问作者Liviu Dorobantu
相关产品推荐
相关产品推荐

