45

我是 Clojure 新手,正在寻找一些关于何时使用协议以及何时使用多方法的具体示例。我知道协议通常旨在创建类型层次结构和典型的 OOP 事物,它们是在多方法之后添加到语言中的,并且协议通常具有更好的性能,所以我的问题是:

协议是否旨在取代多方法?如果没有,你能给我一个例子,我会使用多方法而不是协议吗?

4

4 回答 4

37

协议和多方法是互补的,适用于略有不同的用例。

  • 协议根据第一个参数的类型提供有效的多态调度。因为能够利用一些非常有效的 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!")))

请注意,由于以下两个原因,您不能在此处使用协议:

  • 分派函数需要使用这两个参数来确定使用哪个方法实现(即它是一个多分派案例)。
  • 您也无法区分第一个参数的类型(大概总是一个数值)
于 2011-11-10T03:14:32.733 回答
35

多方法更强大,更昂贵,

在足够的情况下使用协议,但如果您需要根据从火星看到的月相进行调度,那么多方法是您的最佳选择。

协议的存在是为了让简单的东西保持简单,并为 clojure 提供一种生成与等效 java 相同的字节码的方法。似乎大多数人大部分时间都使用协议。当我需要调度多个参数时,我会使用多方法,尽管我不得不承认这只出现过一次,而且完整isa的层次结构使用得更少(我)。所以简而言之,在需要时使用 Multimethods

在我的经验中,最好的例子是在一开始,在core.clj

于 2011-11-09T19:24:02.707 回答
6

正如 Arthur 所提到的,多方法更强大且更昂贵。事实上,协议可以被认为是多种方法的一个特例,其中 dispatch 函数是 class. 当然,情况并非如此,因为协议不止于此。

如果您需要调度除第一个参数的类以外的其他内容,则需要使用多方法或重新设计。对类型进行调度是协议的一个很好的用例。

于 2011-11-09T22:00:29.067 回答
3

当您不需要类层次结构时,我喜欢多方法。例如,如果您有一个媒体数据库并且您的记录是这样的,{: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 世界应该留下的祸根。多方法只是函数,对我来说感觉更像是clojuresque。

于 2015-01-13T16:20:41.863 回答