2012-09-28 3 views
10

Wir verwenden in der Regel Erbauer in Java, wie folgt aus:Was ist der Clojure-Weg zum Builder-Muster?

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

Einige der Attribute Standardwert haben, und einige nicht.

Attribute in einer Karte Passing kann eine Lösung sein, aber es macht das Argument wirklich mehr:

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

Also, meine Frage ist, gibt es eine elegante Möglichkeit, dies zu erreichen?

+1

Der Erbauer ist meiner Meinung nach wirklich nur eine Arbeit um für einen Mangel an benannten Parametern. Das Initialisieren einer Gruppe von Feldern, die nur als Positionsargumente unterschieden werden, ist äußerst umständlich und sehr schwer später zu lesen, daher das Builder-Muster. Das Destrukturieren von Karten erreicht die gleichen Ziele in einem einzigen Funktionsaufruf, da Ankur vorschlägt, eine Karte auf mehrere Zeilen aufzuteilen, um die Lesbarkeit zu gewährleisten. –

Antwort

9

Wenn Sie etwas clojure Struktur bauen möchten, können Sie Destrukturierung Muster in Funktionsargumente verwenden. Sie werden dann das ähnliche erreichen, was Sie bereits geschrieben haben.

Ich bezweifle, dass Sie etwas in weniger Code als das tun können.

Wenn Sie ein Java-Objekt konstruieren möchten (mithilfe seiner Setter), können Sie den von Nicolas vorgeschlagenen Ansatz verwenden.

+2

Zur Vollständigkeit, um Ihre Standardwerte zu beantworten, defragmentieren Sie default-Werte mit 'or':' (defn make-user [& {: keys [name pass vip visible]: oder {vip true}}] ' – DanLebrero

+0

Ja, du ' Ich würde auch hinzufügen, dass, wenn der variable Standardwert nicht in ': or' festgelegt ist und er nicht im tatsächlichen Aufruf angegeben ist, wird er zu 'null' –

+1

Wie stellst du dir den Soor vor (' -> ') würde anstelle von' doto' arbeiten, da der Rückgabewert dieser Methodenaufrufe nicht 'this' ist? –

2

Eine einfache Möglichkeit ist es, die doto Makro zu verwenden:

Hier ein Beispiel wird ein Array-Liste mit einigen Werten zu füllen:

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

Stuart hat some perfect examples wie doto mit Schaukel zu verwenden. hier mit einem Panel:

(doto (JPanel.) 
      (.setOpaque true) 
      (.add label) 
      (.add button)) 

hier mit einem Rahmen:

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

Normalerweise würde ich Attribute über eine Map übergeben - es gibt kein echtes Problem, da die Attributzuordnung wirklich nur ein Argument für die make-user-Funktion ist. Sie können auch nette Dinge innerhalb von make-user tun, wie das Zusammenführen von Standardattributen.

Wenn Sie wirklich eine solche Karte mit einem Builder-Muster erstellen möchten, können Sie es mit einem Einfädeln Makro wie folgt vorgehen:

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

Rewrite

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

in mehrere Zeilen:

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

Der Vollständigkeit halber erwähnte niemand defrecord, die Sie „Builder-Funktionen“ gibt automatisch

(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} 
Verwandte Themen