04月25, 2020

软件构造 - Lab3中方案5及方案6优缺点

在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中的单一/多个位置),对于它们的共性操作(如设定位置)则需要再分出两个类分别实现。

W09P01.png

二.方案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。

本文链接:http://blog.zireaels.com/post/SCW09.html

-- EOF --

Comments