1.装饰模式的定义
装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。 ——— 百度百科
1.1 问题场景
用制作咖啡为例(案例来自尚硅谷设计模式)
咖啡底料:意式浓缩咖啡(espresso)、卡布奇诺(cappuccino)、黑咖啡(blackcoffee)
咖啡辅料:糖(sugar)、巧克力(chocolate)、牛奶(milk)
按照套餐思路:
计算一下,发现,应该有3 x 3 = 9种组合,啊不对!因为加了糖也可以加巧克力,而且份数也可以不只一份,这个不太好算,把每个套餐写成一个类,每个套餐都有自己独立的计价。但是,当咖啡底料或者辅料增加时,就需要增加大量的类,比如有了上面9种套餐之后,突然辅料新出来了蜂蜜,则需要与之前所有已经存在的饮品组合一下,脑袋已经短路了!
装饰模式思路:
这个时候不用慌,想象过度包装是什么样子的。一个主要的核心物品,套一层外壳,再套一层,再套一层…这里,核心物品不就是上面所说的咖啡底料吗?外壳不就是这些咖啡辅料吗?
一份意式浓缩咖啡 + 一份糖 + 二份巧克力 + 三份牛奶
就变成:
一份意式浓缩咖啡(主体) => 纯咖啡
纯咖啡 + 放在一份糖里面混合 => 混合咖啡
混合咖啡 + 放在一份巧克力里面混合 => 混合咖啡
混合咖啡 + 放在一份巧克力里面混合 => 混合咖啡
混合咖啡 + 放在一份牛奶里面混合 => 混合咖啡
混合咖啡 + 放在一份牛奶里面混合 => 混合咖啡
混合咖啡 + 放在一份牛奶里面混合 => 混合咖啡
2.优缺点
优点:
- 采用装饰模式扩展对象的功能比采用继承方式更加灵活。
- 可以设计出多个不同的具体装饰类,创造出多个不同行为的组合。
缺点:
- 装饰模式增加了许多子类,如果过度使用会使程序变得很复杂。
3.组成结构
- 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
- 具体构件(Concrete Component)角色:实现抽象构件,通过装饰角色为其添加一些职责。
- 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
- 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。
4.代码示例
#pragma once
#include <string>
#include <iostream>
using namespace std;
class Coffe
{
public:
Coffe() {}
virtual string desc() = 0;
virtual float cost() = 0;
};
//意式咖啡
class Espresso : public Coffe
{
public:
Espresso() {}
virtual string desc() { // 咖啡描述
return "意大利式";
}
virtual float cost() { // 一杯意式浓咖啡的价格
return 60.5f;
}
};
class Cappuccino : public Coffe
{
public:
Cappuccino() {}
virtual string desc() {
return "卡布奇诺";
}
virtual float cost() {
return 30.0f;
}
};
//装饰类也是继承自抽象主体类
class Decorator : public Coffe
{
protected:
Coffe* coffe;
public:
Decorator(Coffe* cof) : coffe(cof) {}
};
//具体装饰类继承自装饰抽象类
class Sugar : public Decorator
{
public:
Sugar(Coffe* cof) : Decorator(cof) {}
virtual string desc() {
return coffe->desc() + "一勺糖";
}
virtual float cost() {
return coffe->cost() + 1.0f; // 一勺糖1¥
}
};
class Chocolate : public Decorator
{
public:
Chocolate(Coffe* cof) : Decorator(cof) {}
virtual string desc() {
return coffe->desc() + "一块巧克力";
}
virtual float cost() {
return coffe->cost() + 3.0f;// 一块巧克力3¥
}
};
class Milk : public Decorator
{
public:
Milk(Coffe* cof) : Decorator(cof) {}
virtual string desc() {
return coffe->desc() + "一勺牛奶";
}
virtual float cost() {
return coffe->cost() + 2.0f;// 一勺牛奶2¥
}
};
int main() {
// 点一杯卡布奇诺咖啡
Cappuccino* coffe = new Cappuccino();
// 计算一杯卡布奇诺咖啡的价格
cout << coffe->desc() << " " << coffe->cost() << endl;
// 加一勺糖
Sugar* sg = new Sugar(coffe);
// 计算一杯卡布奇诺咖啡加一勺糖的价格
cout << sg->desc() << " " << sg->cost() << endl;
// 加一勺牛奶
Milk* mk = new Milk(sg);
// 计算一杯卡布奇诺咖啡加一勺糖、加一勺牛奶的价格
cout << mk->desc() << " " << mk->cost() << endl;
// 加一块巧克力
Chocolate* ct = new Chocolate(mk);
// 计算一杯卡布奇诺咖啡加一勺糖、加一勺牛奶、加一块巧克力的价格
cout << ct->desc() << " " << ct->cost() << endl;
return 0;
}
这里需要注意几个要点:
装饰抽象类继承自主体抽象类,主体抽象类是其它所有类的基类。
装饰类的构造函数参数里是一个主体抽象类对象的指针
5.总结
- 通过采用组合而非继承的手法, Decorator模式实现了在运行时 动态扩展对象功能的能力,而且可以根据需要扩展多个功能。避免 了使用继承带来的“灵活性差”和“多子类衍生问题”
- Decorator类在接口上表现为is-a Component的继承关系,即 Decorator类继承了Component类所具有的接口。但在实现上又 表现为has-a Component的组合关系,即Decorator类又使用了 另外一个Component类。
- Decorator模式的目的并非解决“多子类衍生的多继承”问题, Decorator模式应用的要点在于解决主体类在多个方向上的扩展功能——是为"装饰"的含义。
6.引用
C++设计模式04-——装饰设计模式_c++装饰模式-CSDN博客