面向对象编程——继承与多态
都按需要提供特定的实现。
由此可见,对于一个新的抽象,必须将它放在已经设计好的类和对象层次结构的上下文中。实际上,这既不是自上而下的活动,也不是自下而上的活动。Halbert和O 'Brien指出,"当在一个类型层次结构中设计类时,并非总是从基类开始,然后创建子类。通常会创建一些看起来不相似的类型,当意识到它们是相关时,然后才将它们的共性分离出来,放到一个基类或多个基类中……"实践经验证明,类和对象的设计是一个增量、迭代的过程。Stroustrup认为,"最常见的类层次结构的组织方式是从两个类中提取公共部分放到一个新类中,或将一个类拆分为两个新类。" 比如,将RangeValidator和OddEvenValidator的共性上移到Validator中。
由于许多开发者常常忽略了为对象和类正确地命名,因此必须确保创建类、属性和方法名时,不仅要遵循约定,还要让名字具有描述性,让人一目了然。否则解释权在程序员自己,因为程序员的个性,时常有可能创建一些只对他们自己很有道理的约定,而其他人却完全不能理解。类名不能为动词,类名应该用常见的名词命名,比如,Validator或RangeValidator,避免使用Manager、Processor、Data或Info这样的类名。对象名应该用合适的名词短语命名,比如,rangeValidator或oddEvenValidator。特别地,选择的名字应该是业务领域专家使用和认知的名字。方法名应该是动词或动词短语,比如,pushWithValidate。
当开发者决定采用某种协作模式后,工作会被分解给对象,即在相应的类上定义适当的方法。归根到底,单个类的协议包含了实现所有行为,以及实现与其实例相关的所有机制所需要的全部操作。因此与类层次结构的设计一样,机制代表了战略的设计决策。
实际上,机制就是在长期的实践中发现和总结的各种模式。在底层开发模式中,惯用法是一种表现形式;在高层开发模式中,则有一组类组成的框架。框架代表了大规模的复用,比如,ZLG的AMetal框架和AWorks框架,MVC框架和MVVM框架以及微软的.NET框架或开源代码。所以机制代表了一种层次的复用,它高于单个类的复用。
虽然代码表明了基类与子类的关系,但还是不够深刻。在这里,将以Validator与RangeValidator之间的继承关系为例,通过UML图进一步形象地描述,详见图 4.4。
图 4.4 继承关系图
继承关系为何指向基类?其深刻的设计思想是它代表了依赖的方向。所谓依赖关系是指两个元素之间的一种关系,其中一个元素变化将会引起另一个元素变化。UML图中采用从子类指向基类的空心箭头表示继承,暗示基类的变化可能导致子类的变化。简而言之,被依赖的先构造,依赖于其它元素的后构造。
其实继承是一个非常传统和经典的术语,从Smalltalk问世时就被广泛使用,将一般类和它的特殊类之间的关系称为继承关系。它在很多场合还以动词或形容词的面目出现。比如,特殊类继承了一般类的属性和操作,面向对象编程语言具有继承性和封装性等。
而一般-特殊恰当地一般类和它的特殊类之间的相对关系,既可以称为一般-特殊关系,也可以形成一般-特殊结构。当"一般"这个术语generalization翻译成中文时,很容易与上下文混淆,因此翻译成"泛化"更准确。即一般类(父类)对特殊类(子类)而言是泛化,反之就是特化。因此一般类和特殊类之间的关系称为一般-特殊关系,一般-特殊结构是由一组具有一般-特殊关系(继承关系)的类所形成的结构。
显而易见,一般-特殊结构是问题域中各类事物之间客观存在的一种结构,在面向对象分析模型中建立一般-特殊结构,使模型更清晰地映射了问题域中事物的分类关系——将对象划分为类,用类描述属于它的全部对象实例。它将具有一般-特殊关系的类组织在一起,可以简化对复杂系统的认识,使人们对系统的认识和描述更接近日常思维中一般概念和特殊概念的处理方式。
在面向对象的开发中,一般-特殊结构可以使开发者简化对类的定义,因而对象的共同特征只需在一般类中给出,特殊类通过继承而自动地拥有这些特征,不必再重复定义。
不同的方法学对如何发现一般-特殊结构,有不同的策略。其最大的问题让人们感到更多地是依赖于直觉,如果分析方法是一门艺术,也就意味着让人具有很大的不确定性。而事实上,使用共性和差异化分析工具,并从概念、规约和实现三个不同的视角看待对象,就可以简化复杂的系统,详见《嵌入式软件工程方法与实践丛书——面向对象的分析与设计》。
>>> 4.3.3 职责驱动设计
OO强调的是在现实世界或