Jump to content

Двойная отправка

(Перенаправлено из Множественный полиморфизм )

В обеспечения разработке программного двойная диспетчеризация — это особая форма множественной диспетчеризации и механизм, который отправляет вызов функции различным конкретным функциям в зависимости от типов времени выполнения двух объектов, участвующих в вызове. В большинстве объектно-ориентированных систем конкретная функция, вызываемая из вызова функции в коде, зависит от динамического типа одного объекта, и поэтому они известны как вызовы одиночной диспетчеризации или просто вызовы виртуальных функций .

Дэн Ингаллс первым описал, как использовать двойную диспетчеризацию в Smalltalk , назвав это множественным полиморфизмом . [1]

Общая решаемая проблема заключается в том, как отправить сообщение различным методам в зависимости не только от получателя, но и от аргументов.

С этой целью такие системы, как CLOS, реализуют множественную диспетчеризацию . Двойная диспетчеризация — еще одно решение, которое постепенно уменьшает полиморфизм в системах, не поддерживающих множественную диспетчеризацию.

Варианты использования

[ редактировать ]

Двойная диспетчеризация полезна в ситуациях, когда выбор вычислений зависит от типов их аргументов во время выполнения. Например, программист может использовать двойную диспетчеризацию в следующих ситуациях:

  • Сортировка смешанного набора объектов: алгоритмы требуют, чтобы список объектов был отсортирован в некотором каноническом порядке. Решение о том, стоит ли один элемент перед другим, требует знания обоих типов и, возможно, некоторого подмножества полей.
  • Адаптивные алгоритмы столкновений обычно требуют, чтобы столкновения между разными объектами обрабатывались разными способами. Типичным примером является игровая среда, где столкновение космического корабля с астероидом рассчитывается иначе, чем столкновение космического корабля с космической станцией. [2]
  • Алгоритмы рисования , которые требуют, чтобы точки пересечения перекрывающихся спрайтов отображались другим способом.
  • Системы управления персоналом могут распределять разные типы работ разным сотрудникам. А schedule Алгоритм, которому дан объект человека, типизированный как бухгалтер, и объект работы, типизированный как инженерное дело, отклоняет планирование этого человека для этой работы.
  • Системы обработки событий , которые используют как тип события, так и тип объекта-рецептора для вызова правильной процедуры обработки событий.
  • Системы замков и ключей , в которых существует множество типов замков и множество типов ключей, и каждый тип ключа открывает несколько типов замков. Вам необходимо знать не только типы задействованных объектов, но и подмножество «информации о конкретном ключе, которая важна для определения того, открывает ли конкретный ключ конкретный замок», различается для разных типов замков.

Распространенная идиома

[ редактировать ]

Общая идиома, как и в примерах, представленных выше, заключается в том, что выбор соответствующего алгоритма основан на типах аргументов вызова во время выполнения. Таким образом, вызов сопряжен со всеми обычными дополнительными затратами на производительность, связанными с динамическим разрешением вызовов, обычно больше, чем в языке, поддерживающем отправку только одного метода. В C++ , например, вызов динамической функции обычно разрешается путем одного вычисления смещения , что возможно, поскольку компилятор знает расположение функции в таблице методов объекта и поэтому может статически вычислить смещение. В языке, поддерживающем двойную диспетчеризацию, это немного дороже, поскольку компилятор должен сгенерировать код для расчета смещения метода в таблице методов во время выполнения, тем самым увеличивая общую длину пути инструкции (на величину, которая, вероятно, будет не более общее количество вызовов функции, которое может быть не очень значительным).

Пример на Руби

[ редактировать ]

Распространенным вариантом использования является отображение объекта на порту дисплея, которым может быть экран, принтер или что-то еще, чего еще не существует. Это наивная реализация того, как обращаться с этими различными медиа.

class Rectangle
  def display_on(port)
    # selects the right code based on the object class
    case port
      when DisplayPort
        # code for displaying on DisplayPort
      when PrinterPort
        # code for displaying on PrinterPort
      when RemotePort
        # code for displaying on RemotePort
    end
  end
end

То же самое нужно было бы написать для Овала, Треугольника и любого другого объекта, который хочет отображать себя на носителе, и все это нужно было бы переписать, если бы нужно было создать новый тип порта. Проблема в том, что существует более одной степени полиморфизма: одна для отправки метода display_on объекту, а другая для выбора правильного кода (или метода) для отображения.

Гораздо более чистое и удобное в сопровождении решение — выполнить вторую отправку, на этот раз для выбора правильного метода отображения объекта на носителе:

class Rectangle
  def display_on(port)
    # second dispatch
    port.display_rectangle(self)
  end
end

class Oval
  def display_on(port)
    # second dispatch
    port.display_oval(self)
  end
end

class DisplayPort
  def display_rectangle(object)
    # code for displaying a rectangle on a DisplayPort
  end
  def display_oval(object)
    # code for displaying an oval on a DisplayPort
  end
  # ...
end

class PrinterPort
  def display_rectangle(object)
    # code for displaying a rectangle on a PrinterPort
  end
  def display_oval(object)
    # code for displaying an oval on a PrinterPort
  end
  # ...
end

Двойная отправка в C++

[ редактировать ]

На первый взгляд двойная диспетчеризация кажется естественным результатом перегрузки функций . Перегрузка функции позволяет вызываемой функции зависеть от типа аргумента. Однако перегрузка функции выполняется во время компиляции с использованием « искажения имен », когда внутреннее имя функции кодирует тип аргумента. Например, функция foo(int) может быть внутренне вызван __foo_i и функция foo(double) можно назвать __еда . Таким образом, не происходит конфликта имен и нет поиска в виртуальной таблице. Напротив, динамическая диспетчеризация основана на типе вызывающего объекта, то есть она использует виртуальные функции (переопределение) вместо перегрузки функций и приводит к поиску в виртуальной таблице. Рассмотрим следующий пример , написанный на C++ столкновений в игре :

class SpaceShip {};
class ApolloSpacecraft : public SpaceShip {};

class Asteroid {
public:
  virtual void CollideWith(SpaceShip&) {
    std::cout << "Asteroid hit a SpaceShip\n";
  }
  virtual void CollideWith(ApolloSpacecraft&) {
    std::cout << "Asteroid hit an ApolloSpacecraft\n";
  }
};

class ExplodingAsteroid : public Asteroid {
public:
  void CollideWith(SpaceShip&) override {
    std::cout << "ExplodingAsteroid hit a SpaceShip\n";
  }
  void CollideWith(ApolloSpacecraft&) override {
    std::cout << "ExplodingAsteroid hit an ApolloSpacecraft\n";
  }
};

Если у вас есть:

Asteroid theAsteroid;
SpaceShip theSpaceShip;
ApolloSpacecraft theApolloSpacecraft;

затем из-за перегрузки функции,

theAsteroid.CollideWith(theSpaceShip); 
theAsteroid.CollideWith(theApolloSpacecraft);

напечатает, соответственно, Asteroid hit a SpaceShip и Asteroid hit an ApolloSpacecraft, без использования какой-либо динамической отправки. Более того:

ExplodingAsteroid theExplodingAsteroid;
theExplodingAsteroid.CollideWith(theSpaceShip); 
theExplodingAsteroid.CollideWith(theApolloSpacecraft);

напечатаю ExplodingAsteroid hit a SpaceShip и ExplodingAsteroid hit an ApolloSpacecraft соответственно опять же без динамической отправки.

Со ссылкой на Asteroid, используется динамическая отправка, и этот код:

Asteroid& theAsteroidReference = theExplodingAsteroid;
theAsteroidReference.CollideWith(theSpaceShip); 
theAsteroidReference.CollideWith(theApolloSpacecraft);

принты ExplodingAsteroid hit a SpaceShip и ExplodingAsteroid hit an ApolloSpacecraft, опять же, как и ожидалось. Однако следующий код не работает должным образом:

SpaceShip& theSpaceShipReference = theApolloSpacecraft;
theAsteroid.CollideWith(theSpaceShipReference);
theAsteroidReference.CollideWith(theSpaceShipReference);

Желаемое поведение — связать эти вызовы с функцией, которая принимает theApolloSpacecraft в качестве аргумента, поскольку это конкретизированный тип переменной, а это означает, что ожидаемый результат будет Asteroid hit an ApolloSpacecraft и ExplodingAsteroid hit an ApolloSpacecraft. Однако на самом деле результат Asteroid hit a SpaceShip и ExplodingAsteroid hit a SpaceShip. Проблема в том, что, хотя виртуальные функции в C++ выполняются динамически, перегрузка функций выполняется статически.

Описанную выше проблему можно решить, моделируя двойную отправку, например, используя шаблон посетителя . Предположим, что существующий код расширен так, что оба SpaceShip и ApolloSpacecraft даны функция

virtual void CollideWith(Asteroid& inAsteroid) {
  inAsteroid.CollideWith(*this);
}

Затем, хотя предыдущий пример все еще работает неправильно, переформулирование вызовов так, чтобы космический корабль был агентом, дает нам желаемое поведение:

SpaceShip& theSpaceShipReference = theApolloSpacecraft;
Asteroid& theAsteroidReference = theExplodingAsteroid;
theSpaceShipReference.CollideWith(theAsteroid);
theSpaceShipReference.CollideWith(theAsteroidReference);

Он распечатывает Asteroid hit an ApolloSpacecraft и ExplodingAsteroid hit an ApolloSpacecraft, как и ожидалось. Ключ в том, что theSpaceShipReference.CollideWith(theAsteroidReference); во время выполнения выполняет следующее:

  1. theSpaceShipReference является ссылкой, поэтому C++ ищет правильный метод в vtable. В этом случае он вызовет ApolloSpacecraft::CollideWith(Asteroid&).
  2. В пределах ApolloSpacecraft::CollideWith(Asteroid&), inAsteroid это ссылка, поэтому inAsteroid.CollideWith(*this) приведет к еще одному поиску в виртуальной таблице . В этом случае, inAsteroid является ссылкой на ExplodingAsteroid так ExplodingAsteroid::CollideWith(ApolloSpacecraft&) будет вызван.

Двойная отправка в C#

[ редактировать ]

В C# при вызове метода экземпляра, принимающего аргумент, можно добиться множественной отправки без использования шаблона посетителя. Это делается с помощью традиционного полиморфизма и приведения аргумента к динамическому . [3] Связующее средство во время выполнения выберет соответствующую перегрузку метода во время выполнения. Это решение будет учитывать тип экземпляра объекта во время выполнения (полиморфизм), а также тип аргумента во время выполнения.

Двойная отправка в Эйфеле

[ редактировать ]

Язык программирования Eiffel может использовать концепцию агентов для решения проблемы двойной диспетчеризации. В приведенном ниже примере языковая конструкция агента применяется к проблеме двойной диспетчеризации.

Рассмотрим проблемную область с различными формами ФОРМЫ и ПОВЕРХНОСТЬЮ, на которой можно нарисовать ФОРМУ. И SHAPE, и SURFACE знают о функции draw в себе, но не друг в друге. Мы хотим, чтобы объекты двух типов ковариантно взаимодействовали друг с другом в двойной отправке с использованием шаблона посетителя.

Задача состоит в том, чтобы заставить полиморфную ПОВЕРХНОСТЬ нарисовать на себе полиморфную ФОРМУ.

В приведенном ниже примере вывода показаны результаты полиморфной передачи двух объектов-посетителей SURFACE по списку полиморфных объектов SHAPE. Шаблон кода посетителя учитывает только SHAPE и SURFACE в целом, но не конкретный их тип. Вместо этого код полагается на полиморфизм во время выполнения и механику агентов для достижения очень гибкой ковариантной связи между этими двумя отложенными классами и их потомками.

draw a red POLYGON on ETCHASKETCH
draw a red POLYGON on GRAFFITI_WALL
draw a grey RECTANGLE on ETCHASKETCH
draw a grey RECTANGLE on GRAFFITI_WALL
draw a green QUADRILATERAL on ETCHASKETCH
draw a green QUADRILATERAL on GRAFFITI_WALL
draw a blue PARALLELOGRAM on ETCHASKETCH
draw a blue PARALLELOGRAM on GRAFFITI_WALL
draw a yellow POLYGON on ETCHASKETCH
draw a yellow POLYGON on GRAFFITI_WALL
draw a purple RECTANGLE on ETCHASKETCH
draw a purple RECTANGLE on GRAFFITI_WALL

Настраивать

[ редактировать ]

Прежде чем рассматривать SHAPE или SURFACE, нам необходимо изучить высокоуровневое отдельное использование нашей двойной диспетчеризации.

Шаблон посетителя

[ редактировать ]

Шаблон посетителя работает посредством объекта посетителя, полиморфно посещающего элементы структуры данных (например, списка, дерева и т. д.), применяющего некоторое действие (вызов или агент) к объектам полиморфных элементов в посещаемой целевой структуре.

В нашем примере ниже мы составляем список полиморфных объектов SHAPE, посещая каждый из них с помощью полиморфной SURFACE и прося отрисовать SHAPE на SURFACE.

	make
			-- Print shapes on surfaces.
		local
			l_shapes: ARRAYED_LIST [SHAPE]
			l_surfaces: ARRAYED_LIST [SURFACE]
		do
			create l_shapes.make (6)
			l_shapes.extend (create {POLYGON}.make_with_color ("red"))
			l_shapes.extend (create {RECTANGLE}.make_with_color ("grey"))
			l_shapes.extend (create {QUADRILATERAL}.make_with_color ("green"))
			l_shapes.extend (create {PARALLELOGRAM}.make_with_color ("blue"))
			l_shapes.extend (create {POLYGON}.make_with_color ("yellow"))
			l_shapes.extend (create {RECTANGLE}.make_with_color ("purple"))

			create l_surfaces.make (2)
			l_surfaces.extend (create {ETCHASKETCH}.make)
			l_surfaces.extend (create {GRAFFITI_WALL}.make)

			across l_shapes as ic_shapes loop
				across l_surfaces as ic_surfaces loop
					ic_surfaces.item.drawing_agent (ic_shapes.item.drawing_data_agent)
				end
			end
		end

Начнем с создания коллекции объектов SHAPE и SURFACE. Затем мы перебираем один из списков (SHAPE), позволяя элементам другого (SURFACE) посещать каждый из них по очереди. В приведенном выше примере кода объекты SURFACE посещают объекты SHAPE.

Код выполняет полиморфный вызов {SURFACE}.draw косвенно через `drawing_agent', который является первым вызовом (отправкой) шаблона двойной отправки. Он передает косвенный и полиморфный агент («drawing_data_agent»), позволяя нашему коду посетителя знать только о двух вещах:

  • Что такое агент рисования поверхности (например, al_surface.drawing_agent в строке № 21)?
  • Что такое агент данных рисования фигуры (например, al_shape.drawing_data_agent в строке № 21)?

Поскольку и SURFACE, и SHAPE определяют своих собственных агентов, наш код посетителя освобождается от необходимости знать, какой вызов следует сделать, полиморфный или иной. Этот уровень косвенности и разделения просто недостижим в других распространенных языках, таких как C, C++ и Java, за исключением той или иной формы отражения или перегрузки функций с сопоставлением сигнатур.

ПОВЕРХНОСТЬ

[ редактировать ]

Внутри полиморфного вызова {SURFACE}.draw находится вызов агента, который становится вторым полиморфным вызовом или отправкой в ​​шаблоне двойной отправки.

	deferred class
		SURFACE
	
	feature {NONE} -- Initialization
	
		make
				-- Initialize Current.
			do
				drawing_agent := agent draw
			end
	
	feature -- Access

		drawing_agent: PROCEDURE [ANY, TUPLE [STRING, STRING]]
				-- Drawing agent of Current.
	
	feature {NONE} -- Implementation
	
		draw (a_data_agent: FUNCTION [ANY, TUPLE, TUPLE [name, color: STRING]])
				-- Draw `a_shape' on Current.
			local
				l_result: TUPLE [name, color: STRING]
			do
				l_result := a_data_agent (Void)
				print ("draw a " + l_result.color + " " + l_result.name + " on " + type + "%N")
			end
	
		type: STRING
				-- Type name of Current.
			deferred end
	
	end

Аргумент агента в строке № 19 и вызов в строке № 24 являются полиморфными и несвязанными. Агент отделен, поскольку функция {SURFACE}.draw не знает, на каком классе основан `a_data_agent'. Невозможно определить, от какого класса произошел агент операции, поэтому он не обязательно происходит от SHAPE или одного из его потомков. Это явное преимущество агентов Eiffel перед одинарным наследованием, динамическим и полиморфным связыванием других языков.

Агент является динамически полиморфным во время выполнения, поскольку объект создается в тот момент, когда он необходим, динамически, причем в этот момент определяется версия объективированной процедуры. Единственным строго связанным знанием является тип Result сигнатуры агента, то есть именованный TUPLE с двумя элементами. Однако это конкретное требование основано на требовании включающей функции (например, строка № 25 использует именованные элементы TUPLE для выполнения функции «рисования» SURFACE), которая необходима и ее нельзя избежать (и, возможно, невозможно). .

Наконец, обратите внимание, что в ЛЮБОЙ клиент экспортируется только функция «drawing_agent»! Это означает, что код шаблона посетителя (который является ЕДИНСТВЕННЫМ клиентом этого класса) должен знать об агенте только для выполнения своей работы (например, использование агента в качестве функции, применяемой к посещаемым объектам).

Класс SHAPE имеет основу (например, данные чертежа) для того, что рисуется, возможно, на ПОВЕРХНОСТИ, но это не обязательно. Опять же, агенты обеспечивают косвенность и независимость от классов, необходимые для того, чтобы сделать ковариантные отношения с SHAPE максимально отделенными.

Кроме того, обратите внимание на тот факт, что SHAPE предоставляет «drawing_data_agent» только как полностью экспортируемую функцию для любого клиента. Таким образом, единственный способ взаимодействия с SHAPE, кроме создания, — это использование средств draw_data_agent, который используется ЛЮБЫМ клиентом для косвенного и полиморфного сбора данных чертежа для SHAPE!

	deferred class
		SHAPE
	
	feature {NONE} -- Initialization
	
		make_with_color (a_color: like color)
				-- Make with `a_color' as `color'.
			do
				color := a_color
				drawing_data_agent := agent drawing_data
			ensure
				color_set: color.same_string (a_color)
			end

	feature -- Access
	
		drawing_data_agent: FUNCTION [ANY, TUPLE, like drawing_data]
				-- Data agent for drawing.
	
	feature {NONE} -- Implementation
	
		drawing_data: TUPLE [name: like name; color: like color]
				-- Data needed for drawing of Current.
			do
				Result := [name, color]
			end
	
		name: STRING
				-- Object name of Current.
			deferred end
	
		color: STRING
				-- Color of Current.

	end

Классический пример космического корабля

[ редактировать ]

В вариации классического примера космического корабля один или несколько объектов космического корабля бродят по вселенной, наполненной другими объектами, такими как неконтролируемые астероиды и космические станции. Нам нужен метод двойной диспетчеризации для обработки встреч (например, возможных столкновений) между двумя ковариантными объектами в нашей воображаемой вселенной. В нашем примере ниже выходное отклонение наших USS Enterprise и USS Excelsior будет:

Starship Enterprise changes position from A-001 to A-002.
Starship Enterprise takes evasive action, avoiding Asteroid `Rogue 1'!
Starship Enterprise changes position from A-002 to A-003.
Starship Enterprise takes evasive action, avoiding Asteroid `Rogue 2'!
Starship Enterprise beams a science team to Starship Excelsior as they pass!
Starship Enterprise changes position from A-003 to A-004.
Starship Excelsior changes position from A-003 to A-005.
Starship Enterprise takes evasive action, avoiding Asteroid `Rogue 3'!
Starship Excelsior is near Space Station Deep Space 9 and is dockable.
Starship Enterprise changes position from A-004 to A-005.
Starship Enterprise beams a science team to Starship Excelsior as they pass!
Starship Enterprise is near Space Station Deep Space 9 and is dockable.

Посетитель

[ редактировать ]

Посетитель классического примера космического корабля также имеет механизм двойной отправки.

make
		-- Allow SPACESHIP objects to visit and move about in a universe.
	local
		l_universe: ARRAYED_LIST [SPACE_OBJECT]
		l_enterprise,
		l_excelsior: SPACESHIP
	do
		create l_enterprise.make_with_name ("Enterprise", "A-001")
		create l_excelsior.make_with_name ("Excelsior", "A-003")
		create l_universe.make (0)
		l_universe.force (l_enterprise)
		l_universe.force (create {ASTEROID}.make_with_name ("Rogue 1", "A-002"))
		l_universe.force (create {ASTEROID}.make_with_name ("Rogue 2", "A-003"))
		l_universe.force (l_excelsior)
		l_universe.force (create {ASTEROID}.make_with_name ("Rogue 3", "A-004"))
		l_universe.force (create {SPACESTATION}.make_with_name ("Deep Space 9", "A-005"))
		visit (l_enterprise, l_universe)
		l_enterprise.set_position ("A-002")
		visit (l_enterprise, l_universe)
		l_enterprise.set_position ("A-003")
		visit (l_enterprise, l_universe)
		l_enterprise.set_position ("A-004")
		l_excelsior.set_position ("A-005")
		visit (l_enterprise, l_universe)
		visit (l_excelsior, l_universe)
		l_enterprise.set_position ("A-005")
		visit (l_enterprise, l_universe)
	end
feature {NONE} -- Implementation
visit (a_object: SPACE_OBJECT; a_universe: ARRAYED_LIST [SPACE_OBJECT])
		-- `a_object' visits `a_universe'.
	do
		across a_universe as ic_universe loop
			check attached {SPACE_OBJECT} ic_universe.item as al_universe_object then
				a_object.encounter_agent.call ([al_universe_object.sensor_data_agent])
			end
		end
	end

Двойную отправку можно увидеть в строке № 35, где два косвенных агента работают вместе, чтобы обеспечить два ковариантных вызова, работающих в идеальном полиморфном согласовании друг с другом. «a_object» функции «visit» имеет «encounter_agent», который вызывается с данными датчика «sensor_data_agent», поступающими из «al_universe_object». Другая интересная часть этого конкретного примера — класс SPACE_OBJECT и его функция «встречи»:

Действия посетителя

[ редактировать ]

Единственными экспортируемыми функциями SPACE_OBJECT являются агенты для обнаружения и данные датчиков, а также возможность устанавливать новую позицию. Когда один объект (космический корабль) посещает каждый объект во вселенной, данные датчиков собираются и передаются посещающему объекту в его агенте встречи. Там данные датчика из Sensor_data_agent (то есть элементы данных Sensor_data TUPLE, возвращаемые запросом Sensor_data_agent) сравниваются с текущим объектом, и на основе этой оценки принимается порядок действий (см. SPACE_OBJECT ниже). Все остальные данные экспортируются в {NONE}. Это похоже на области C, C++ и Java Private. Поскольку данные и процедуры не экспортируются, данные и процедуры используются только внутри каждого SPACE_OBJECT. Наконец, обратите внимание, что вызовы print не содержат конкретной информации о возможных классах-потомках SPACE_OBJECT! Единственное, что можно найти на этом уровне наследования, — это общие реляционные аспекты, полностью основанные на том, что можно узнать из атрибутов и подпрограмм общего SPACE_OBJECT. Тот факт, что результаты «отпечатка» имеют смысл для нас, людей, исходя из того, что мы знаем или представляем себе о звездных кораблях, космических станциях и астероидах, является просто логическим планированием или совпадением. SPACE_OBJECT не запрограммирован с учетом каких-либо конкретных знаний о его потомках.

deferred class
SPACE_OBJECT
feature {NONE} -- Initialization
make_with_name (a_name: like name; a_position: like position)
    -- Initialize Current with `a_name' and `a_position'.
  do
    name := a_name
    position := a_position
    sensor_data_agent := agent sensor_data
    encounter_agent := agent encounter
  ensure
    name_set: name.same_string (a_name)
    position_set: position.same_string (a_position)
  end
feature -- Access
encounter_agent: PROCEDURE [ANY, TUPLE]
    -- Agent for managing encounters with Current.
sensor_data_agent: FUNCTION [ANY, TUPLE, attached like sensor_data_anchor]
    -- Agent for returning sensor data of Current.
feature -- Settings
set_position (a_position: like position)
    -- Set `position' with `a_position'.
  do
    print (type + " " + name + " changes position from " + position + " to " + a_position + ".%N")
    position := a_position
  ensure
    position_set: position.same_string (a_position)
  end
feature {NONE} -- Implementation
encounter (a_sensor_agent: FUNCTION [ANY, TUPLE, attached like sensor_data_anchor])
    -- Detect and report on collision status of Current with `a_radar_agent'.
  do
    a_sensor_agent.call ([Void])
    check attached {like sensor_data_anchor} a_sensor_agent.last_result as al_sensor_data then
      if not name.same_string (al_sensor_data.name) then
        if (position.same_string (al_sensor_data.position)) then
          if ((al_sensor_data.is_dockable and is_dockable) and
              (is_manned and al_sensor_data.is_manned) and
              (is_manueverable and al_sensor_data.is_not_manueverable)) then
            print (type + " " + name + " is near " + al_sensor_data.type + " " +
                al_sensor_data.name + " and is dockable.%N")
          elseif ((is_dockable and al_sensor_data.is_dockable) and
                (is_manned and al_sensor_data.is_manned) and
                (is_manueverable and al_sensor_data.is_manueverable)) then
            print (type + " " + name + " beams a science team to " + al_sensor_data.type + " " +
                al_sensor_data.name + " as they pass!%N")
          elseif (is_manned and al_sensor_data.is_not_manned) then
            print (type + " " + name + " takes evasive action, avoiding " +
                al_sensor_data.type + " `" + al_sensor_data.name + "'!%N")
          end
        end
      end
    end
  end
name: STRING
    -- Name of Current.
type: STRING
    -- Type of Current.
  deferred
  end
position: STRING
    -- Position of Current.
is_dockable: BOOLEAN
    -- Is Current dockable with another manned object?
  deferred
  end
is_manned: BOOLEAN
    -- Is Current a manned object?
  deferred
  end
is_manueverable: BOOLEAN
    -- Is Current capable of being moved?
  deferred
  end
sensor_data: attached like sensor_data_anchor
    -- Sensor data of Current.
  do
      Result := [name, type, position, is_dockable, not is_dockable, is_manned, not is_manned, is_manueverable, not is_manueverable]
    end

  sensor_data_anchor: detachable TUPLE [name, type, position: STRING; is_dockable, is_not_dockable, is_manned, is_not_manned, is_manueverable, is_not_manueverable: BOOLEAN]
      -- Sensor data type anchor of Current.

end

Существует три класса-потомка SPACE_OBJECT:

SPACE_OBJECT
ASTEROID
SPACESHIP
SPACESTATION

В нашем примере класс ASTEROID используется для предметов «Разбойник», SPACESHIP для двух звездных кораблей и SPACESTATION для Deep Space Nine. В каждом классе единственной специализацией является настройка признака «тип» и определенных свойств объекта. «Имя» указывается в процедуре создания, а также «позиция». Например: Ниже приведен пример КОСМИЧЕСКОГО КОРАБЛЯ.

class
SPACESHIP
inherit
SPACE_OBJECT
create
make_with_name
feature {NONE} -- Implementation
type: STRING = "Starship"
  -- <Precursor>
is_dockable: BOOLEAN = True
  -- <Precursor>
is_manned: BOOLEAN = True
  -- <Precursor>
is_manueverable: BOOLEAN = True
  -- <Precursor>
end

Итак, любой КОСМИЧЕСКИЙ КОРАБЛЬ в нашей Вселенной может стыковаться, пилотироваться и маневрировать. Другие объекты, такие как астероиды, не относятся ни к чему из этого. С другой стороны, космическая станция может быть как пристыкованной, так и пилотируемой, но не маневренной. Таким образом, когда один объект сталкивается с другим, он сначала проверяет, помещают ли они их в непосредственной близости друг от друга, и если да, то объекты взаимодействуют на основе своих основных свойств. Обратите внимание, что объекты с одинаковым типом и именем считаются одним и тем же объектом, поэтому взаимодействие логически запрещено.

Заключение по примеру Эйфеля

[ редактировать ]

Что касается двойной диспетчеризации, Eiffel позволяет дизайнеру и программисту дополнительно удалить уровень прямого знания объекта-объекта, отделив подпрограммы классов от их классов, сделав их агентами, а затем передав этих агентов вместо создания прямых функций объекта. звонки. Агенты также имеют определенные подписи и возможные результаты (в случае запросов), что делает их идеальными средствами статической проверки типов , не отказываясь от конкретных деталей объекта. Агенты полностью полиморфны, поэтому результирующий код содержит только те конкретные знания, которые необходимы для выполнения локальной работы. В противном случае не будет дополнительной нагрузки на обслуживание, поскольку знание конкретных внутренних функций класса будет распределено по множеству ковариантных объектов. Это обеспечивается использованием и механикой агентов. Одним из возможных недостатков использования агентов является то, что агент требует больше вычислительных затрат, чем его аналог с прямым вызовом. Имея это в виду, никогда не следует предполагать использование агентов в двойной рассылке и их применение в шаблонах посещений. Если можно ясно увидеть ограничение дизайна в отношении области типов классов, которые будут участвовать в ковариантных взаимодействиях, то прямой вызов является более эффективным решением с точки зрения вычислительных затрат. Однако если ожидается, что классовая область участвующих типов будет существенно расти или отличаться, то агенты представляют собой отличное решение для уменьшения нагрузки на обслуживание в схеме двойной отправки.

См. также

[ редактировать ]
  1. ^ Простой метод обработки множественного полиморфизма. В Proceedings of OOPSLA '86, «Системы объектно-ориентированного программирования, языки и приложения», страницы 347–349, ноябрь 1986 г. Напечатано как уведомления SIGPLAN, 21 (11). ISBN   0-89791-204-7
  2. ^ Более эффективный C++ Скотта Мейерса (Аддисон-Уэсли, 1996).
  3. ^ «Использование динамического типа (Руководство по программированию на C#)» . Сеть разработчиков Microsoft . Майкрософт. 30 сентября 2009 г. Проверено 25 мая 2016 г. . ... Разрешение перегрузки происходит во время выполнения, а не во время компиляции, если один или несколько аргументов в вызове метода имеют тип динамический...
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: 6b7bf6def69d6b97e1e011d3b6c5db44__1659979860
URL1:https://arc.ask3.ru/arc/aa/6b/44/6b7bf6def69d6b97e1e011d3b6c5db44.html
Заголовок, (Title) документа по адресу, URL1:
Double dispatch - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)