Многократная отправка: концептуальная необходимость?
Интересно, следует ли включать в объектно-ориентированный язык концепцию множественной диспетчеризации (то есть встроенную поддержку, как будто динамическая диспетчеризация виртуальных методов распространяется и на аргументы метода), если ее влияние на производительность будет незначителен.
проблема
Рассмотрим следующий сценарий: у меня есть - не обязательно плоская - иерархия классов, содержащая типы животных. В разных местах моего кода я хочу выполнить некоторые действия над объектом-животным. Мне все равно, и я не могу контролировать, как эта ссылка на объект получается. Я мог бы столкнуться с этим, просматривая список животных, или это могло бы быть дано мне как один из аргументов метода. Действие, которое я хочу выполнить, должно быть специализированным в зависимости от типа времени выполнения данного животного. Примеры таких действий:
- Создайте модель представления для животного, чтобы представить его в графическом интерфейсе.
- Создайте объект данных (для последующего хранения в БД), представляющий этот тип животных.
- Кормите животное небольшим количеством пищи, но давайте разные виды пищи в зависимости от типа животного (что для него более полезно)
Все эти примеры работают с публичным API объекта животного, но то, что они делают, не является собственным делом животного и поэтому не может быть включено в само животное.
Решения
Одним из "решений" было бы выполнить проверку типов. Но этот подход подвержен ошибкам и использует отражающие функции, что (на мой взгляд) почти всегда свидетельствует о плохом дизайне. Типы должны быть концепцией только во время компиляции.
Другим решением будет "злоупотребление" (своего рода) шаблоном посетителей, чтобы имитировать двойную рассылку. Но это потребовало бы, чтобы я изменил своих животных, чтобы принять посетителя.
Я уверен, что есть и другие подходы. Также необходимо решить проблему расширения: если новые виды животных присоединяются к вечеринке, сколько мест кода нужно адаптировать и как я могу их надежно найти?
Вопрос
Итак, в свете этих требований, не должна ли множественная диспетчеризация быть неотъемлемой частью какого-либо хорошо разработанного объектно-ориентированного языка?
Разве не естественно сделать внешние (а не только внутренние) действия зависимыми от динамического типа данного объекта?
С наилучшими пожеланиями!
3 ответа
Вы предлагаете динамическую диспетчеризацию на основе имени метода / подписи в сочетании с фактическими типами аргументов во время выполнения. Я думаю, что ты сумасшедший.
Итак, в свете этих требований, не должна ли множественная диспетчеризация быть неотъемлемой частью какого-либо хорошо разработанного объектно-ориентированного языка?
То, что существуют проблемы, из-за которых наличие стратегии диспетчеризации, которую вы планируете, упростит кодирование, является слабым аргументом в пользу того, что такая диспетчеризация встроена в любой данный язык, а тем более в каждый язык OO.
Разве не естественно сделать внешние (а не только внутренние) действия зависимыми от динамического типа данного объекта?
Возможно, но не все, что кажется "естественным", на самом деле хорошая идея. Например, одежда не натуральная, но посмотрите, что произойдет, если вы попытаетесь обойтись без публики (во всяком случае, где-нибудь, кроме Беркли).
Некоторые языки уже имеют статическую диспетчеризацию, основанную на типах аргументов, условно называемую "перегрузкой". С другой стороны, динамическая диспетчеризация, основанная на типах аргументов, представляет собой настоящий беспорядок, если нужно рассмотреть более одного аргумента, и она не может не быть медленной (er). Сегодняшние популярные ОО-языки позволяют вам выполнять двойную диспетчеризацию там, где это необходимо, без необходимости поддерживать ее в подавляющем большинстве мест, где вы этого не хотите.
Кроме того, хотя реализация двойной диспетчеризации действительно создает проблемы обслуживания, возникающие из-за тесной связи между отдельными компонентами, существуют стратегии кодирования, которые могут помочь сохранить ее управляемость. В любом случае неясно, в какой степени наличие множественной диспетчеризации, основанной на аргументах, встроенной в конкретный язык, действительно помогло бы решить эту проблему.
Одним из "решений" было бы выполнить проверку типов. Но этот подход подвержен ошибкам и использует отражающие функции, что (на мой взгляд) почти всегда свидетельствует о плохом дизайне. Типы должны быть концепцией только во время компиляции.
Ты не прав. Все виды использования виртуальных функций, виртуального наследования и тому подобного включают отражающие функции и динамические типы. Способность откладывать печать до необходимого времени выполнения является абсолютно критической и присуща даже самой базовой формулировке ситуации, в которой вы находитесь, что в буквальном смысле не может даже возникнуть без использования динамических типов. Вы даже описываете свою проблему как желание делать разные вещи в зависимости от.. динамического типа. В конце концов, если нет динамической типизации, зачем вам нужно делать что-то по-другому? Вы уже знаете конкретный конечный тип.
Конечно, небольшая типизация во время выполнения может справиться с проблемой, с которой вы столкнулись во время выполнения.
Просто создайте словарь / хэш-таблицу от типа к функции. Вы можете добавлять записи в эту структуру динамически для любых динамически связанных производных типов, это хороший O(1) для просмотра и не требует внутренней поддержки.
Если кто-то ограничивается ситуацией, когда знание того, как объект типа X должен вызывать затруднения у объекта типа Y, должно храниться либо в классе X, либо в классе Y, можно иметь базовый тип Y, включая метод, который принимает ссылку на Базовый тип X и указывает, насколько объект знает о том, как быть обделенным объектом, идентифицированным этой ссылкой, а также метод, который запрашивает Y
иметь X
обижай это.
Сделав это, можно X
"s Fnorble(Y)
Метод начать с вопроса Y
сколько он знает о том, чтобы быть обделенным определенным типом X
, Если Y
знает больше о X
чем X
знает о Y
, затем X
"s Fnorble(Y)
метод должен вызывать Y BeFnorbledBy(X)
Способ; в противном случае X
должен облагородить Y
как умеет.
В зависимости от того, сколько разных видов X
а также Y
имеются, Y
мог определить BeFnorbledBy
перегружает методы для разных видов X
, что когда X
звонки target.BeFnorbledBy(this)
это автоматически отправило бы к подходящему методу; такой подход, однако, потребует каждого Y
знать о каждом типе X
это было "интересно" для всех, независимо от того, имел ли он какой-либо интерес к этому конкретному типу.
Обратите внимание, что этот подход не учитывает ситуацию, когда может существовать внешний объект класса Z, который знает вещи о том, как X должен обозначать Y, что ни X, ни Y не знают напрямую. С такими ситуациями лучше всего справиться, если иметь объект "книги правил", в котором все, что знает о том, как различные типы X должны обозначать различные типы Y, могут сообщать книге правил, а код, который хочет, чтобы X обозначал Y, может попросить книгу правил: сделать это. Хотя языки могут оказать помощь в тех случаях, когда книги правил являются одиночными, иногда могут быть полезны несколько книг правил. Семантика в этих случаях, вероятно, лучше всего обрабатывается путем непосредственного использования кода в книгах правил.