Какой способ clojure для построения шаблона?

Обычно мы используем шаблон builder в 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}))

Итак, мой вопрос: есть ли элегантный способ достичь этого?

Ответ 1

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

Ответ 2

Я бы обычно передавал атрибуты через карту - нет реальной проблемы с этим, так как карта атрибутов на самом деле является всего лишь одним аргументом функции make-user. Вы также можете делать приятные вещи внутри make-user, например merge в атрибутах по умолчанию.

Если вы действительно хотите построить такую ​​карту с шаблоном построителя, вы можете сделать это с помощью макроса с потоком следующим образом:

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

Ответ 3

Переписать

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

на несколько строк:

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

Ответ 4

Простым способом является использование макроса doto:

Вот пример, чтобы заполнить список массивов некоторыми значениями:

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

Стюарт некоторые прекрасные примеры о том, как использовать doto с Swing. Здесь с панелью:

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

Здесь с рамкой:

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

Ответ 5

Для полноты, никто не упомянул defrecord, который автоматически дает вам функции "builder"

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