第五章--第二节:复用性的实施
第五章:面向软件构造的可复用性的构建方法
问题一:设计可复用的类
1.行为子类型和LSP原则
子类型多态:客户端可用统一的方式处理不同类型的对象
子类型多态要遵守的原则:
- 客户端可用统一的方式处理不同类型的对象
- 子类型需要实现抽象类型中的所有未实现方法
- 子类型中重写的方法必须有相同或子类型的返回值
- 子类型中重写的方法必须使用同样类型的参数
- 子类型中重写的方法不能抛出额外的异常
方法中的LSP原则:更强的不变量、更弱的前置条件、更强的后置条件
**例:看下列子类是否满足LSP原则
父类:
子类:
(解:子类具有和父类一样的表示不变量,并且有额外的表示不变量,表示不变量没有变弱;并且重写的方法有相同的前置和后置条件,满足LSP)
**例:看下列子类是否满足LSP原则
次父类:
子类:
(解:次父类的)**********************
2.LSP是一种对子类型关系的特殊的定义,被称为 强行为子类化
要求:
- 前置条件不能强化
- 后置条件不能弱化
- 不变量要保持
- 子类型方法参数:逆变
- 子类型方法的返回值:协变
问题二:Covariance (协变) 与 Contravariance (反协变、逆变)
1. 协变Covariance
父类型-->子类型:越来越具体specific
返回值类型:不变或变得更具体
异常的类型:也是如此。
例:
2. 反协变、逆变Contravariance
父类型-->子类型:越来越具体specific
参数类型:要相反的变化,要不变或越来越抽象
例:
3.总结协变与逆变
(1.子类型(属性、方法)关系;2.不变性,重写方法;3.协变,方法返回值变具体;4.逆变,方法参数变抽象;5.协变,参数变的更具体,协变不安全)
问题三:各种应用中的LSP
1.数组中协变(数组是协变的)
(在java的子类型规则中,一个T类型的数组,其中包含的元素可以是T类型也可以是T的子类型)
2.泛型中的LSP
- ***
(注意:第一行符合子类型关系,而第二行不符合子类型关系)
(原因:因为在泛型中存在 类型擦出(type erasure))
(所以,泛型不是协变)
例:???????????????
- 总结泛型中的LSP
尽管Integer是Number的子类型,但Box<Integer>也不是Box<Number>的子类型
3.为了解决类型擦除的问题-----Wildcards(通配符)
(?是一种不确定的类型)
1.更低边界的通配符:
2.更高边界的通配符:
3.带有通配符的泛型的LSP:
- List<Number> is a subtype of List<?>
- List<Number> is a subtype of List<? extends Object>
- List<Object> is a subtype of List<? super String>
-
-
问题四:委派(Delegation)(复用的一种常见形式)
1.委派:一个对象请求另一个对象的功能(“委托”发生在objet层面,而“继承”发生在class层面)
*例:继承与委派的对比
委派的类型:
- Use(A use B)
- Composition/aggregation (A owns B)
- Association (A has B)
模型:
①Use--临时性的delegation
例:
(直接在方法中委托其他类的对象,只是使用的其他类的对象,是临时的)
②Association--永久性的delegation
例:
(一个类有其他类的对象声明(has),是永久性的)
③Composition--更强的delegation
例:
(一个类有另一个类的对象声明(is part of),更是永久性的)
④Aggregation
例:
2.委派使用的情况
- 如果子类只需要复用父类中的一小部分方法
- 一个类不需要继承另一个类的全部方法,通过委托机制调用部分方法
问题五:组合(Composition)
CRP原则:Composite Reuse Principle
**例:一个公司要分红,但公司不同职位(经理、程序员、秘书)的分红不同(用继承和组合机制对比实现)
继承:
父类:
子类:
(但不同的人分红不同,所以每种职位都要重写父类中的方法,代码很赘余)
(核心问题:每个Employee对象的奖金计算方法都不同,在object层面而非class层面。)
组合:(CRP)
更为一般的委派的设计:
**例:对比继承和 组合
继承:
组合:
问题六:两种模型
1.
2.