面向复用的软件构造技术
-
设计可复用的类
- LSP原则
客户端可用统一的方式处理 不同类型的对象
在可以使用父类对象的场景,都可以用子类对象代替而不会有任何问题
静态检查规则:
(1) 子类型可以增加方法,但不可删
(2) 子类型需要实现抽象类型中的所有未实现方法
(3) 子类型中重写的方法必须有相同或子类型的返回值
(4) 子类型中重写的方法必须使用同样类型的参数
(5) 子类型中重写的方法不能抛出额外的异常
== 更强的不变量,更弱的前置条件,更强的后置条件 ==
LSP原则:强行为子类型化
协变:
父类型->子类型:越来越具体specific;返回值/异常类型:不变或变得更具体
子类型返回值是父类型返回值的子类型;
子类型中异常要与父类型相同/父类中的子类型/不抛出
数组是协变的:类型为T的数组可以包含类型为T的元素或T子类的元素
泛型不是协变的:
但可采用类型通配符(List<?>)避免协变现象
表示下界的通配符:<? super A> 与所有A的父类型匹配
表示上界的通配符:<? extends A> 与所有A的子类匹配 - LSP原则
List<Integer> myInt = new ArrayList();
List<Number> myNums = myInt; //error! 泛型不是协变的!
Box<Integer> 不是 Box<Number> 的子类型
反协变,逆变
父类型->子类型:越来越具体specific;参数类型:不变或越来越抽象
Java中当作Overload
- Delegation and Composition
ADT 比较大小 或放入Collections或Arrays进行排序
method1:实现Comparator接口并override compare()函数
//client
public void sort(List<Edge> edges) {
Comparator comparator = new EdgeComparator();
//委派
Collections.sort(edges, comparator);
}
//class
public class EdgeComparator implements Comparator<Edge>{
//重写的compare方法
@Override
public int compare(Edge o1, Edge o2) {
if(o1.getWeight() > o2.getWeight())
return 1;
else if (.. == ..) return 0;
else return -1;
}
}
method2:ADT实现Comparable接口,然后override compareTo() 方法
与使用Comparator的区别:不需要构建新的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委派/委托:一个对象请求另一个对象的功能
委派模式:通过运行时动态绑定,实现对其他类中代码的动态复用(复用的一种常见形式)
可实现功能改造:
如下为可查看日志的log
使用委派的情况:
子类只需要复用父类中的一小部分方法,可不需继承,通过委托机制调用部分方法,避免大量无用的方法。
“委托”发生在object层面,而“继承”发生在class层面
Composite Reuse Principle (CRP原则)
更普适的使用继承:
使用接口定义系统必须对外展示的不同侧面的行为
接口之间通过extends实现行为的扩展(接口组合)
类implements 组合接口,从而规避了复杂的继承关系
(1)Dependency:临时性的delegation:没有用来保存delegation的域
一个类利用另一个类里的函数,但不把这个类的对象作为属性。该对象可能是参数或者是方法里的局部变量。这种关系被称为"use-a"
class Duck {
//no field to keep Flyable object
void fly(Flyable f) {
f.fly();
}
}
//client
Flyable f = new FlyWithWings();
Quackable q = new Quack();
Duck d = new Duck();
d.fly(f);
d.quack(q);
(2)Association: 永久性的delegation(包括Composition,Aggregation)
"has_a"关系
- Composition: 更强的association,但难以变化
"is_part_of"关系
class Duck {
Flyable f = new FlyWithWings();
void fly() {
f.fly();
}
}
//client
Duck d = new Duck();
d.fly();
- Aggregation: 更弱的association,可动态变化
class Duck {
Flyable f;
void Duck(Flyable f) {
this.f = f;
}
void setFlyBehavior(f) {
this.f = f;
}
void fly() { f.fly();}
}
//client
Flyable f = new FlyWithWings();
Duck d = new Duck(f);
d.fly();
d.setFlyBehavior(new CannotFly());//动态变化
d.fly();
白/黑盒框架的原理与实现:
- 白盒框架 – 继承 知道其内部实现
通过子类型与重写进行扩展
常用的设计模式:Template Method - 黑盒框架 – 委派/组合 只知道接口方法
通过重写plugin interface进行扩展
常用的设计模式:Strategy, Observer