第1节 复用模块的层次和形态学模式
第2节 设计可复用类
第3节 设计系统级可复用的API库和框架
复用模块的层次和形态学模式
复用的层面:代码层面;软件构造过程中的任何实体都可能被复用。
代码复用的种类:白盒复用:源代码可见,可修改和扩展;黑盒复用:源代码不可见,不能修改;
1.1源代码复用
源代码复用是最低级别
类型:
白盒复用
当代码本身可用时重用代码。通常需要某种修改或适应。于是,就把源码Ctrl+C并Ctrl+V,这是复用
好处: 可以自定义,以便于适应不同的情况
坏处: 增加了代码的复杂性,而且你也不一定能看的源码
黑盒复用
通过API接口来使用,无法修改代码
好处: 简单,清晰
坏处: 适应性差
通常都是将源代码组织为库的形式,来进行复用的传播
可重用软件组件的来源:
内部(公司)代码库(Guava)
第三方库(Apache)
语言自身提供的(JDK)
来自教程、示例、书籍等的代码示例。
代码专家或知识渊博的同事
现有系统代码
开源软件
代码搜索网站:
grepcode.com
github.com/search
searchcode.com
1.2模块级复用:类/接口
不需要源代码,只需要类文件或jar/zip。可以使用javap工具来获得一个类的公共方法头文件,需要将相关的类打包在一起 -- 静态链接。
inheritance继承:
delegation委托:
1.3模块级复用:库层面复用:API/Package
库:一组提供可重复使用功能的类和方法(API)。开发者构造可运行软件实体,其中涉及到对可复用库的调用。
框架:可重复使用的骨架代码,可以定制成一个应用程序。Framework作为主程序加以执行,执行过程中调用开发者所写的程序
1.4系统级的复用:框架
(1)框架:一组具体类、抽象类、及其之间的连接关系,开发者根据framework的规约,填充自己的代码进去,形成完整系统。将framework看作是更大规模的API复用,除了提供可复用的API,还将这些模块之间的关系都确定下来,形成了整体应用的领域复用。
(2)分类:框架可以按照用于扩展的技术进行分类
白盒框架,通过代码层面的继承进行框架扩展,通过继承和动态绑定实现可扩展性,通过子类化框架基类和重写预定义的钩子方法来扩展现有的功能。
黑盒框架,通过实现特定接口/delegation进行框架扩展,通过为可以插入框架的组件定义接口来实现可扩展性,通过定义符合特定接口的组件来复用现有的功能
设计可复用类
2.1行为子类和里氏替换原则(LSP)
行为子类:子类型多态:客户端可用统一的方式处理不同类型的对象。
Liskov替换原则(LSP):
第一种定义:如果对每一个类型为S的对象o1,都有类型为T的对象o2,使得以T定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型S是类型T的子类型。
第二种定义:所有引用基类的地方必须能透明地使用其子类的对象。
LSP:
Covariance (协变):父类型à子类型:越来越具体specific返回值类型:不变或变得更具体,异常的类型也是如此。
Contravariance (反协变、逆变):父类型à子类型:越来越具体specific参数类型:要相反的变化,要不变或越来越抽象,目前Java中遇到这种情况,当作overload看待
(4)泛型不是协变的:编译代码完成后,编译器丢弃类型参数的类型信息;因此,此类型信息在运行时不可用。
2.2委托和组成Delegation and Composition
Delegation委托:委派/委托:一个对象请求另一个对象的功能。例如,排序器将功能委托给一些比较器。委托可以被描述为实体之间共享代码和数据的低级机制。
The delegation pattern委派模式:通过运行时动态绑定,实现对其他类中代码的动态复用。
使用委托来扩展功能
复合重用原则(CRP):类通过“组合”实现多态和复用(通过引入其他类的实例来实现功能)而不是通过基类或父类。
“委托”发生在object层面,而“继承”发生在class层面
接口组合:
Dependency: 临时性的delegation:仅仅是在参数中出现,调用了相应方法,甚至都没有实例化对象。
Association: 永久性的delegation:一个类有另一个作为属性/实例变量,但彼此仍可分
Composition: 更强的association,但难以变化:不同于一般的Association,这里变成了其一部分属性,并且彼此不可分,在内部创建。
Aggregation: 更弱的association,可动态变化:对象存在于另一个对象之外,是在外部创建的,因此它作为参数传递给构造函数。
设计系统级可复用的API库和框架