3

假设我正在构建一个不可变的 Yahtzee 记分卡类:

public final class Scorecard {

    private Map<Category, Integer> scorecard = new HashMap<Category, Integer>();

    public Scorecard() {
        // Instantiates a new empty scorecard
    }

    private Scorecard(Map<Category, Integer> scorecard) {
        this.scorecard = scorecard;
    }

    public Scorecard withScore(Category category, int[] roll) {
        newScorecard = new HashMap<Category, Integer>(scorecard); // Pretend that this is a deep-copy
        newScorecard.put(category, calculateScoreFromRoll(roll));
        return new Scorecard(newScorecard);
    }

    public int getScore(Category category) {
        return scorecard.get(category);
    }

}

基本上我不想暴露班级的内部。如果我没有私有构造函数,那么我将需要使用带有Map参数的公共构造函数,就像私有构造函数一样(而且我基本上也可能会丢失该withScore()方法)以允许评分。但这是一种有效的工厂方法吗?

4

2 回答 2

5

一个非常常见且良好的模式是拥有所有私有构造函数和公共静态工厂方法:

public class MyClass {
    private MyClass() {}
    public static MyClass fromA(A foo) {
        MyClass o = new MyClass();
        o.field = bar; // etc
        return o;
    }
    public static MyClass fromB(B foo) {
        MyClass o = new MyClass();
        o.field = bar; // etc
        return o;
    }
}

注意:这允许不同的工厂方法具有相同的参数类型,这是构造函数不允许的。

于 2014-05-02T23:49:59.657 回答
1

工厂方法旨在允许您在不指定确切类型的情况下获取对象。

例如,来自 Effective Java,第 2 版:

1.5 版中引入的类 java.util.EnumSet(第 32 项)没有公共构造函数,只有静态工厂。它们返回两种实现之一,具体取决于底层枚举类型的大小:如果它具有 64 个或更少的元素,就像大多数枚举类型一样,静态工厂返回一个 RegularEnumSet 实例,该实例由单个 long 支持;如果枚举类型有 65 个或更多元素,则工厂返回一个由长数组支持的 JumboEnumSet 实例。

这两个实现类的存在对客户端是不可见的。如果 RegularEnumSet 不再为小型枚举类型提供性能优势,那么它可以从未来的版本中删除而不会产生不良影响。同样,如果证明对性能有益,未来的版本可能会添加第三或第四个 EnumSet 实现。客户既不知道也不关心他们从工厂返回的对象的类别;他们只关心它是 EnumSet 的某个子类。

使用构造函数而不是您建议的静态方法会破坏工厂方法模式,因为通过直接使用构造函数,您正在指定一个实现。

在您的情况下,如果您想使用工厂方法,您可以将默认构造函数设为私有,以便客户端无法直接实例化ScoreCard. 此时,您可以自由使用ScoreCard工厂方法中的任何特定实现。例如,如果您创建了一个ScoreCard由 支持的第二个类TreeMap,您可以ScoreCard通过更改静态工厂来切换客户端获得的实现。

于 2014-05-02T23:50:48.790 回答