“对象如何通过传递消息进行通信”究竟如何?

时间:2010-02-27 16:06:26

标签: language-agnostic oop

在几篇关于面向对象编程的介绍性文章中,我遇到了上述陈述。

来自维基百科,“在OOP中,每个对象都能够接收消息,处理数据和发送消息到其他对象,并且可以被视为独立的'机器“有着独特的角色或责任。”

该声明在代码中到底意味着什么?

class A
{ 
    methodA()
    {

    }
}


class B
{
    methodB()
    {

    }
}


class C
{
    main()
    {
        A a=new A();
        B b=new B();
        a.methodA(); // does this mean msgs passing??
        b.methodB(); // or does this?? I may be completely off-track here..
    }
}

10 个答案:

答案 0 :(得分:23)

如果我们谈论OOP而不是“消息传递”这个术语来自Smalltalk。简而言之,Smalltalk的基本原则是:

  1. Object 是面向对象系统的基本单元。
  2. 对象有自己的状态
  3. 对象通过发送和接收消息进行通信
  4. 如果您对Smalltalk感兴趣,请查看PharoSqueak

    Java / C#/ C ++和许多其他语言使用的方法略有不同,可能来自Simula。您调用方法而不是传递消息。

    我认为这些术语或多或少相同。可能唯一有趣的区别是消息传递(至少在Smalltalk中)总是依赖于动态调度和后期绑定,而在方法调用的情况下,也可以使用静态调度和早期绑定。例如,C ++(AFAIK)默认进行早期绑定,直到“虚拟”关键字出现在某个地方......

    无论如何,无论您的编程语言用于两个对象之间的通信(消息传递或方法调用),哪种形式主义都被认为是禁止直接访问Smalltalk术语中的实例变量或C ++术语中的数据成员的良好OOP样式。无论你的编程语言使用什么术语。

    Smalltalk直接禁止在语法级别访问实例变量。正如我上面提到的,Smalltalk程序中的对象只能通过传递/接收消息进行交互。许多其他语言允许在语法级别访问实例变量,但这被认为是一种不好的做法。例如,着名的Effective C++书包含相应的建议:第22项:声明数据成员私有。

    原因是:

    • 语法一致性(客户端访问对象的唯一方法是通过成员函数或消息传递);
    • 更精确地控制数据成员的可访问性(您可以不实现访问,只读访问,读写访问,甚至只写访问);
    • 您可以稍后在不破坏公共界面的情况下替换数据成员。

    最后一个是最重要的。这是封装的本质 - 隐藏在类级别的信息。

      

    关于封装的观点比最初看起来更重要。如果从客户端隐藏数据成员(即封装它们),则可以确保始终维护类不变量,因为只有成员函数才能影响它们。此外,您保留以后更改实施决策的权利。如果你不隐藏这样的决定,你很快就会发现,即使你拥有一个类的源代码,你更改公共内容的能力也受到极大限制,因为太多的客户端代码将被破坏。公共意味着未被封装,实际上,未封装意味着不可更改,特别是对于广泛使用的类。然而,广泛使用的类最需要封装,因为它们最能从使用更好的实现替换一个实现的能力中受益。

    (с)Scott Meyers,Effective C ++:55改进程序和设计的具体方法(第3版)

答案 1 :(得分:9)

不完全是您的问题的答案,但对消息调度与方法调用的一点关注:

术语消息指的是您不知道由于多态而将调用哪个方法。你要求一个对象做某事(因此术语消息)并且它会相应地起作用。方法调用一词具有误导性,因为它建议您选择一种精确的方法。

术语消息也更接近动态语言的实际情况,您可以实际发送对象不理解的消息(请参阅Smalltalk中的doesNotUnderstand)。如果没有匹配,那么你就不能真正谈论方法调用,并且消息调度将失败。在静态类型语言中,可以防止此问题。

答案 2 :(得分:8)

“传递信息”是一种抽象。

今天大多数OO语言都以特征调用的形式实现了这种抽象。按功能我的意思是一个方法一个操作(见下面的编辑),属性或类似的东西。 Bertrand Meyer在OOSC2中认为特征调用是现代OO语言中的基本计算单位;这是一种完全有效且连贯的方式来实现“对象通过消息传递进行通信”的旧抽象概念。

其他实现技术也是可能的。例如,由某些中间件系统管理的对象通过队列工具传递消息进行通信。

总结:不要将抽象与代码混淆。在过去,程序员过去常常关心理论,因为编程几乎不存在于主流职业中。今天,大多数程序员很少熟悉代码背后的理论。 : - )

顺便说一下,对于理论倾向,面向代理的建模和编程方法通常强调消息传递作为代理之间的通信机制,认为它源自speech act theory。这里不涉及方法调用。

修改即可。在大多数OO语言中,您调用操作而不是方法。它是运行时引擎,它决定调用哪个特定方法作为对您调用的操作的响应。这使得能够实现经常提到的多态性机制。当我们提到“调用方法”时,这种细微差别在常规用语中通常会被遗忘。但是,有必要区分操作(作为实现消息传递的功能)和方法(作为所述操作的特定版本)。

答案 3 :(得分:5)

它们指的是客户端可以在接收对象上调用方法并将数据传递给该对象,但该对象可以自主决定如何处理该数据,并根据需要维护自己的状态。

客户端对象无法直接操纵接收对象的状态。这是封装的一个优点 - 接收对象可以独立地强制执行自己的状态并更改其实现,而不会影响客户端与其交互的方式。

答案 4 :(得分:4)

在OOP中,对象不一定通过传递消息来相互通信。它们以某种方式相互通信,允许它们指定他们想要完成的任务,但将该行为的实现留给接收对象。传递消息是实现接口与实现分离的一种方式。另一种方法是在接收对象中调用(虚拟)方法。

关于哪个成员函数调用真的符合这些要求,在语言无关的基础上说它有点难。例如,在Java中,成员函数默认是虚拟的,因此您对a.methodA()b.methodB()的调用等同于传递消息。您的(尝试a)调用b.methodA()a.methodB()将无法编译,因为Java是静态类型的。

相反,在C ++中,默认情况下成员函数虚拟,因此您的调用都不等同于消息传递。要获得等效的东西,您需要将至少一个成员函数显式声明为virtual:

class A { 
    virtual void methodA() {}
};
然而,就目前而言,这基本上是“没有区别的区别”。要了解这意味着什么,您需要使用一些继承:

struct base { 
    void methodA() { std::cout << "base::methodA\n"; }
    virtual void methodB() { std::cout << "base::methodB\n"; }
};

struct derived { 
    void methodA() { std::cout << "derived::methodA\n"; }
    virtual void methodB() { std::cout << "derived::methodB"; }
};

int main() { 
    base1 *b1 = new base;
    base2 *b2 = new derived;

    b1->methodA();   // "base::methodA"
    b1->methodB();   // "base::methodB"
    b2->methodA();   // "base::methodA"
    b2->methodB();   // "derived::methodB"
    return 0;
}

答案 5 :(得分:1)

您发布的内容不会以任何oop语言进行编译,因为methodB不属于对象AmethodA不属于对象B。< / p>

如果您调用了正确的方法,则这两个方法都是按对象C传递的消息:

a.methodA();
b.methodB();

来自维基百科:

The process by which an object sends data to another object or asks the other object to invoke a method.

答案 6 :(得分:1)

您的示例不适用于Java或Python,因此我更正并注释了您的主要

class C{
  main()
  {
   A a=new A();
   B b=new B();
   a.methodA(); // C says to a that methodA should be executed
   // C says to b that methodB should be executed
   // and b says to C that the result is answer
   answer = b.methodB(); 
  }
}

答案 7 :(得分:1)

关于面向对象的一些早期学术工作是关于将对象传递给对方以调用行为的对象。一些早期的OO语言实际上就是这样编写的(SmallTalk?)。

像C ++,C#和Java这样的现代语言根本不会这样。他们只是在对象上有代码调用方法。这与过程语言完全相同,只是在调用中传递了对被调用类的隐藏引用(“this”)。

答案 8 :(得分:1)

将object作为参数传递给属于不同类类型的对象的方法。 这样,您可以将对象的属性传递给另一个类的另一个对象 只需调用另一个类的对象的方法。 所以你可以创建这个类的对象来获取不同类的其他对象的信息。 注意:  这没有覆盖方法,因为它们可以是相同的名称,但属于不同的类类型。 覆盖方法是你在子类中传承一个方法,并且你改变了为超类的继承所获得的相同方法的行为。 要调用的方法取决于您放入方法或数据类型的参数。系统调用正确的方法,它们可以位于超类的对象或子类的对象中。

很多人都问同样的问题。当他们使用OOP时。 我建议阅读那些旧书。 了解什么是OOP,而不是学习编程语言中面向对象的编程,如CPP,JAVA和PHP。 OOP简介(Timothy Buud) 面向对象的程序设计:一种进化的方法  (Brad J Cox.Andrew J Novobilski) 并且不要忘记阅读Bjarne stroustrup CPP新书。

video_1

答案 9 :(得分:-1)

该代码有效吗?

无论如何你不在路上......

消息传递是进程间通信的一种方式,是众多其他方式之一。这意味着两个(或更多)对象只能通过消息传递彼此说话,这应该说明是谁,谁,以及... ...

你可以看到它与共享内存非常不同,例如......