多重调度:概念上的必要性?
Multiple Dispatch: A conceptual necessity?
不知multiple dispatch的概念(即内建支持,好像虚方法的动态分派也扩展到了方法的参数)如果它对性能的影响可以忽略不计,则包含在面向对象的语言中。
问题
考虑以下场景:我有一个——不一定是扁平的——class 层次结构,其中包含各种动物。在我的代码的不同位置,我想对动物对象执行一些操作。我不管,我也不能控制,这个对象引用是怎么得到的。我可能会在遍历动物列表时遇到它,或者它可能作为方法的参数之一提供给我。我要执行的操作应该根据给定动物的运行时类型进行专门化。此类操作的示例是:
- 为动物构建视图模型以便在 GUI 中显示它。
- 构造一个表示此类动物的数据对象(稍后存储到数据库中)。
- 给动物喂一些食物,但根据动物的类型给予不同种类的食物(哪种食物对它更健康)
这些例子都是对动物对象的publicAPI进行操作,但是它们所做的不是动物自己的事,因此不能放到动物本身。
解决方案
一个 "solution" 将执行类型检查。但是这种方法容易出错并且使用了反射特性,(在我看来)这几乎总是糟糕设计的标志。类型应该只是一个编译时概念。
另一种解决方案是 "abuse"(有点)访问者模式来模仿双重调度。但这需要我改变我的动物来接受访客。
我相信还有其他方法。另外,扩展的问题应该解决:如果新类型的动物加入聚会,需要适配多少代码位置,如何可靠地找到它们?
问题
那么,根据这些要求,多重分派不应该成为任何设计良好的面向对象语言的组成部分吗?
根据给定对象的动态类型进行外部(不仅仅是内部)操作不是很自然吗?
此致!
One "solution" would be to perform type checks. But this approach is
error-prone and uses reflective features, which (in my opinion) is
almost always an indication of bad design. Types should be a
compile-time concept only.
你错了。虚函数、虚继承等的所有使用都涉及反射特性和动态类型。将打字推迟到 运行 需要的时间的能力是绝对关键的,甚至是你所处情况的最基本表述中所固有的,字面上 甚至不会出现 不使用动态类型。您甚至将您的问题描述为想要根据.. 动态类型做不同的事情。毕竟,如果没有动态类型,你为什么需要做不同的事情呢?您已经知道具体的最终类型。
当然,运行 次打字可以解决您在 运行 次打字时遇到的问题。
简单地构建一个dictionary/hash table 从类型到功能。您可以为任何动态链接的派生类型动态地向此结构添加条目,查找它是一个很好的 O(1),并且不需要内部支持。
您建议根据方法名称/签名结合运行时实际参数类型进行动态调度。我觉得你疯了。
So, in the light of these requirements, shouldn't multiple dispatch be an integral part of any well-designed object-oriented language?
对于某些问题,您设想的那种调度策略的可用性会简化编码,但对于将这种调度内置到任何给定语言中来说,这是一个薄弱的论据,更不用说 every 面向对象语言。
Isn't it natural to make external (not just internal) actions dependent on the dynamic type of a given object?
也许,但并非所有看起来 "natural" 实际上都是好主意。例如,衣服不是自然的,但看看如果你尝试在 public 中四处走动(无论如何,除了伯克利以外的其他地方)会发生什么。
一些语言已经有基于参数类型的静态分派,更传统地称为 "overloading"。另一方面,如果要考虑多个参数,则基于参数类型的动态调度真的是一团糟,而且速度很慢(呃)。当今流行的 OO 语言允许您在需要的地方执行双重分派,而无需在绝大多数您不需要的地方支持它的开销。
此外,虽然实施双重分派确实会出现因独立组件之间的紧密耦合而引起的维护问题,但有一些编码策略可以帮助保持这种可管理性。无论如何,目前还不清楚在给定语言中内置基于参数的多重分派在多大程度上真正有助于解决这个问题。
如果将自己局限于这样一种情况,即类型 X 的对象应该如何处理类型 Y 的对象的知识必须存储在 class X 或 class Y 中,那么可以有Y 的基类型包括一个方法,该方法接受 X 的基类型的引用,并指示一个对象知道多少该引用所标识的对象如何调用,以及一个方法,该方法要求 Y
有一个 X
fnorble it.
完成后,可以让 X
的 Fnorble(Y)
方法开始询问 Y
它对被特定类型的 X
.如果 Y
对 X
的了解多于 X
对 Y
的了解,那么 X
的 Fnorble(Y)
方法应该调用 Y 的 BeFnorbledBy(X)
方法;否则,X
应该尽其所能地改变 Y
。
根据 X
和 Y
的种类,Y
可以为不同种类的 X
定义 BeFnorbledBy
重载方法,这样当 X
调用 target.BeFnorbledBy(this)
时,它会自动直接分派到合适的方法;然而,这种方法需要每个 Y
了解任何人 "interesting" 的每种 X
类型,无论它是否对该特定类型本身感兴趣。
请注意,这种方法不适应 class Z 的外部对象可能知道 X 和 Y 都不直接知道的 X 应该如何处理 Y 的情况。处理这种情况的最好方法是拥有一个 "rulebook" 对象,在该对象中,所有知道各种 X 应该如何处理各种 Y 的信息都可以告诉规则手册,而想要 X 处理 Y 的代码可以询问实现这一目标的规则手册。尽管语言可以在规则手册是单例的情况下提供帮助,但有时拥有多个规则手册可能会有用。这些情况下的语义可能最好通过让代码直接使用规则手册来处理。
不知multiple dispatch的概念(即内建支持,好像虚方法的动态分派也扩展到了方法的参数)如果它对性能的影响可以忽略不计,则包含在面向对象的语言中。
问题
考虑以下场景:我有一个——不一定是扁平的——class 层次结构,其中包含各种动物。在我的代码的不同位置,我想对动物对象执行一些操作。我不管,我也不能控制,这个对象引用是怎么得到的。我可能会在遍历动物列表时遇到它,或者它可能作为方法的参数之一提供给我。我要执行的操作应该根据给定动物的运行时类型进行专门化。此类操作的示例是:
- 为动物构建视图模型以便在 GUI 中显示它。
- 构造一个表示此类动物的数据对象(稍后存储到数据库中)。
- 给动物喂一些食物,但根据动物的类型给予不同种类的食物(哪种食物对它更健康)
这些例子都是对动物对象的publicAPI进行操作,但是它们所做的不是动物自己的事,因此不能放到动物本身。
解决方案
一个 "solution" 将执行类型检查。但是这种方法容易出错并且使用了反射特性,(在我看来)这几乎总是糟糕设计的标志。类型应该只是一个编译时概念。
另一种解决方案是 "abuse"(有点)访问者模式来模仿双重调度。但这需要我改变我的动物来接受访客。
我相信还有其他方法。另外,扩展的问题应该解决:如果新类型的动物加入聚会,需要适配多少代码位置,如何可靠地找到它们?
问题
那么,根据这些要求,多重分派不应该成为任何设计良好的面向对象语言的组成部分吗?
根据给定对象的动态类型进行外部(不仅仅是内部)操作不是很自然吗?
此致!
One "solution" would be to perform type checks. But this approach is error-prone and uses reflective features, which (in my opinion) is almost always an indication of bad design. Types should be a compile-time concept only.
你错了。虚函数、虚继承等的所有使用都涉及反射特性和动态类型。将打字推迟到 运行 需要的时间的能力是绝对关键的,甚至是你所处情况的最基本表述中所固有的,字面上 甚至不会出现 不使用动态类型。您甚至将您的问题描述为想要根据.. 动态类型做不同的事情。毕竟,如果没有动态类型,你为什么需要做不同的事情呢?您已经知道具体的最终类型。
当然,运行 次打字可以解决您在 运行 次打字时遇到的问题。
简单地构建一个dictionary/hash table 从类型到功能。您可以为任何动态链接的派生类型动态地向此结构添加条目,查找它是一个很好的 O(1),并且不需要内部支持。
您建议根据方法名称/签名结合运行时实际参数类型进行动态调度。我觉得你疯了。
So, in the light of these requirements, shouldn't multiple dispatch be an integral part of any well-designed object-oriented language?
对于某些问题,您设想的那种调度策略的可用性会简化编码,但对于将这种调度内置到任何给定语言中来说,这是一个薄弱的论据,更不用说 every 面向对象语言。
Isn't it natural to make external (not just internal) actions dependent on the dynamic type of a given object?
也许,但并非所有看起来 "natural" 实际上都是好主意。例如,衣服不是自然的,但看看如果你尝试在 public 中四处走动(无论如何,除了伯克利以外的其他地方)会发生什么。
一些语言已经有基于参数类型的静态分派,更传统地称为 "overloading"。另一方面,如果要考虑多个参数,则基于参数类型的动态调度真的是一团糟,而且速度很慢(呃)。当今流行的 OO 语言允许您在需要的地方执行双重分派,而无需在绝大多数您不需要的地方支持它的开销。
此外,虽然实施双重分派确实会出现因独立组件之间的紧密耦合而引起的维护问题,但有一些编码策略可以帮助保持这种可管理性。无论如何,目前还不清楚在给定语言中内置基于参数的多重分派在多大程度上真正有助于解决这个问题。
如果将自己局限于这样一种情况,即类型 X 的对象应该如何处理类型 Y 的对象的知识必须存储在 class X 或 class Y 中,那么可以有Y 的基类型包括一个方法,该方法接受 X 的基类型的引用,并指示一个对象知道多少该引用所标识的对象如何调用,以及一个方法,该方法要求 Y
有一个 X
fnorble it.
完成后,可以让 X
的 Fnorble(Y)
方法开始询问 Y
它对被特定类型的 X
.如果 Y
对 X
的了解多于 X
对 Y
的了解,那么 X
的 Fnorble(Y)
方法应该调用 Y 的 BeFnorbledBy(X)
方法;否则,X
应该尽其所能地改变 Y
。
根据 X
和 Y
的种类,Y
可以为不同种类的 X
定义 BeFnorbledBy
重载方法,这样当 X
调用 target.BeFnorbledBy(this)
时,它会自动直接分派到合适的方法;然而,这种方法需要每个 Y
了解任何人 "interesting" 的每种 X
类型,无论它是否对该特定类型本身感兴趣。
请注意,这种方法不适应 class Z 的外部对象可能知道 X 和 Y 都不直接知道的 X 应该如何处理 Y 的情况。处理这种情况的最好方法是拥有一个 "rulebook" 对象,在该对象中,所有知道各种 X 应该如何处理各种 Y 的信息都可以告诉规则手册,而想要 X 处理 Y 的代码可以询问实现这一目标的规则手册。尽管语言可以在规则手册是单例的情况下提供帮助,但有时拥有多个规则手册可能会有用。这些情况下的语义可能最好通过让代码直接使用规则手册来处理。