面向复用的软件构造技术 {ignore=true}
1. 为什么要复用
复用可以有很大的适应性,并能极大的降低开发成本和开发时间。
对于复用编程,我们通常进行充分的测试,这样一来,我们就可以保证复用的代码是可靠并且稳定。
除此之外,复用需要提供统一的定义,这能保证在不同应用中保持一致。
复用并不全是优点,其缺点主要集中在少量复用情况下开发成本,因为开发成本主要集中在复用情况下足够的适应性,并且由于适应性的问题,复用情况下的性能不如面对具体情况写出的程序性能。
2. 软件复用的基本概念、
- 白盒复用:源代码可见,可修改和扩展
- 复制已有代码到正在开发的系统,进行修改
- 可定制化程度高
- 对其修改增加了软件的复杂度,且需要对其内部充分的了解
- 黑盒复用:源代码不可见,只能使用不能修改
- 只能通过API接口来使用,无法修改代码
- 简单、清晰
- 适应性差些
3. 相关工具
复用是为了更高的开发效率,并不是说从头自己写下去就是最好的,因为他人的代码可能比自己实现的更安全,性能更好,所以由此产生一个问题,如何找到合适的代码来复用。
tips: 开源有相关的协议规定,比如GPL协议,要求使用者在使用的时候必须开源,这点我们一定要注意,每一个开源代码都会有相应的协议进行约束,我们不可以违背协议。
- GitHub: 一个开源的代码托管平台,可以在上面找到大量的开源代码
- Searchcode: 一个代码搜索引擎,可以在上面搜索到大量的开源代码.
- 自己的类继承复用
- 他人编写的API/Package: 一些开源的API/Package,比如Java的JDK,Python的numpy等等
- 他人编写的框架: 一些开源的框架,比如Java的Spring,Python的Django等等,也有白盒框架和黑盒框架区分。
4. Liskov替换原则(LSP)
Liskov替换原则(LSP)是面向对象设计的基本原则之一,它是由麻省理工学院计算机科学实验室的里斯科夫(Liskov)女士在1987年的OOPSLA大会上发表的一篇文章《数据抽象和层次》中提出来的,她提出:继承必须确保超类所拥有的性质在子类中仍然成立(Inheritance should ensure that any property proved about supertype objects also holds for subtype objects)。
简单来说,对于子类来说,需要有 更强的不变量、更弱的前置条件、更强的后置条件。
针对协变与逆变的问题,我们可以通过下面的例子来说明:
public class T {
public Object foo() {
return null;
}
public void bar(String s) {}
}
public class S extends T {
@Override
public String foo() {
return null;
}
@Override
public void bar(Object o) {}
}
对于上面的例子,我们可以看到,子类S中的foo方法返回值类型是String,而父类T中的foo方法返回值类型是Object,这就是协变,子类的返回值类型是父类的子类型。
而对于bar方法,子类S中的bar方法的参数类型是Object,而父类T中的bar方法的参数类型是String,这就是逆变,子类的参数类型是父类的父类型。
tips: 目前
Java
中逆变会被当成Overload。
5. 委派
委派是一种特殊的聚合关系,它是一种 对象结构型模式。在委派模式中,包含有两个对象,一个委派者对象持有一个被委派者对象的引用,它把所有的请求委派给被委派者处理。委派者不对外提供服务,而是通过调用被委派者的方法来提供特定的服务。
当子类只需要复用父类中的一小部分方法时,我们就可以利用委派实现复用。
CPR(Composition over Inheritance Principle):组合优于继承。