软件构造学习心得——面向可复用性和可维护性的软件构造(9、11讲)
内容
Programing for/with reuse
LSP
协变、反协变
数组的子类型化
泛型的子类型化
泛型中的通配符(?)
Delegation
Comparator和Comparable
CRP原则
接口的组合
白盒框架的原理与实现
黑盒框架的原理与实现
Programing for/with reuse
复用分类:
源代码级别的复用
模块级别的复用:类/抽象类/接口
库级别的复用:API/包
系统级别的复用:框架
另一种分类:
白盒框架,通过代码层面的继承进行框架扩展
黑盒框架,通过实现特定接口/delegation进行框架扩展
Behavioral subtyping and Liskov Substitution Principle (LSP)
行为子类型
子类型可以增加方法,但不可删
子类型需要实现抽象类型中的所有未实现方法
子类型中重写的方法必须有相同或子类型的返回值
子类型中重写的方法必须使用同样类型的参数
子类型中重写的方法不能抛出额外的异常
更强的不变量、更弱的前置条件和更强的后置条件
强行为子类型化:
Covariance (协变)
父类型-》子类型:越来越具体specific
返回值类型:不变或变得更具体 (子类返回值是父类返回值的子类)
异常的类型:也是如此。(子类异常是父类异常的子类)
Contravariance (反协变、逆变)
参数类型:要相反的变化,要不变或越来 越抽象(子类的参数是父类参数的父类)
Java中遇到这种情况,当作overload看待
数组的子类型化
Integer和Double都是Number的子类型,所以允许如此赋值,数组本身是协变的。但是如果将子类型直接赋值给父类型变量引用,再次修改内容时会出现运行时错误,即后面几行代码的情况。
泛型的子类型化
泛型不是协变的,会保持子类型关系
在编译过程中泛型会发生类型擦除,把类型占位符都换成对应的类型。所以,不能通过泛型的子类关系来代替整体的子类关系
eg:
(下图必考)
泛型中的通配符(?) Wildcards
只有通过通配符泛型才有子类型关系
1.无界通配符:
2.
Comparator和Comparable
int compare(T o1, T o2): Compares its two arguments for order.
如果你的ADT需要比较大小,或者要放入Collections或Arrays进行排序,可实现Comparator接口并override compare()函数。
另一种方法是让你的ADT实现Comparable接口,然后override compareTo() 方法
与使用Comparator的区别:不需要构建新的Comparator类,比较代 码放在ADT内部。
Delegation委托
委派/委托:一个对象请求另一个对象的功能
e.g. the Sorter is delegating functionality to some Comparator
委托vs继承
继承是通过扩展或重写方法,而委托是通过将工作委托给其他对象进行。
不过,两者并非只能使用一个,很多设计模式同时使用了继承与委托,一方面继承类,另一方面在成员变量中实例化对象。
如果子类只需要复用父类中的一小部分方法,可以不需要使用继承,而是通过委派机制来实现。
一个类不需要继承另一个类的全部方法,通过委托机制调用部分方法 。
从而避免大量无用的方法
CRP原则
类应该通过它们的组合(通过包含实现所需功能的其他类的实例)来实现多态行为和代码重用,而不是从基类或父类继承。
简单的说,如果仅仅是提供功能“has_a”,委托就可以,而如果关系“is_a”,使用继承。
.“委托” 发生在object层面,而“继承”发生在class层面
接口的组合
接口可以定义抽象行为然后在具体类中实现具体行为
接口的组合:
委托的种类:
耦合度不同 使用、组合、聚合、关联
1.use Dependency: 临时性的delegation
在参数中出现,调用了相应方法,甚至都没有实例化对象
2.has Association: 永久性的delegation
对象之间的长期静态联系 实例化对象
3.Composition:更强的association,但难以变化
组合:部分与整体的关系,彼此不可分 a part of
4.Aggregation: 更弱的association,可动态变化
聚合:部分与整体的关系,但彼此可分——has a
前者聚合Aggregation,后者组合Composition
白盒框架、黑盒框架
白盒框架多用继承和重写
黑盒框架多用委托/组合和接口