Clojure мультиметоды против протоколов - PullRequest
43 голосов
/ 09 ноября 2011

Я новичок в Clojure и искал несколько конкретных примеров того, когда использовать протоколы, а когда использовать мультиметоды. Я знаю, что протоколы обычно ориентированы на создание иерархии типов и типичных ООП, что они были добавлены в язык после мультиметодов, и что протоколы обычно имеют лучшую производительность, поэтому мой вопрос таков:

Предназначены ли протоколы для замены мультиметодов? Если нет, не могли бы вы привести пример, где я бы использовал мультиметоды вместо протоколов?

Ответы [ 4 ]

35 голосов
/ 10 ноября 2011

Протокол и мультиметоды дополняют друг друга и предназначены для немного разных вариантов использования.

  • Протоколы обеспечивают эффективную полиморфную диспетчеризацию на основе типа первого аргумента. Поскольку протокол способен использовать некоторые очень эффективные функции JVM, протоколы дают вам наилучшую производительность .
  • Мультиметоды обеспечивают очень гибкий полиморфизм, который может распределяться на основе любой функции аргументов метода. Мультиметоды медленнее, но очень гибкие

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

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

(defn balance-available? [amount balance] (> balance amount))

(defmulti withdraw balance-available?)

(defmethod withdraw true [amount balance] 
  (- balance amount))

(defmethod withdraw false [amount balance] 
  (throw (Error. "Insufficient balance available!")))

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

  • Функция диспетчеризации должна использовать оба аргумента, чтобы определить, какую реализацию метода использовать (т. Е. Это случай множественной диспетчеризации).
  • Вы также не можете различить тип первого аргумента (который, по-видимому, всегда является числовым значением)
34 голосов
/ 09 ноября 2011

Мультиметоды более мощные и более дорогие,

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

Существуют протоколы, позволяющие простым вещам оставаться простыми и позволяющие clojure генерировать почти тот же байт-код, что и эквивалентный Java. Кажется, что большинство людей используют протоколы большую часть времени. Я использую мультиметоды, когда мне нужно отправить более одного аргумента, хотя я должен признать, что это произошло только один раз, а полные иерархии isa используются еще реже (мной). Короче говоря используйте мультиметоды, когда они вам нужны

лучший пример По моему опыту в самом начале, в core.clj

6 голосов
/ 10 ноября 2011

Как уже упоминал Артур, мультиметоды более мощные и более дорогие.Действительно, протоколы можно рассматривать как особый случай муллиметодов, где функция диспетчеризации равна class.Конечно, это не совсем так, поскольку протоколы - это нечто большее.

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

1 голос
/ 13 января 2015

Мне нравятся мультиметоды, когда вам не нужна иерархия классов. Например, если у вас есть база данных мультимедиа и ваши записи похожи на {:media-type :video, :bytes ...}, тогда вы можете использовать мультиметод

(defmulti make-grayscale :media-type)

Тогда вы можете сделать различные

; in video.clj
(defmethod make-grayscale :video [record]
  (ffmpeg ... (:bytes record))

; in photo.clj
(defmethod make-grayscale :photo [record]
  (imagemagick ... (:bytes record))

Таким образом, вы можете избежать центрального выражения cond, поэтому вы получите модульность классов. Но вам не нужно проходить через весь этот шаблон «иерархии классов-оболочек», что для меня является проклятием, которое следует оставить для мира Java. Мультиметоды - это всего лишь функций и они кажутся мне более сомнительными.

...