2012-02-13 45 views
1

我想创建一个工厂,在clojure中,创建者的参数数量在运行时会发生变化。在Clojure中,如何编写具有可变数量参数的工厂

例如:

(defn create-long-document [direction] 
    (str "long document " direction)) 

(defn create-short-document[] 
    "short document") 

(def creator-map { 
:english create-short-document 
:hebrew create-long-document 
}) 
(def additional-arg-map { 
:english nil 
:hebrew "rtl" 
}) 

(defn create-document [language] 
    (let [creator (language creator-map) arg (language additional-arg-map)] 
    (if arg (creator arg) (creator)))) 


(println (create-document :hebrew)); long document rtl 
(println (create-document :english)); short document 

我找改写的create-document身体的优雅方式。我想摆脱if。也许通过引入智能宏?

请分享你的想法。

回答

2

我建议指定的集合让您的附加参数:

(def additional-arg-map { 
    :english [] 
    :hebrew ["rtl"]}) 

然后你可以使用在创建文档功能apply,是这样的:

(defn create-document [language] 
    (let [creator (creator-map language) 
     args (additional-arg-map language)] 
    (apply creator args))) 

注意,替代(或者可能是互补的?)方法是定义一个变量函数,如果你想允许调用者提供特定的额外参数,例如喜欢的东西:

(defn create-document 
    ([language] 
    .....handle no argument.....) 
    ([language arg] 
    .....handle 1 argument.....) 
    ([language arg & more-args] 
    .....handle more than one argument.....)) 
+0

没有必要允许呼叫者提供特定的额外的参数。在我的情况下,所有额外的参数必须从调用者隐藏。 – viebel 2012-02-13 10:44:34

2

我猜multimethods会导致更干净的代码。使用相同的定义,create-long-documentcreate-short-document你写道:

; change identity to something more sophisticated 
; if you want sanity checks on input: 
(defmulti create-document identity) 

(defmethod create-document :default [lang] "language not supported") 

(defmethod create-document :english [lang] (create-short-document)) 

(defmethod create-document :hebrew [lang] (create-long-document "rtl")) 

然后,create-document将如预期:

user=> (create-document :hebrew) 
"long document rtl" 
user=> (create-document :english) 
"short document" 
user=> (create-document :italian) 
"language not supported" 

这样分派逻辑是由多方法API提供,而不需要你可以编写你自己的调度功能。这种方法的好处在于可扩展性:支持新语言只需要新的defmethod

+0

这种方法的问题是,如果'create-document'的代码变得更加丰富一些,则必须在multimethod的每个实例中复制代码。 – viebel 2012-02-13 18:06:45

+0

我敢肯定,你能够将常见部分分解为一些外部函数 – skuro 2012-02-13 19:00:16

1

我想这可能是简单的:

(defn create-long-document [direction] 
    (str "long document " direction)) 

(defn create-short-document[] 
    "short document") 

(def creator-map { 
:english #(create-short-document) 
:hebrew #(create-long-document "rtl") 
}) 

(defn create-document [language] 
    ((creator-map language)) 
) 
+0

这不是一个优雅的解决方案,因为创建者映射是**知道**它定义的函数被调用的方式。 creator-map和create document之间有一个耦合:例如,如果create-document想要放弃其他参数,则还需要修改creator-map。 – viebel 2012-02-13 18:04:50

+0

某处你需要有耦合。在你的代码中它是在create-document函数中。真实世界中没有任何代码根本没有“无耦合” – Ankur 2012-02-14 03:58:00

相关问题