文章目录
面向复用的软件构造技术
1 Programing for/with reuse
- programming for reuse 面向复用编程:
开发出可复用的软件 - programming with reuse 基于复用编程:
利用已有的可复用软件搭建应用系统
2 LSP
- 行为子类型与Liskov替代原则(LSP)
- 子类型可以增加方法,但不可删除父类中的方法
- 子类型需要实现抽象类型(接口、抽象类)中所有未实现的方法
- 子类型中重写的方法必须有相同或子类型的返回值或者符合co-variant的参数
- 子类型中重写的方法必须使用同样类型的参数或者符合contra-variant的参数(此种情况Java目前按照重载overload处理)
- 子类型中重写的方法不能抛出额外的异常
- 行为子类型也适用于以下特殊方法:
- 更强的不变量
- 更弱的前置条件
- 更强的后置条件
- 在编程语言中,LSP依赖于以下限制:
- 前置条件不能强化
- 后置条件不能弱化
- 保持不变量
- 子类型方法参数:逆变
- 子类型方法的返回值:协变
- 异常类型:协变
3 协变、反协变
- 父类型→子类型,返回值、异常协变(更具体)
更具体的类可能有更具体的返回类型,这称为子类型中返回类型的协变 - 父类型→子类型,参数逆变(更宽松)
子类型参数的逆变在Java中被当做overload处理
4 数组的子类型化
Java中数组是协变的: 对T[]数组,可以保存类型T及其子类型的数据
5 泛型的子类型化
- 类型擦除
泛型信息只存在于编译阶段,在运行时类型参数在编译后被丢弃,运行时不可用。 - 无论何时定义泛型类型,都会自动提供相应的原始类型。原始类型的名称只是泛型类型的名称,类型参数已删除;擦除时类型变量会被擦除,替换为限定类型,如果没有限定类型则替换为Object类型。
6 Delegation
- 委派/委托:一个对象请求另一个对象的功能
- 实现:在B类中创建A类的成员变量,调用A类的方法。
- 很多设计模式将继承和委托结合使用。
- 如果子类只需要复用父类的小部分方法,可以考虑委托机制实现,不需要继承父类所有方法,避免继承大量无用的方法
- 委派模式:通过运行时动态绑定,实现对其他类中代码的动态复用
7 Comparator和Comparable
比较器:int compare(T a,T b):比较其两个参数的顺序。
-
Comparator
- 如果你的ADT需要比较大小,或者要放入Collections或Arrays中进行排序,可实现Comparator接口并override compare()函数。这使用了委托的思想。
- 如果你的ADT需要比较大小,或者要放入Collections或Arrays中进行排序,可实现Comparator接口并override compare()函数。这使用了委托的思想。
-
Comparable
- 让你的ADT实现Comparable接口,然后override compareTo() 方法。
- 与使用Comparator的区别:不需要构建新的Comparator类,比较代码放在ADT内部。这不是委托。
8 CRP原则
- 复合重用原则CRP
-
CRP原则更倾向于使用委派而不是继承来实现复用。
-
“委托”发生在object层面,而“继承”发生在class层面
-
接口之间通过extends实现行为扩展,类实现组合接口,避免复杂的继承关系。
-
9 接口的组合
- 定义组合接口
interface
,扩展extends
两个以上的接口interface
interface Flyable {//定义抽象行为的接口
public void fly();
}
interface Quackable {//定义抽象行为的接口
public void quack();
}
class FlyWithWings implements Flyable {//接口的具体实现
@Override
public void fly() {
System.out.println("fly with wings");
}
}
class Quack implements Quackable {//接口的具体实现
@Override
public void quack() {
System.out.println("quack like duck");
}
}
interface Ducklike extends Flyable, Quackable {}//接口的组合,定义了行为的组合
public class Duck implements Ducklike {//从组合接口中派生具体类
Flyable flyBehavior;//委托
Quackable quackBehavior;
void setFlyBehavior(Flyable f) {//设置委托对象实例
this.flyBehavior = f;
}
void setQuackBehavior(Quackable q) {
this.quackBehavior = q;
}
@Override
public void fly() {//通过委托实现具体行为
this.flyBehavior.fly();
}
@Override
public void quack() {
this.quackBehavior.quack();
}
}
*10 委派的类型(都支持一对多):
-
Dependency,临时性的委派,用完即释放
- Delegation关系通过方法的参数传递建立起来
- Delegation关系通过方法的参数传递建立起来
-
Association,永久性的委派,一直占着
- Delegation关系通过固有的field(成员变量)建立起来
- Delegation关系通过固有的field(成员变量)建立起来
-
Composition,更强的association,但难以变化
- Delegation关系通过类内部field(成员变量初始化)建立起来,无法修改
- Delegation关系通过类内部field(成员变量初始化)建立起来,无法修改
-
Aggregation, 更弱的association,可动态变化
- Delegation关系通过客户端调用构造函数或专门方法建立起来
- Delegation关系通过客户端调用构造函数或专门方法建立起来
11 白盒框架的原理与实现
- 白盒框架,通过代码层面的继承进行框架扩展
- 通过子类和重写方法进行扩展(继承)
- 常见的设计模式:模板方法
- 子类拥有main方法,但可以控制框架
12 黑盒框架的原理与实现
- 黑盒框架,通过实现特定接口/delegation进行
框架扩展 - 通过实现插件接口进行扩展(委托)
- 通用设计模式:策略、观察者模式
- 通过插件加载机制加载插件,并为框架提供控制