目录
一、什么是软件的可复用性
1.概念
软件的可复用性就是用现有的软件组件来更新软件的过程,它分为四个层面:
对应下来有两种编程的方式:面向复用编程和基于复用编程。前者就是为了能够去复用你正在编写的板块而有目的性地编程,后者就是利用现有的可复用模块去开发新功能。
2.复用的几个level
1.源代码级别的复用,最主要的复用就是代码层面的复用,任何实体都可能被复用:规约、代码、测试用例
2.模块级别的复用:类/抽象类/接口,对类的复用又可以分成继承和委托
3.库级别的复用:API/包
4.系统级别的复用:框架(Framwork 一组具体类、抽象类及其之间的连接关系)
二、设计一个可复用的类
1.行为子类型与Liskov替换原则
子类型多态:客户端可用统一的方式处理不同类型的对象
eg.若Cat是Animal的子类型,任何用Animal的场合都可以用Cat来替代
Animal a = new Animal();
Animal c1 = new Cat();
Animal c2 = new Cat();
//这么替换是没问题的
//a=c1;
//a=c2;
2.Liskov替换原则
我认为其中比较重要的有:
1.子类型中重写的方法必须有相同或者子类型的返回值,或符合co-variance的参数
2.子类型中重写的方法必须用同类型的参数或者符合contra-variance的参数
LSP对归约的应用:
子类型有同样或更强的不变量,同样或更弱的前置条件,同样或更强的后置条件
首先子类必须完全继承不变量,如下Car类必须继承speed<limit的不变量
abstract class Vehicle{
int speed,limit;
//@invariant speed < limit
...
}
class Car extands Vehicle{
int fuel;
boolean engine;
//@invariant speed < limit
//@invariant (someother condision)
...
}
其次子类中重写的方法规约不能弱于父类的方法
class Car extands Vehicle{
int fuel;
boolean engineOn;
//@ invariant speed < limit;
//@ invariant fuel >= 0;
//@ requires fuel > 0 && !engineOn;
//@ ensures engineOn;
void start(){...}
//@ requires speed != 0;
//@ ensures speed < \old(speed)
void brake(){...}
}
class Hybrid extands Car{
int charge;
//@ invariant charge >= 0;
//@ requires (charge > 0 || fuel > 0) && !engineOn;
//@ ensures engineOn;
void start(){...}
//@ requires speed != 0;
//@ ensures speed < \old(speed);
//@ ensures charge > \old(charge)
void brake(){...}
}
可以看到Hybrid的start()方法的前置条件是弱于Car类start()方法的前置条件的,但Hybrid的brake()方法的后置条件是强于Car的brake()方法的。
3.协变与逆变
1.协变是从父类型到子类型越来越具体,返回值类型和异常的类型不变或者变具体
class T{
Object a(){...}
}
class S extands T{
@Override
String a(){...}
}
2.逆变也叫反协变,子类型的参数越来越宽泛,目前Java遇到这种情况当作overload看待
class T {
void c(String s) {...}
}
class S extands T {
@Override
void c(Object s ){...}
}
3.数组是协变的
Number [] numbers = new Number[2];
numbers[0] = new Integer (10);
numbers[1] = new Double (3.14);
因为Integer和Double都是Number的子类,所以直接把这些对象赋值给number类型是ok的,但是下面这种情况不行:
Number [] numbers ; //只有声明,没对象
Integer myInts = {1,2,3,4};
numbers = myInts; //现在numbers是一个Integer类型的数组,相当于这个数组指向了myInts数组
numbers[0] = 3.14 //run-time 报错