Lab3最终敲定使用Decorator设计模式进行开发,最终结果证明这个选择是完全正确的。
Decorator设计模式本身强大的可扩展性和可复用性完全符合我的预期,对于上层ADT的开发没有占据多少时间(大量的时间都耗在了用户交互开发上了,打印信息真的是个枯燥而无脑的工作)。而且接口的具体实现类、Decorator类以及各个具体装饰类借助IDEA强大的纠错能力,根本没有调试,后续直接调用跑测试没出现任何底层的问题(问题还是全出现在输入信息提取、信息打印格式上)。
Decorator设计模式的核心是编写装饰类。一个基本的抽象装饰类Decorator是必不可少的,同时这也是Decorator设计模式最巧妙的地方。基本装饰类本身定义为接口的实现类,但又往往用abstract修饰,也就是说基础装饰类是不能直接调用的,他只是各具体装饰类相互结合的一个跳板。基础装饰类内部通过委派一个接口的多态来实现继承各个方法,正是因为这个委派,使得逐层装饰的效果都可以被组合。
直接看一下用具体装饰类组装一个具有多种特性的ADT的定义方式:
private final NonBlankIntervalSet<Employee> DutyIntervalSet =
new NonBlankIntervalSet<Employee>(
new NonOverlapIntervalSet<Employee>(
new UniqueIntervalSet<Employee>(
new CommonIntervalSet<Employee>())));
这是应用一中定义无空白、无重叠、标签唯一时间轴的方式。代表三种特性的具体装饰类依次嵌套,并作为参数传给上一层类的构造器进行构造。这种定义形式看起来非常复杂,但再看一下具体装饰类的写法就一目了然了:
public class UniqueIntervalSet<L> extends IntervalDecorator<L> {
public UniqueIntervalSet(IntervalSet<L> set) {
super(set);
}
/**
* 在当前时间轴中插入新的时间段和标签。
* 若待插入时间段的标签与某个已有时间段标签相同,则不执行插入。
*
* @param label 待插入时间段的标签
* @param start 待插入时间段的开始时间
* @param end 待插入时间段的结束时间
*/
@Override
public void insert(L label, long start, long end) {
if (super.labels().contains(label)) {
System.out.println("Label already exist!");
return;
}
super.insert(label, start, end);
}
}
这里标签唯一特性表现为重写了insert方法,在每次插入时间轴前检查该标签是否已经存在,如果不存在,再调用父类的插入方法,这就是新的特性。注意这里使用的是super,调用的是父类的方法。而每一个具体装饰类的父类都是基础装饰类Decorator。再看一下Decorator类的结构
public abstract class IntervalDecorator<L> implements IntervalSet<L>{
protected final IntervalSet<L> intervalSet;
protected IntervalDecorator(IntervalSet<L> set) {
this.intervalSet = set;
}
@Override
public void insert(L label, long start, long end) {
intervalSet.insert(label,start,end);
}
@Override
//其他方法依次委派...........
//.........................
}
也就是说在定义时将无特性的接口实现类CommonIntervalSet作为参数传入UniqueIntervalSet,UniqueIntervalSet的构造器将则个类传给父类进行构造,此时基础装饰类Decorato委派给了CommonIntervalSet。那么每次执行insert操作时,会进入UniqueIntervalSet的特性插入方法,如果达到了插入条件,他再使用super.insert通过父类操作,而其父类Decorator内部正是CommonIntervalSet,所以最后还是会调用CommonIntervalSet的插入方法。那么试想这样逐层嵌套,执行一个方法会根据这样的委派体系依次经历那些装饰类的特性操作,这样便实现了特性的组合。
需要注意的是,嵌套定义具有组合特性的ADT时,定义出来的ADT只能使用公用方法和最外层具体装饰类的特有方法。具体装饰类中一般通过重写方法来赋予特性。如果在具体装饰类中撰写新方法赋予特性,那么在定义时该类必须位于最外层嵌套,嵌套在内层的类的特有方法是无法直接调用的。
最后通过这样一种强大的设计模式,实验三最后新的变化一节修改特性时只需修改一行定义代码即可。因为对于Decorator设计模式来说修改特性就是重新组合嵌套装饰类,我们只需改变一下组装具体ADT时用到的装饰类即可。