1. 程式人生 > >C#的靜態工廠方法與建構函式對比

C#的靜態工廠方法與建構函式對比

最近,在與同事進行協同程式設計時,我們開始討論在C#中初始化新物件的最佳方法。我一直是使用建構函式實現,儘管他傾向於靜態工程方法。這引起了關於每種型別的利弊的大量來來回回的討論。

為了說明我所說的內容,這是兩個例子:

// Using the constructor
SqlConnection myConnection = new SqlConnection(connectionString);
// Using a static factory method
IDbConnection myConnection = SqlConnection.FromConnectionString(connectionString);

之前我從未考慮過實現這些靜態工廠方法,我並自嘲問不瞭解其內容。自從那以後,我改變了注意,讓我們深入探討其優缺點。

靜態工廠方法的優點

無須返回一個新的例項

而建構函式總是返回一個新的物件。

當新物件建立失敗時,你不能使用一個快取的物件或返回 null。特別是在編寫庫程式碼時,將來可能會很靈活。

你可以使用方法參考

如果你傾向於以一種實用的方式編寫C#,你可能會感激你可以在程式碼中傳遞該方法(或正式稱為“方法組”)的引用。對比一下:

// Static factory method - the method group can be passed in directly as a function reference
var bars = myFoo.Select(bar.FromFoo)
​
// Constructors - you have to pass in a lambda that constructs the instance via new.
var bars = myFoo.Select(f => new Bar(f));

這段程式碼沒有功能上的差異,只是程式碼風格上的一個問題。因此可能不應在決策中過分重視。

你能通過名字瞭解

對於某些物件,尤其是可以通過多種類似方式構造的物件-能夠在構造物件的方式上獲益良多。讓我們以Color類為例,該類可以通過CMYK和RGB引數構造。

// With constructors
var color = new Color(25, 25, 5, 80);
var color = new Color(100, 150, 50);
​
// With static factory methods
var color = Color.FromCMYK(25, 25, 5, 80);
var color = Color.FromRGB(100, 150, 50);

與更具描述性的靜態工程方法進行對比,除非你知道Color的四個值的建構函式是CMYK,三個值的建構函式是RGB,否則無法通過閱讀程式碼來區別出來。

我認為,如果你有不同的構造物件的方式,尤其是引數彼此相似的方式,有很充分的理由來使用靜態工廠方法。

工廠方法可以返回不同的類

new Foo()總是返回一個Foo類的一個新的例項,Foo.FromBar很容易的返回一個IFoo介面,或者Foo的一個子類。一個可能與之相關的真實示例:

// This could create an IpV4IpAddress that implements IIpAddress 
IIpAddress ipv4Address = IpAddress.FromString("127.0.0.1");

// This could create an IpV6IpAddress that implements IIpAddress 
IIpAddress ipv6Address = IpAddress.FromString("2001:0db8:0a0b:12f0:0000:0000:0000:0001")

在提供公共API(例如在庫上下文中)時,能夠根據輸入返回不同的實際型別可能非常有價值。特別是因為這意味著你可以在介面或者基類後面隱藏一些實現細節。

我不確定應用程式程式碼中的價值是否一樣大,你可以在其中控制整個庫程式碼,並使大規模重構變得更加容易。

在建構函式中你不應該做的事情

通常,人們並不期望建構函式除了構造物件之外,還能做其他很多事情。你管你可以在建構函式中執行I/O,資料庫訪問等操作,但大多數人並不期望這樣做。按照慣例,你可以自由的以靜態工廠方法執行更多的工作,而無需任何人引起注意。

有些人也不認為你應該在建構函式中丟擲異常。也許這取決於語言,但在C#中完全可以,如果要在建構函式中建立非託管資源,請注意一下幾點。

靜態工廠方法的缺點

在建構函式中不應該做的事情

按照慣例,建構函式通常更簡單。當我呼叫建構函式時,通常不希望它執行I/O或
其他。這使建構函式的構造靈活性大大降低,這既是福也是禍。

意味著更多程式碼

無論如何,你仍然需要建構函式來實際構造物件。靜態工廠方法是更多的程式碼,而程式碼是一中責任。它通常不是很複製的程式碼,並且通常靜態工廠方法也不是特別長,因此這可能不是一個很大的缺點。

很難找到

通常,當我嘗試構造一個新物件時,我會先尋找建構函式。通過自動完成功能很難找到靜態方法,因為他們通常無法與其他靜態方法區分開。

我認為靜態方法最大的問題是你失去了可發現性。


經過研究和思考之後,我認為我目前的看法是:

  • 你應該始終建立一個建構函式,該建構函式將1:1對映到類內部的欄位
  • 如果你需要花很多實踐來建立物件(例如IO),或者對快取物件並重新使用它們感興趣,請使用靜態工廠方法。
  • 如果你需要API穩定(例如用於庫開發),請隱藏該建構函式並使用靜態工廠方法,因為它為你提供了實現的靈活性.
  • 如果你有多種不同的方法來建立類,請建立靜態工廠方法並使用它們,因為它們為你提供了描寫性.