为什么要为记录定义工厂功能?

时间:2015-05-26 11:20:24

标签: clojurescript

在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)) ... ))

或作为防止在用作库时更改实现。

是吗?

缺点是额外的,不必要的功能,需要与实现并行更新,并且可能稍微不清楚名称。

我不喜欢样板代码,所以在没有解释的情况下给出这样的建议时我总是很沮丧。

2 个答案:

答案 0 :(得分: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)的所有出现次数。

答案 1 :(得分:1)

  

或作为防止在用作库时更改实现。

     是吗?

我认为就是这样。

如果您的记录仅供内部使用,那么只使用其中一个提供的构造函数就可以做到最直接。也许您已经使用地图进行了原型设计,并且由于某种原因意识到您需要记录。所以(可能)没有回头,->Record& map->Record没问题。

但是当您为其他人提供API时,它应该尽可能稳定。你不想让消费者感到惊讶并让他们进行大规模的重构。至少,通过提供自定义构造函数,您可以生成明确的弃用警告。

我通常会考虑->Record& map->Record构造器实现细节并隐藏它们。

TL; DR:当您编写一个可能被其他一千个人使用的库时,您自己编写代码,为自己和另一个故事编写代码,这是一个不同的故事。