一、概述
为什么需要装饰者模式?在不允许直接修改原代码的情况下进行扩展时需要使用装饰者模式。装饰者模式动态的将责任(行为)附加到对象身上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案。 这就是设计中的开放——关闭原则,对扩展开放,对修改关闭。以下时关于装饰者模式的一些相关特征:
- 装饰者与被装饰者拥有相同的超类
- 可以用一个或多个装饰者去包装一个对象
- 装饰者和被装饰者拥有相同的超类,所以在任何需要原始对象(被包装的)的场合,可以用装饰过的对象来替代它
- 装饰者可以在所委托被装饰者的行为之前与/或之后,加上自己的行为,以达到特定的目的。
- 对象可以在任何时候被装饰,所以可以在运行时动态的,不限量的使用装饰者来装饰对象。
二、例
我们都知道,一个专业中包括主修课程和选修课程,主修课程和选修课程都是可以获得学分的,而一般来说主修课程就体现了一个专业的类型(是哪个专业)。现在就通过这个例子使用装饰者模式来实现一下这种情况。
其中会包括超类/最高级接口、组件(被装饰者)、装饰者。那么超类/最高级接口就应该是专业,组件就应该是专业中的主修课程,装饰者就应该是专业中的选修课程。前面说过:装饰者可以在所委托被装饰者的行为之前与/或之后,加上自己的行为,以达到特定的目的。这里面是通过选修课程获得学分,以达到专业允许毕业学分和专业组成的目的。
超类(专业):
package decorator2;
/**
* 装饰者模式中的超类(专业)
*/
public abstract class AbstractMajor {
String description = "Unknown Subject";
/**
* 学科描述
* @return
*/
public String getDescription() {
return description;
}
/**
* 可获得的学分
* @return
*/
public abstract double cost();
}
组件(BiologySubject主修生物学-----被装饰者):
package decorator2;
/**
* 主修生物学
*/
public class BiologySubject extends AbstractMajor {
public BiologySubject() {
description = "主修学科:Biology";
}
/**
* 学分计算
* @return
*/
@Override
public double cost() {
return 1;
}
}
装饰者共同实现的接口或继承的抽象类:
package decorator2;
/**
* 选修课
*/
public abstract class AbstractElectiveSubject extends AbstractMajor {
@Override
public abstract String getDescription();
}
装饰者A:
package decorator2;
/**
* 英语选修
*/
public class EnglishSubject extends AbstractElectiveSubject {
/** 学科 */
AbstractMajor abstractSubject;
public EnglishSubject(AbstractMajor abstractSubject) {
this.abstractSubject = abstractSubject;
}
@Override
public String getDescription() {
return abstractSubject.getDescription() + " 选修English";
}
@Override
public double cost() {
return 2 + abstractSubject.cost();
}
}
装饰者B:
package decorator2;
/**
* 高数选修
*/
public class MathematicsSubject extends AbstractElectiveSubject {
/** 学科 */
AbstractMajor abstractSubject;
public MathematicsSubject(AbstractMajor abstractSubject) {
this.abstractSubject = abstractSubject;
}
@Override
public String getDescription() {
return abstractSubject.getDescription() + " 选修:mathematics";
}
@Override
public double cost() {
return 3 + + abstractSubject.cost();
}
}
测试类:
package decorator2;
public class Test {
public static void main(String[] args) {
AbstractMajor abstractSubject = new BiologySubject();
abstractSubject = new MathematicsSubject(abstractSubject);
abstractSubject = new EnglishSubject(abstractSubject);
System.out.println(abstractSubject.getDescription() + " 学分共:" + abstractSubject.cost());
}
}
测试结果就不粘贴了,各位看官可以自己尝试。
前面由提到,装饰者提供了比继承更有弹性的解决方案,但是在代码中都是使用了继承,代码我们使用继承是达到了“类型匹配”的效果,而不是通过继承来获得行为(方法),就像前面提到的“装饰者与被装饰者拥有相同的超类”,这里面组件(专业)获得新的行为(学科)是通过组合对象获得的,不是通过继承获得的。