文章目录
- 4.1 可复用性的度量、形态与外部表现
- 4.2 面向复用的软件构造技术
-
- 1. 设计可复用的类
-
- 1.1 LSP具体细则
- 2. 泛型相关的子类父类(*)
- 3. 继承和委托
4.1 可复用性的度量、形态与外部表现
注:最主要的复用在代码层面,但是其余例如测试用例、文档等也都可能被复用
-
为什么需要可复用性?
降低成本、可靠性强
-
可复用性衡量
复用机会是否频繁、复用场合个数、复用代价多少
-
白盒复用:
通过继承复用
缺点:增加了软件的复杂度
-
黑盒复用
通过委托复用
缺点:效率较低、适应性差
-
分层级复用
基于代码层级的复用
基于模块层级(类)的复用
基于库文件的复用
基于framework层级的复用——领域知识的复用
- 可复用性的原则
- 类型可变(泛型)
- 实现可变:ADT有不同的实现,提供不同的representations和abstract functions,但具有同样的spec(precondition、postcondition)
- 方法的聚集和分处
- 表示独立性、信息隐藏
- 抽象出共用的行为形成抽象类、父类
4.2 面向复用的软件构造技术
1. 设计可复用的类
1.1 LSP具体细则
父类能做的,子类一定能做
需要满足的条件有:
a) RI:相同或更强的不变量,子类RI>=父类RI
子类型:更弱的前置条件,更强的后置条件 (JAVA静态检测无法检测出来)
b)子类型可以增加方法,但不可删
c) 子类型需要实现抽象(父)类型中所有未实现方法
d) 子类型返回值类型与父类型返回值类型相同或是其子类型(更具体)
e) 子类型参数类型与父类型参数类型相同或是其父类型(更宽泛)
f) 子类型抛出的异常可以相同或是父类型抛出异常的子类型(更具体),不能抛出额外的异常
例题:
1. 前置更弱,后置更强
2. 当子类不满足父类方法时:
3. 子类父类RI不同时:
-
协变:
子类重写父类方法后,返回值/抛出异常(或不抛出)类型更具体
数组支持协变:子类可以赋值给父类,但不能直接赋值数字
-
反协变、逆变:
子类重写父类方法后,传入参数类型更宽泛
注:JAVA不支持反协变,会将其当成重载overload而非override - 通过协变进行方法调用
- 子类赋值给父类
- 方法的参数是父类,调用时直接用子类调用
2. 泛型相关的子类父类(*)
- 如果两个泛型之间存在subtyping关系,那么两个类的泛型是一模一样的。 例如:直接在编译阶段报错:
- 带?的泛型
- 使用object类型时
- 使用的方法和如List<?>的List类型无关
-
定下界写法<? superA>——存A或A的父类
定上界写法:<? extends A>——存A或A的子类3.