我正在閱讀Joshua Bloch的「Effective Java Programming Language Guide」。
他解釋了靜態工廠方法可以用來避免不必要的重複對象。
我還沒完全明白這一點。
任何人都可以解釋嗎?靜態工廠避免重複對象的方法
回答
一個真實的例子:
的Java支持基本的Object類型來表示一個字節。當轉換到原始對象,你可以這樣做:
Byte b = new Byte((byte) 65);
對接,這將在每次調用創建一個新的實例。相反,你這樣做:
Byte b = Byte.valueOf((byte) 65);
每次調用,該方法的valueOf()將返回一個字節對象代表的字節值65
10000電話後第一例將創造10000點的對象相同的實例,而第二個只有一個,因爲Byte類有一個Byte對象的內部緩存,代表-128到127之間的所有數字。
當你調用一個構造函數時,它總會返回一個新的對象(除非拋出異常)。靜態工廠方法或任何類型的工廠都不必總是返回一個新對象。例如,傳統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
我希望這些例子有所幫助。
您能否提供代碼片段以便更好地理解?謝謝! – 2009-09-13 03:30:18
我試過:-) – SingleShot 2009-09-13 03:44:21
factory method pattern對於不需要創建對象的新實例以執行某些操作的時間很有用。
這裏有一對夫婦的一般情況下,我能想到的地方,它返回相同對象的靜態工廠方法就可以派上用場:
目的是昂貴的創建 - 有一個當對象被實例化時很多處理,因此不止一次實例化對象是不可取的。 (這也與Singleton pattern)
的對象保持無狀態 - 如果在實例之間沒有狀態差,沒有好的目的,以創建新的對象每次。
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;
}
}
(僅供參考,上述代碼是單的一個例子同樣,它不是線程安全。 )
如果你有一個工廠類來創建對象實例,每次你去創建一個對象,你也必須實例化工廠類。基本上你會創建這個工廠類的重複。
如果它是靜態的,則只有工廠的一個實例被使用。
不,這不是關於工廠,而是關於它創建的對象。 – 2009-09-13 03:31:27
我能閱讀這本書的一些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;
}
}
好吧,我無法從書中理解緩存部分,也許以後我可以問這個問題。 – 2009-09-13 03:41:44
呃...緩存節省了你創建一個你真正不想要的東西的新實例的開銷。在這種情況下,將呼叫者交回同一個實例是可以的,因爲狀態並不重要。一個例子是您在代碼中使用的服務。該服務爲您提供了一項功能。您可以通過將呼叫者交回相同的對象來節省創建新實例的開銷 – 2009-09-13 03:55:24
所有非重複的答案似乎把重點放在單件模式,這是不可複製的一個很好的例子,但一個壞的模式在一般情況下使用。在我看來,一個給定的應用程序應該有零個到一個單例,並且優先爲零。但是,這與不創建不必要的對象無關。
請考慮一個必須製作大量Date對象的應用程序。它製作了很多Date對象,以致Date對象的構造對性能產生不利影響。因此,改爲調用Date對象的構造函數,將代碼重構爲僅通過工廠方法創建日期。在這個工廠方法中,檢查一個Map來查看請求的日期是否已經被創建。如果是這樣,那麼從Map返回相同的對象。否則,創建一個新的,放入地圖並返回。
什麼似乎混淆你是如何通過調用工廠方法,防止創建一個重複的對象。僅僅通過調用工廠方法並不能改變任何事情。調用工廠允許的是代碼接管並作出關於創建對象的決定。撥打新電話時,不能做出這樣的決定。
另請參閱this question瞭解該模式及其用途。
是的,我也這麼認爲。關於辛格爾頓的好點! – 2009-09-13 04:36:57
日期是一個不好的例子,因爲它是(通過糟糕的設計)可變的,它真的不應該是一個瓶頸(除非我錯過了某些東西)。 – 2009-09-13 10:00:46
@Tom,在標準API中爲true。我正在考慮一個JODA時間。 – Yishai 2009-09-13 15:29:09
如果我沒記錯的話,他也會在書中給出一個例子。考慮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對象。相反,您可以使用工廠方法完全控制您創建的實例數量。這就是爲什麼它被稱爲工廠。
- 1. 工廠類與對象初始化 - 試圖避免靜態
- 2. 靜態工廠方法vs工廠
- 3. AutoMapper靜態工廠方法
- 4. 靜態工廠方法(Spring)
- 5. 靜態工廠方法
- 6. 靜態工廠方法
- 7. Autofac工廠靜態方法
- 8. 如何使用靜態工廠方法創建對象?
- 9. 創建一個從靜態工廠方法工廠,然後調用靜態工廠方法上的ID
- 10. 工廠方法VS工廠對象
- 11. 避免主要方法靜態定義?
- 12. 避免與數據對象重複
- 13. 靜態接口工廠方法[Java 8]
- 14. Spring 3 @Component和靜態工廠方法
- 15. Java 8接口 - 靜態工廠方法
- 16. 靜態工廠方法問題!
- 17. mysql - 避免重複的最好方法
- 18. 避免鍵盤重複的方法?
- 19. 如何避免重複代碼的靜態多態性
- 20. 工廠方法模式,以避免基於條件邏輯實例化對象
- 21. Java中的抽象靜態工廠方法[getInstance()]?
- 22. 使用枚舉作爲工廠vs靜態工廠方法
- 23. 如何用靜態工廠方法創建抽象類?
- 24. 如何避免子類的工廠方法中的開關盒
- 25. 避免重複
- 26. c。與私有構造#mock對象,通過靜態工廠方法
- 27. 靜態工廠方法不會在MVC控制器中返回對象實例
- 28. 內部工廠的靜態工廠方法網關 - 代碼異味?
- 29. 避免重複行
- 30. R:避免重複$
它將通過「緩存」的初始化創建256,或者如果緩存已經初始化,則爲none。沒有基於實例進行初始化。 – 2009-09-13 10:02:16
實際上,緩存的初始化將創建256個實例。這是因爲在預先填充緩存的Byte類中有一個靜態初始化程序。顯然,開發人員認爲預先填充緩存比懶惰初始化效率更高。 但是,這不是靜態工廠方法的重要特徵。它可以以任何方式實施。 – idrosid 2009-09-13 10:19:40