使用私有构造函数用于工厂方法?

时间:2014-05-02 23:42:20

标签: java immutability private-constructor

让我们说我正在构建一个不可改变的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()方法)允许得分。但这是一种有效的工厂方法吗?

2 个答案:

答案 0 :(得分:3)

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

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;
    }
}

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

答案 1 :(得分:1)

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

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

  

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

     

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

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

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