为什么要为记录定义工厂函数?

3
在swannodette的clojurescript教程中(https://github.com/swannodette/lt-cljs-tutorial/blob/master/lt-cljs-tutorial.cljs),提到: 定义一个工厂函数,该函数返回defrecord/deftype所创建的实例是惯用的(也被推荐)。使用dash-case作为工厂名称是惯用的。 例如:
(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
2个回答

1
作为防止在使用库时更改实现的保护措施。就是这样吗?
我想就是这样。
如果您的记录仅供内部使用,只需使用提供的构造函数可能是最直接的方法。也许您已经使用映射进行了原型设计,并且已经意识到出于某种原因需要记录。因此,->Recordmap->Record可能会很好用。
但是,当您为他人提供API时,它应该尽可能稳定。您不希望使消费者感到惊讶,并要求他们执行大规模的重构。至少,通过提供自定义构造函数,您可以产生明确的弃用警告。
我通常认为->Recordmap->Record构造函数是实现细节,并将其隐藏起来。
简而言之:当您自己编写代码时,情况就完全不同了,当您编写可能被数千个其他人使用的库时,情况就另当别论了。

1
Java允许使用不同签名的多个构造函数,比如说:
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的字段向量不匹配的签名构建对象,则需要一个包装器函数来设置initial-state的默认值。

如果defrecord的字段向量发生更改,则工厂方法将为您提供未来保障,以免您不得不更改代码中所有(->Foo state)的出现。


你会得到两个构造函数:->Recordmap->Record - muhuk
我不知道那个map->构造函数。那非常酷。我不认为它会减损我的回答,但如果你想用除了nil之外的默认值初始化对象,你仍然需要一个“工厂”函数。 - clumsyjedi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接