(一)什么是装饰器模式
装饰模式(Decorator Pattern),也被称为包装模式,是一种通过对客户端透明的方式来扩展对象功能的设计模式,它提供了一种替代继承关系的方案。装饰模式通过将要添加的附加功能放在独立的类中,并让这个类包含要装饰的对象,实现了在运行时动态地给对象添加功能的能力。
在装饰模式中,抽象构件定义了对象的接口,具体构件是被装饰的对象,装饰者持有一个构件对象的引用,并定义与抽象构件一致的接口。装饰者可以在调用构建对象的方法前后添加额外的行为,从而扩展对象的功能。
通过使用装饰模式,我们可以灵活地在运行时动态地给对象添加功能,而无需改变原有类的结构。客户端可以按照需要选择、按顺序使用不同的装饰者来包装对象,从而达到组合各种功能的目的。这种方式实现了功能的扩展和复用,同时也避免了使用继承带来的静态特性和多个子类的维护问题。
(二)为什么要使用装饰器模式
- 装饰模式可以动态的给一个对象添加一 些额外的职责。
- 就增加功能来说,此模式比生成子类更为灵活,通过子类继承的方式,但是如果后续继续增加功能的话,便要继续继承现有的类,如此就会使继承的层次越来越深,不利与代码的维护和可读性。
- 故最好的方式便是通过装饰者模式来完成。
(三)装饰器模式的实现步奏
- 提供一个抽象组件类:抽象被装饰者的行为
- 提供一个或多个具体组件类:被装饰者的行为具体实现
- 提供一个抽象装饰器类:抽象组件指针与抽象组件一致接口
- 提供一个具体的装饰器类:为具体组件附加责任
(四)代码示例
场景:
- 对于一些职场人士在出席一些重要场合的时候,大部分都是西装革履的。因此,我在这里就模拟男士穿衣的场景
首先,定义一个抽象基类Boy代表男生;
class Boy { public: virtual void wearClothes() = 0; };
接下来,创建具体的男生类;
class ConcreteBoy : public Boy { public: void wearClothes() override { cout << "首先穿上打底衫" << endl; } };
然后,定义装饰器基类;
class Decorator : public Boy { protected: Boy* boy; public: Decorator(Boy* b) : boy(b) {} virtual void wearClothes() override { if (boy != nullptr) { boy->wearClothes(); } } };
接着,创建具体的装饰器类;
//穿西装 class JacketDecorator : public Decorator { public: JacketDecorator(Boy* b) : Decorator(b) {} void wearClothes() override { Decorator::wearClothes(); cout << "紧接着穿上西服" << endl; } }; //打领带 class TieDecorator : public Decorator { public: TieDecorator(Boy* b) : Decorator(b) {} void wearClothes() override { Decorator::wearClothes(); cout << "最后再打领带" << endl; } };
最后,在主函数中使用这些类来展示男生穿衣服的场景;
int main() { Boy* boy = new TieDecorator(new JacketDecorator(new ConcreteBoy())); boy ->wearClothes(); delete boy ; return 0; }
(五)装饰器模式优缺点
优点:
- 灵活性:装饰器模式允许动态地给对象添加功能,而且可以根据需要多次进行装饰,从而实现各种组合方式,使得功能扩展变得非常灵活。
- 开闭原则:装饰器模式符合开闭原则,可以在不修改现有代码的情况下,增加新的功能。
缺点:
- 复杂性:装饰器模式引入了许多小的对象和类,可能会导致系统中对象数量的增加,从而增加系统的复杂性。
- 容易出错:由于装饰器模式允许灵活地组合对象,因此在设计时需要特别小心,以避免出现过度复杂或混乱的装饰器组合,导致难以理解和维护的问题。
总体来说,装饰器模式是一种非常灵活的设计模式,能够有效地扩展对象的功能,同时也需要在设计时注意控制复杂性,避免出现过度装饰的情况。