设计类
- 可复用性的外部观察
- 类型可变:类型参数化,可以适应不同的数据类型,可复用组件应当是泛型的,并且要满足LSP
- 实现可变:ADT有多种不同的实现,提供不同的representation和AF,但是具有同样的spec(前置条件,后置条件,不变量)从而可以适应不同的应用场景
- 功能分组:提供完备的细粒度的操作,保证功能的完整性,不同场景下复用不同的操作
- 共性抽取:将共同行为抽象出来,形成可复用实体
- 子类型多态和LSP
- 子类型多态:客户端用统一的方式处理不同类型对象
- 对于Java中编译器遵循的原则(静态类型检查,也就是LSP):
- 子类型可以增加但是不能删除方法
- 子类中要实现抽象类型中所有未实现的方法
- 子类型中重写方法必须有相同或子类型的返回值
- 子类型中重写的方法必须使用同样类型的参数
- 子类型中重写方法不能抛出额外的异常
- 对于子类型,要有相同或者更强的不变量,有相同或者更弱的前置条件, 相同或者更强的后置条件
- LSP
- LSP是子类型关系的特定定义,叫做强行为子类型化
- 需要遵守的限制有:
- 前置条件不能强化
- 后置条件不能弱化
- 父类型的不变量必须在子类型中保留
- 子类型方法参数:采取逆变的形式
- 子类型方法的返回值:采取协变的形式
- 没有新异常的抛出,除非异常是父类中异常的子类:异常类型是:协变。
- 什么是协变
- 父类型—>子类型越来越具体,返回值和异常类型不变或者变得更加具体。(协同变化)
- 什么是逆变
- 父类型—>子类型越来越具体,但是参数不变或者变化的更加抽象。(反协同变化),但是这一行为在Java中是不允许的。
- 对于泛型的讨论
- 泛型不是协变的:类型参数在编译过程中被类型擦除
- List<Integer>不是List<Object>的子类型
- ArrayList<String>是List<String>的子类
- 在编译器进行静态检查的时候,类型参数全部被擦除,直接全部看成Object类型,所以第一个并不是父类型和子类型的关系。
- 泛型中的通配符
- 如果真的想做到刚才的第一个例子,那么采用类型参数采用?的通配符就好了,这样就OK了
- 通配符大概常用的有三种:无限制通配符,下界通配符,上界通配符
- 泛型不是协变的:类型参数在编译过程中被类型擦除
- 委托和继承
- 委托/委派:一个对象请求另一个对象的功能
- 委派是复用的一种常见形式
- 委派是一种实体之间分享代码和数据的方式
- 委派依赖于动态绑定,因为它需要给定的方法调用可以运行时调用其他不同的代码段,所以很依赖于动态绑定
- 基本过程就是:client —>接收者—> 委派者
- 什么时候使用委派来取代继承?
- 当子类只需要用到父类一少部分的方法或者子类不能继承父类数据的时候,创建一个字段并且放入一个父类对象,采取委派的方法。
- 委托/委派:一个对象请求另一个对象的功能
- CRP复合复用原则
- 类之间应当获取多态性和代码之间重用性通过他们的组合(获取其他类的实例)而不仅仅是继承。
- 委派可以看作是对象层面的代码复用机制,继承是类这个层面的代码复用机制。