一、装饰器模式的核心思想
装饰器模式属于结构型设计模式,其主要目标是在不修改原有类结构和内部逻辑的基础上,通过动态地为对象附加新的功能(即“装饰”),实现行为的灵活扩展。这种方式有效避免了传统继承机制下可能出现的“类爆炸”问题——即因功能组合过多而导致子类数量急剧膨胀。每个新增的功能都被封装成独立且可复用的组件。
以家具制造为例进行说明:基础家具产品如椅子、桌子构成了系统的起点,而诸如刷漆、雕花、包边等工艺则属于可选的附加功能。若采用继承方式来实现这些组合,将产生大量具体类,例如“红漆雕花椅子”“清漆包边桌子”等;而使用装饰器模式,则可以将基础家具与各个装饰操作分别建模为独立类,按需在运行时动态叠加,显著提升系统的灵活性与可维护性。
二、装饰器模式在家具生产中的角色划分
抽象组件(Component)
定义家具对象的统一接口规范,确保无论是原始家具还是经过多层装饰后的复合对象都能对外提供一致的行为。在此场景中对应的是“家具”这一抽象基类,其中声明了展示基本信息的核心方法。
具体组件(Concrete Component)
作为抽象组件的具体实现,代表未被装饰的基础家具实体,如“椅子”或“桌子”。它们仅负责自身属性的呈现,不涉及任何额外装饰逻辑,是装饰链的起始点。
抽象装饰器(Decorator)
继承自抽象组件,并持有一个指向抽象组件的引用。它既遵循家具接口的标准,又具备包裹其他家具对象的能力,是所有具体装饰器的共同父类。该角色提供了功能扩展所需的结构基础,对应于“家具装饰器”抽象类。
具体装饰器(Concrete Decorator)
实现特定的装饰功能,如“刷漆装饰器”“雕花装饰器”“包边装饰器”。每个此类都专注于单一职责,在保留原对象行为的同时,增加新的特性并重写相关方法以体现装饰效果。
#include <iostream>
#include <string>
using namespace std;
// 1. 抽象组件:家具
class Furniture {
public:
virtual ~Furniture() {}
// 核心方法:展示家具信息(包含基础属性和装饰效果)
virtual string showInfo() const = 0;
};
// 2. 具体组件:基础家具 - 椅子
class Chair : public Furniture {
public:
string showInfo() const override {
return "基础椅子(材质:实木)";
}
};
// 2. 具体组件:基础家具 - 桌子
class Table : public Furniture {
public:
string showInfo() const override {
return "基础桌子(材质:实木)";
}
};
// 3. 抽象装饰器:家具装饰器
class FurnitureDecorator : public Furniture {
protected:
// 持有一个家具对象的引用(可包裹基础家具或已装饰的家具)
Furniture* furniture;
public:
// 构造函数:传入要装饰的家具
FurnitureDecorator(Furniture* fur) : furniture(fur) {}
~FurnitureDecorator() {
// 释放被装饰的家具对象(此处简化为直接释放,实际项目需考虑内存管理策略)
delete furniture;
}
// 委托给被装饰的家具实现基础展示,后续具体装饰器再追加效果
string showInfo() const override {
return furniture->showInfo();
}
};
// 4. 具体装饰器:刷漆装饰
class PaintDecorator : public FurnitureDecorator {
private:
// 装饰属性:漆的颜色
string color;
public:
// 构造函数:传入要装饰的家具和漆的颜色
PaintDecorator(Furniture* fur, const string& col) : FurnitureDecorator(fur), color(col) {}
// 重写展示方法:在基础信息后追加刷漆效果
string showInfo() const override {
return FurnitureDecorator::showInfo() + ",装饰:刷" + color + "漆";
}
};
// 4. 具体装饰器:雕花装饰
class CarvingDecorator : public FurnitureDecorator {
private:
// 装饰属性:雕花样式
string style;
public:
CarvingDecorator(Furniture* fur, const string& sty) : FurnitureDecorator(fur), style(sty) {}
string showInfo() const override {
return FurnitureDecorator::showInfo() + ",装饰:" + style + "雕花";
}
};
// 4. 具体装饰器:包边装饰
class EdgeWrappingDecorator : public FurnitureDecorator {
private:
// 装饰属性:包边材质
string material;
public:
EdgeWrappingDecorator(Furniture* fur, const string& mat) : FurnitureDecorator(fur), material(mat) {}
string showInfo() const override {
return FurnitureDecorator::showInfo() + ",装饰:" + material + "包边";
}
};
// 测试代码
int main() {
// 场景1:基础椅子 → 刷红色漆 → 加牡丹雕花
Furniture* redPeonyChair = new CarvingDecorator(
new PaintDecorator(new Chair(), "红色"),
"牡丹"
);
cout << "产品1:" << redPeonyChair->showInfo() << endl;
delete redPeonyChair;
// 场景2:基础桌子 → 刷清漆 → 加胡桃木包边
Furniture* clearVarnishTable = new EdgeWrappingDecorator(
new PaintDecorator(new Table(), "清"),
"胡桃木"
);
cout << "产品2:" << clearVarnishTable->showInfo() << endl;
delete clearVarnishTable;
// 场景3:基础椅子 → 直接加龙凤雕花(不刷漆)
Furniture* dragonPhoenixChair = new CarvingDecorator(new Chair(), "龙凤");
cout << "产品3:" << dragonPhoenixChair->showInfo() << endl;
delete dragonPhoenixChair;
return 0;
}
三、代码实现详解与执行结果分析
抽象组件 Furniture
声明了一个纯虚函数,作为整个系统中所有家具及其装饰形态的统一调用接口。这种设计保障了即使经过多层包装,最终对象仍能以标准方式被处理,维持了类型兼容性。
showInfo()
具体组件 Chair / Table
实现了最基本的家具信息输出功能,是装饰过程的目标对象。它们无需感知装饰的存在,保持了简单性和专注性,符合开闭原则。
抽象装饰器 FurnitureDecorator
继承自Furniture类,并包含一个Furniture类型的指针成员,用于引用被装饰的对象。其构造函数接收待装饰实例,核心方法会将调用委托给内部持有的对象,从而实现责任链式的传递,为后续的具体装饰器提供“包裹”能力。
具体装饰器 PaintDecorator 等
继承自抽象装饰器类,引入了额外的状态字段(如颜色、样式等),并对核心展示方法进行重写。在调用父类方法获取基础信息后,追加自身的装饰描述,实现功能的逐层增强,体现了动态扩展的本质。
四、在家具生产场景下的优势体现
灵活扩展,防止类数量失控
如果依赖继承机制实现“红漆雕花椅子”这类复合产品,需要建立多级子类结构(如Chair → RedPaintChair → RedPaintPeonyChair),导致类数目随功能组合呈指数增长;而装饰器模式通过对象组合的方式,每新增一种装饰只需添加一个装饰器类,极大降低了系统复杂度。
支持运行时动态装配
可以根据实际需求在程序执行过程中自由组合不同的装饰效果。例如客户临时变更要求,将原本的“清漆桌子”调整为“红漆雕花桌子”,系统无需重构或重新编译,只需更换装饰顺序即可完成切换。
遵循单一职责原则
基础家具类仅关注自身固有属性,各装饰器也只负责一项具体修饰功能。这种职责分离使得各个模块更易于测试、维护和复用。例如,“刷漆装饰器”不仅可以作用于椅子,也可应用于桌子或其他家具类型,提升了代码的通用性。
五、典型应用场景总结
当面临以下情况时,装饰器模式是一个理想选择:
- 需要在运行期动态地为对象添加或移除职责;
- 希望通过组合而非继承来避免类数量激增;
- 需要支持多种功能的自由组合,且组合顺序可能影响最终结果。
除了家具生产的模拟案例外,该模式还广泛应用于其他领域,例如C++中的IO流处理体系——通过iostream、fstream、stringstream等类型的层层包装实现功能叠加;以及图形用户界面开发中对组件的修饰,如为按钮添加边框、背景色或图标等视觉效果。
istream
ifstream
istringstream