装饰模式【Decorator Pattern】
装饰模式顾名思义就是将程序装饰进行装饰完成我们所想要的效果。山寨手机需要装饰,我们的程序有时候也需要进行装饰。
下面以向家长汇报分数让家长签字为例。先看下最初的类图。
倘若这样子直接拿给老爸看(直接汇报成绩,要签名),那岂不是要挨板子。对于差生来讲,还得先装饰一下好。
下面再看一下改善后的类图。
这样子设计的话,就可以在report()方法中做点手脚了,可以先汇报一下最高分,毕竟老子跟最高分差了10来分而已。然后可以报告一下排名,毕竟全班60人我考了38,(其实已经退学了快20人了)。
程序设计如下:
public class SugarFouthGradeSchoolReport extends FouthGradeSchoolReport {
//首先要定义你要美化的方法,先给老爸说学校最高成绩
private void reportHighScore(){
System.out.println("这次考试语文最高是75,数学是78,自然是80");
}
//在老爸看完毕成绩单后,我再汇报学校的排名情况
private void reportSort(){
System.out.println("我是排名第38名...");
}
//由于汇报的内容已经发生变更,那所以要重写父类
@Override
public void report(){
this.reportHighScore(); //先说最高成绩
super.report(); //然后老爸看成绩单
this.reportSort(); //然后告诉老爸学习学校排名
}
}
这样老爸看到的成绩单已经是美化过的了:
public class Father {
public static void main(String[] args) {
//美化过的成绩单拿过来
SchoolReport sr= new SugarFouthGradeSchoolReport();
//看成绩单
sr.report();
//然后老爸,一看,很开心,就签名了
sr.sign("老三"); //我叫小三,老爸当然叫老三
}
上面的例子通过继承来解决这个问题,通过继承只实现装饰最高分这一项,倘若我们要装饰得条件很多,那就会出问题了,出现类爆炸的情况。想想维护起来的成本。。。
我们还得好好装饰一下,先看改善后的类图:
增加一个抽象类和两个实现类,其中 Decorator 的作用是封装 SchoolReport 类。
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);
}
}
Decorator 的目的就是要让子类来对SchoolReport 进行封装。看下这么封装:
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();
}
}
上面两个子类重写了report方法,达到了想要的结果。
老爸看成绩单:
SchoolReport sr;
sr = new FouthGradeSchoolReport(); //原装的成绩单
//加了最高分说明的成绩单
sr = new HighScoreDecorator(sr);
//又加了成绩排名的说明
sr = new SortDecorator(sr);
//看成绩单
sr.report();
//然后老爸,一看,很开心,就签名了
sr.sign("老三"); //我叫小三,老爸当然叫老三
通过这样的装饰模式就躲过了一顿海扁。
下面再看看其通用类图。
看类图,Component 是一个接口或者是抽象类,就是定义我们最核心的对象,也就是最原始的对象,比
如上面的成绩单,记住在装饰模式中,必然有一个被提取出来最核心、最原始、最基本的接口或抽象类,就是 Component。
ConcreteComponent 这个事最核心、 最原始、 最基本的接口或抽象类的实现, 你要装饰的的对象。
Decorator 一般是一个抽象类,实现接口或者抽象方法。
ConcreteDecoratorA 和 ConcreteDecoratorB 是两个具体的装饰类。
总结:
(1)装饰者模式的存在,解决的问题主要就是继承带来的弊端,首先可能带来的类是上面所说的类爆炸的问题。同时继承的话由于子类必须继承父类的方法,所以当中间类的方法改变时,那么就会牵一发动全身了,利用装饰模式就可以将其进行分离,降低其耦合性,方便程序的扩展。
(2)继承与装饰模式的选取:选择继承还是装饰模式时主要考虑类之间的继承层数问题,如果只是一层,那可以考虑继承,如果出现多层继承的情况,那就应该考虑装饰模式了。