4.1可复用性的度量,形态和外部表现
一.软件复用(面向复用编程+基于复用编程)
面向复用编程:开发可复用软件
基于复用编程:利用已有可复用软件搭建应用系统
复用的好处是可以降低成本和时间,但是需要标准化
二.复用方式和复用层次
1.复用方式
(1).白盒复用:源代码可见,可修改可扩展
(2).黑盒复用:源代码不可见,不可修改
2.复用层次
(1).代码复用:复制一部分或全部源代码,根据实际情况修改代码
(2).类或接口的复用:将一些架包以压缩包的形式打包放入classpath中使用
(3).API复用:类库复用
(4).框架复用:一组具体类,抽象类及其之间的联系关系复用,内部实现需要复用者完成
三.可复用性的外部表现
类型可变(泛型):适应不同的类型,且满足LSP原则
实现可变:ADT有不同的Rep和AF,但是spoc(规约)是不变的
表示独立:内部的改变不影响客户端的实现
共性抽取:将相同的行为抽取出来形成可复用的方法或者类
4.2面向复用的软件构造技术
一.设计可复用类
1.子类型多态:客户端可以用统一的方式处理不同类型的对象
例如Cat是Animal的子类
Animal a = new Animal();
Animal b = new Cat();
Cat c = new Cat();
//在任何可以使用a的情况,都可以用b,c代替
2.LSP(李氏替换原则)(重点)
若类型T的对象x,q(x)成立;则对于类型T的子类型S的对象y,q(y)也成立
主要可以分为以下五点:
(1).子类型可以增加方法,但不可以删除
(2).子类型需要实现抽象类型(接口,抽象类)中所有未实现的方法
(3).子类型中重写的方法必须有相同或子类型的返回值符合co-varient(协变)的参数
(4).子类型中重写的方法必须使用相同类型的参数或符合contra-varient(逆变)的参数
(5).子类型中重写的方法不能抛出额外的异常
3.协变和逆变
(1).协变:同方向变化
例:
class T{
Object a(){...}
}
class S extends T(){
@override
String a(){...}
}
其中S是T的子类,且重写方法中String也是Object的子类
(2).逆变:反方向变化
例:
class T{
void c(String s){...}
}
class S extends T(){
@override
void c(Object s){...}
}
其中S是T的子类,但重写方法中Object是String的父类
注:Java将逆变视为overload(重载),若加入@override则会报错
4.泛型,通配符
(1).泛型
泛型是类型不变的(不协变)
如:ArrayList< String >是List< String >的子类
但是List< String >不是List< Object >的子类
原因:类型擦除,即类型参数在编译后被丢弃,在运行时不可用
(2).通配符(用于实现两个泛型类的协变)
1.<?>:无限定通配符(任何类型都是适配的)
2.<? super A>:下限通配符(A以及A的父类都是适配的)
3.<? extends A>:上限通配符(A以及A的子类都是适配的)
注:通配符意味着(以List为例)可以匹配多种类型中的一种,并不代表同一个List中可存放多种类型的数据。
二.委托(Delegation)和重组(Composition)
1.委托(一个对象请求另一个对象的功能)
例:
class A{
void foo(){
this.bar();
}
void bar(){
system.out.println("a.bar");
}
}
class B{
private A a;//delegation link(委托连接)
public B(A a){
this.a = a;
}
void foo(){
a.foo();//通过调用A的foo来实现
}
void bar(){
system.out.println("b.bar");
}
}
//执行以下代码
A a = new A();
B b = new B(a);
b.foo();
执行结果为:a.bar
委托主要可以分为以下四种
(1).临时性的委托
(2).永久性的委托
(3).更强的永久性委托,但难以变化
(4).更弱的永久性委托,但可以动态变化
2.CRP(基于组合的重组原则)
(1).通过接口定义系统必须对外展示不同的侧面行为
(2).接口之间通过extends实现行为拓展
(3).类implements组合接口
4.3面向复用的设计模式
一.结构型模式
1.适配器模式
将某个类或者接口转换为client期望的其他形式
可以解决类之间接口不兼容的问题
2.装饰器模式(重点)
以一个冰激凌的制作为例
public interface Icecream{//顶层接口
void AddTopping();
}
public class PlainIcecream implements Icecream{//基础实现,无添加的冰激凌
@override
public void AddTopping(){
system.out.println("Plain Icecream ready for some toppings");
}
}
public abstract class ToppingDecorator implement Icecream{//装饰器基类
protected final Icecream input;
public ToppingDecorator(Icecream i){
this.input = i;
}
public abstract void AddTopping();//留给具体装饰类实现
}
public class CandyTopping extends ToppingDecorator{
public CandyTopping(Icecream i){
super(i);
}
public void AddTopping(){
input.AddTopping();
system.out.println("Candy Topping added!");
}
}
public class NutTopping extends ToppingDecorator{
public NutTopping(Icecream i){
super(i);
}
public void AddTopping(){
input.AddTopping();
system.out.println("Nut Topping added!");
}
}
3.外观模式
客户端通过一个简化的接口来访问复杂系统中的功能
二.行为型模式
1.策略模式:让用户端从待选方法中选择某一项完成
2.模板模式:做事情的步骤一样,但具体方法不同
3.迭代器模式:用户希望对于任何数据,都有相同的迭代方式