在lab3中,我们可以选择6种方案对计划项的特性功能进行开发。
我在两个方案都尝试过了之后,总结了一些优缺点。
零.方案5及方案6内容
方案5:CRP,通过接口组合实现局部共性特征的复用 针对方案4,通过delegation机制进行改造。每个维度分别定义自己的接口,针对每个维度的不同特征取值,分别实现针对该维度接口的不同实现类,实现其特殊操作逻辑。进而,通过接口组合,将各种局部共性行为复合在一起,形成满足每个应用要求的特殊接口(包含了该应用内的全部特殊功能),从而该应用子类可直接实现该组合接口。在应用子类内,不是直接实现每个特殊操作,而是通过delegation到外部每个维度上的各具体实现类的相应特殊操作逻辑。
public interface MultipleLocationEntry{...}
public interface BlockableEntry {...}
public interface MultipleSortedResourceEntry {...}
...
public class MultipleLocationEntryImpl implements MultipleLocationEntry
public class BlockableEntryImplimplements BlockableEntry
public class MultipleSortedResourceEntryImplimplements MultipleSortedResourceEntry
public interface TrainPlanningEntry extends MultipleLocationEntry,BlockableEntry,MultipleSortedResourceEntry { }
public class TrainEntryextendsCommonPlanningEntryimplements TrainPlanningEntry {
private MultipleLocationEntryImpl mle;
private BlockableEntryImpl be;
private MultipleSortedResourceEntryImpl msre;
public TrainEntry(MultipleLocationEntryImpl mle, BlockableEntryImpl be, MultipleSortedResourceEntryImpl msre) {
...//设置delegation关系
}
@Override
public void setLocations(List<Location> locs){
mle.setLocations(locs);
}
@Override
public void block(Calendar time) {
be.block(time);
}
...
}
方案6:使用decorator设计模式 将CommonPlanningEntry看作是原始的、未被装饰的计划项实体,将这五个维度看作是五种“装饰”(每个维度的不同特征取值可以产生不同的“装饰”效果)。请参照讲义上关于该设计模式的说明,设计相应的子类型继承关系树,然后在具体应用中通过为一个CommonPlanningEntry对象逐层装饰五个不同特征,即可实现应用所需的组合特征。
一. 方案5的优缺点
1.优点
- 将软件的特征不断细分,形成多个类接口,再通过实现接口的方式自行取件,组合成一个完整的软件。
- 使用委托,在修改时只需要修改底层接口就行。
2.缺点
- 设计困难。
- 将父类的属性下方到了接口之中,进行操作时可能会产生一些不必要的多层委托。(例如在我最初的设计中,location属性是放在SingleLocationEntry/MultipleLocationEntry中的,而为了区分是否可设定/更改,则又设置了其他的类。在设定/更改类中,委托Single/Multiple类进行更改,而Single/Multiple类更改location的接口是暴露出去的,故产生了一层不必要的委托)
- 产生大量的类/接口。
- 当某一个维度的某一特征有区别时(例如lab3中的单一/多个位置),对于它们的共性操作(如设定位置)则需要再分出两个类分别实现。
二.方案6(decorator)的优缺点
1.优点
- 写起来简单,用起来也简单,不停地选取需要的特性一层一层套就好了。
2.缺点
- 父类需要包含所有子类的接口,不满足LSP。
例如下面的代码:
interface Normal{
public void print();
}
class NormalImp implements Normal{
public void print(){
System.out.println("NormalImp");
}
}
abstract class Decorator implements Normal{
protected final Normal normal;
public Decorator(Normal normal){
this.normal = normal;
}
public void print(){
this.normal.print();
}
}
class DecB extends Decorator implements Normal{
public DecB(Normal normal){
super(normal);
}
public void bprint(){
System.out.println("DecB");
}
}
class DecC extends Decorator implements Normal{
public DecC(Normal normal){
super(normal);
}
public void cprint(){
System.out.println("DecC");
}
}
如果我想要一个既有B特性又有C特性的实例,我需要
Normal normal = new DecB(new DecC(new NormalImp()));
那么问题就出现了,以这种方式声明的normal实例只能调用print方法,而没法调用bprint和cprint,即没法使用B和C的特性。
要解决这个问题,我们就需要在Normal接口中包含所有特性的接口。
但若这么做,则显然违反了LSP。
Comments