面向可复用的软件构造技术

作者:syt

什么是软件复用

软件复用

软件复用是利用已有的软件组件来实现或更新软件系统的过程。
面向复用编程——开发出可复用的软件。
基于复用编程——利用已有的可复用软件搭建应用系统。

软件复用的好处

降低成本和开发时间。
经过充分测试,可靠,稳定。
标准化,在不同应用中保持一致。

软件复用的代价

为了复用的编程代价高。
利用复用的编程代价也高。

可复用的软件

开发可复用的软件:
开发成本高于一般软件的成本:要有足够高的适应性。
性能差些:针对普适场景,面对具体场景缺乏针对性。
使用已有软件开发:
可复用软件库,对其进行有效管理。
往往无法拿来就用,需要适配。

如何衡量“可复用性”

衡量可复用性

复用的机会有多频繁?复用的场合有多少?
复用的代价有多大——搜索、获取;适配、扩展;实例化;与软件其他部分互连的程度。

可复用性

可复用性意味着需要对以下环节进行明确的管理:构建、打包、分发、安装、配置、部署、维护以及升级相关问题。
高可复用性的软件资产应具备以下特性:
小,简单。
与标准兼容。
灵活可变。
可扩展。
泛型、参数化。
模块化。
变化的局部性。
稳定。
丰富的文档和帮助。

可复用组件的层级与形态

复用的等级

最主要的复用是在代码层面。
但软件构造过程中的任何实体都可能被复用。

代码复用的类型

白盒复用——源代码可见,可修改和扩展。
复制已有代码到正在开发的系统,进行修改。
可定制化程度高。
对其修改增加了软件的复杂度,且需要对其内部充分了解。
黑盒复用——源代码不可见,不能修改。
只能通过API接口来使用,无法修改代码。
简单,清晰。
适应性差。

可复用组件的分发形式

形式——源代码,软件包。
可复用软件组件的来源:
组织的内部代码库(Guava)。
第三方提供的库(Apache)。
语言自身提供的库(JDK)。
代码示例、来自同事、已有系统内的代码、开源软件的代码。

源代码复用

源代码复用——最低级别。
将部分或全部代码复制/粘贴到程序中。
维护问题:需在多个地方修正代码;需处理的代码量太大。
过程出错高风险。
可能需要了解所用软件的工作原理。
需要获取源代码。

模块级复用——类和接口

继承——复用类的方法。
无需编写大量仅用于转发或委派任务的冗余方法。
能更好建立贴近真实世界的概念模型。
需在手写代码前设计好整个继承层级。
继承是“全盘接受”,无法直接取消父类的属性或方法,要防止过度设计。
委派——复用类的方法。
委派:一个对象依赖另一个对象来实现其部分功能。
明智的委派能带来极高的代码复用率。
继承+委派=灵活的复用。

库级复用——API

库——提供复用功能的一组类/方法(APIs)。
框架——可复用的骨架代码,可以被定制开发成应用程序。框架会回调客户端代码。
好的API的特性:
易于学习;易于使用(甚至没有文档);难以被滥用;易于阅读/理解代码并使用;能满足需求;易于开发;适合受众。

系统级复用——框架

框架——一组具体类、抽象类、及其之间的连接关系。
开发者根据framework的规约,填充自己的代码,形成完整系统。
库VS框架:
库——开发者构造可运行的软件实体,其中涉及到对可复用库的调用。
Framework作为主程序加以执行,执行过程中调用开发者所写的程序。

框架设计:
框架与应用程序的区别——抽象层次不同,不完整性。
框架的分类——白盒框架、黑盒框架。
白盒框架——代码层面的继承进行框架扩展。
黑盒框架——实现特定接口/delegation进行框架扩展。

设计可复用的类

行为子类型化与里氏替换原则 (LSP)

子类型多态——客户端可用统一方法处理不同类型对象。
Python中的类型约束:
方法:子类型可增加方法,但不可删。
抽象类型:子类型需要实现抽象类型中所有未实现的方法。
参数类型:子类型中重写的方法必须有相同类型的返回值或者符合协变 (co-variance) 的子类型返回值。
异常处理:子类型中重写的方法不能抛出额外的异常,抛出相同或符合协变 (co-variance) 规则。

里式替换原则(LSP):
不变量要保持。
前置条件不能强化。
后置条件不能弱化。
子类型方法参数——逆变。
子类型返回值——协变。
异常类型——协变。

协变:类型转化的方向与继承方向一致。在子类型化关系中,随着父类型向子类型演变(变得越来越具体),相关类型也随之变得更具体。
返回值类型——不变或变得更具体。
异常类型——不变或变得更具体。
逆变:类型转化的方向与继承方向相反。即随着父类型向子类型演变,相关类型反而变得更抽象(Abstract)。
参数类型——要相反的变化,要不变或越来越抽象。

委托与组合

委托:
委托/委派——一个对象请求另一个对象的功能。
委派是复用的一种常见形式。
委派模式——通过运行时动态绑定,实现对其他类中代码的动态复用。

委托与继承的对比:
继承——通过增加新操作或覆写既有操作来扩展基类。
委托——捕获一个操作并将其发送给另一个对象处理。
以委托取代继承:
如果一个子类仅需要使用父类的一部分方法时(或者无法继承父类数据时)——可不使用继承而是使用委派。
一个类不需要继承另一个类的全部方法,通过委托机制调用部分方法,从而避免大量无用的方法。

组合优于继承原则/组合复用原则(CRP)。
“委托”发生在object层面,“继承”发生在class层面。
CRP原则更倾向于使用委派而不是继承。

委托的类型:
Use、Association、Composition/aggregation。
1.Dependency——临时性的委托。
2.Association——永久性的委托。
3.Composition——更强的association,但难以变化。
4.Aggregation——更弱的association,可动态变化。

设计系统级可复用性:API库和框架

库——一组提供可复用功能的类和方法(APIS)。
为什么API设计很重要?
API是程序员最重要的资产和“荣耀”,吸引外部用户,提高声誉。
框架:一组具体类、抽象类、及其之间的连接关系。
白盒框架——通过代码层面继承进行框架扩展——继承。
黑盒框架——通过实现特定接口/委派进行框架扩展——委派/组合。

← 返回博客目录