《基于模型的软件开发》——3.3 多态

简介:

本节书摘来自华章计算机《基于模型的软件开发》一书中的第3章,第3.3节,作者:[美]H. S.莱曼(H. S. Lahman)著, 更多章节内容可以访问云栖社区“华章计算机”公众号查看。

3.3 多态

多态是比泛化和继承更复杂的一个话题。在开始讨论什么是多态之前,我们先来消除在讨论OO多态时一个常见的错误。在网上论坛,甚至在一些文献中,很多人认为多态是实现的替代。虽然在OOP层次的某些状况中,这在技术上是正确的,但是对于OOA/D来说,这种理解是完全不正确的。
有关实现替代的一个典型例子是关于如何完成数组排序职责的。在不影响结果的情况下,也就是在数组总是以指定的顺序出现的情况下,我们可以使用各种可替代的排序算法(例如:插入、快速排序等)来实现数组排序的职责。也就是说,实现被替代之后,可观察的结果并不会发生变化。因为我们在接口背后使用了封装技术特意隐藏实现。同时,我们定义的DbC契约仅仅依赖于集合排序这个概念。
在泛化中的OO多态是关于替代行为的。
例如,考虑猎物在受到攻击时的响应行为。如果猎物是野鸡,那么它的行为可能是飞走。如果猎物是啮齿类,那么它的行为可能是慌乱地钻进最近的地洞里。如果猎物是瞪羚,那么它的行为可能是跑远。对于任何合理行为的定义,具体的行为可能各不相同,可观察的结果也各不相同。但是通过神奇的OO多态,我们有时候可以自由地替代它们。
我们的概念重点在于猎物实体如何与其他实体,例如肉食动物实体,进行协作。当狮子正在进食时,如果有一些小猎物在周围游荡,那么狮子可能会低吼或者假装冲锋来驱赶这些小猎物。狮子看起来在攻击,实际上只是为了将那这些闯入者赶走。从狮子的角度来看,它并不关心这些小猎物是如何逃走的,它只是关心它们是否逃走了。那么当狮子发出攻击消息的时候,它并不关心得到的响应是飞走、钻进洞里还是跑远。狮子与小猎物之间的DbC契约可以仅仅用小猎物离开现场来表示。
实际上,我们提出的关于狮子和小猎物之间DbC契约的抽象层次使得小猎物的具体行为细节变得没那么重要了。但是从小猎物的角度出发,每一类动物的响应方式和可观察的结果都是不同的。
另一个关键因素在于消息与方法的分离。狮子向猎物发送一条“我在攻击”的消息。狮子在发送消息的时候忽略掉了恰好在那里的小猎物的特定种类。只要每一种小猎物都对消息进行了响应并且通过某种方式离开了,狮子就满意了。
关键点在于:
1)协作存在于很高的抽象层次,潜在行为可观察到的结果差异对于客户端来讲并不重要。
2)与客户端的DbC契约的定义与协作的抽象层次一致。
3)存在一种机制来替代不同的行为,从而在不同的解决方案上下文中满足第2条要求。
在不同的解决方案上下文中替代行为的这种功能是一种非常强大的工具。然而,鉴于第12章将继续讨论,因此我们在此需要非常小心地讨论如何使用它,以避免在朴素的景观中突然出现摩天大楼这种不和谐的情况。与之相关的管理用于保证在所有的协作上下文中不同的可观察结果都能够被接受。
OO的多态用一种优雅的方式通过抽象与封装的组合来实现。首先,通过抽象,行为的语义被泛化了。这使得那些独特但是类似的行为在更高的、更通用的抽象层次上被看待,使得实现时的差异变得不重要了。也就是说,从某个特定角度来看,它们都是一样的。
其次,多态通过封装接口隐藏了实现的细节,为这些实现提供了统一的访问方式。具体来说,多态依赖于面向所有替代行为的公共接口。这使得客户端可以通过相同的方式来访问每一个行为。从客户端的角度,这为特定行为提供了一定程度上的匿名,客户端不需要了解它访问的是哪一个具体的行为。这种不了解和不依赖使得我们可以有效进行去耦合。
OO范式明确支持以下方法提供行为替代:
特别多态(Ad hoc polymorphism):在单个职责中提供多种行为。当该方法被调用时,这些行为中的一个将在运行时被选出来。在过去这是过程应用中最常见的一种多态形式,它在过程中表现为“switch”语句,其中,每个“case”表示一个特定的行为。同时,我们也可以在某个独立的OO方法中做同样的事情。
包含多态(又称内在多态)(Inclusion polymorphism):不同的行为响应可以在运行时动态地绑定到一个消息之上。这是在OOPL这一层次最常见的一种多态形式,可以通过泛化技术实现。从本质上讲,超类提供了一个可以供其子类共享的接口,但是不同子类的成员提供它们特有的行为。猎物的例子展示了这种多态机制:狮子的消息发送到猎物超类上,当前存在的独立子类执行特定的行为,如飞走。
重载多态(Overload polymorphism):通过为同一名称重载不同的行为来实现行为替代。在OOP的上下文中,当算术运算符或者逻辑运算符基于使用它们的对象的上下文拥有替代机制的时候,经常出现这种形式的多态。这在OOPL中有明确的定义,但是通常也隐式地定义于OOA/D这一层。也就是说,在OOA/D中假定抽象数据类型属性的基本操作对于这个抽象数据类型来讲是适合的,同时在OOP中进行相应的实现。
参数多态(也称为泛型)(Parametric polymorphism):这种多态出现在行为替代参数化的时候。也就是说,可观察结果基于状态变量的值发生变化。这种多态与特别多态的区别在于,通常只有单个通用行为执行,但是根据行为所接受的状态变量值的不同而动态产生不同的执行结果。这是OOA/D层次最重要的多态形式。因为这种多态能使我们使用不变量,同时将细节委托给配置数据来完成。第5章将进一步讨论这个话题。
对于任何使用程序语言编程的人来说,特别多态相对简单而直观。重载多态对于OOA/D来说意义不大,因为这种视图已经抽象了名字重载所支持的各种差异。这里重点讨论以下两种形式的多态。
包含多态(又称固有多态)
泛化使包含多态成为可能,包含多态又增强了OO泛化的能力。包含多态使用超类来获得必要的共同性和接口的通用性。共同性通过抽象实现,而接口的通用性通过接口继承来实现。通过定义,超类表现了其子类的共同特征。因此,我们收集超类下面类似但是各不相同的子类,在超类中概念性地概括它们共享的特征,然后在超类层次上提供针对这些特征的通用接口。
在图3-2中,超类定义了取款(Withdrawal)、存款(Deposit)和查询余额(Get Balance)的基本职责。每个子类都实现了其超类的职责。但是在实现时,子类可以为不同的行为提供不同的可观察结果。到达超类的消息会被指派给当前子类提供的特定行为。除非我们正在处理银行业务,否则,对于这些行为,我们的期望是完全相同的。
然而,在详细行为的层次上会存在一些具体的问题,包括:法律约束(例如,养老金账户提前支取罚款)、政策约束(例如,对于存款的不同终端存在的延迟)、规则约束(例如,对于没有储蓄账户的支票账户的透支保护)、报告需求(例如,银行的损益和资产负债表)以及其他许多的内容。这些内容最终不可避免地表现为具有不同的可观察结果的不同行为,因为不同的业务规则和策略应用于每一个子类的上下文中。
然而,很多客户端对于“取款”、“存款”和“查询余额”等账户特征拥有一种更基本的视图,在视图中这些细节上的差异并不相关。例如:“这里有一叠钱,把它存起来。但是我不想了解细节,免得被困扰”。包含多态让客户端向账户的超类发送一条通用的“存款”消息,而不是向每个单独的子类进行发送,从而实现这一点。

相关文章
|
29天前
|
存储 编译器 数据安全/隐私保护
【软件设计师备考 专题 】面向对象开发方法:理解类、对象和封装
【软件设计师备考 专题 】面向对象开发方法:理解类、对象和封装
51 0
|
26天前
|
存储 编译器 C++
C++:多态究竟是什么?为何能成为面向对象的重要手段之一?
C++:多态究竟是什么?为何能成为面向对象的重要手段之一?
49 0
|
1月前
|
设计模式 C++
53继承在软件开发中的重要意义
53继承在软件开发中的重要意义
14 0
|
9月前
面向对象中的多态(落实代码)
面向对象中的多态(落实代码)
|
10月前
|
C#
【C#本质论 八】类-从设计的角度去认知(多态)
【C#本质论 八】类-从设计的角度去认知(多态)
49 0
|
网络协议 数据管理 Java
面向对象编程必要性 | 学习笔记
快速学习面向对象编程必要性
78 0
|
测试技术
浅谈面向对象方法学
浅谈面向对象方法学
327 0
浅谈面向对象方法学
|
架构师 安全 NoSQL
多态对一个软件架构师的重要性
面试经常会被问到的题目之一,面向对象的三大特征是什么?多态则是三大特征之一,个人认为三大特征中最为重要的,另外的两大特征是封装和继承。 为什么说多态对软件架构师非常重要,对系统软件非常重要呢?举个例子,当软件面向一个客户的时候,你会发现软件写得很简单,很快就能满足其需求。随着时间的推移,软件面向的不再是一个客户。每个客户提出的需求千差万别,尤其当出现针对性的、个性化的需求。软件的迭代、升级会变得相对困难,拓展功能变得困难。
578 0
多态对一个软件架构师的重要性
面向对象的特征之三:多态
1、多态性,可以理解为一个事物的多种表现形态        1> 方法的多态性:重载与重写        2>子类对象的多态性:父子类对象的转换                        ①、父类的引用指向子类对象(即向上转型),当调用方法时,实际上执行的是子类重写父类已有的方法。
1044 0

热门文章

最新文章