使用跨多个名称空间定义的Clojure多方法

虽然下面的例子看起来有点奇怪,但是因为我试图减少一个我目前得到的一个相当大的问题.当他们坐在几个抽象层后面并且在多个名称空间中定义了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?

初步说明

很难说你是否在简化示例的过程中混淆了一些东西,或者它们是否完全不正确.举一个我所指的例子,考虑购买项目,尽管get-item-price的问题类似:

> defmulti调用是单参数函数
> defmethod调用每个都有两个参数
> buy-something中的调用将向量传递给purchase-item,但在向量中查找:supplier关键字将始终返回nil

你的顾虑

>

Every time I implement a new supplier’s API, I’ll need to change myapp.suppliers.api to :require that new supplier’s methods and recompile.

>如果您需要myapp.suppliers.interface命名空间myapp.suppliers.api,则可以避免此问题

>

This can’t work, because :supplier2 hasn’t been defined anywhere in this namespace.

>简单地说,这将有效. 🙂

>

Is there a more elegant way to write this code? In particular, in myapp.core, how can I buy-something from :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"

如您所见,供应商/ buy-something调用传播到适当的多方法实现.希望这有助于您到达目的地.

相关文章
相关标签/搜索