热门问题
时间线
聊天
视角
工廠 (物件導向編程)
来自维基百科,自由的百科全书
Remove ads
在物件導向程序設計中,工廠通常是一個用來創建其他對象的對象。工廠是構造方法的抽象,用來實現不同的分配方案。
工廠對象通常包含一個或多個方法,用來創建這個工廠所能創建的各種類型的對象。這些方法可能接收參數,用來指定對象創建的方式,最後返回創建的對象。
有時,特定類型對象的控制過程比簡單地創建一個對象更複雜。在這種情況下,工廠對象就派上用場了。工廠對象可能會動態地創建產品類的對象,或者從對象池中返回一個對象,或者對所創建的對象進行複雜的配置,或者應用其他的操作。
這些類型的對象很有用。幾個不同的設計模式都應用了工廠的概念,並可以使用在很多語言中。例如,在《設計模式》一書中,像工廠方法模式、抽象工廠模式、建造者模式,甚至是單例模式都應用了工廠的概念。
簡單工廠
普通的工廠方法模式通常伴隨著對象的具體類型與工廠具體類型的一一對應,客戶端代碼根據需要選擇合適的具體類型工廠使用。然而,這種選擇可能包含複雜的邏輯。這時,可以創建一個單一的工廠類,用以包含這種選擇邏輯,根據參數的不同選擇實現不同的具體對象。
這個工廠類不需要由每個具體產品實現一個自己的具體的工廠類,所以可以將工廠方法設置為靜態方法。而且,工廠方法封裝了對象的創建過程。如果創建過程非常複雜(比如依賴於配置文件或用戶輸入),工廠方法就非常有用了。比如,一個程序要讀取圖像文件。程序支持多種圖像格式,每種格式都有一個對應的ImageReader
類用來讀取圖像。程序每次讀取圖像時,需要基於文件信息創建合適類型的ImageReader
。這個選擇邏輯可以包裝在一個簡單工廠中:
public class ImageReaderFactory {
public static ImageReader imageReaderFactoryMethod(InputStream is) {
ImageReader product = null;
int imageType = determineImageType(is);
switch (imageType) {
case ImageReaderFactory.GIF:
product = new GifReader(is);
case ImageReaderFactory.JPEG:
product = new JpegReader(is);
//...
}
return product;
}
}
Remove ads
工廠方法
雖然工廠方法模式的背後動機是允許子類選擇創建對象的具體類型,但是使用工廠方法模式也有一些其他的好處,其中很多並不依賴於子類。因此,有時候也會創建不使用多態性創建對象的工廠方法,以得到使用工廠方法的其他好處。
如果拋開設計模式的範疇,「工廠方法」這個詞也可以指作為「工廠」的方法,這個方法的主要目的就是創建對象,而這個方法不一定在單獨的工廠類中。這些方法通常作為靜態方法,定義在方法所實例化的類中。
每個工廠方法都有特定的名稱。在許多物件導向的程式語言中,構造方法必須和它所在的類具有相同的名稱,這樣的話,如果有多種創建對象的方式(重載)就可能導致歧義。工廠方法沒有這種限制,所以可以具有描述性的名稱。舉例來說,根據兩個實數創建一個複數,而這兩個實數表示直角坐標或極坐標,如果使用工廠方法,方法的含義就非常清晰了。當工廠方法起到這種消除歧義的作用時,構造方法常常被設置為私有方法,從而強制客戶端代碼使用工廠方法創建對象。
Remove ads
示例
下面的例子展示了在不同的程式語言中實現複數創建的代碼:
class Complex {
public static Complex fromCartesianFactory(double real, double imaginary) {
return new Complex(real, imaginary);
}
public static Complex fromPolarFactory(double modulus, double angle) {
return new Complex(modulus * cos(angle), modulus * sin(angle));
}
private Complex(double a, double b) {
//...
}
}
Complex product = Complex.fromPolarFactory(1, pi);
Remove ads
Public Class Complex
Public Shared Function fromCartesianFactory(real As Double, imaginary As Double) As Complex
Return (New Complex(real, imaginary))
End Function
Public Shared Function fromPolarFactory(modulus As Double, angle As Double) As Complex
Return (New Complex(modulus * Math.Cos(angle), modulus * Math.Sin(angle)))
End Function
Private Sub New(a As Double, b As Double)
'...
End Sub
End Class
Complex product = Complex.fromPolarFactory(1, pi);
Remove ads
public class Complex {
public double real;
public double imaginary;
public static Complex fromCartesianFactory(double real, double imaginary) {
return new Complex(real, imaginary);
}
public static Complex fromPolarFactory(double modulus , double angle) {
return new Complex(modulus * Math.Cos(angle), modulus * Math.Sin(angle));
}
private Complex (double a, double b) {
real = a;
imaginary = b;
}
}
Complex product = Complex.fromPolarFactory(1,pi);
Remove ads
局限性
使用工廠方法有三個局限,第一個與代碼重構有關,另外兩個與類的擴展有關。
- 第一個局限是,重構已經存在的類會破壞客戶端代碼。例如,
Complex
類是一個標準的類,客戶端使用構造方法將其實例化。可能會有很多這樣的客戶端代碼:一旦Complex的編寫者意識到Complex c = new Complex(-1, 0);
Complex
的實例化應該使用工廠方法實現,他會將Complex
的構造方法設為私有。而此時使用它的構造方法的客戶端代碼就都失效了。
- 第二個局限是,因為工廠方法所實例化的類具有私有的構造方法,所以這些類就不能擴展了。因為任何子類都必須調用父類的構造方法,但父類的私有構造方法是不能被子類調用的。
- 第三個局限是,如果確實擴展了工廠方法所實例化的類(例如將構造方法設為保護的,雖然有風險但也是可行的),子類必須具有所有工廠方法的一套實現。例如,在上述
Complex
的例子中,如果Complex
有了一個子類StrangeComplex
,那麼StrangeComplex
必須提供屬於它自己的所有工廠方法,否則將會返回一個StrangeComplex.fromPolar(1, pi);
Complex
(父類)的實例,而不是所希望的子類實例。但有些語言的反射特性可以避免這個問題。
通過修改底層程式語言,使工廠方法稱為第一類的類成員,可以緩解這三個問題。[1]
Remove ads
引用
Wikiwand - on
Seamless Wikipedia browsing. On steroids.
Remove ads