装饰模式 结构性模式之三

1.装饰模式的定义

  装饰模式指的是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。                                ——— 百度百科

1.1 问题场景

用制作咖啡为例(案例来自尚硅谷设计模式) 
咖啡底料:意式浓缩咖啡(espresso)、卡布奇诺(cappuccino)、黑咖啡(blackcoffee)
咖啡辅料:糖(sugar)、巧克力(chocolate)、牛奶(milk)

按照套餐思路:
  计算一下,发现,应该有3 x 3 = 9种组合,啊不对!因为加了糖也可以加巧克力,而且份数也可以不只一份,这个不太好算,把每个套餐写成一个类,每个套餐都有自己独立的计价。但是,当咖啡底料或者辅料增加时,就需要增加大量的类,比如有了上面9种套餐之后,突然辅料新出来了蜂蜜,则需要与之前所有已经存在的饮品组合一下,脑袋已经短路了!

装饰模式思路:
  这个时候不用慌,想象过度包装是什么样子的。一个主要的核心物品,套一层外壳,再套一层,再套一层…这里,核心物品不就是上面所说的咖啡底料吗?外壳不就是这些咖啡辅料吗?

一份意式浓缩咖啡 + 一份糖 + 二份巧克力 + 三份牛奶
就变成:
一份意式浓缩咖啡(主体)   => 纯咖啡
纯咖啡 + 放在一份糖里面混合 => 混合咖啡
混合咖啡 + 放在一份巧克力里面混合 => 混合咖啡
混合咖啡 + 放在一份巧克力里面混合 => 混合咖啡
混合咖啡 + 放在一份牛奶里面混合 => 混合咖啡
混合咖啡 + 放在一份牛奶里面混合 => 混合咖啡
混合咖啡 + 放在一份牛奶里面混合 => 混合咖啡

2.优缺点

优点:

  1. 采用装饰模式扩展对象的功能比采用继承方式更加灵活。
  2. 可以设计出多个不同的具体装饰类,创造出多个不同行为的组合。

缺点:

  1. 装饰模式增加了许多子类,如果过度使用会使程序变得很复杂。

3.组成结构

  1. 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
  2. 具体构件(Concrete    Component)角色:实现抽象构件,通过装饰角色为其添加一些职责。
  3. 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  4. 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

4.代码示例

#pragma once
#include 
#include 

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.总结

  1. 通过采用组合而非继承的手法, Decorator模式实现了在运行时 动态扩展对象功能的能力,而且可以根据需要扩展多个功能。避免 了使用继承带来的“灵活性差”和“多子类衍生问题”
  2. Decorator类在接口上表现为is-a Component的继承关系,即 Decorator类继承了Component类所具有的接口。但在实现上又 表现为has-a Component的组合关系,即Decorator类又使用了 另外一个Component类。
  3. Decorator模式的目的并非解决“多子类衍生的多继承”问题, Decorator模式应用的要点在于解决主体类在多个方向上的扩展功能——是为"装饰"的含义。

6.引用

C++设计模式04-——装饰设计模式_c++装饰模式-CSDN博客

装饰模式(Decorator模式)详解-CSDN博客

你可能感兴趣的:(c++,设计模式)