工廠方法

工廠方法模式是一種實現了“工廠”概念的面向物件設計模式。就像其他建立型模式一樣，它也是處理在不指定物件具體類別的情況下建立物件的問題。工廠方法模式的實質是“定義一個建立物件的介面，但讓實現這個介面的類別來決定例項化哪個類別。工廠方法讓類別的例項化推遲到子類別中進行。”建立一個物件常常需要複雜的過程，所以不適合包含在一個複合物件中。建立物件可能會導致大量的重複程式碼，可能會需要複合物件訪問不到的資訊，也可能提供不了足夠級別的抽象，還可能並不是複合物件概念的一部分。工廠方法模式透過定義一個單獨的建立物件的方法來解決這些問題。由子類別實現這個方法來建立具體類別的物件。物件建立中的有些過程包括決定建立哪個物件、管理物件的生命週期，以及管理特定物件的建立和銷燬的概念。工廠.在面向物件程式設計中，工廠通常是一個用來建立其他物件的物件。工廠是構造方法的抽象，用來實現不同的分配方案。工廠物件通常包含一個或多個方法，用來建立這個工廠所能建立的各種類別的物件。這些方法可能接收引數，用來指定物件建立的方式，最後返回建立的物件。有時，特定類別物件的控制過程比簡單地建立一個物件更復雜。在這種情況下，工廠物件就派上用場了。工廠物件可能會動態地建立產品類別的物件，或者從物件池中返回一個物件，或者對所建立的物件進行復雜的配置，或者應用其他的操作。這些類別的物件很有用。幾個不同的設計模式都應用了工廠的概念，並可以使用在很多語言中。例如，在《設計模式》一書中，像工廠方法模式、抽象工廠模式、生成器模式，甚至是單例模式都應用了工廠的概念。程式碼舉例.例如，有一個codice_1類別表示按鈕，另有它的兩個子類別codice_2和codice_3分別代表Windows和Mac風格的按鈕，那麼這幾個類別和用於建立它們的工廠類別在Java中可以如下實現：//幾個Button類//他們的工廠類別interface ButtonFactory {abstract Button createButton();class WinButtonFactory implements ButtonFactory {Button createButton() {return new WinButton();class MacButtonFactory implements ButtonFactory {Button createButton() {return new MacButton();變種.雖然工廠方法模式的背後動機是允許子類別選擇建立物件的具體類別，但是使用工廠方法模式也有一些其他的好處，其中很多並不依賴於子類別。因此，有時候也會建立不使用多型性建立物件的工廠方法，以得到使用工廠方法的其他好處。工廠「方法」而非工廠「類別」.如果拋開設計模式的範疇，“工廠方法”這個詞也可以指作為“工廠”的方法，這個方法的主要目的就是建立物件，而這個方法不一定在單獨的工廠類別中。這些方法通常作為靜態方法，定義在方法所例項化的類別中。每個工廠方法都有特定的名稱。在許多面向物件的程式語言中，構造方法必須和它所在的類別具有相同的名稱，這樣的話，如果有多種建立物件的方式就可能導致歧義。工廠方法沒有這種限制，所以可以具有描述性的名稱。舉例來說，根據兩個實數建立一個複數，而這兩個實數表示直角座標或極座標，如果使用工廠方法，方法的含義就非常清晰了。當工廠方法起到這種消除歧義的作用時，構造方法常常被設定為私有方法，從而強制客戶端程式碼使用工廠方法建立物件。下面的例子展示了在不同的程式語言中實現複數建立的程式碼：Java.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);VB.NET.Public Class ComplexPublic Shared Function fromCartesianFactory(real As Double, imaginary As Double) As ComplexReturn (New Complex(real, imaginary))End FunctionPublic Shared Function fromPolarFactory(modulus As Double, angle As Double) As ComplexReturn (New Complex(modulus * Math.Cos(angle), modulus * Math.Sin(angle)))End FunctionPrivate Sub New(a As Double, b As Double)End SubEnd ClassComplex product = Complex.fromPolarFactory(1, pi);C#.public class Complexpublic 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 real, double imaginary)Real = real;Imaginary = imaginary;var product = Complex.FromPolarFactory(1, pi);簡單工廠.普通的工廠方法模式通常伴隨著物件的具體類別與工廠具體類別的一一對應，客戶端程式碼根據需要選擇合適的具體類別工廠使用。然而，這種選擇可能包含複雜的邏輯。這時，可以建立一個單一的工廠類別，用以包含這種選擇邏輯，根據引數的不同選擇實現不同的具體物件。這個工廠類別不需要由每個具體產品實現一個自己的具體的工廠類別，所以可以將工廠方法設定為靜態方法。而且，工廠方法封裝了物件的建立過程。如果建立過程非常複雜，工廠方法就非常有用了。比如，一個程式要讀取影象檔案。程式支援多種影象格式，每種格式都有一個對應的codice_4類別用來讀取影象。程式每次讀取影象時，需要基於檔案資訊建立合適類別的codice_4。這個選擇邏輯可以包裝在一個簡單工廠中：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;適用性.工廠方法，適用於面向介面程式設計與實現依賴反轉原則。下列情況可以考慮使用工廠方法模式：工廠方法模式常見於工具包和框架中，在這些庫中可能需要建立客戶端程式碼實現的具體類別的物件。平行的類別層次結構中，常常需要一個層次結構中的物件能夠根據需要建立另一個層次結構中的物件。工廠方法模式可以用於測試驅動開發，從而允許將類別放在測試中。舉例來說，codice_6這個類別建立了一個codice_7物件，但是codice_7物件不能放在自動的單元測試中，而這是一個模擬物件。這樣，單元測試就可以使用codice_13來測試codice_6的功能，從而避免了使用codice_7物件帶來的副作用。侷限性.使用工廠方法有三個侷限，第一個與程式碼重構有關，另外兩個與類別的擴充套件有關。透過修改底層程式語言，使工廠方法稱為第一類別的類別成員，可以緩解這三個問題。