【问题标题】:How exactly do "Objects communicate with each other by passing messages"?“对象通过传递消息相互通信”究竟是如何实现的?
【发布时间】:2010-02-27 16:06:25
【问题描述】:

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

来自维基百科,“在 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..
    }
}

【问题讨论】:

  • 好吧,你不能调用a.methodB(),因为a是A的一个实例,而A类只定义了methodA()。所以这不会用我知道的任何语言编译。
  • 哦,我没有意识到这是一个错误。
  • 请在类名后打开类括号。在同一行。像这样:C类{}

标签: language-agnostic oop


【解决方案1】:

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

  1. 对象是面向对象系统的基本单元。
  2. 对象有自己的状态
  3. 对象通过发送和接收消息进行通信。

如果您对 Smalltalk 感兴趣,请查看 PharoSqueak

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

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

无论如何,无论您的编程语言使用哪种形式来进行两个对象之间的通信(消息传递或方法调用),在 Smalltalk 术语中禁止直接访问实例变量或 C++ 术语中的数据成员或您的编程语言中使用的任何术语。

Smalltalk 在语法级别直接禁止访问实例变量。正如我上面提到的 Smalltalk 程序中的对象只能通过传递/接收消息进行交互。许多其他语言允许在语法级别访问实例变量,但这被认为是一种不好的做法。例如,著名的Effective C++ 书中包含相应的推荐:Item 22: Declare data members private。

原因是:

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

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

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

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

【讨论】:

  • 在 c++,java,C# 中,调用方法本身就意味着消息传递。
  • @Zaki:我编辑了我的答案以澄清一些观点。希望这会有所帮助。
【解决方案2】:

不完全是您问题的答案,而是关于消息调度与方法调用的一点题外话

message 一词指的是由于多态性,你不知道哪个 方法会被调用。你要求一个对象做某事(因此称为message),它就会相应地行动。术语方法调用具有误导性,因为它建议您选择一种确切的方法。

message 一词也更接近动态语言的现实,您实际上可以发送对象无法理解的消息(参见 Smalltalk 中的 doesNotUnderstand)。鉴于没有匹配项,您就不能真正谈论方法调用,并且消息调度将失败。在静态类型语言中,这个问题是可以避免的。

【讨论】:

    【解决方案3】:

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

    当今大多数 OO 语言都以功能调用的形式实现该抽象。我所说的功能是指一种方法一种操作(见下面的编辑)、属性或类似的东西。 OOSC2 中的 Bertrand Meyer 认为特征调用是现代 OO 语言中的基本计算单元。这是实现“对象通过消息传递进行通信”这一古老抽象概念的一种完全有效且连贯的方式。

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

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

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

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

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

      • 原则上同意,虽然在日常使用中我会说“消息传递”一词有更具体的含义,即它在 Smalltalk 中的使用方式。另见en.wikipedia.org/wiki/Message_passing
      • “客户端可以调用接收对象上的方法,并将数据传递给该对象..”将数据作为传递参数传递给该方法,方法定义决定如何处理传递的数据。这是正确的吗?
      【解决方案5】:

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

      至于您的哪些成员函数调用真正符合这些要求,在与语言无关的基础上说起来有点困难。举个例子,在 Java 中,成员函数默认是虚拟的,所以你对 a.methodA()b.methodB() 的调用相当于传递一条消息。您(尝试)对b.methodA()a.methodB() 的调用无法编译,因为Java 是静态类型的。

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

      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;
      }
      

      【讨论】:

        【解决方案6】:

        您发布的内容不会以任何 oop 语言编译,因为 methodB 不属于对象 AmethodA 不属于对象 B

        如果你调用了正确的方法,那么这两个都是对象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.

        【讨论】:

        • 而b没有methodA()! =)
        【解决方案7】:

        早期关于 OO 的一些学术工作是关于对象相互传递消息以调用行为的。一些早期的 OO 语言实际上是这样编写的(SmallTalk?)。

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

        【讨论】:

          【解决方案8】:

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

          很多人都会问同样的问题。当他们使用 OOP 时。 我建议阅读那些旧书。 了解什么是 OOP,而不是学习使用 CPP、JAVA 和 PHP 等编程语言编写面向对象的编程。 OOP 简介(蒂莫西·布德) 面向对象编程:一种进化方法 (布拉德·J·考克斯。安德鲁·J·诺沃比尔斯基) 不要忘记阅读 Bjarne stroustrup CPP 的新书。

          #include <iostream>
          #include <string>
          using namespace std;
          
          class Car{
          string brand;   
          public:
          void setBrand(string newBrand){this->brand=newBrand;}
          void Driver(){cout<<" IS DRIVING THIS CAR BRAND "<<brand<<endl;}
          void Brake(){cout<<"IS BRAKING"<<endl;}
          };
          
          class Person{
          private:string name;
          public:
          void setName(string newName){this->name=newName;}
          //HERE WE CALL METHOD OF CAR CLASS AND REDEFINE METHODS NO OVERRIDE OK   
          void Driver(Car objectOfClassCar){cout<<this->name<<ends;  
          objectOfClassCar.Driver();}
           void Brake(string str, Car objectOfClassCar){cout<<this->name<<"  
          "<<str<<ends;objectOfClassCar.Brake();}
            };
          
            int main(){
            Car corolla;
            corolla.setBrand("TOYOTA");   
            Person student;
            student.setName("MIGUEL");
            student.Driver(corolla);
            student.Brake("CAR",corolla);
            //it open a lot of opportunities to do the same.
            }
          

          【讨论】:

            【解决方案9】:

            您的示例不适用于 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(); 
              }
            }
            

            【讨论】:

              【解决方案10】:

              该代码有效吗?

              不管怎样,你已经不在路上了......

              消息传递是进程间通信的一种方式,其中之一。这意味着两个(或更多)对象只能通过消息相互交谈,这应该说从谁,对谁,以及什么......

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

              【讨论】:

              • 我还看到其他文本在谈论 OO 时使用了“消息传递”一词,尽管我从未在这里或从其他程序员那里听说过。我认为这是 OO 时代早期使用的术语。
              • 没有。消息传递是一般的 OO 实践。看看 Smalltalk。
              • OP 正在尝试理解 OOP。
              • 对。我从未听说过 OO 编程中的消息传递!
              • 根据我的研究,“消息传递”是指 Smalltalk 和 Objective-C(其设计深受 Smalltalk 影响)的观点。在这些语言中,对象通过以下语法发送消息:[myObject aMessage:aParameter];这与(例如在 Java 中)是同义词: myObject.aMessage(aParameter);在 Objective-C 中你是在“发送消息”,而在 Java 中你是在“调用和实例方法”
              猜你喜欢
              • 2011-12-28
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-11-08
              • 2020-04-04
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多