知识点概要:
-
什么是软件复用?
-
如何衡量“可复用性”?
-
可复用组件的级别和形态
——源代码级别的复用
——模块级别的复用:类/抽象类/接口
——库级别的复用:API/包
——系统级别的复用:框架 -
设计可复用的类
——继承与重写
——重载
——参数多态与泛型编程
——行为子类型和 Liskov 替换原则 (LSP)
——组合与委托 -
设计可复用库与框架
——API 和库
——框架
——Java 集合框架(一个例子)
一、软件复用
面向复用编程:开发出可复用的软件
基于复用编程:利用已有的可服用软件搭建应用系统
复用的原因:
- 很大的适应性
- 降低成本和开发时间
- 充分的测试→高可靠、稳定
- 标准化、在不同应用中保持一致
虽然复用具有如此多的好处,但是也许要付出很高的代价,不仅program for reuse代价高,program with reuse代价也高
开发可复用的软件开发成本高,而且性能差,因为要针对普适场景;使用已有软件进行开发,利用可复用的软件库,可以对其进行有效的管理,但往往无法直接拿来用,需要适配。
二、复用性的衡量
根据复用的频率、场合和代价来衡量,复用性好的软件应具有如下特性:小、简单;与标准兼容;灵活可变;可扩展性;泛型、参数化;模块化;变化的局部性;稳定;丰富的文档和帮助。当然这是绝对理想化的复用性高的软件,在写可复用的软件时应朝着这些目标来努力。
三、可复用组件的级别和形态
最主要的可复用组件当然是代码层面,但在完整的软件构造过程中,任何实体都可以被复用,比如:需求、spec、数据、测试用例、文档等等。
代码复用的类型:
- 白盒复用:源代码课件,可修改和扩展,复制已有代码修改,可定制化程度高,但复杂度高,需要对代码内部充分了解
- 黑盒复用:源代码不可见,不能修改,只能通过API接口来使用,无法修改代码,简单清晰,但适应性差
Module级别的复用(类/接口):
将类引用或实现接口,利用继承和委派可实现灵活的复用。
Library级别的复用(API/Package):
调用不同API进行复用
System级别的复用(Framework):
利用框架进行复用,根据框架的规约和相应的接口,填充自己的代码,完成整个系统,其中也包括白盒框架和黑盒框架。白盒框架是通过代码层面进行扩展,而黑盒框架是通过接口/委派进行框架扩展,各有优略。
四、设计可复用的类
LSP原则:
- 子类型可以增加方法,但不可删除
- 子类型需要实现抽象类型中所有未实现的方法
- 子类型中重写的方法必须有相同的返回值或符合co-variance的返回值
- 子类型中重写的方法必须使用相同的参数或者符合contra-variance的参数
- 子类型中重写的方法不能抛出额外的异常,相同或符合co-variance的异常
对于程序中的方法,还要遵守:更强的不变量、更弱的前置条件和更强的后置条件
子类型->父类型:越来越具体的specific,返回值类型和异常的类型也都必须不变或变得更具体(协变),如下:
而参数类型要相反的变化,要不变或越来越抽象,如下:
这两点也就是之前提到的更弱的前置条件和更强的后置条件的具体表现。
注意:泛型不存在协变!!
组合与委托:
如果你的ADT需要比较大小,或者要放入Collections或Arrays进行排序,可实现Comparator接口并override compare()函数。如图:
另一种方法:让你的ADT实现Comparable接口,然后override compareTo() 方法,与使用Comparator的区别:不需要构建新的Comparator类,比较代码放在ADT内部,例子如下:
委托:一个对象请求另一个对象的功能,是复用的一种常见形式。委派的模式使用过运行时动态绑定,实现对其他类中的代码的动态复用,最常见的动态绑定就是实例化一个类的对象,并调用类中的方法。
委托和继承的比较:继承是extending一个基础类,并在基础上进行继承或重写方法,委托是捕获一个操作并将其发送到另一个对象。在日常使用中委派和继承经常结合起来使用,效果更佳。
使用委托的合适场景:如果子类只需要复用父类中的一小部分方法,则委托效果更好,不需要用继承关系,从而避免大量无用的方法。
CRP原则:类应该通过它们的组合(通过包含实现所需功能的其他类的实例)实现多态行为和代码重用,而不是从基类或父类继承。
“委托”发生在object层面,而“继承”发生在class层面
五、设计可复用库与框架
框架:一组具体类、抽象类、及其之间的连接关系白盒框架通常使用继承,黑盒框架通常使用委派和组合
标签:知识点,框架,代码,复用,接口,面向,类型,软件 From: https://www.cnblogs.com/JayLv/p/17417913.html