OO中传递的消息是什么?


35

我一直在研究OO编程,主要使用C ++,C#和Java。我以为我对封装,继承和多态性有了很好的了解(以及在此站点上阅读了很多问题)。

似乎突然出现的一件事是“消息传递”的概念。显然,在以当今主流语言进行OO编程时,并没有使用此方法,但是Smalltalk支持了这种方法。

我的问题是:

  • 什么是消息传递?(有人可以举一个实际的例子吗?)
  • C ++,C#或Java是否支持这种“消息传递”?

4
我不久前在SO上回答了这个问题:stackoverflow.com/a/3104741/10259
Frank Shearar,2012年

1
您是否阅读了Wikipedia文章
扬尼斯

4
以我的拙见,Objective-C将成为主流语言。至少与C#一样多。
mouviciel 2012年

我同意这一点,我是根据自己的经验说出“主流”语言的
汤姆(Tom)

成员函数调用是传递消息的一种实现。传递的消息由函数名称标识,并且包括来自参数的信息。后期绑定允许接收类以与其他类不同的方式处理同一消息。这并不是Simula的创建者想要的,很多人会反对称它为消息传递,并指出(有充分的理由)进行消息传递是使Simula与众不同的关键,​​但是成员函数调用实际上仍然是相同的工作。
2012年

Answers:


60

什么是消息传递?(有人可以举一个实际的例子吗?)

消息传递只是意味着(在非常抽象的水平上)程序执行的基本机制是对象彼此发送消息。重要的是,这些消息的名称和结构不必在源代码中预先确定,而本身可以是其他信息。这是Alan Kay最初设想的“面向对象编程”的重要组成部分。

C ++,C#或Java是否支持这种“消息传递”?

这些语言实现了通过方法调用传递的消息的有限版本。有限,因为可以发送的消息集仅限于在类中声明的方法。这种方法的优点是可以非常有效地实现它,并且可以进行非常详细的静态代码分析(这将带来各种有用的好处,例如代码完成)。

相反,实现“真实”消息传递的语言通常也具有方法定义,这是实现消息处理程序的便捷方法,但允许类实现更灵活的消息处理程序,使对象能够使用任意名称接收“方法调用”(不固定)在编译时)。

Groovy中的一个示例演示了此概念的强大功能:

def xml = new MarkupBuilder(writer)
xml.records() {
  car(name:'HSV Maloo', make:'Holden', year:2006) {
    country('Australia')
    record(type:'speed', 'Production Pickup Truck with speed of 271kph')
  }
}

将产生以下XML:

<records>
  <car name='HSV Maloo' make='Holden' year='2006'>
    <country>Australia</country>
    <record type='speed'>Production Pickup Truck with speed of 271kph</record>
  </car>
</records>

需要注意的是recordscarcountryrecord在语法上的方法调用,但没有在定义名称的方法MarkupBuilder。相反,它具有一个包罗万象的消息处理程序,该处理程序接受所有消息并将消息名称解释为XML元素的名称,将参数解释为属性,将闭包解释为子元素。


+1直接回答问题。接受为代码示例。感谢您的帮助:)
汤姆(Tom)

所以不能简单地用一个简单的实现sendMessage(property_name, Array of arguments),并getMessage(property_name, Array of arguments)在静态语言?
起搏器

1
@Pacerier:当然,但这结合了这两种方法的缺点-您失去了类型安全性,并且仍然“ sendMessage”污染了您的代码,因此您不会获得优雅的语法。
Michael Borgwardt

在Groovy示例中,说消息处理程序正在接收消息而不是方法调用,是否更正确?最初,您在“方法调用”一词周围加上了引号,但在最后一句话中,您说它“接受所有方法 ”,而不是“消息”。
亚当·泽纳

@AdamZerner:您是对的,我已解决。
Michael Borgwardt

28

消息传递是一种不同的方式,用于处理OO代码中的一个对象使另一个对象(或可能本身)执行某项操作的需求。

在大多数源于C ++方法的现代语言中,我们都是通过方法调用来实现的。在这种情况下,被调用对象(通过其类定义)列出了它接受的调用方法的大清单,然后调用对象的编码器只需编写调用即可:

public void doSomething ( String input )
...
other_object.dosomething ( local )

对于静态类型的语言,然后编译器可以检查正在调用的事物的类型,并确认已声明该方法。对于动态类型的语言,则在运行时执行。

但实际上发生的是将一堆变量发送到特定的代码块。

讯息传递

在消息传递语言(例如,Objective C)中,方法不是接收器,而是接收器,但是从广义上讲,定义它们和调用它们的方法是相同的-区别在于处理方法。

用消息传递的语言,编译器可能会检查您所调用的接收者是否存在,但在最坏的情况下,它将弹出警告,指出不确定该接收者是否存在。这是因为在运行时将发生的情况是,将同时传递变量束和要调用的接收方的签名,从而调用接收对象上的代码块。然后,该代码块寻找接收器并调用它。但是,如果接收者不存在,则代码将简单地返回默认值。

结果,从C ++ / Java移到Objective C时发现的奇怪之处之一是,您可以对未在编译时类型上声明但甚至不存在于编译时类型上的对象进行“调用方法”运行时类型...并且该调用不会导致引发异常,但实际上是将结果传回。

这种方法的优点是它使子类层次结构变得扁平,并且避免了大多数对接口/多重继承/鸭子类型的需求。它也允许对象在被要求执行其没有接收者的操作时定义默认行为(通常是“如果我不这样做,则将请求转发给另一个对象”)。它还可以简化到回调的链接(例如,用于UI元素和定时事件),尤其是在静态类型的语言(例如Java)上(因此,您可以让按钮将接收器称为“ runTest”,而不是在内部类上调用“ actionPerformed”方法) “ RunTestButtonListener”为您执行调用。

但是,这似乎要以开发人员进行额外检查为代价,即他们认为自己正在进行的调用位于具有正确类型的正确对象上,并以正确的顺序传递正确的参数,因为编译器可能不会警告您,它将在运行时完美运行(只需返回默认响应)。可以说,额外的查找和参数传递也会影响性能。

如今,动态类型化语言可以为消息传递的OO提供很多好处,而所产生的问题却更少。


1
我喜欢这个答案-解释差异及其含义。
HappyCat 2012年

@Gavin,那么它与PHP和Javascript的动态方法处理程序完全一样吗?
起搏器

11

消息传递体系结构是简单的系统,其中每个组件彼此独立,具有在它们之间传递数据的通用机制。您可以将方法调用视为消息传递的一种形式,但这样做并不实际-​​会使问题感到困惑。这是因为如果您的类具有定义明确的方法,并且有一些调用这些方法的代码,则必须将整个内容编译在一起,从而将代码和对象耦合在一起。您会看到它是如何关闭的(在传递消息时,编译器正在强制执行正确性,但是它失去了解耦系统的很多灵活性)。

消息传递体系结构通常允许在运行时添加对象,并且经常允许消息重定向到一个或多个对象。因此,我可以使用一些代码向所有已加载到系统中的对象广播“数据x已更新”消息,并且每个对象都可以对该信息采取任何喜欢的操作。

一个奇怪的例子是网络。HTTP是消息传递系统-您将命令动词和“数据包”传递给服务器进程。(例如GET http:\ myserver \ url)无论您的浏览器还是网络服务器都不关心您发送的数据或发送到的位置。服务器会将其传递给将打包另一个“数据包”数据并将其发送回给您的代码。该系统中的任何一个组件都不了解其他组件的工作或其所从事的工作,他们只知道用于消息通信的协议。


@gbjbannb,需要一些伪代码解释....
Pacerier
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.