淺談設計模式——工廠模式

城北有個混子發表於2020-11-13

基本概念

  工廠顧名思義就是建立產品。

  該模式用於封裝和管理物件的建立,是一種建立型模式。

工廠模式一般分為3類:

  ① 簡單工廠模式(Simple Factory)

  ② 工廠方法模式(Factory Method)

  ③ 抽象工廠模式(Abstract Factory)

  這三個模式從前到後,依次逐步抽象化。

 


簡單工廠模式

概述

  這是工廠模式中最簡單的一種,專門定義一個類來負責建立其他類的例項,同時被建立的例項具有共同的父類。

簡單工廠模式包括3個主要的角色:

  ➷ 簡單工廠類(SimpleFactory):只包含建立產品的靜態方法。

  ➷ 抽象產品父類(Product):簡單工廠類中生產的產品介面,宣告瞭產品的抽象方法。

  ➷ 具體產品子類(ProductA 或ProductB):具體產品的實現。

UML圖

場景描述

  某城市氣候宜人,盛產水果,尤其盛產蘋果(Apple)和香蕉(Banana),很多年來,各家果農都是自己負責蘋果或香蕉的採摘、包裝、銷售,每個果農都要自備生產包裝的裝置,大家不能複用這些裝置產出水果,因此生產效率非常低下。

  有一個老總看到這個情況,決定投資建立了一家水果工廠(SimpleFactory),統一負責生產這些蘋果或香蕉(Product),各家果農只要負責提供自己的蘋果(ProductA)或者香蕉(ProductB),工廠負責根據果農的指定生產具體的水果(採摘、包裝、銷售等),因此生產流程效率大大提高,這就是簡單工廠模式

場景角色對映:

  ★ 簡單工廠類:SimpleFactory→FruitFactory。

  類 FruitFactory 對應 SimpleFactory,它用來統一生產蘋果或香蕉(包裝、銷售等),工廠中有一個靜態方法CreateFruit(),就相當於一套流水線裝置,可以根據提供的水果型別來生產不同的水果(Apple 或者 Banana)。

  ★ 抽象產品父類:Product→Fruit。

  水果介面類,統一定義蘋果類和香蕉類的處理方法。

  ★具體產品子類(ProductA或ProductB):ProductA→Apple,ProductB→Banana。

程式碼演示

抽象產品父類

package com.design;

// 抽象產品父類
abstract class Fruit {
    // 構造方法
    public Fruit(){
        System.out.println("生產原料準備中。。");
    };
    // 定義水果的處理方法
    public abstract void operateFruit();
}

ProductA(Apple)

package com.design;

// 產品A
public class Apple extends Fruit{

    @Override
    public void operateFruit() {
        // 蘋果的生產過程
        System.out.println("開始生產");
        System.out.println("蘋果:生產中。。。");
        System.out.println("蘋果:加工中。。。");
        System.out.println("蘋果:包裝中。。。");
        System.out.println("生產結束");
        System.out.println("這是一個蘋果");
    }

}

ProductB(Banana)

package com.design;

// 產品B
public class Banana extends Fruit{

    @Override
    public void operateFruit() {
        // 香蕉的生產過程
        System.out.println("開始生產");
        System.out.println("香蕉:生產中。。。");
        System.out.println("香蕉:加工中。。。");
        System.out.println("香蕉:包裝中。。。");
        System.out.println("生產結束");
        System.out.println("這是一個香蕉");
    }

}

簡單工廠類

package com.design;

// 簡單工廠類
public class FruitFactory {
    // 水果生產裝置
    public static Fruit createFruit(String type) {
        Fruit fruit = null;
        if ("A".equals(type)) {
            fruit = new Apple();
        }else if ("B".equals(type)) {
            fruit = new Banana();
        }
        return fruit;
    }
}

測試:

package com.design;

public class TestFactory {

    public static void main(String[] args) {
        // 建立工廠
        FruitFactory factory = new FruitFactory();
        
        // 1.生產蘋果
        Fruit apple = factory.createFruit("A");
        apple.operateFruit();
        
        System.out.println("------------------------------------");
        
        // 2.生產香蕉
        Fruit banana = factory.createFruit("A");
        banana.operateFruit();
    }
}

執行結果:

  簡單工廠模式會專門建立一個工廠類FruitFactory,並用一個方法createFruit()根據傳遞引數的不同建立不同型別的例項。

  這裡要注意一點,在FruitFactory 中建立物件的型別可以不同(可以是蘋果或者香蕉),但都應當屬於同一個父類(抽象產品父類Fruit)的子類。

  類FruitFactory 就是一個簡單的工廠,將建立物件(蘋果或香蕉)的工作單獨負責下來,使得外界呼叫統一的方法 createFruit()即可完成不同物件建立(生產蘋果或香蕉)。

 


工廠方法模式

概述

  工廠方法模式是對簡單工廠模式的改進,它去掉了簡單工廠模式中工廠方法(例如,createFruit()這個方法)的靜態屬性,使得該方法能夠被子類繼承,將簡單工廠模式中在靜態工廠方法中集中建立物件的操作轉移到各子類中完成,從而減輕了父類方法的負擔。

工廠方法模式包括4個主要的角色:

  ➷ 抽象工廠類(AbstractFactory):工廠方法模式的核心,是具體工廠類必須實現的介面或者必須繼承的抽象父類。

  ➷ 具體工廠類(ConcreteFactoryA 或者 ConcreteFactoryB):由具體的類來實現,用於建立工廠類的物件,它必須實現抽象工廠的方法。

  ➷ 抽象產品類(Product):具體產品繼承的抽象父類或者實現的介面,定義了產品類的方法。

  ➷ 具體產品類(ProductA 或ProductB):具體工廠類產生的物件就是具體產品類的物件。

UML圖

場景描述

  接上文,隨著生產規模的不斷擴大,一個工廠一套裝置生產蘋果和香蕉已經不能滿足公司的業務需求,所以公司的銷售給老總建議,如果把蘋果和香蕉的生產線分開生產效率會更高。原因有兩點:一是兩條生產線互不干擾進度,二是將來可方便增補其他水果的生產線從而擴充套件業務。

  於是老總將原來的水果工廠進一步抽象整合,成立了一個水果公司集團(AbstractFactory),集團管理兩家工廠,一家蘋果廠(ConcreteFactoryA),一家香蕉廠(ConcreteFactoryB),集團只制定統一的規章制度,由這兩家廠具體生產產品,一家生產蘋果,一家生產香蕉,這就是工廠方法模式

場景角色對映

  ★ 抽象工廠:AbstractFactory→FruitCompany。

  FruitCompany 類是例項中描述的水果公司類,該類相當於工廠方法模式中的抽象工廠AbstractFactory,它只用來制定規章制度(生產水果的方法),實際的生產都交給下面的具體工廠去做。

  ★ 具體工廠:ConcreteFactoryA→AppleFactory,ConcreteFactoryB→BananaFactory。

  AppleFactory 類和 BananaFactory 類分別表示蘋果工廠類和香蕉工廠類,它們都要繼承自抽象工廠類FruitCompany,蘋果工廠按照水果公司的要求生產蘋果;香蕉工廠按照水果公司的要求生產香蕉。

  ★ 抽象產品:Product→Fruit。

  水果介面類,統一定義蘋果和香蕉的處理方法。

  ★ 具體產品:ProductA→Apple,ProductB→Banana。

程式碼演示

抽象產品父類

package com.design;

// 抽象產品父類
abstract class Fruit {
    // 構造方法
    public Fruit(){
        System.out.println("生產原料準備中。。");
    };
    // 定義水果的處理方法
    public abstract void operateFruit();
}

ProductA(Apple)

package com.design;

// 產品A
public class Apple extends Fruit{

    @Override
    public void operateFruit() {
        // 蘋果的生產過程
        System.out.println("開始生產");
        System.out.println("蘋果:生產中。。。");
        System.out.println("蘋果:加工中。。。");
        System.out.println("蘋果:包裝中。。。");
        System.out.println("生產結束");
        System.out.println("這是一個蘋果");
    }

}

ProductB(Banana)

package com.design;

// 產品B
public class Banana extends Fruit{

    @Override
    public void operateFruit() {
        // 香蕉的生產過程
        System.out.println("開始生產");
        System.out.println("香蕉:生產中。。。");
        System.out.println("香蕉:加工中。。。");
        System.out.println("香蕉:包裝中。。。");
        System.out.println("生產結束");
        System.out.println("這是一個香蕉");
    }

}

抽象工廠類

package com.design;

// 抽象工廠類
abstract class FruitCompany {
    
    // 宣告一個統一的水果生產方法
    public abstract Fruit createFruit();
}

具體工廠A(AppleFactory)

package com.design;

// 蘋果工廠
public class AppleFactory extends FruitCompany{

    @Override
    public Fruit createFruit() {
        Fruit fruit = new Apple();
        return fruit;
    }

}

具體工廠B(BananaFactory)

package com.design;

// 香蕉工廠
public class BananaFactory extends FruitCompany{

    @Override
    public Fruit createFruit() {
        Fruit fruit = new Banana();
        return fruit;
    }

}

測試:

package com.design;

public class TestFactory {

    public static void main(String[] args) {
        // 建立工廠
        FruitCompany factoryA = new AppleFactory();
        FruitCompany factoryB = new BananaFactory();
        
        // 1.生產蘋果
        Fruit apple = factoryA.createFruit();
        apple.operateFruit();
        
        System.out.println("------------------------------------");
        
        // 2.生產香蕉
        Fruit banana = factoryB.createFruit();
        banana.operateFruit();
    }
}

執行結果:

  工廠方法模式對簡單工廠模式進行了改進,不是用一個工廠類來生產物件,而是用不同的具體工廠子類 AppleFactory 或 BananaFactory來生產不同的物件。

 


抽象工廠模式 

概述

  抽象工廠模式又是工廠方法模式的升級版本。它的主要思想:提供了一個建立一系列相關或者相互依賴物件的介面。

  它和工廠方法模式的區別:抽象工廠模式針對的是有多個產品(稱為產品族)的建立模式(蘋果廠生產蘋果、蘋果脯;香蕉廠生產香蕉、香蕉幹);而工廠方法針對的只是一種產品的建立模式(蘋果廠生產蘋果;香蕉廠生產香蕉)。抽象工廠模式中的抽象工廠介面裡有多個工廠方法。

抽象工廠模式包括以下主要角色:

  ➷ 抽象工廠類(AbstractFactory):模式的核心,是具體工廠類必須實現的介面或者必須繼承的抽象父類。

  ➷ 具體工廠類(ConcreteFactoryA 或者 ConcreteFactoryB):由具體的類來實現,用於建立工廠類的物件,它必須實現抽象工廠的方法。

  ➷ 抽象產品(AbstractProductA和AbstractProductB):具體產品繼承的父類或者實現的介面,在Java中由抽象類或者介面實現。

  ➷ 具體產品(ProductA*或ProductB*):具體工廠類產生的物件就是具體產品類的物件。每一個抽象產品都可以有多個具體產品實現類或者繼承子類,稱為產品族。

UML圖

場景描述

  接上文,水果公司集團的經營良好,發展規模越來越大,集團全方位發展,產品也開始不再單一,而是向多個產品線(產品族)發展。原來的蘋果廠不再單一地生產蘋果,它開始生產蘋果脯等深加工產品。香蕉廠也同樣生產香蕉乾等產品,這就形成了所謂的抽象工廠模式

場景角色對映

  ★ 抽象工廠類:AbstractFactory→FruitGroup。

  成立一個水果集團(FruitGroup),該集團就相當於工廠方法模式中的抽象工廠,它的作用只用來制定規章制度(生產水果的方法),實際的生產都交給下面的具體工廠去做,下屬多個工廠,有蘋果廠、香蕉廠等。

  ★ 具體工廠類:ConcreteFactoryA→AppleFactory;Concrete FactoryB→BananaFactory。

  AppleFactory 類和 BananaFactory 類分別表示蘋果工廠類和香蕉工廠類,它們都要實現抽象工廠FruitGroup,蘋果工廠按照水果集團的要求生產蘋果及蘋果脯;香蕉工廠生產按照水果集團的要求生產香蕉和香蕉幹。

  ★ 抽象產品類:AbstractProductA→Fruit;AbstractProductB→DriedFruit。

  Furit 類和 DriedFruit類分別為水果和水果脯類的抽象父類,具體產品類要繼承這兩個類。

  ★ 具體產品:ProductA1→Apple,ProductA2→Banana;ProductB1→DriedApple,ProductB2→DriedBanana。

程式碼演示

抽象產品類(Fruit)

package com.design;

// 抽象Fruit產品父類
abstract class Fruit {
    // 構造方法
    public Fruit(){
        System.out.println("生產原料準備中。。");
    };
    // 定義水果的處理方法
    public abstract void operateFruit();
}

具體產品(Apple)

package com.design;

// Fruit產品A
public class Apple extends Fruit{

    @Override
    public void operateFruit() {
        // 蘋果的生產過程
        System.out.println("開始生產");
        System.out.println("蘋果:生產中。。。");
        System.out.println("蘋果:加工中。。。");
        System.out.println("蘋果:包裝中。。。");
        System.out.println("生產結束");
        System.out.println("這是一個蘋果");
    }

}

具體產品(Banana)

package com.design;

// Fruit產品B
public class Banana extends Fruit{

    @Override
    public void operateFruit() {
        // 香蕉的生產過程
        System.out.println("開始生產");
        System.out.println("香蕉:生產中。。。");
        System.out.println("香蕉:加工中。。。");
        System.out.println("香蕉:包裝中。。。");
        System.out.println("生產結束");
        System.out.println("這是一個香蕉");
    }

}

抽象產品類(DriedFruit)

package com.design;

// 抽象果脯產品父類
abstract class DriedFruit {

    // 果脯生產方法
    public abstract void operateProduct();
}

具體產品(DriedApple)

package com.design;

// 蘋果乾
public class DriedApple extends DriedFruit{

    @Override
    public void operateProduct() {
        System.out.println("生產了一個蘋果乾");
    }

}

具體產品(DriedBanana)

package com.design;

// 香蕉幹
public class DriedBanana extends DriedFruit{

    @Override
    public void operateProduct() {
        System.out.println("生產了一個香蕉幹");
    }

}

抽象工廠類

package com.design;

// 抽象工廠類,水果集團:生產水果和果脯。
abstract class FruitGroup {

    // 生產水果
    public abstract Fruit createFruit();
    // 生產果脯
    public abstract DriedFruit createDriedFruit();
    
}

具體工廠類(AppleFactory)

package com.design;

// 蘋果工廠
public class AppleFactory extends FruitGroup{

    @Override
    public Fruit createFruit() {
        Fruit fruit = new Apple();
        return fruit;
    }

    @Override
    public DriedFruit createDriedFruit() {
        DriedFruit driedFruit = new DriedApple();
        return driedFruit;
    }

}

具體工廠類(BananaFactory)

package com.design;

// 香蕉工廠
public class BananaFactory extends FruitGroup{

    @Override
    public Fruit createFruit() {
        Fruit fruit = new Banana();
        return fruit;
    }

    @Override
    public DriedFruit createDriedFruit() {
        DriedFruit driedFruit = new DriedBanana();
        return driedFruit;
    }
    

}

測試:

package com.design;

public class TestFactory {

    public static void main(String[] args) {
        // 建立工廠
        FruitGroup factoryA = new AppleFactory();
        FruitGroup factoryB = new BananaFactory();
        
        // 1.生產蘋果
        Fruit apple = factoryA.createFruit();
        apple.operateFruit();
        
        // 生產蘋果乾
        DriedFruit driedApple = factoryA.createDriedFruit();
        driedApple.operateProduct();
        
        System.out.println("------------------------------------");
        
        // 2.生產香蕉
        Fruit banana = factoryB.createFruit();
        banana.operateFruit();
        
        // 生產香蕉幹
        DriedFruit driedBanana = factoryA.createDriedFruit();
        driedBanana.operateProduct();
        
    }
}

執行結果:

  抽象工廠是一個超級工廠,它生產了多種產品而不只是一種產品(例如,蘋果工廠既可以生產蘋果也可以生產蘋果脯),因此它解決了工廠方法模式下一個工廠只能生產一種產品的侷限問題。

  抽象工廠模式隔離了具體類的生成,所有的具體工廠都實現了抽象工廠中定義的公共介面,因此只需要改變具體工廠的程式碼,就能改變某個工廠的行為。

 


工廠模式總結

  工廠模式,顧名思義就是生產物件的。

根據其抽象程度可分為以下三種:

簡單工廠模式

  一個工廠(具體工廠),生產多種產品,根據所傳型別,生產相應的產品。

➷ 優點:

  簡單工廠類功能統一,使得外界從建立具體物件中分離出來,不用負責具體建立哪個類的物件,只要呼叫方法,由簡單工廠統一建立即可。

➷ 缺點:

  簡單工廠類中集中了所有要建立的物件,如果需要新增新的建立物件型別,就必須改變簡單工廠類的程式碼,這將違背開放封閉原則。這種情況在工廠方法模式中得到了部分解決。

工廠方法模式

  一個公司(抽象工廠),生產多種產品,公司下面有多個工廠(具體工廠),每個工廠只生產一種產品。

➷ 優點:

  解決了簡單工廠模式中靜態工廠方法在新增物件型別分支時需要修改方法程式碼的問題。在工廠方法模式中新增物件型別只需新增程式碼即可,不需要修改已有的程式碼,這樣遵循了開放封閉原則,即對擴充套件開放,對修改封閉的原則。

➷ 缺點:

  每增加一個產品型別,相應地也要增加一個子工廠,加大了額外的開發量。

抽象工廠模式

  一個集團(抽象工廠),生產多個產品族的產品,集團下面有多個工廠(具體工廠),每個工廠只生產一個產品族的多種產品。

➷ 優點:

  抽象工廠模式既具有工廠方法模式的上述優點,同時又解決了工廠方法模式一個具體工廠只能建立一類產品的侷限,抽象工廠模式中每個工廠可以建立多種型別的產品(產品族)。同時,如果需要生產新的產品(不新增產品族),只需要擴充套件該產品對應的具體工廠和該產品的具體實現類,不需要改變抽象工廠類和抽象產品類。 

➷ 缺點:

  抽象工廠最大的缺點是產品族難以擴充套件。假如產品族中需要增加一個新的產品,則幾乎所有的工廠類都需要進行修改。所以使用抽象工廠模式時對產品等級結構的劃分是非常重要的。

 

相關文章