第九章:面向复用的软件构造技术
面向复用编程:开发出可复用的软件
基于复用编程:利用已有的可复用软件搭建应用系统
设计可复用的类:
LSP(里氏替换原则)
子类型多态性:不同类型的对象可以通过客户端代码统一处理。
如果Cat类型是动物类型的子类型,则在任何地方使用动物类型的表达式时,都可以使用Cat类型的表达式。
1.子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法;
2、子类中可以增加自己特有的方法。
子类必须实现父类所有非私有的属性和方法,尽量把父类设计为抽象类或者接口。让子类继承父类或实现父接口,并实现在父类中声明的方法,运行时,子类实例替换父类实例,我们可以很方便地扩展系统的功能,同时无须修改原有子类的代码,增加新的功能可以通过增加一个新的子类来实现。
子类型可以增加方法,但不可删,子类型需要实现抽象类型中的所有未实现方法 ,子类型中重写的方法必
须有相同或子类型的返回值或者符合co-variance的参数,子类型中重写的方法不能抛出额外的异常。
Same or stronger invariants 更强的不变量
Same or weaker preconditions 更弱的前置条件
Same or stronger postconditions 更强的后置条件;
协变:更特定的类可能有更特定的返回类型,这被称为子类型中返回类型的协变。
为子类型的方法声明的每个异常都应该是为超类型的方法声明的某个异常的子类型。
反协变、逆变:
目前Java中遇到这种情况,当作overload看待。
数组是协变的:
要区别一个对象的类型和引用类型;
泛型不是协变的:
List不是List的子类型
类型擦除—— 编译前VS编译后
我们不能考虑整数列表是数字列表的子类型。这将被认为对类型系统是不安全的,并且编译器会立即拒绝它。
Box 不是Box 的子类型即使 Integer 是Number的子类型
A与B有关系,Box(A)与Box(B)没有关系。
MyClass和MyClass的共同父级是object。
泛型编程中的通配符:List<?>
打印列表的目标是打印任何类型的列表,但未能实现该目标——它只打印object实例列表;它不能打印List<整数>、List、List等等,因为它们不是List的子类型。
使用下面的方法可以实现:
List是List<?>的一个子类型
List 是 List<? extends Object>的子类型
List 是List<? super String>的子类型
继承复用——白箱复用,可复用性不好
授权和组成
int compare(T o1, T o2)比较了它的两个顺序参数
如果你的ADT需要比较大小,或者要放入Collections或Arrays进行
排序,可实现Comparator接口并override compare()函数。
1.构建新的类,然后调用:
2.重写compareTo()
委派/委托:一个对象请求另一个对象的功能,委派是复用的一种常见形式,委派可以被描述为在实体之间共享代码和数据的低级机制。
委派模式:通过运行时动态绑定,实现对其他类中代码的动态复用
设计经常采用继承和委托结合的方式
如果子类只需要复用父类中的一小部分方法,可以不需要使用继承,而是通过委派机制来实现,一个类不需要继承另一个类的全部方法,通过委托机制调用部分方法,从而避免大量无用的方法。
(CRP) 复合重用原则
类通过“组合”实现多态和复用(通过引入其他类的实例来实现功能)而不是通过基类或父类。
“委托”发生在object层面,而“继承”发生在class层面
核心问题:每个Employee对象的奖金计
算方法都不同,在object层面而非class层面。
委派的三种类型:
依赖:对象之间的动态的、临时的通信联系
关联:
组成:
聚合:
聚合和组成的区分:
设计系统级可重复重用的API库和框架:
白盒框架:通过子类和覆盖方法扩展通用设计模式:模板方法子类有主要方法,但对框架进行控制
黑框框架-
通过实现插件接口进行扩展-
通用设计模式:策略、观察器-
插件加载机制加载插件并对框架进行控制