虽然下面的例子看起来有点奇怪,但是因为我试图减少一个相当大的问题,目前我已经得到了一个很小的例子。当他们坐在几个抽象层后面并且在多个名称空间中定义了defmulti和相应的defmethods时,我正在努力研究如何调用多方法。我真的觉得我在这里遗漏了一些明显的东西......
假设我有以下情况:
使用Clojure,实现通用接口的推荐方法是通过协议或多方法。在这种情况下,当我根据供应商的价值进行切换时,我认为处理下面描述的情况的最佳方法是通过多方法(但我可能是错的)。
我的多方法定义看起来像这样,它定义了一个我想用来与每个供应商的API交流的通用接口:
(ns myapp.suppliers.interface)
(defmulti purchase-item :supplier)
(defmulti get-item-price :supplier)
对于每个供应商,我可能想要这样的东西:
(ns myapp.suppliers.supplier1
(:require [myapp.suppliers.interface :as supplier-api]))
(defmethod purchase-item :supplier1 [item quantity] ...)
(defmethod get-item-price :supplier1 [item] ...)
和
(ns myapp.suppliers.supplier2
(:require [myapp.suppliers.interface :as supplier-api]))
(defmethod purchase-item :supplier2 [item quantity] ...)
(defmethod get-item-price :supplier2 [item] ...)
到目前为止,没问题
现在我的代码调用这些抽象的方法,我假设它们看起来像:
(ns myapp.suppliers.api
(:require [myapp.suppliers.supplier1 :as supplier1]
[myapp.suppliers.supplier2 :as supplier2])
(defn buy-something
[supplier item quantity]
(purchase-item [supplier item quantity])
(defn price-something
[supplier item]
(get-item-price [supplier item])
这开始看起来有点......丑陋。每次我实施新供应商的API时,我都需要将myapp.suppliers.api更改为:要求新供应商的方法并重新编译。
现在我在上一级工作,我想从supplier2购买一个小部件。
(ns myapp.core
(:require [myapp.suppliers.api :as supplier])
(def buy-widget-from-supplier2
(buy-something :supplier2 widget 1)
这不起作用,因为:在此命名空间的任何位置都没有定义supplier2。
是否有更优雅的方式来编写此代码?特别是在myapp.core中,我如何从以下方面购买东西:supplier2?
答案 0 :(得分:10)
很难判断你是否在简化示例的过程中混淆了一些东西,或者它们是否完全没有出现在门外。有关我所指的内容的示例,请考虑purchase-item
,尽管get-item-price
的问题类似:
defmulti
调用是单参数函数defmethod
个调用各有两个参数buy-something
中的来电将向量传递给purchase-item
,但在向量中查找:supplier
关键字将始终返回nil
每次实施新供应商的API时,我都需要将myapp.suppliers.api更改为:要求新供应商的方法并重新编译。
myapp.suppliers.interface
命名空间myapp.suppliers.api
,则可以避免此问题这不起作用,因为:在此命名空间的任何位置都没有定义supplier2。
是否有更优雅的方式来编写此代码?特别是在myapp.core中,我如何从以下方面购买东西:supplier2?
如果不偏离原始设计太远,这里有一个完整的例子,说明我如何解释你想要实现的目标:
myapp.suppliers.interface
(ns myapp.suppliers.interface)
(defmulti purchase-item (fn [supplier item quantity] supplier))
myapp.suppliers.supplier1
(ns myapp.suppliers.supplier1
(:require [myapp.suppliers.interface :as supplier-api]))
(defmethod supplier-api/purchase-item :supplier1 [supplier item quantity]
(format "Purchasing %dx %s from %s" quantity (str item) (str supplier)))
myapp.suppliers.supplier2
(ns myapp.suppliers.supplier2
(:require [myapp.suppliers.interface :as supplier-api]))
(defmethod supplier-api/purchase-item :supplier2 [supplier item quantity]
(format "Purchasing %dx %s from %s" quantity (str item) (str supplier)))
myapp.suppliers.api
(ns myapp.suppliers.api
(:require [myapp.suppliers.interface :as interface]))
(defn buy-something [supplier item quantity]
(interface/purchase-item supplier item quantity))
myapp.core
(ns myapp.core
(:require [myapp.suppliers.api :as supplier]))
(def widget {:id 1234 :name "Monchkin"})
(supplier/buy-something :supplier1 widget 15)
;;=> "Purchasing 15x {:id 1234, :name \"Monchkin\"} from :supplier1"
(supplier/buy-something :supplier2 widget 3)
;;=> "Purchasing 3x {:id 1234, :name \"Monchkin\"} from :supplier2"
如您所见,supplier/buy-something
调用传播到适当的多方法实现。希望这有助于您到达目的地。