面向复用的编程相关理论基础这一讲主要讲述了如何实现编程的复用性。首先引入了复用性的概念,介绍了复用性的意义并给出了衡量是否要执行复用性编程的一些指标。分别就复用性编程的优点和缺点进行了介绍,让我们可以自己未来衡量是否要复用性编程。之后自下而上,从四个层面剖析复用性在每一个层级中的意义并介绍了在每一个层级中如何实现我们自身编程的复用性。
复用性的衡量:频率/代价
不同的可复用组件级别- 代码层面的复用
- 方法
- 声明
- 。。。
- 模块层面的复用(继承 or 委托)
- 类
- 接口
- 库层面的复用
- .jar
- 结构层面的复用:框架
白盒复用
黑盒复用
Liskov Substitution Principle (LSP)——强行为子类型化子类型多态:客户端可用统一的方式处理不同类型的对象
行为子类型:子类型可以增加方法,但不可删
子类型需要实现抽象类型中所有未实现的方法
子类型中重写的方法必须有相同或子类型的返回值或者符合协变的参数
子类型中重写的方法必须使用同样类型的参数或者符合反协变的参数(按overload处理)
子类型中重写的方法不可以抛出额外的异常
-
更强的不变量
-
更弱的前置条件
-
更强的后置条件
-
不变量要保持
-
子类型方法参数:协变
-
子类型返回值参数:协变
-
异常类型:协变
父类型-》子类型:越来越具体
返回值和异常类型:不变或变得更具体
反协变父类型-》子类型:越来越具体
参数类型:变化相反,不变或越来越抽象
下面这张图表现了子类型相关变化的情况
数组的子类型化Java中数组是协变的:对于T []数组,可以保存类型T及其子类型的数据
泛型的子类型化泛型是类型不变的
类型参数在编译后被丢弃,运行时不可用(类型擦除)
定义泛型类型时,会自动提供一个对应的原始类型(非泛型类型),原始类型名就是去掉类型参数后的泛型类型名。擦除时类型变量会被擦除,替换为限定类型,如没有则换成Object,有多个限定时用第一个
运行时类型查询只适用于原始类型!!!
可采用通配符实现两个泛型类的协变。
super A>下限通配符
extends A>上限通配符
委托(Delegation)一个对象请求另一个对象的功能
委托的UML图标识如下
Dependency委托通过方法的参数或者在方法的局部中使用发生联系
Association委托将一个对象包含,作为自己的一个属性
Composition委托作为更强的association,难以变化
Aggregation委托更弱的association,可以动态变化
Comparator和Comparable CRP原则(组合优先于继承!)”委托“发生在object层面,而”继承“发生在class层面
当问题的核心是由于对象不同而产生的时候,利用对象层面的委托,下面是CRP通用设计图
PPT中以银行计算的例子进行讲解
接口的组合使用接口定义系统必须对外展示的不同侧面的行为(抽象行为)
接口之间通过extends实现行为的拓展
类implements组合接口,规避了复杂的继承关系
框架的原理与实现白盒框架:通过代码层面的继承进行框架扩展框架:一组具体类、抽象类及其之间的连接关系。将框架看作是更大规模的API复用,并将这些模块间的关系确定下来,形成整体应用的领域复用
扩展性通过继承和动态绑定实现
已存在的功能通过子类化基于框架的类和重写先前定义的hook方法实现扩展
通用设计模式:Template Method
子类有main方法但是控制权移交给框架
黑盒框架:通过实现特定接口/delegation进行框架扩展
扩展性通过实现插件接口达成
通用设计模式:Strategy, Observer
插件载入式机制加载插件并将控制权移交给框架
二者对比
最后老规矩,上一个这一章的思维导图



