面向复用的软件构造技术
1.设计可复用的类
(1).子类型多态与LSP原则
子类型多态
客户端可用统一的方式处理 不同类型的对象
在可以使用父类对象的场景,都可以用子类对象代替而不会有任何问题
静态检查规则:
子类型可以增加方法,但不可删
子类型需要实现抽象类型中的所有未实现方法
子类型中重写的方法必须有相同或子类型的返回值
子类型中重写的方法必须使用同样类型的参数
子类型中重写的方法不能抛出额外的异常
对于特定方法:
更强的不变量;更弱的前置条件;更强的后置条件
LSP原则
协变
父类型→子类型:返回值类型/异常的类型不变或变得更具体
逆变
父类型→子类型:参数类型不变或越来越抽象
数组是协变的:类型为T的数组可以包含类型为T的元素或T子类的元素
泛型不是协变的:类型擦除
ArrayList<String>是List<String> 的子类
List<String>不是List<Object> 的子类
类型参数的通配符:List<?>
表示下界的通配符:<? super A> 与所有A的父类型匹配
表示上界的通配符:<? extends A> 与所有A的子类匹配
(2)Delegation and Composition
两个类之间的比较
方法1:实现Comparator接口并override compare()函数
public class EdgeComparator implements Comparator<Edge>{
@Override
public int compare(Edge o1,Edge o2){
if(o1.getWeight() > o2.getWeight())
return 1;
else if (.. == ..) return 0;
else return -1;
}
}
调用:
public void sort(List<Edge> edges){
EdgeComparator comparator = new EdgeComparator();
Collections.sort(edges,comparator);
}
方法2:让ADT实现Comparable接口,然后override compareTo()方法
※不需创建新的Comparator类,比较代码在ADT内部
public class Edge implements Comparable<Edge>{
Vertex s,t;
double weight;
...
public int compareTo(Edge o){
if(this.getWeight() > o.getWeight())
return 1;
else if (.. == ..) return 0;
else return -1;
}
}
Delegation:一个对象请求另一个对象的功能
依赖于动态绑定
例:
如果子类只需要复用父类中的一小部分方法,可以不需要使用继承,而是通过委派机制来实现。
CRP原则:类应该能够实现多态化的行为,通过组合而不是继承更好地实现代码复用
Delegation:has_a ——object层面
Inheritance:is_a——class层面
delegation的类型
(1) Dependency:临时性
一个类利用另一个类里的函数,但不把这个类的对象作为属性。该对象可能是参数或者是方法里的局部变量。这种关系被称为"use-a"
(2)Association:永久性
"has_a"关系
包括Composition,Aggregation
Composition:难以变化
Aggregation:可动态变化
以上几种delegation都支持一对多的delegation
2.设计系统级可复用类库与框架
之所以library和framework被称为系统层面的复用,是因为它们不仅定义了1个可复用的接口/类,而是将某个完整系统中的所有可复用的接口/类都实现出来,并且定义了这些类之间的交互关系、调用关系,从而形成了系统整体的“架构”
白盒框架与黑盒框架
白盒框架:继承
示例:
黑盒框架:委托
示例:
补充一道课后习题:
上图中表示继承与实现关系的是:ce
表示委托关系的是:b