Какой способ 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
Шаблон построителя, на мой взгляд, на самом деле просто обходной путь из-за отсутствия именованных параметров. Инициализация набора полей, отличающихся только как позиционные аргументы, чрезвычайно громоздка и очень трудна для чтения позже, отсюда и шаблон построителя. Деструктуризация карты достигает тех же целей в одном вызове функции, поскольку Анкур предлагает разделить карту на несколько строк, чтобы сохранить удобочитаемость.   -  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 и не указано в фактическом вызове, оно становится 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)))

У Стюарта есть отличные примеры использования 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