本文主要参考资料:《设计模式之禅》
辅助教程:https://www.cnblogs.com/stonefeng/p/5679638.html
本文主要目录为:
1)案例
2)装饰模式改进案例
3)装饰模式的定义
1. 案例
小学的时候成绩很差,到了四年级的时候,学校突然把成绩单打印出来让我们带回去给父亲签字。具体的类图和代码如下所示
public abstract class SchoolReport {
//成绩单的主要展示的就是你的成绩情况
public abstract void report();
//成绩单要家长签字,这个是最要命的
public abstract void sign(String name);
}
public class FouthGradeSchoolReport extends SchoolReport {
//我的成绩单
public void report() {
//成绩单的格式是这个样子的
System.out.println("尊敬的XXX家长:");
System.out.println(" ......");
System.out.println(" 语文 62 数学65 体育 98 自然 63");
System.out.println(" .......");
System.out.println(" 家长签名: ");
}
//家长签名
public void sign(String name) {
System.out.println("家长签名为:"+name);
}
}
public class Father {
public static void main(String[] args) {
//成绩单拿过来
SchoolReport sr = new FouthGradeSchoolReport();
//看成绩单
sr.report();
//签名?休想!
}
}
成绩单一出来,你别看什么62,65之类的成绩,你要知道,在小学低于90分基本就是中下了。这个成绩还找老爸签字?准备挨揍吧!
2. 装饰模式改进案例
但我当时并没有直把成绩单交给我老爸,而是在他之前做了点技术,我要把成绩单封装一下,封装分为两步来实现,如下图所示:
我们通过增加一个子类SugarFouthGradeSchoolReport继承FouthGradeReport,重写report方法,确实能解决问题。但现实的情况很复杂:可能老爸听了我汇报最高成绩以后,就直接乐开了花,直接签名了后面的排名就没有必要看,或则老爸先要看排名情况,那怎么办?继续扩展?你能扩展几个类?这还是一个比较简单的场景,一旦需要装饰的条件非常多,比如20个,就会出现类爆炸的情况,而且以后维护困难,谁愿意维护一大滩本质相似的代码?并且,在面向对象的设计中,如果超过两层继承,你就应该想想是不是设计出了问题(不绝对),继承的层次越多以后维护的成本就越多。
所以我们现在需要定义一批专门负责装饰的类,然后根据实际情况来决定是否需要进行装饰:
public abstract class SchoolReport {
//成绩单的主要展示的就是你的成绩情况
public abstract void report();
//成绩单要家长签字,这个是最要命的
public abstract void sign(String name);
}
public class FouthGradeSchoolReport extends SchoolReport {
//我的成绩单
public void report() {
//成绩单的格式是这个样子的
System.out.println("尊敬的XXX家长:");
System.out.println(" ......");
System.out.println(" 语文 62 数学65 体育 98 自然 63");
System.out.println(" .......");
System.out.println(" 家长签名: ");
}
//家长签名
public void sign(String name) {
System.out.println("家长签名为:"+name);
}
}
public abstract class Decorator extends SchoolReport{
//首先我要知道是那个成绩单
private SchoolReport sr;
//构造函数,传递成绩单过来
public Decorator(SchoolReport sr){
this.sr = sr;
}
//成绩单还是要被看到的
public void report(){
this.sr.report();
}
//看完毕还是要签名的
public void sign(String name){
this.sr.sign(name);
}
}
public class HighScoreDecorator extends Decorator {
//构造函数
public HighScoreDecorator(SchoolReport sr){
super(sr);
}
//我要回报最高成绩
private void reportHighScore(){
System.out.println("这次考试语文最高是75,数学是78,自然是80");
}
//最高成绩我要在老爸看成绩单前告诉他,否则等他一看,就抡起笤帚有揍我,我那还有机会说呀
@Override
public void report(){
this.reportHighScore();
super.report();
}
}
public class SortDecorator extends Decorator {
//构造函数
public SortDecorator(SchoolReport sr){
super(sr);
}
//告诉老爸学校的排名情况
private void reportSort(){
System.out.println("我是排名第38名...");
}
//老爸看完成绩单后再告诉他,加强作用
@Override
public void report(){
super.report();
this.reportSort();
}
}
public class Father {
public static void main(String[] args) {
//成绩单拿过来
SchoolReport sr;
//原装的成绩单
sr = new FouthGradeSchoolReport();
//加了最高分说明的成绩单
sr = new HighScoreDecorator(sr);
//又加了成绩排名的说明
sr = new SortDecorator(sr);
//看成绩单
sr.report();
//然后老爸,一看,很开心,就签名了
sr.sign("老三"); //我叫小三,老爸当然叫老三
}
}
3. 装饰模式的定义
定义:Attach additional responsibilities to an object dynamically keeping the same interface. Decorators provide a flexible alternative to subclassing for extending functionality.(动态地给一个对象添加一些额外的职责。就增加功能来说,装饰模式相比生产子类更为灵活。)
装饰模式的通用类图如下图:
在类图中,有四个角色需要说明:
- Component抽象构件:Component是一个接口或者是抽象类,就是定义我们最核心的对象,也就是最原始的对象。
- ConcreteComponent具体构件:ConcreteComponent是最核心、最原始、最基本的接口或抽象类的实现,你要装饰的就是它。
- Decorator装饰角色:一般是一个抽象类,做什么用呢?实现接口或者抽象方法,它里面可不一定有抽象的方法呀,在它的属性里必然有一个private变量指向Component抽象构件。
- 具体装饰对象:ConcreteDecorator1和ConcreteDecorator2是两个具体的装饰类,你要把最核心的、最原始的、最基本的东西装饰城其他东西。
装饰模式的应用
1.装饰模式的优点
- 装饰类与被装饰类可以独立发展,而不会耦合。换句话说,Component类无须知道Decorator类,Decorator类是从外部来扩展Component类的功能,而Decorator类也不用知道具体的构件。
- 装饰模式时继承关系得一个替代方案。我们看装饰类Decorator,不管装饰多少层,返回的对象还是Component,实现的还是is-a的关系。
- 装饰模式可以动态地扩展一个实现类得功能。
2.装饰模式的缺点
对于装饰模式记住一点就足够了:多层的装饰是很复杂的。因此,尽量减少装饰类的数量,以便降低系统的复杂度。
3.装饰模式的使用场景
- 需要扩展一个类的功能,或给一个类增加附加功能。
- 需要动态地给一个对象增加功能,这些功能可以再动态地撤销。
- 需要为一批的兄弟类进行改装或加装功能,当然是首选装饰模式。