第九章 面向复用的软件构造技术
第九章 面向复用的软件构造技术
Reading Sources
Objectives
- 软件复用的优缺点
- 复用的构造
- 通用可复用组件的特征
- 开发可移植系统的方法
什么是软件复用
- 面向复用编程
- 基于复用编程
如何衡量可复用性
可复用组件的级别和形态
复用的级别
-
最主要在代码层面
-
软件构造过程中的任何实体都可能被复用
-
我们所关注的
源代码级别:方法、语句等
模块级别:类和接口
库级别:API
架构级别:框架
代码复用的类型
-
白盒复用:源代码可见,可修改和扩展
复制已有代码到正在开发的系统,进行修改
可定制化程度高
对其修改增加了软件的复杂度,且需要对其内部充分的了解
-
黑盒复用:源代码不可见,不能修改
只能通过API接口来使用,无法修改代码
简单,清晰
适应性差些
源代码复用--Lowest Level
-
将部分/全部复制/粘贴到您的程序中
-
维护问题
需要在多个地方更正代码
代码过多(许多版本)
-
过程中出错的高风险
-
可能需要了解所用软件的工作原理 §需要访问源代码
模块复用:类,接口
复用类
继承
- Classes extend the properties/behavior of existing classes
- In addition, they might override existing behavior
委托
委托模式是软件设计模式中的一项基本技巧。在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。
- 增强代码复用
- 显式委托:将发送对象传递给接收对象
- 隐式委托:通过语言的成员查找规则
- 委托可以描述为实体之间共享代码和数据的低级机制
库级复用:API/包
-
库:一组提供可重用功能的类和方法
-
框架:可以定制到应用程序中的可重用骨架代码
ramework calls back into client code -
Library vs. framework 一般区别
系统级复用:框架
应用框架层
- 框架是子系统设计,包含抽象类和具体类的集合以及每个类之间的接口
- 框架是一种抽象,其中提供通用功能的软件可以通过其他用户编写的代码有选择地更改,从而提供特定于应用程序的软件
- 开发者根据 framework的规约,填充自己的代码进去,形成完整系统
框架设计
-
框架与应用程序不同
抽象级别不同,因为框架为一系列相关问题提供解决方案,而不是单个问题。
为了适应这一系列问题,框架是不完整的,包含incorporating hot spots and hooks 以允许定制 -
框架可以通过用于扩展它们的技术进行分类。
1.白盒框架:通过代码层面的继承进行框架扩展
2.黑盒框架:通过实现特定接口/委托进行框架扩展
设计可复用类
在OOP中设计可复用类
行为子类型和LSP
子类型多态:客户端可用统一的方式处理 不同类型的对象
LSP:假设 q(x) 是关于 T 类型的对象 x 的可证明性质,那么 q(y) 应该对于 S 类型的对象 y 是可证明的,其中 S 是 T 的子类型。
LSP
Co-variance and Contra-variance(协变和逆变)
- 数组协变的:给定 Java 的子类型规则,类型 T[] 的数组可能包含类型 T 的元素或 T 的任何子类型。
- 实例
- 第二部分出现运行错误的原因分析:
1.在运行时,Java 知道这个数组实际上被实例化为一个整数数组,它只是恰好是通过 Number[] 类型的引用来访问的。
2.myNumber作为一个Number[]类型的引用在编译时的静态检查能够通过,因为它指向的是一个子类型对象,而运行时就不管引用的类型了只管实例化的对象
泛型中的LSP
-
对于泛型,类型参数的类型信息在代码编译完成后被编译器丢弃;因此,此类型信息在运行时不可用。
-
-->类型擦除
- 类型擦除:如果类型参数是无界的,则将泛型类型中的所有类型参数替换为其边界或 Object。因此,生成的字节码只包含普通的类、接口和方法
-
实例
-
泛型不是协变的
-
Consider LSP -->Wildcards(通配符)-->在类型参数相关时如何在两个泛型类之间创建类似子类型的关系的信息。
泛型中的通配符
- 实例
- 类型参数的上下界
- 实例
委托和组件(Delegation and Composition)
1.委托(Delegation)
- 一个对象请求另一个对象的功能
- 复用的常见形式
- 委托可以描述为在实体之间共享代码和数据的低级机制。
– 显式委托:将发送对象传递给接收对象
– 隐式委托:通过语言的成员查找规则- 实例:
- 实例:
2.使用委托来扩展功能
动态绑定 + 功能委派 +功能扩展
3.委托与继承的比较
委托解决了当子类只需要复用父类中的一小部分方法时如果使用继承造成的大量无用的方法的问题
4.组合复用原则(Compositive Reuse Principle)
- 类应该通过将那些它所需的功能的类的实例组合起来,实现多态行为和代码重用,而不是从基类或父类继承来。
- 组合一个对象可以做什么(has_a 或 use_a)比扩展它是什么(is_a)更好。
- 实例:父类委托于抽象类,子类根据自己的对象需求,自行实现对于抽象类的多态子类的委托
5.委托的类型
- 这种分类是根据委托人和委托人之间的“耦合度”来划分的。
- USE
- 使用类的最简单形式是调用它的方法;两个类之间的这种关系形式称为“uses-a”关系,其中一个类使用另一个类而不实际将其合并为属性。
- 例如,它可以是一个参数或在方法中本地使用。
- 依赖关系:一个对象需要其他对象(供应商)来实现它们的临时关系。
- ASSOCIATION(关联) A has B -->永久性的委托
- 关联:对象类之间的持久关系,允许一个对象实例导致另一个对象实例代表它执行操作。
- has_a:一个类有另一个类作为属性/实例变量
- 这种关系是结构性的,因为它指定一种对象连接到另一种对象并且不代表行为。
- Composition (组合/聚合)A owns B 更强:难以变化
- 组合是一种将简单对象或数据类型组合成更复杂对象的方法。
- is_part_of:一个类有另一个类作为属性/实例变量
- 实现使得一个对象包含另一个对象。
- Aggregation 更弱:可动态变化
设计可复用API库与框架
库
库:一组提供可重用功能的类和方法 (API)
白盒框架 黑盒框架
- 白盒框架
- 通过继承和重写方法进行扩展
- 通用设计模式:模板方法
- 子类具有主方法但将控制权交给框架
- 白盒框架所执行的是框架所写好的代码,只有通过override其方法来实现新的功能,客户端启动的的是第三方开发者派生的子类型。
- 黑盒框架
- – 通过实现接口进行扩展
- – 通用设计模式:策略、观察者
-
- 插件加载机制加载插件并将控制权交给框架