工廠模式(Factory Pattern)

一、概述

工廠模式包括簡單工廠模式(Simple Factory Pattern)工廠方法模式抽象工廠模式,是 Java 中最常用的一類創建型設計模式,它提供了一種創建對象的最佳方式。正如它的名字,可以像工廠一樣生產出想要的東西,只要提供原料。在工廠模式中,在創建對象時不會對客戶端暴露創建邏輯,而是通過使用一個共同的接口來指向新創建的對象。簡單工廠模式是工廠方法模式的“小弟”,它不屬于 GoF 23 種設計模式,但在軟件開發中應用也較為頻繁,通常將它作為學習其他工廠模式的入門。此外,工廠方法模式還有一位“大哥”——抽象工廠模式。這三種工廠模式各具特色,難度也逐個加大,在軟件開發中它們都得到了廣泛的應用,成為面向對象程序中常用的創建對象的工具。

1??在 Java 中,通常有以下幾種創建對象的方式:

  1. 使用 new 關鍵字直接創建對象(最簡單,靈活性較差)
  2. 通過反射機制創建對象
  3. 通過 clone() 創建對象
  4. 通過工廠類創建對象

2??意圖
定義一個創建對象的接口,讓其子類自己決定實例化哪一個工廠類,工廠模式使其創建過程延遲到子類進行。

3??主要解決
主要解決接口選擇的問題。

4??何時使用
明確地計劃不同條件下創建不同實例時。

5??如何解決
讓其子類實現工廠接口,返回的也是一個抽象的產品。

6??關鍵代碼
創建過程在其子類執行。

7??應用實例

  1. 需要一輛汽車,可以直接從工廠里面提貨,而不用去管這輛汽車是怎么做出來的,以及這個汽車里面的具體實現。
  2. Hibernate 換數據庫只需換方言和驅動就可以。

8??優點

  1. 屏蔽產品的具體實現,調用者只關心產品的接口。
    對調用者屏蔽具體的產品類。如果使用工廠模式,調用者只關心產品的接口就可以了,至于具體的實現,調用者根本無需關心。即使變更了具體的實現,對調用者來說沒有任何影響。
  2. 可以使代碼結構清晰,有效地封裝變化。在編程中,產品類的實例化有時候是比較復雜和多變的,通過工廠模式,將產品的實例化封裝起來,使得調用者根本無需關心產品的實例化過程,只需依賴工廠即可得到自己想要的產品。
  3. 擴展性高,如果想增加一個產品,只要擴展一個工廠類就可以。降低耦合度。產品類的實例化通常來說是很復雜的,它需要依賴很多的類,而這些類對于調用者來說根本無需知道,如果使用了工廠方法,我們需要做的僅僅是實例化好產品類,然后交給調用者使用。對調用者來說,產品所依賴的類都是透明的。

9??缺點

每次增加一個產品時,都需要增加一個具體類和對象實現工廠,使得系統中類的個數成倍增加,在一定程度上增加了系統的復雜度,同時也增加了系統具體類的依賴。這并不是什么好事。

??使用場景

  1. 日志記錄器:記錄可能記錄到本地硬盤、系統事件、遠程服務器等,用戶可以選擇記錄日志到什么地方。
  2. 數據庫訪問,當用戶不知道最后系統采用哪一類數據庫,以及數據庫可能有變化時。
  3. 設計一個連接服務器的框架,需要三個協議“POP3”、“IMAP”、“HTTP”,可以把這三個作為產品類,共同實現一個接口。

1??1??注意事項

作為一種創建類模式,在任何需要生成復雜對象的地方,都可以使用工廠方法模式。有一點需要注意的地方就是復雜對象適合使用工廠模式。而簡單對象,特別是只需要通過 new 就可以完成創建的對象,無需使用工廠模式。如果使用工廠模式,就需要引入一個工廠類,會增加系統的復雜度。

二、簡單工廠模式不屬于23種GOF設計模式

又叫做靜態工廠方法(StaticFactory Method)模式。 主要用于創建同一類的對象,比如創建圖形類(Rectangle 矩形、Square 正方形、Circle 圓形)

創建一個 Shape 接口和實現 Shape 接口的實現類。下一步是定義工廠類 ShapeFactory。FactoryPatternDemo,演示類使用 ShapeFactory 來獲取 Shape 對象。它將向 ShapeFactory 傳遞信息(RECTANGLE、SQUARE、CIRCLE),以便獲取它所需對象的類型。

1??創建一個接口

public interface Shape {
    void draw();
}

2??創建實現接口的實現類

public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Inside Rectangle::draw() method.");
    }
}
public class Square implements Shape {
    @Override
    public void draw() {
        System.out.println("Inside Square::draw() method.");
    }
}
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Inside Circle::draw() method.");
    }
}

3??創建一個工廠,生成基于給定信息的實現類的對象

public class ShapeFactory {
    //使用 getShape 方法獲取形狀類型的對象
    public Shape getShape(String shapeType){
        if("CIRCLE".equalsIgnoreCase(shapeType)){
            return new Circle();
        }else if("RECTANGLE".equalsIgnoreCase(shapeType)){
            return new Rectangle();
        }else if("SQUARE".equalsIgnoreCase(shapeType)){
            return new Square();
        }
        return null;
    }
}

4??使用該工廠,通過傳遞類型信息來獲取實現類的對象。

public class FactoryPatternDemo {
    public static void main(String[] args) {
        ShapeFactory shapeFactory = new ShapeFactory();

        //獲取 Circle 的對象,并調用它的 draw 方法
        Shape shape1 = shapeFactory.getShape("CIRCLE");
        shape1.draw();

        //獲取 Rectangle 的對象,并調用它的 draw 方法
        Shape shape2 = shapeFactory.getShape("RECTANGLE");
        shape2.draw();

        //獲取 Square 的對象,并調用它的 draw 方法
        Shape shape3 = shapeFactory.getShape("SQUARE");
        shape3.draw();
    }
}

5??執行程序,輸出結果:

Inside Circle::draw() method.
Inside Rectangle::draw() method.
Inside Square::draw() method.

根據傳入不同的 shapeType(如:RECTANGLE、SQUARE、CIRCLE)去創建不同的對象,然后再通過不同的 shapeType 對象去給里面的屬性賦值。無論 RECTANGLE、SQUARE、CIRCLE 都是 Shape,所以說簡單工廠模式就是用來創建同一類對象的不同實例。

三、工廠方法模式

定義一個用于創建對象的接口,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類。在工廠里面可以對對象的創建做嚴格的定義,client 端直接通過工廠創建出來就可以使用了。

1??產品抽象類

public abstract class Product {  
    public abstract void method(); 
}

2??具體A產品

public class ProductA extends Product { 
    @Override
    public void method() {
        System.out.println("產品A");
    }
}

3??具體B產品

public class ProductB extends Product { 
    @Override
    public void method() {
        System.out.println("產品B");
    }
}

4??工廠抽象類

public abstract class Factory { 
    public abstract Product createProduct();
}

5??具體工廠

public class ProductFactory extends Factory {
    @Override
    public Product createProduct() {
        return new ProductA();
    }
}

6??客戶端使用

public class Client {
    public static void main(String[] args) {
        Factory factory = new ProductFactory();
        Product product = factory.createProduct();
        product.method();
    }
}

也可以通過反射的方式

public abstract class Factory {
    public abstract <T extends Product> T createProduct(Class<T> clz);
}
public class ConcreteFactory extends Factory {
    @Override
    public <T extends Product> T createProduct(Class<T> clz) {
        Product  product = null;
        try {
            product= (Product) Class.forName(clz.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return (T) product;
    }
}
public class Client {
    public static void main(String[] args) {
        Factory factory = new ConcreteFactory();
        Product product = factory.createProduct(ConcreteProductB.class);
        product.method();
    }
}

擁有多個工廠的方式稱為多工廠方法模式,如果只有一個工廠可以去掉抽象類直接定義一個 static 的方法返回對象(用 static 修飾可以不用初始化工廠直接返回產品,也可以不用 static),這種叫做工廠方法模式或者靜態工廠模式。

優點和缺點是一樣的:新加一個產品需要相應的產品類和一個工廠子類。

四、抽象工廠模式

為創建一組相關或者是相互依賴的對象提供一個接口,而不需要指定具體實現類。

1??抽象工廠模式與工廠方法模式的區別
抽象工廠模式是工廠方法模式的升級版本,它用來創建一組相關或者相互依賴的對象。它與工廠方法模式的區別就在于,工廠方法模式針對的是一個產品等級結構;而抽象工廠模式則是針對的多個產品等級結構。在編程中,通常一個產品結構,表現為一個接口或者抽象類,也就是說,工廠方法模式提供的所有產品都是衍生自同一個接口或抽象類,而抽象工廠模式所提供的產品則是衍生自不同的接口或抽象類。

在抽象工廠模式中,有一個產品族的概念:所謂的產品族,是指位于不同產品等級結構中功能相關聯的產品組成的家族。抽象工廠模式所提供的一系列產品就組成一個產品族;而工廠方法提供的一系列產品稱為一個等級結構。拿生產汽車的例子來說明他們之間的區別:

在上面的類圖中,兩廂車和三廂車稱為兩個不同的等級結構。而2.0排量車和2.4排量車則稱為兩個不同的產品族。再具體一點,2.0排量兩廂車和2.4排量兩廂車屬于同一個等級結構,2.0排量三廂車和2.4排量三廂車屬于另一個等級結構;而2.0排量兩廂車和2.0排量三廂車屬于同一個產品族,2.4排量兩廂車和2.4排量三廂車屬于另一個產品族。

明白了等級結構和產品族的概念,就理解工廠方法模式和抽象工廠模式的區別了,如果工廠的產品全部屬于同一個等級結構,則屬于工廠方法模式;如果工廠的產品來自多個等級結構,則屬于抽象工廠模式。在本例中,如果一個工廠模式提供2.0排量兩廂車和2.4排量兩廂車,那么屬于工廠方法模式;如果一個工廠模式是提供2.4排量兩廂車和2.4排量三廂車兩個產品,那么這個工廠模式就是抽象工廠模式,因為提供的產品是分屬兩個不同的等級結構。當然,如果一個工廠提供全部四種車型的產品,因為產品分屬兩個等級結構,當然也屬于抽象工廠模式了。

2??Q3 和 Q7 有不同的輪胎、發動機、制動系統。雖然生產的零件不同,型號不同。但是根本上都有共同的約束,就是輪胎、發動機、制動系統。設計如下:

  1. 需要一個抽象工廠,里面有三個接口分別為生產輪胎、發動機、制動系統,抽象類。
  2. 需要三個抽象產品分別為輪胎、發動機、制動系統,抽象接口。
  3. 需要實現上面的三個抽象接口,定義出每個接口不同的對象,比如:普通輪胎和越野輪胎。
  4. 需要兩個具體類繼承自上面的抽象類,實現具體的工廠,比如:生產 Q3 的工廠和生產 Q7 的工廠。
  5. 在客戶端 new 出對應的具體工廠并調用對應的生產方法。

三個產品抽象接口

public interface ITire {
    void tire();//輪胎 
}
public interface IEngine {
    void engine();//發動機
}
public interface IBrake {
    void brake();//制動系統
}

根據抽象接口定義不同的對象

public class NormalBrake implements IBrake{
    @Override
    public void brake() {
        System.out.println("普通制動");
    }
}
public class SeniorBrake implements IBrake{
    @Override
    public void brake() {
        System.out.println("高級制動");
    }
}  //后面的定義省略。。。。。。。。。。。。。

抽象工廠

public abstract class CarFactory {
    /**
     * 生產輪胎
     * @return 輪胎
     * */
    public abstract ITire createTire();

    /**
     * 生產發動機
     * @return 發動機
     * */
    public abstract IEngine createEngine();

    /**
     * 生產制動系統
     * @return 制動系統
     * */
    public abstract IBrake createBrake();

}

實現具體的工廠類

public class Q3Factory extends CarFactory{

    @Override
    public ITire createTire() {
        return new NormalTire();
    }

    @Override
    public IEngine createEngine() {
        return new DomesticEngine();
    }

    @Override
    public IBrake createBrake() {
        return new NormalBrake();
    }
}

客戶端使用

public class Client {
    public static void main(String[] args) {
        //A車廠
        CarFactory factoryQ3 = new Q3Factory();
        factoryQ3.createTire().tire();
        factoryQ3.createEngine().engine();
        factoryQ3.createBrake().brake();
        System.out.println("---------------");
    }
}

//輸出

普通輪胎
國產發動機
普通制動

五、區別

1??工廠方法模式:只有一個抽象產品類,具體工廠類只能創建一個具體產品類的實例。
2??抽象工廠模式:有多個抽象產品類 ,具體工廠類能創建多個具體產品類的實例。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。