略懂設計模式之工廠模式

靚仔聊程式設計發表於2021-08-08

前言

工廠模式應該是大家的老朋友了,相信很多朋友在學習和工作中一定遇到過,但是不一定很瞭解,這篇文章將通過幾個例子,帶大家一起進一步瞭解工廠模式。

簡介

工廠模式(Factory Pattern)是 Java 中最常用的設計模式之一。 這種型別的設計模式屬於建立型模式 ,它提供了一種建立物件的最佳方式。

在建立型模式中工廠模式是比較重要的一種,之所以名稱中包含“工廠”二字,是因為用工廠代替了 new 操作,將物件例項化的過程交給工廠來實現。

工廠模式關心的是最終建立的物件, 而不關心建立的過程。 舉個例子,好比您需要一輛汽車,可以直接從工廠裡面提貨,而不用去管這輛汽車是怎麼做出來的,以及這個汽車裡面的具體實現。

工廠模式可以分為三類:

  • 簡單工廠模式 (Simple Factory)

  • 工廠方法模式 (Factory Method)

  • 抽象工廠模式 (Abstract Factory)

其中簡單工廠模式並不屬於23種 GOF 設計模式之一,而是將其看作工廠方法模式的一種特例,兩者歸為一類。

簡單工廠模式

簡單工廠模式又叫靜態工廠模式,由一個工廠類根據傳入的引數,動態決定應該建立哪一個產品類(繼承自一個父類或介面)的例項。

簡單工廠模式的主要組成:

工廠(Factory): 負責實現建立所有例項的內部邏輯,並提供一個外界呼叫的方法,建立所需的產品物件

抽象產品(Product): 負責描述產品的公共介面

具體產品(ConcreteProduct): 描述生產的具體產品

這裡我們就以生產汽車為例:

img

首先定義產品,先想好要生產什麼:

// 汽車基類
public abstract class Car {
    // 輸出汽車資訊
    public abstract void printInfo();
}

// 比亞迪汽車
public class BydCar extends Car{
    @Override
    public void printInfo() {
        System.out.println("這是比亞迪汽車");
    }
}

// 吉利汽車
public class GeelyCar extends Car{
    @Override
    public void printInfo() {
        System.out.println("這是吉利汽車");
    }
}

然後定義工廠類,想好生產什麼產品之後,就要建工廠了:

// 汽車工廠類
public class CarFactory {
    // 生產汽車
    public static Car productionCar(String brand) {
        if ("geely".equals(brand)) {
            return new GeelyCar();
        } else if ("byd".equals(brand)) {
            return new BydCar();
        } else {
            return null;
        }
    }
}

工廠建好了,有了比亞迪和吉利兩條生產線,就可以大膽生產汽車了:

public class FactoryPatternDemo {
    public static void main(String[] args) {
        Car car = CarFactory.productionCar("byd");
        car.printInfo();
    }
}
// 輸出:這是比亞迪汽車

是不是很簡單,這時候可能有朋友就要問了,那我想要紅旗汽車怎麼辦,還得再建立一個紅旗汽車類,然後修改工廠類的判斷邏輯,顯然這是違背開閉原則的。

那麼可不可以不修改工廠類裡的邏輯呢?

當然可以,還有一種方式是通過反射來建立具體的產品,我們熟悉的 Spring 的 BeanFactory 就是採用反射的方式實現的。

還是汽車工廠類,我們修改一下程式碼:

public class CarFactory {
    public static Car productionCar(Class c){
        Car car = null;
        try {
            car = (Car) Class.forName(c.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return car;
    }
}

反射的方式看起來程式碼簡潔多了是吧,但是某些情況下並不合適,而且反射對程式效能也會有影響。

簡單工廠模式適用於業務簡單的情況下,而對於複雜的業務環境可能就不太適用了。這個時候就要工廠方法模式登場了。

工廠方法模式

在簡單工廠模式中,工廠負責所有產品的生產,就像上面例子中,一個汽車工廠負責所有汽車的生產。而工廠方法模式則是將工廠類抽象化,把生成具體產品的任務分發給繼承抽象方法的具體的產品工廠。

工廠方法模式的主要組成:

抽象工廠(Abstract Factory):描述具體工廠的公共介面

具體工廠(ConcreteFactory):描述具體工廠,建立產品的例項,供外界呼叫

抽象產品(Product):負責描述產品的公共介面

具體產品(ConcreteProduct):描述生產的具體產品

我們還是以生產汽車為例:

img

將簡單工廠方法中的工廠類改為抽象工廠類,再建立具體工廠類來實現汽車的生產工作:

// 抽象汽車工廠類
public abstract class AbstractCarFactory {
    // 生產汽車
    abstract Car productionCar();
}
​
// 比亞迪汽車工廠
public class BydCarFactory extends AbstractCarFactory{
    @Override
    Car productionCar()
        return new BydCar();
    }
}

// 吉利汽車工廠
public class GeelyCarFactory extends AbstractCarFactory{
    @Override
    Car productionCar()
        return new GeelyCar();
    }
}

這下要生產什麼品牌的汽車,就要交給具體的工廠了:

public class FactoryPatternDemo {
    public static void main(String[] args) {
        AbstractCarFactory factory = new BydCarFactory();
        Car car = factory.productionCar();
        car.printInfo();
    }
}
// 輸出:這是比亞迪汽車

工廠方法模式看起來要比簡單工廠模式更復雜一些,每增加一個新的產品就要增加一個工廠,但是他提高了系統的可擴充套件性和可維護性,完全符合開閉原則。

我們可能遇到的大部分業務需求使用工廠方法模式足以應付,但是凡事都有特殊情況。當產品種類更加複雜,存在產品族的時候,就要使用抽象工廠模式了。

抽象工廠模式

在介紹抽象工廠模式前,我們先了解下產品族是什麼:位於不同產品等級結構中,功能相關聯的產品組成的家族。

沒有理解的話可以看下圖:

img

圖中,比亞迪的商務汽車和吉利的商務汽車都屬於商務車產品族,運動車產品族同理。

抽象工廠模式提供了一種方式,可以將同一產品族的單獨的工廠封裝起來。它是三種工廠模式裡面最為抽象、最具一般性的。

抽象工廠模式和工廠方法模式一樣,都符合開閉原則。但是不同的是,工廠方法模式在增加一個具體產品的時候,都要增加對應的工廠。但是抽象工廠模式只有在新增一個型別的具體產品時才需要新增工廠。也就是說,工廠方法模式的一個工廠只能建立一個具體產品。而抽象工廠模式的一個工廠可以建立屬於一類型別的多種具體產品。工廠建立產品的個數介於簡單工廠模式和工廠方法模式之間。

抽象工廠模式的主要組成:

抽象工廠(AbstractFactory):描述具體工廠的公共介面

具體工廠(ConcreteFactory):描述具體工廠,建立產品的例項,供外界呼叫

抽象產品(族)(AbstractProduct):描述抽象產品的公共介面

具體產品(ConcreteProduct):描述具體產品的公共介面

同樣以生產汽車為例:

img

建立商務汽車產品族和運動汽車產品族相關類:

// 商務汽車抽象類
public abstract class BusinessCar {
    public abstract void printInfo();
}

// 比亞迪商務汽車
public class BydBusinessCar extends BusinessCar{
    @Override
    public void printInfo() {
        System.out.println("這是比亞迪商務汽車");
    }
}

// 吉利商務汽車
public class GeelyBusinessCar extends BusinessCar{
    @Override
    public void printInfo(){
        System.out.println("這是吉利商務汽車");
    }
}

// 運動汽車抽象類
public abstract class SportCar {
    public abstract void printInfo();
}

// 比亞迪運動汽車
public class BydSportCar extends SportCar{
    @Override
    public void printInfo()
 {
        System.out.println("這是比亞迪運動汽車");
    }
}

// 吉利運動汽車
public class GeelySportCar extends SportCar{
    @Override
    public void printInfo()
{
        System.out.println("這是吉利運動汽車");
    }
}

建立抽象工廠類和具體工廠類:

// 抽象汽車工廠類
public abstract class AbstractCarFactory {
    // 生產商務汽車
    abstract BusinessCar productionBusinessCar();
    // 生產運動汽車
    abstract SportCar productionSportCar();
}

// 比亞迪汽車工廠
public class BydCarFactory extends AbstractCarFactory{
    @Override
    public BusinessCar productionBusiness() {
        return new BydBusinessCar();
    }
    
    @Override
    public SportCar productionSport() {
        return new BydSportCar();
    }
}

// 吉利汽車工廠
public class GeelyCarFactory extends AbstractCarFactory{
    @Override
    public BusinessCar productionBusiness() {
        return new GeelyBusinessCar();
    }
    
    @Override
    public SportCar productionSport() {
        return new GeelySportCar();
    }
}

開始生產汽車:

public class FactoryPatternDemo {
    public static void main(String[] args) {
        AbstractCarFactory factory = new BydCarFactory();
        BusinessCar car = factory.BydBusinessCar();
        car.printInfo();
    }
}
// 輸出:這是比亞迪商務汽車

抽象工廠模式除了具有工廠方法模式的優點外,最主要的優點就是可以在類的內部對產品族進行約束。但是產品族的擴充套件將是一件十分費力的事情,假如產品族中需要增加一個新的產品,則幾乎所有的工廠類都需要進行修改,不能很好地支援開閉原則。所以使用抽象工廠模式時,對產品等級結構的劃分是非常重要的。

總結

三種工廠模式都各有優缺點,合適的才是最好的,希望大家能夠理解並靈活運用,讓自己的程式碼變得更優雅!

END

往期推薦

吳亦凣事件告訴我們,不懂中間人攻擊會吃大虧

就這?Spring 事務失效場景及解決方案

就這?一篇文章讓你讀懂 Spring 事務

SpringBoot+Redis 實現訊息訂閱釋出

什麼?你還不會在GitHub上搜尋資源?還不點進來看看?

更多精彩推薦,請關注公眾號【靚仔聊程式設計】

相關文章