[C++实现 设计模式(11)] : 装饰模式

16 篇文章 0 订阅

情景描述

       每当会想起考试卷签字这件事都会让我背脊发凉 , 然而在四年级的期末考试 , 学校出了一个损招 , 打印成绩单 , 要让家长签字 , 然后才能上五年级 . 当时那个恐惧 , 有那么一瞬间觉得念书就到此为止了 . 这份成绩单可以使用类来表示 , 代码如下 :

//抽象成绩单
class SchoolReport
{
public:
	//成绩单主要展示的就是你的成绩情况
	virtual void report() = 0;
	//成绩单要家长签字 , 这个是最要命的
	virtual void sign(string name) = 0;
};

//四年级成绩单
class FouthGradeSchoolReport : public SchoolReport
{
public:
	void report()
	{
		cout<<"尊敬的XXX家长 : "<<endl;
		cout<<"    ...........   "<<endl;
		cout<<"  语文 69   数学 65   体育 98   自然 63"<<endl;
		cout<<"    ...........   "<<endl;
		cout<<"            家长签名 :    "<<endl;
	}
	void sign(string name)
	{
		cout<<"家长签名 : "+name<<endl;
	}
};

       看看这惨不忍睹的成绩 , 就这成绩单还怎么能拿得出手 , 然而这不是当时的真实情况 , 我没有把成绩单直接交给老爸 , 而是在交给他之前做了点儿技术工作 , 我要把成绩单封装一下 , 封装分为两步来实现 :

  • 汇报最高成绩
    跟老爸说各个科目的最高分 , 语文最高是75 , 数学是78 , 自然是80 , 然后老爸觉得我的成绩与最高分数相差不多 , 考的还是不错的嘛!这个是实情 , 但是不知道是什么原因,反正期末考试都考得不怎么样 , 但是基本上都集中在70分以上 , 我这60多分基本上还是垫底的角色 .
  • 汇报排名情况
    在老爸看完成绩单后 , 告诉他我在全班排第38名(以前都是45开外) , 这个也是实情 , 为啥呢?有将近十个同学退学了! 这个情况我是不会说的 . 不知道是不是当时第一次发成绩单时学校没有考虑清楚 , 没有写上总共有多少同学 , 排第几名 , 反正是被我钻了个空子 .
//修饰成绩单
class SugarFouthGradeSchoolReport : public FouthGradeSchoolReport
{
public:
	void report()
	{
		//先说最高成绩
		reportHighScore();
		//然后给老爸看成绩单
		FouthGradeSchoolReport::report();
		//然后告诉老爸学校排名
		reportSort();
	}
private:
	//首先要定义你要的美化方法 , 先给老爸说学校最高的成绩
	void reportHighScore()
	{
		cout<<"这次考试语文的最高成绩是75 , 数学是78 , 自然是80"<<endl;
	}
	//老爸看完成绩单后 , 我再汇报学校的排名情况
	void reportSort()
	{
		cout<<"我的排名是第38名..."<<endl;
	}
};

//老爸查看修饰后的成绩单
int main()
{
	//把美化过的成绩单拿过来
	SchoolReport* sr = new SugarFouthGradeSchoolReport();
	//看成绩
	sr->report();
	//然后老爸一看 ,很开心 , 就签名了
	sr->sign("老白");

	delete sr;

	return 0;
}

       是的 , 通过继承就能解决这个问题 . 但是在(项目中)现实的情况是很复杂的 , 可能老爸听了我汇报最高成绩后 , 直接乐开了花 , 直接就签名了 , 后面的排名就没必要看了, 或者老爸要先看排名的情况 , 那怎么办?要不断的扩展?你能扩展多少个类?这还是一个比较简单的场景 , 一旦需要装是的条件非常多 , 比如20个 , 你还能通过继承来解决 , 你想想子类有多少个?你是不是马上就要崩溃了!那么我们该怎么办?好办 , 我们定义一批专门负责装饰的类 , 然后根据实际情况来决定是否需要进行装饰.类图如下 :
在这里插入图片描述

       增加一个抽象类和两个实现类其中Decorator的作用是封装SchoolReport类,如果大家还记得代理模式,那么很容易看懂这个类图,装饰类的作用也就是一个特殊的代理类,真实的执行者还是被代理的角色FouthGradeSchoolReport , 代码如下 :

//修饰的抽象类
class Decorator : public SchoolReport
{
public:
	//构造函数 , 传递成绩单
	Decorator(SchoolReport* _sr)
	{
		sr = _sr;
	}
	//成绩单还是要被看到的
	void report()
	{
		sr->report();
	}
	//看完还是要签名的
	void sign(string name)
	{
		sr->sign(name);
	}
private:
	//首先要知道是哪个成绩单
	SchoolReport* sr;
};

       看到没,装饰类还是把动作的执行委托给需要装饰的对象,Decorator抽象类的目的很简单,就是要让子类来封装SchoolReport的子类怎么封装?重写report方法!

完整代码如下 :

#include<iostream>
#include<string>
//#include<vld.h>

using namespace std;

//装饰模式
#if 0
//抽象成绩单
class SchoolReport
{
public:
	//成绩单主要展示的就是你的成绩情况
	virtual void report() = 0;
	//成绩单要家长签字 , 这个是最要命的
	virtual void sign(string name) = 0;
};

//修饰的抽象类
class Decorator : public SchoolReport
{
public:
	//构造函数 , 传递成绩单
	Decorator(SchoolReport* _sr)
	{
		sr = _sr;
	}
	//成绩单还是要被看到的
	void report()
	{
		sr->report();
	}
	//看完还是要签名的
	void sign(string name)
	{
		sr->sign(name);
	}
private:
	//首先要知道是哪个成绩单
	SchoolReport* sr;
};

//最高成绩修饰
class HighScoreDecorator : public Decorator
{	
public: 
	//构造函数
	HighScoreDecorator(SchoolReport* sr)
		:Decorator(sr)
	{}
	//要在老爸看成绩单之前告诉他最高成绩 , 否则等他一看 , 就抡起扫帚揍我 , 我哪里还有机会说
	void report()
	{
		reportHighScore();
		Decorator::report();
	}
private:
	//首先要定义你要的美化方法 , 先给老爸说学校最高的成绩
	void reportHighScore()
	{
		cout<<"这次考试语文的最高成绩是75 , 数学是78 , 自然是80"<<endl;
	}
};

//排名情况修饰
class SortDecorator : public Decorator
{
public:
	//构造函数
	SortDecorator(SchoolReport* sr)
		:Decorator(sr)
	{}
	//老爸看完成绩单后再告诉他 , 加强作用
	void report()
	{
		Decorator::report();
		reportSort();
	}
private:
	//告诉老爸学校的排名情况
	void reportSort()
	{
		cout<<"我的排名是第38名..."<<endl;
	}
};


//四年级成绩单
class FouthGradeSchoolReport : public SchoolReport
{
public:
	void report()
	{
		cout<<"尊敬的XXX家长 : "<<endl;
		cout<<"    ...........   "<<endl;
		cout<<"  语文 69   数学 65   体育 98   自然 63"<<endl;
		cout<<"    ...........   "<<endl;
		cout<<"            家长签名 :    "<<endl;
	}
	void sign(string name)
	{
		cout<<"家长签名 : "+name<<endl;
	}
};


//老爸查看修饰后的成绩单
int main()
{
	//把成绩单拿过来
	SchoolReport* sr1; SchoolReport* sr2; SchoolReport* sr3;

	//原装的成绩单
	sr1 = new FouthGradeSchoolReport();
	//加了最高分说明的成绩单
	sr2 = new HighScoreDecorator(sr1);
	//右加了成绩排名的说明
	sr3 = new SortDecorator(sr2);
	//看成绩单
	sr3->report();
	//然后老爸一看 , 很开心 , 就签名了
	sr3->sign("老白");
	
	delete sr1; delete sr2; delete sr3;
	return 0;
}

#endif

运行结果 :
在这里插入图片描述

装饰模式的定义

定义 : 动态地给一个对象添加一些额外的职责 . 就增加功能来说,装饰模式相比生成子类更为灵活 .

装饰模式的通用类图 :
在这里插入图片描述

装饰模式的结构

  • Component抽象构件
    Component是一个接口或者是抽象类,就是定义我们最核心的对象,也就是最原始的对象,如上面的成绩单 .
  • ConcreteComponent具体构件
    ConcreteComponent是最核心、最原始、最基本的接口或抽象类的实现,你要装饰的就是它
  • Decorator装饰角色
    一般是一个抽象类,做什么用呢?实现接口或者抽象方法,它里面可不一定有抽象的方法呀,在它的属性里必然有一个private变量指向Component抽象构件 .
  • 具体装饰角色
    ConcreteDecoratorA和ConcreteDecoratorB是两个具体的装饰类,你要把你最核心的、最原始的、最基本的东西装饰成其他东西,上面的例子就是把一个比较平庸的成绩单装饰成家长认可的成绩单 .

注意 : 在装饰模式中,必然有一个最基本、最核心、最原始的接口或抽象类充当Component抽象构件 .

装饰模式的应用

装饰模式的优点
  • 装饰类和被装饰类可以独立发展,而不会相互耦合 . 换句话说,Component类无须知道
    Decorator类,Decorator 类是从外部来扩展Component类的功能,而Decorator也不用知道具体的构件 .
  • 装饰模式是继承关系的一个替代方案 . 我们看装饰类Decorator,不管装饰多少层,返回
    的对象还是Component,实现的还是is-a的关系 .
  • 装饰模式可以动态地扩展一个实现类的功能,这不需要多说,装饰模式的定义就是如此 .
装饰模式的缺点

       于装饰模式记住一点就足够了: 多层的装饰是比较复杂的 . 为什么会复杂呢? 你想想看,就像剥洋葱一样,你剥到了最后才发现是最里层的装饰出现了问题,想象一下工作量吧,因此,尽量减少装饰类的数量,以便降低系统的复杂度 .

装饰模式的使用场景
  • 需要扩展一个类的功能,或给一个类增加附加功能 .
  • 需要动态地给一个对象增加功能,这些功能可以再动态地撤销 .
  • 需要为一批的兄弟类进行改装或加装功能,当然是首选装饰模式 .

参考书籍 :

                  <<设计模式之禅 第二版>>
                  <<设计模式>>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值