面向对象编程——继承与多态
行一个程序时,构成程序的各个函数分别在计算机的内存中拥有了一段存储空间,一个函数在内存中的起始地址就是这个函数的入口地址,因此多态就是将函数名动态绑定到函数入口的运行时绑定机制。尽管多态与继承紧密相关,但通常多态被单独看作面向对象技术最强大的一个优点。
显然,调用校验器就是发送一个消息,它要使用validate函数指针。实际上无论范围值校验器还是奇偶校验器,其校验过程都是由不同内容的函数实现的。在面向对象的编程中,在不同的类中定义了其响应消息的方法,那么在使用这些类时,则不必考虑它们是什么类型,只要发布消息即可。正如在调用校验器时,不必考虑其调用的它们是什么校验器,直接使用validate函数指针,无论什么类型校验器都能实现检查功能。
由于RangValidator和OddEvenValidator类都继承自Validator类,因此没有必要在继承树中对每一种校验器都重复定义这些属性和行为,重复不仅需要做更多的事情,甚至还可能导致错误和出现不一致,详见图 4.5。这种关系在UML中表示为一条线,并有一个箭头指向父类。这种记法非常简明扼要,当遇到这种带箭头的线时,就知道存在一个继承并呈现多态的关系。
图 4.5 抽象类的层次结构
在设计Validator时,对各种校验器的使用进行标准化会有很大的帮助,因为无论是何种校验器,都用一个名为validate的方法。如果遵循这个规范,不管什么时候校验数据,只需要调用validate方法即可。无需考虑这到底是什么校验器,于是就有了一个真正多态的Validator框架——由各个对象自己负责完成校验,不论它是范围值校验、奇偶校验还是质数校验。
根据开闭原则,需要再编写一个扩展push功能的pushWithValidate()函数,其原型如下:
如果需要进行范围值校验,则pValidator指向rangeValidator,否则将pValidator置NULL。
pushWithValidate()的具体实现如下:
其调用形式如下:
使用通用校验器的应用范例程序详见程序清单 4.11。
程序清单 4.11 使用通用校验器的范例程序
由此可见,虽然OOA和OOD的边界是模糊的,但它们关注的重点不一样。OOA关注的是分析面临的问题域,从问题域词汇表中发现类和对象,实现对现实世界的建模。OOD关注的是如何设计泛化的抽象和一些新的机制,规定对象的协作方式。