2014-05-02 73 views
2

比方说,我要建一个不变的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

这听起来像你在说什么更多的是一种生成器比一个工厂,但没错,这是一个很好的方法。 – chrylis

+0

@chrylis你会说工厂方法不应该建立在现有的实例上吗? –

+0

我无法理解这个问题。 – chrylis

回答

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

注:这使得不同厂家的方法具有相同的参数类型,构造不允许。

+0

这种模式的一个可能的问题是它不允许子类化。如果你不想让它放在第一位,请将课程设置为“final”来表示。如果你想允许它,使用一个受保护的构造函数,并考虑设置该类为“抽象”,如果它会导致子分类。 – user1803551

+0

@ user1803551不幸的是,最终类会干扰大量的AOP和其他基于反射的技术(如果一个方法返回'String',则会导致一个Spring特性中断),所以它不像五年前那样容易推荐。 – chrylis

+0

@chrylis我不知道这一点。然而,非final方法中的私有构造函数允许内部类为其父类继承。我认为这是推荐'final'的唯一案例? – user1803551

1

工厂方法的目的是让你得到一个对象,而无需指定确切的类型。

例如,从有效的Java,第二版:

类java.util.EnumSet中(第32条),在1.5版推出了,有没有公共构造,只有静态工厂。它们返回两个实现中的一个,具体取决于底层枚举类型的大小:如果它具有64个或更少的元素,如大多数枚举类型所做的那样,静态工厂将返回一个RegularEnumSet实例,该实例由单个long支持;如果枚举类型具有六十五个或更多元素,则工厂将返回一个JumboEnumSet实例,并由一个长阵列支持。

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

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

在你的情况,如果你想使用一个工厂方法你会作出默认的构造函数私有,以便客户端不能直接实例化一个ScoreCard。此时,您可以自由使用工厂方法中的任何具体实现ScoreCard。例如,如果您制作第二个ScoreCard类,该类支持TreeMap,则可以通过更改静态工厂来切换客户端获得的ScoreCard的哪个实现。

+0

对于* abstract *工厂方法,这不是工厂方法。 – Bohemian

+0

我的印象是这是静态工厂模式,因为它使用静态方法,而抽象工厂模式有一个接口定义方法,并且你编写实现该对象的对象......但即使如此,一种与上述两种不同的常规工厂方法? – awksp

+0

你的第一句话是错的;它描述了抽象工厂方法,而不是工厂方法。一个工厂方法返回一个已知的类型,这就是这个问题的关键。抽象工厂方法返回一个抽象类型(例如一个接口或抽象类)的实现,而不需要调用者知道将返回何种确切类型。 – Bohemian

相关问题