2009-09-13 85 views

回答

7

一個真實的例子:

的Java支持基本的Object類型來表示一個字節。當轉換到原始對象,你可以這樣做:

Byte b = new Byte((byte) 65); 

對接,這將在每次調用創建一個新的實例。相反,你這樣做:

Byte b = Byte.valueOf((byte) 65); 

每次調用,該方法的valueOf()將返回一個字節對象代表的字節值65

10000電話後第一例將創造10000點的對象相同的實例,而第二個只有一個,因爲Byte類有一個Byte對象的內部緩存,代表-128到127之間的所有數字。

+0

它將通過「緩存」的初始化創建256,或者如果緩存已經初始化,則爲none。沒有基於實例進行初始化。 – 2009-09-13 10:02:16

+0

實際上,緩存的初始化將創建256個實例。這是因爲在預先填充緩存的Byte類中有一個靜態初始化程序。顯然,開發人員認爲預先填充緩存比懶惰初始化效率更高。 但是,這不是靜態工廠方法的重要特徵。它可以以任何方式實施。 – idrosid 2009-09-13 10:19:40

5

當你調用一個構造函數時,它總會返回一個新的對象(除非拋出異常)。靜態工廠方法或任何類型的工廠都不必總是返回一個新對象。例如,傳統Singleton設計模式上的getInstance()方法是一種總是返回完全相同對象的工廠方法。在某些情況下,你有時想要做這種事情,無論是強制一個對象只能被實例化一次,還是創建某種對象池等。一般來說,我認爲這是一個附帶原因靜態工廠方法。主要目的是創建精美的僞構造函數。

下面是一個使用靜態工廠方法來製作精美的僞構造函數的例子(有點愚蠢)。考慮這個類:

class Person { 

    public Person(Role role) { 
     setRole(role); 
    } 

    ... 
} 

沒有靜態工廠方法,你可能會做這樣的事情:

Person employee = new Person(Role.EMPLOYEE); 
Person manager = new Person(Role.MANAGER); 

相反,你可以創建靜態工廠方法:

class Person { 

    public static Person newEmployee() { 
     return new Person(Role.EMPLOYEE); 
    } 

    public static Person newManager() { 
     return new Person(Role.MANAGER); 
    } 

    private Person(Role role) { 
     setRole(role); 
    } 

    ... 
} 

,你可能反而做像這樣:

Person employee = Person.newEmployee(); 
Person manager = Person.newManager(); 

這可能不是一個好例子,但考慮一個更復雜的構造函數或一個描述性較弱的參數。有時使用工廠方法路線會使代碼更清晰。當然也有缺點......

至於限制對象的創建,考慮到一些奇怪的約束像永遠不會有一個以上的CEO:

class Person { 

    private static Person singletonCEO = new Person(Role.CEO); 

    public static Person newCEO() { 
     return singletonCEO; 
    } 

    ... 
} 

以及它將如何被創建:

Person ceo1 = Person.newCEO(); 
Person ceo2 = Person.newCEO(); 

assertThat(ceo1, is(ceo2)); // JUnit 4.x 

我希望這些例子有所幫助。

+0

您能否提供代碼片段以便更好地理解?謝謝! – 2009-09-13 03:30:18

+1

我試過:-) – SingleShot 2009-09-13 03:44:21

2

factory method pattern對於不需要創建對象的新實例以執行某些操作的時間很有用。

這裏有一對夫婦的一般情況下,我能想到的地方,它返回相同對象的靜態工廠方法就可以派上用場:

  1. 目的是昂貴的創建 - 有一個當對象被實例化時很多處理,因此不止一次實例化對象是不可取的。 (這也與Singleton pattern

  2. 的對象保持無狀態 - 如果在實例之間沒有狀態差,沒有好的目的,以創建新的對象每次。

Wikipedia page on the factory method pattern有關於此主題的更多信息。


讓我們來看一個具體的例子。

DateFormat類使用getInstance靜態方法來返回DateFormat實例,其可用於根據機器的區域設置到Date格式化成預設格式。

由於返回的DateFormat對每個日期格式化操作都使用相同的格式,因此每次都沒有真正的原因創建新的DateFormat實例。

通常,實現的方式是在實例尚不存在的情況下創建實例,然後保留對該實例的引用。如果再次需要實例,則返回引用。 (這是Singleton模式通常如何實現的爲好。)

例如:

class MySingleInstanceObject { 

    private MySingleInstanceObject instance; 

    private MySingleInstanceObject() { 
    // Initialize the object. 
    // This may be expensive. 
    } 

    public MySingleInstanceObject getInstance() { 
    if (instance == null) { 
     instance = new MySingleInstanceObject(); 
    } 

    return instance; 
    } 
} 

(僅供參考,上述代碼是單的一個例子同樣,它不是線程安全。 )

+0

如果多次調用工廠方法,它以什麼方式確保只返回一個對象?它是否持有對該對象的引用並將其檢查爲空? – 2009-09-13 03:39:27

+1

是的。在wiki上有代碼示例:http://en.wikipedia.org/wiki/Singleton_pattern – jimyi 2009-09-13 03:45:10

+2

請注意,通過使用IoC容器(依賴注入)框架,您可以獲得更大的靈活性(加上可測試性)並讓它控制物體的壽命。 – TrueWill 2009-09-13 04:00:14

-1

如果你有一個工廠類來創建對象實例,每次你去創建一個對象,你也必須實例化工廠類。基本上你會創建這個工廠類的重複。

如果它是靜態的,則只有工廠的一個實例被使用。

+0

不,這不是關於工廠,而是關於它創建的對象。 – 2009-09-13 03:31:27

0

我能閱讀這本書的一些here。在閱讀他所寫的內容之後,他似乎在說靜態工廠方法爲開發人員提供了更大的靈活性,並且還可以讓您更清楚地知道返回的內容。當你將它與一個構造函數進行對比時,構造函數可能不會提供返回內容的清晰度。另外,你可以在靜態工廠方法中做緩存等工作,我認爲這很吸引人。如果您需要這種級別的控制和靈活性,這種方法似乎是一種很好的方法。

如果您要使用緩存,則不會創建不必要的重複對象。使用這種靜態工廠方法,您可以在每次調用時將相同的對象返回到靜態工廠方法。

一個例子:

public class Person 
{ 

    private Person(string firstName, string lastName) 
    { 
     this.FirstName = firstName; 
     this.LastName = lastName; 
    } 

    public string FirstName {get; private set;} 
    public string LastName {get; private set;} 

    private static Dictionary<string, Person> objectPool = new Dictionary<string, Person>(); 
    private object lockObject = new object(); 

    public static Person CreatePerson(string firstName, string lastName) 
    { 
     var result = objectPool[firstName + lastName]; 
     Person person = null; 
     if (result != null) 
     { 
     return result 
     } 
     lock(lockObject) 
     { 
      person = new Person(firstName, lastName); 
      objectPool.Add(firstName + lastName, person) 
     } 
     return person; 
    } 
} 
+0

好吧,我無法從書中理解緩存部分,也許以後我可以問這個問題。 – 2009-09-13 03:41:44

+1

呃...緩存節省了你創建一個你真正不想要的東西的新實例的開銷。在這種情況下,將呼叫者交回同一個實例是可以的,因爲狀態並不重要。一個例子是您在代碼中使用的服務。該服務爲您提供了一項功能。您可以通過將呼叫者交回相同的對象來節省創建新實例的開銷 – 2009-09-13 03:55:24

6

所有非重複的答案似乎把重點放在單件模式,這是不可複製的一個很好的例子,但一個壞的模式在一般情況下使用。在我看來,一個給定的應用程序應該有零個到一個單例,並且優先爲零。但是,這與不創建不必要的對象無關。

請考慮一個必須製作大量Date對象的應用程序。它製作了很多Date對象,以致Date對象的構造對性能產生不利影響。因此,改爲調用Date對象的構造函數,將代碼重構爲僅通過工廠方法創建日期。在這個工廠方法中,檢查一個Map來查看請求的日期是否已經被創建。如果是這樣,那麼從Map返回相同的對象。否則,創建一個新的,放入地圖並返回。

什麼似乎混淆你是如何通過調用工廠方法,防止創建一個重複的對象。僅僅通過調用工廠方法並不能改變任何事情。調用工廠允許的是代碼接管並作出關於創建對象的決定。撥打新電話時,不能做出這樣的決定。

另請參閱this question瞭解該模式及其用途。

+0

是的,我也這麼認爲。關於辛格爾頓的好點! – 2009-09-13 04:36:57

+0

日期是一個不好的例子,因爲它是(通過糟糕的設計)可變的,它真的不應該是一個瓶頸(除非我錯過了某些東西)。 – 2009-09-13 10:00:46

+0

@Tom,在標準API中爲true。我正在考慮一個JODA時間。 – Yishai 2009-09-13 15:29:09

2

如果我沒記錯的話,他也會在書中給出一個例子。考慮Decimal。零經常使用。因此,如果您要調用靜態工廠方法Decimal.valueOf("0")(不知道這是否是實際的API,但這對此示例無關緊要),它將返回Decimal reprezenting 0的一個實例,它將是任何呼叫的相同實例。實現將是這樣的:

public class Decimal { 
    private static Decimal zero = new Decimal(0); 

    public static Decimal valueOf(String s) { 
     if (s.equals("0")) { 
      return zero; 
     } else { 
      return new Decimal(parse(s)); // or whatever 
     } 

    // rest of the class 
} 

請注意,只有一個零實例,而對於任何其他數字創建一個新的對象。此外,這與工廠方法一起工作,並且你不能用構造函數來做到這一點。布洛赫試圖指出這是前者的優勢。正如Yishai所說,這與Singleton並沒有太大的關係。正如你所看到的,你可以有大量的Decimal對象。相反,您可以使用工廠方法完全控制您創建的實例數量。這就是爲什麼它被稱爲工廠。