2014-05-02 37 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

相關問題