产生背景
很多情况下,当一个类已经存在,并且可以对外提供核心功能时。但是,某个时刻,希望对这个类进行功能增强,当然,我们可以修改原来的类,并增加对应的增强功能。
但是,这种方式违背了“开-闭”原则,需要修改原来的代码;而且不够灵活,如果有某个时刻又不想使用这个功能,又需要修改原来的代码,显然,这不是一个很好的解决方案。因此,就引出了装饰器这个东东。
装饰器模式概述
概念
定义:动态地给一个对象添加一些额外的职责。就添加功能来说,Decorator模式相比生成子类更为灵活。属于结构型模式。
本质:在面向对象的设计中,我们应该尽量使用对象组合,而不是对象继承来扩展和复用功能。
装饰器模式就是基于对象组合的方式,可以很灵活的给对象添加所需要的功能。
装饰器模式的本质就是动态组合。动态是手段,组合才是目的。总之,装饰模式就是通过把复杂的功能简单化,分散化,然后在运行期间,根据需要来动态组合的模式。它使得我们可以给某个对象而不是整个类添加一些功能。
UML图
模式中的角色
- 抽象构建(Component):定义一个抽象接口,用以给这些对象动态地添加职责。
- 具体构建(ConcreteComponent):定义一个具体的对象,也可以给这个对象添加一些功能。
- 装饰类(Decorator): 装饰抽象类,继承了Component,从外类来扩展Component类的功能。
- 具体装饰者(ConcretorDecorator):负责给构建对象添加功能。
举例:
为一个蛋糕添加装饰。蛋糕店刚生产出来的蛋糕是最原始的,只是一个蛋糕原型,我们需要再这个蛋糕上加上奶油,加上巧克力,加上水果,写上字等等,将原始蛋糕作为一个Cake类,我们给这个Cake类的对象作装饰,为了不破坏开放-封闭原则,也为了更好的拓展,我们不能直接在Cake这个类里修改,而通过装饰器来给这个蛋糕做装饰。
代码示例:
#pragma once
#include <iostream>
class Cake //一个原始的抽象类蛋糕
{
public:
virtual void showCake() = 0;
virtual ~Cake() {};
std::string name;
};
class ConcreteCake :public Cake//具体的蛋糕类
{
public:
ConcreteCake()
{
name = "原始蛋糕";
}
void showCake() { std::cout << name.c_str() << std::endl; };
virtual ~ConcreteCake() {};
};
#pragma once
#include "test.h"
#include<iostream>
using namespace std;
class CakeDecrator :public Cake
{
protected:
Cake* pCake; //维护一个Cake对象的引用,为Cake对象添加装饰
public:
virtual void showCake() = 0;
virtual ~CakeDecrator() {};
};
class CakeDecratorMilk : public CakeDecrator//具体添加牛奶的装饰类
{
public:
CakeDecratorMilk(Cake* pCake)
{
this->pCake = pCake;
}
void showCake()
{
this->name = pCake->name + "加奶油";
cout << name<<endl;
};
virtual ~CakeDecratorMilk() {};
};
class CakeDecratorCholate : public CakeDecrator//具体添加巧克力的类
{
public:
CakeDecratorCholate(Cake* pCake)
{
this->pCake = pCake;
}
void showCake()
{
this->name = pCake->name + "加巧克力";
std::cout << name<<endl;
};
virtual ~CakeDecratorCholate() {};
};
#include "d.h"
int main()
{
ConcreteCake* ConCake = new ConcreteCake();
ConCake->showCake();
CakeDecratorMilk* MilkCake = new CakeDecratorMilk(ConCake);
MilkCake->showCake();
CakeDecratorCholate* ChoCake = new CakeDecratorCholate(ConCake);
ChoCake->showCake();
CakeDecratorCholate* CMCake = new CakeDecratorCholate(MilkCake);
CMCake->showCake();
delete ConCake;
delete MilkCake;
delete ChoCake;
delete CMCake;
return 0;
}
优点
- 每个装饰对象只关心自己的功能,不需要关心如何被添加到对象当中。
- 类的核心职责与动态添加的职责是分离的。如果再向主类中添加新的功能,一是违反了开放封闭原则,二是增加了主类的复杂度。
- 装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。装饰模式允许系统动态决定“贴上”一个需要的“装饰”,或者除掉一个不需要的“装饰”。继承关系则不同,继承关系是静态的,它在系统运行前就决定了。
- 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。
装饰模式的缺点
使用装饰模式会产生比使用继承关系更多的对象。更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。
适用场景
- 当需要为已有功能动态地添加更多功能时。
- 类的核心功能无需改变,只是需要添加新的功能时。