В нескольких вводных текстах на Объектно-ориентированном программировании я столкнулся с вышеупомянутым оператором.
Из Википедии, "В ООП, каждый объект способен к получению сообщений, обработке данных и отправке сообщений к другим объектам и может быть просмотрен как независимая 'машина' с отличной ролью или ответственностью".
Что точно оператор означает в коде?
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..
}
}
Если мы говорим об ООП, то термин «передача сообщений» взят из Smalltalk . Вкратце основные принципы Smalltalk следующие:
Если вас интересует Smalltalk, посмотрите Pharo или Squeak .
Java / C # / C ++ и многие другие языки используют несколько иной подход, вероятно, унаследованный от Simula . Вы вызываете метод вместо передачи сообщения.
Я думаю, что эти термины более или менее эквивалентны. Возможно, единственное интересное отличие состоит в том, что передача сообщений (по крайней мере, в Smalltalk) всегда зависит от динамической отправки и позднего связывания, тогда как в случае вызова метода можно также использовать статическую отправку и раннее связывание. Например, C ++ (AFAIK) по умолчанию выполняет раннее связывание до тех пор, пока где-то не появится ключевое слово virtual ...
В любом случае, независимо от того, какой формализм ваш язык программирования использует для связи между двумя объектами (передача сообщений или вызов метода), он всегда считается хорошим стилем ООП, запрещающим прямой доступ к переменным экземпляра в терминологии Smalltalk или членам данных в терминологии C ++ или к любому другому термину, используемому в вашем языке программирования.
Smalltalk прямо запрещает доступ к переменным экземпляра на уровне синтаксиса. Как я уже упоминал выше, объекты в программе Smalltalk могут взаимодействовать только путем передачи / получения сообщений.Многие другие языки разрешают доступ к переменным экземпляра на уровне синтаксиса, но это считается плохой практикой. Например, известная книга Эффективный C ++ содержит соответствующую рекомендацию: Правило 22: Объявите элементы данных закрытыми.
Причины:
Последний - самый важный. В этом суть инкапсуляции - сокрытие информации на уровне класса.
Дело об инкапсуляции более важно, чем может показаться на первый взгляд. Если вы скрываете элементы данных от клиентов (т. Е. Инкапсулируете их), вы можете гарантировать, что инварианты классов всегда поддерживаются, потому что только функции-члены могут влиять на них. Кроме того, вы оставляете за собой право изменить свое решение о реализации позже. Если вы не скрываете такие решения, вы скоро обнаружите, что даже если вы владеете исходным кодом класса, ваша способность изменять что-либо публичное чрезвычайно ограничена, потому что слишком много клиентского кода будет сломано. Public означает неинкапсулированный, и практически говоря, неинкапсулированный означает неизменный, особенно для широко используемых классов.Тем не менее, широко используемые классы больше всего нуждаются в инкапсуляции, потому что именно они могут получить наибольшую выгоду от возможности замены одной реализации на лучшую.
(с) Скотт Мейерс, Эффективный C ++: 55 конкретных способов улучшения ваших программ и дизайнов (3-е издание)
В ООП объекты не обязательно взаимодействуют друг с другом посредством передачи сообщений. Они общаются друг с другом некоторым способом, который позволяет им указать, что они хотят сделать, но оставляет реализацию этого поведения принимающему объекту. Передача сообщения - это один из способов отделения интерфейса от реализации. Другой способ - вызвать (виртуальный) метод в принимающем объекте.
Что касается вызовов функций-членов, которые действительно соответствуют этим требованиям, трудно сказать с точки зрения языковой независимости. Например, в Java функции-члены по умолчанию являются виртуальными, поэтому ваши вызовы a.methodA ()
и b.methodB ()
будут эквивалентны передаче сообщения. Ваши (пытается a) вызовы 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;
}
Они имеют в виду тот факт, что клиент может вызывать метод на принимающем объекте и передавать данные этому объекту, но этот объект может автономно решать, что делать с этими данными, и поддерживать свое собственное состояние как обязательный.
Клиентский объект не может напрямую управлять состоянием принимающего объекта. Это преимущество инкапсуляции - принимающий объект может независимо применять свое состояние и изменять свою реализацию, не влияя на то, как клиенты взаимодействуют с ним.
То, что вы опубликовали, не будет компилироваться ни на одном языке oop, поскольку methodB
не принадлежит объекту A
и methodA
не принадлежит объекту B
.
Если вы вызвали правильный метод, то оба они являются передачей сообщения объектом C
:
a.methodA();
b.methodB();
Из википедии:
Процесс, посредством которого объект отправляет данные другому объекту или запрашивает другой объект для вызова метода.
Ваш пример не будет работать с Java или Python, поэтому я исправил и аннотировал ваш main
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();
}
}
Не совсем ответ на ваш вопрос, но небольшое отступление о отправке сообщений и вызове метода :
Термин сообщение относится к тому факту, что вы этого не сделаете. знать , какой метод будет вызван из-за полиморфизма. Вы просите объект сделать что-то (отсюда термин сообщение ), и он действует соответственно. Термин вызов метода вводит в заблуждение, поскольку предлагает вам выбрать один точный метод.
Термин сообщение также ближе к реальности динамического языка, где вы могли бы фактически отправить сообщение, которое объект не понимает (см. doesNotUnderstand
в Smalltalk). Тогда вы можете не говорить о вызове метода, учитывая, что совпадений нет, и отправка сообщения завершится ошибкой. В языке со статической типизацией эта проблема устранена.
Некоторые из ранних академических работ по ОО были связаны с передачей объектами сообщений друг другу для вызова поведения. Некоторые ранние языки ОО были написаны именно так (SmallTalk?).
Современные языки, такие как C++, C# и Java, вообще не работают таким образом. В них код просто вызывает методы на объектах. Это в точности похоже на процедурный язык, за исключением того, что в вызове передается скрытая ссылка на вызываемый класс ("this
").
"Передача сообщения" - это абстракция.
Большинство OO-языков сегодня реализуют эту абстракцию в форме вызова функции. Под функцией я подразумеваю метод, операцию (см. правку ниже), свойство или что-то подобное. Бертран Мейер в OOSC2 утверждает, что вызов функции является основной единицей вычислений в современных ОО-языках; это совершенно правильный и последовательный способ реализации старой абстрактной идеи, что "объекты общаются посредством передачи сообщений".
Возможны и другие методы реализации. Например, объекты, управляемые некоторыми системами промежуточного программного обеспечения, взаимодействуют путем передачи сообщений через очередь.
В заключение: не путайте абстракции с кодом. В былые времена программистов очень заботила теория, потому что программирование едва ли существовало как основная профессия. Сегодня большинство программистов редко знакомы с теорией, стоящей за кодом. :-)
Кстати, для склонных к теории, агент-ориентированные подходы к моделированию и программированию часто подчеркивают передачу сообщений как механизм коммуникации между агентами, утверждая, что это вытекает из теории речевых актов. Здесь не происходит вызова метода.
Редактировать. В большинстве ОО-языков вы вызываете операции, а не методы. Именно механизм выполнения решает, какой конкретный метод вызвать в ответ на вызванную вами операцию. Это позволяет реализовать так часто упоминаемые механизмы полиморфизма. Этот маленький нюанс обычно забывается в обыденной речи, когда мы говорим о "вызове метода". Однако он необходим для того, чтобы различать операции (как функции, реализующие передачу сообщений) и методы (как конкретные версии этих операций).
Этот код работает?
Как бы то ни было, вы в стороне ...
Передача сообщений - это способ межпроцессного взаимодействия, один из многих других. Это означает, что два (или более) объекта могут общаться друг с другом только посредством обмена сообщениями, в которых должно быть указано, кто, кому и что ...
Вы можете видеть, что это сильно отличается от общей памяти, например ...