2012-09-28 26 views
10

我们通常在Java中使用生成器模式,就像这样:什么是构建模式的clojure方式?

UserBuilder userBuilder = new UserBuilder(); 
User John = userBuiler.setName("John") 
         .setPassword("1234") 
         .isVip(true) 
         .visableByPublic(false) 
         .build(); 

的一些属性有默认值,而有些则没有。

传递属性的地图可能是一个解决方案,但它使论点真的长:

(def john (make-user {:name "John" :pass "1234" :vip true :visible false})) 

所以,我的问题是,是否有一个优雅的方式来实现这一目标?

+1

Builder模式在我看来真的只是因为缺少命名参数的解决办法。初始化一组只能作为位置参数的字段非常麻烦,而且以后很难阅读,因此构建器模式。在一次函数调用中,地图解构实现了相同的目标,Ankur建议将地图分割成多行以保持可读性。 –

回答

9

如果你想建立一些Clojure的结构,你可以在函数参数使用解构模式。然后,你会实现你已经写过的类似的东西。

(defn make-user [& {:keys [name pass vip visible]}] 
    ; Here name, pass, vip and visible are regular variables 
    ; Do what you want with them 
) 

(def user (make-user :name "Name" :pass "Pass" :vip false :visible true)) 

我怀疑你可以用比这少的代码做点什么。

如果你想构建Java对象(使用它的setters),你可以使用Nicolas建议的方法。

+2

为了完整性,回答您的默认值,解构支持使用'or'设置默认值:'(defn make-user [&{:keys [name pass vip visible]:or {vip true}}]' – DanLebrero

+0

是的,如果变量的缺省值没有在':or'映射中设置,并且在实际调用中没有指定,它将变为'nil'。 –

+1

你如何想象鹅口疮(' - > '')可以代替'doto',因为这些方法调用的返回值不是'this'? –

2

一个简单的方法是使用doto宏:

下面是一个例子来填充一些值的数组列表:

(def al (doto (java.util.ArrayList.) (.add 11) (.add 3)(.add 7))) 

斯图尔特对如何使用多托与Swing some perfect examples。 这里有一个小组:

(doto (JPanel.) 
      (.setOpaque true) 
      (.add label) 
      (.add button)) 
有框架

这里:

(doto (JFrame. "Counter App") 
    (.setContentPane panel) 
    (.setSize 300 100) 
    (.setVisible true)) 
4

我通常会通过地图传递属性 - 这样做并没有真正的问题,因为属性映射实际上只是make-user函数的一个参数。你也可以在默认属性中进行合并等好东西。

如果你真的想构建一个生成器模式这样的地图,你可以用一个线程宏观如下做到这一点:

(def john 
    (-> {} 
    (assoc :name "John") 
    (assoc :pass "1234") 
    (assoc :vip true) 
    (assoc :visible false) 
    make-user)) 
4

重写

(def john (make-user {:name "John" :pass "1234" :vip true :visible false}))

成多行:

(def john (make-user {:name "John" 
         :pass "1234" 
         :vip true 
         :visible false})) 
0

为了完整,没有人提到defrecord,让你“建设者功能”自动

(defrecord User [name pass vip visible]) 

(User. "John" "1234" true false) 
;;=>#user.User{:name "John", :pass "1234", :vip true, :visible false} 

(->User "John" "1234" true false) 
;;=>#user.User{:name "John", :pass "1234", :vip true, :visible false} 

(map->User {:name "John" :pass "1234" :vip true :visible false}) 
;;=>#user.User{:name "John", :pass "1234", :vip true, :visible false} 
相关问题