情景描述
每当会想起考试卷签字这件事都会让我背脊发凉 , 然而在四年级的期末考试 , 学校出了一个损招 , 打印成绩单 , 要让家长签字 , 然后才能上五年级 . 当时那个恐惧 , 有那么一瞬间觉得念书就到此为止了 . 这份成绩单可以使用类来表示 , 代码如下 :
//抽象成绩单
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的关系 . - 装饰模式可以动态地扩展一个实现类的功能,这不需要多说,装饰模式的定义就是如此 .
装饰模式的缺点
于装饰模式记住一点就足够了: 多层的装饰是比较复杂的 . 为什么会复杂呢? 你想想看,就像剥洋葱一样,你剥到了最后才发现是最里层的装饰出现了问题,想象一下工作量吧,因此,尽量减少装饰类的数量,以便降低系统的复杂度 .
装饰模式的使用场景
- 需要扩展一个类的功能,或给一个类增加附加功能 .
- 需要动态地给一个对象增加功能,这些功能可以再动态地撤销 .
- 需要为一批的兄弟类进行改装或加装功能,当然是首选装饰模式 .
参考书籍 :
<<设计模式之禅 第二版>>
<<设计模式>>