一.复用的等级和形态
- 源代码级别的复用:复制粘贴
- 模块级别的复用:类与接口
- 库级别的复用:API和包
二.复用的类型
- 白盒复用:复制并修改一段代码。可以对代码的功能进行定制,但是愮指导代码内部的实现逻辑,增加了软件的复杂度。
- 黑盒复用:不能直接看到源代码,只是利用别人提供的API,更加简单,但是适用性差。
三.源代码级别的复用
粘贴代码
四.模块级别的复用:类与接口
继承:
- 设计继承树
- 可能会重写一堆方法(@Override)
- extend
- 是类级别的复用
代理:
- 显式代理:传递对象给需要使用这些功能的对象
- 隐式代理:将某个对象定义为这个对象的成员,去调用其方法
- 是对象级别的复用
五.库级别的复用:API和包
- 库:开发者构造软件,去调用库中的功能
- 框架:可以复用的骨架代码,可以定制功能到某个应用。框架作为主程序去执行,执行过程中调用开发者自己写的程序
六.框架级别的复用
框架:一组抽象类和具体类,以及他们之间的关系。开发者根据框架的规约,填充一些自己的代码进去,形成完整的代码
- 白盒框架:通过继承进行框架的扩展
- 黑盒框架:通过实现特定的接口、或者通过dialing来进行扩展
七.行为子类型和LSP原则
子类型多态:客户端可以用统一的方式来处理不同类型的对象。使用父类的地方,都可以无条件用子类来代替。
静态类型检查(即编译器对子类型的要求):
- 子类型可以添加方法,但是不可以删除
- 子类型需要实现抽象类型中的所有未实现的方法
- 子类型中重写的方法必须有相同或子类型的返回值候符合协变的返回值
- 子类中重写的方法必须使用同样类型的参数或者符合逆变的参数
- 子类型中重写的方法不能抛出额外的异常
总结:
更强的不变量、更弱的前置条件、更强的后置条件
协变:
返回值的类型和异常的类型要更加具体。
Object-->String
Throwable-->IOException
逆变:
参数类型要更加抽象。
String-->Object
理论上是这样的,但是目前java还不支持这样,还是会将其当作overload而不是override看待。
但是泛型中的参数不能这样理解,协变后的泛型不是原类型的子类型:
List<Integer>不是List<Number>的子类型。
也就是说Myclass<A>和Myclass<B>没有任何关系,无论A和B是否有关系。
泛型中的通配符
List<?>是一个不知道什么类型的列表
使用通配符的典型场景:
- 你实现的方法可以通过使用Object类的方法来实现
- 当你的方法在泛型类中不依赖参数的类型时,如List.size,List.clear等
- List<Object>很可能使错误的用法,而正确的用法使List<>?>
- 通配符下界:<? super A>,A的父类和自己都可以传入
- 通配符下界:<? extends A>,A的子类和自己都可以传入
- List<Number>是List<?>的子类型
- List<Number>是List<?extends Object>的子类型
- List<Object>是List<?super String>的子类型
八.委派(delegation)
Interface Comparator<T>
int compare(T o1, T o2)
如果ADT需要实现比较大小的操作,或着要放入Collections或Arrays中进行排序,实现Comparator接口并重写compare方法
Interface Comparable<T>
让ADT实现Comparable接口,然后override compareTo方法
- 区别:不用构建新的Comparator类,比较代码放在ADT内部
委派(delegation):一个对象请求另一个对象的功能,通过运行时动态绑定,实现对其他类中代码的复用
显示委派:将对象作为参数传入
隐式委派:放入实现类的内部
client—calls—>Receiver—delegates to—>delegate
问题:如果子类中只需要复用父类中的一小部分代码,那么就不需要继承,继承是没有意义的,而是需要动态请求一个对象,通过委派来实现代码的复用,避免大量无用的复用
Composite over inheritance principle(CRP):组合比继承更好,has_a、use_a比is_a更好。
代理的类型: - use:
临时性的delegation,通过参数传入,方法运行完,两者就彻底脱离关系了 - association:
永久性的delegation,在rep中定义一个属性存储要请求的对象,通过构造方法传参构造,但是传入后就不能修改了,就固定住了 - composition:
更强的association,直接在写ADT的时候就将类型固定好了,不能通过传参设定,难以变化,可以理解为is_part_of - Aggregation:
更弱的association:不仅可以通过构造方法传参构造,还可以在后续的客户端代码处,通过特殊的设定方法,随时改变属性。