Какъв е моделът на clojure за изграждане?

Обикновено използваме модел на строител в java, като този:

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}))

И така, въпросът ми е има ли елегантен начин да се постигне това?


person qiuxiafei    schedule 28.09.2012    source източник
comment
По мое мнение моделът на конструктора наистина е просто заобиколен за липсата на именувани параметри. Инициализирането на набор от полета, разграничени само като позиционни аргументи, е изключително тромаво и много трудно за четене по-късно, оттук и моделът на строителя. Деструктурирането на картата постига същите цели в едно извикване на функция, тъй като Ankur предлага да се раздели карта на няколко реда, за да се поддържа четимост.   -  person Alex Stoddard    schedule 28.09.2012


Отговори (5)


Ако искате да конструирате някаква 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 обект (използвайки неговите настройки), можете да използвате подхода, предложен от Николас.

person Vladimir Matveev    schedule 28.09.2012
comment
За пълнота, отговаряйки за вашите стойности по подразбиране, деструктуриране на поддържаните стойности по подразбиране с or: (defn make-user [& {:keys [name pass vip visible] :or {vip true}}] - person DanLebrero; 28.09.2012
comment
Да, напълно си прав. Бих добавил също, че ако стойността по подразбиране на променливата не е зададена в :or map и не е посочена в действителното извикване, тя става nil. - person Vladimir Matveev; 28.09.2012
comment
Как си представяте, че млечницата (->) ще работи на мястото на doto, тъй като върнатата стойност на тези извиквания на метода не е this? - person Marko Topolnik; 01.10.2012
comment
Да ти си прав. Някак си не го бях запомнил, когато писах, а също така (очевидно погрешно) си помислих, че съм видял някъде за взаимозаменяемостта на тези макроси. Премахна това известие. - person Vladimir Matveev; 01.10.2012

Обикновено бих предал атрибути чрез карта - няма реален проблем с това, тъй като картата на атрибутите всъщност е само един аргумент към функцията make-user. Можете също така да правите хубави неща в make-user, като сливане в атрибути по подразбиране.

Ако наистина искате да конструирате такава карта с шаблон за изграждане, можете да го направите с макрос за нишки, както следва:

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

нова редакция

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

на няколко реда:

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

Лесен начин е да използвате doto макрос:

Ето пример за попълване на списък с масиви с някои стойности:

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

Stuart има някои идеални примери за това как да използвате doto със Swing. Тук с панел:

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

Тук с рамка:

(doto (JFrame. "Counter App")
  (.setContentPane panel)
  (.setSize 300 100)
  (.setVisible true))
person Nicolas Modrzyk    schedule 28.09.2012

За пълнота, никой не спомена 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}
person Scott    schedule 09.06.2016