2015-05-26 2 views
3

В swannodettes clojurescript учебника (https://github.com/swannodette/lt-cljs-tutorial/blob/master/lt-cljs-tutorial.cljs), он утверждал, что:Зачем определять заводскую функцию для записей?

Это считается идиоматическим (и рекомендуется), чтобы определить функцию фабрики, которая возвращает созданный экземпляр defrecord/deftype. Идиоматично использовать тире для названий фабрик.

В примере:

(defn person [first last] 
    (->Person first last)) 

Почему?

Единственное, что я могу думать, если вы используете один набор параметров и они не соответствуют реализации, либо как преобразование:

(defn person [full-name] 
    (->Person (first (split full-name)) ...)) 

Или охранником против изменения осуществления когда используется как библиотека.

Это он?

Недостатки были бы лишними, ненужными функциями, которые должны быть обновлены в параллели с реализациями и, возможно, немного более нечеткими именами.

Мне не нравится шаблонный код, поэтому я всегда разочарован, когда даю такие рекомендации без объяснений.

+3

http://stuartsierra.com/2015/05/17/clojure-record-constructors – ClojureMostly

ответ

1

Java позволяет несколько конструкторов с разными сигнатурами IE

public class Foo implements Bar { 
    private final Boolean initialState; 
    public Foo() { this.initialState = false; }  
    public Foo (Boolean initialState) { this.initialState = initialState; } 
    public void sayState() {System.out.println(this.initialState)} 
} 

Clojure конструкторы не могут быть настроены таким образом. В основном вы создаете для вас один созданный конструктор, основываясь на векторе полей, указанном в defrecord.

(defrecord Foo [initial-state] Bar (sayState [this] (println initial-state))) 

Так что, если вы хотите построить объект на основе подписи, который не совпадает с полем вектором defrecord вам понадобится обертка Fn, чтобы установить значение по умолчанию начального состояния.

И если изменится вектор полей defrecord, фабричный метод будет в будущем доказательством того, что вам не придется менять все вхождения (->Foo state) в ваш код.

+0

Вы получаете два конструктора; '-> Record' &' map-> Record'. – muhuk

+0

Я не знал об этом map-> constructor. Это круто. Я не думаю, что это отвлекает от моего ответа, хотя, если вы хотите инициализировать объекты по умолчанию, кроме nil, вам все равно понадобится «фабрика» fn. – clumsyjedi

1

Или как защита от изменения реализации при использовании в качестве библиотеки.

Это он?

Я думаю, что это все.

Если ваши записи предназначены для внутреннего использования, использование одного из поставляемых конструкторов может быть самым простым делом. Возможно, вы прототипировали карты и поняли, что вам нужны записи по какой-то причине. Так что (возможно) нет возврата, ->Record & map->Record будет в порядке.

Но когда вы предоставляете API для других, он должен быть как стабильным, насколько это возможно. Вы не хотите удивлять потребителей и им приходится выполнять большие рефакторинги.По крайней мере, предоставляя настраиваемый конструктор, вы можете создавать четкие предупреждения об отказе.

Я обычно считаю ->Record & map->Record детали реализации конструкторов и скрыть их.

TL; Это совсем другая история, когда вы пишете код самостоятельно, для себя и другую историю, когда вы пишете библиотеку, которая потенциально может быть использована тысячей других.

Смежные вопросы