【问题标题】:Clojure multimethods vs. protocolsClojure 多方法与协议
【发布时间】:2011-12-25 14:28:05
【问题描述】:

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

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

【问题讨论】:

    标签: clojure multimethod


    【解决方案1】:

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

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

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

    • 调度函数需要使用这两个参数来确定使用哪个方法实现(即它是一个多调度案例)。
    • 您也无法区分第一个参数的类型(大概总是一个数值)

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        【解决方案3】:

        多方法更强大,也更昂贵,

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

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

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

        【讨论】:

        • 协议迫使你放弃很大程度的动态性。它们在重新加载时会在开发中引起非常混乱的行为,并将您锁定为仅在第一个参数的类型上进行调度,这使得您很难更改您的代码。协议显然并不简单。它们是一种挂钩 JVM 低级调度的方法,以获得自托管所需的速度。多方法很简单——它们只是带有调度函数的变量。
        • 另一方面,如果你是 Clojure 新手并且你认为你需要多态性,那么你很可能错了。在 Clojure 中,真正需要定义新的多态功能是非常罕见的。
        • @technomancy,你能详细说明一下吗?对于习惯于以某种方式思考和工作的 clojure 新手来说,如果没有多态性,似乎很难完成某些任务。你能想出任何例子来说明多态性的惯用 clojure 吗?
        【解决方案4】:

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-05-29
          • 1970-01-01
          • 2011-03-17
          • 2011-05-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多