1. 程式人生 > >Java設計模式學習之工廠模式

Java設計模式學習之工廠模式

在Java(或者叫做面嚮物件語言)的世界中,工廠模式被廣泛應用於專案中,也許你並沒有聽說過,不過也許你已經在使用了。Java 設計模式之工廠模式 簡單來說,工廠模式的出現源於增加程式序的可擴充套件性,降低耦合度。之所以叫做工廠模式,是用工廠生產產品來形象的比喻程式碼中生產物件的過程。總體來說,工廠模式分為以下幾種:

  • 簡單工廠模式(Simple Factory Pattern)
  • 工廠方法模式(Factory Method Pattern)
  • 抽象工廠模式(Abstract Factory Pattern)

簡單工廠模式(Simple Factory Pattern)

我們模擬一種場景,有一家汽車廠(AutoFactory)要生產汽車,現在主要生產小轎車(Car)和大巴車(Bus),那用程式碼模擬如下:

首先“設計”一個汽車原型(定義汽車介面),這個介面體現了所有汽車的共性:


public interface Auto {
    //所有汽車都可以被駕駛
    public void drive();
}

接下來我們“設計”兩種汽車:小轎車和大巴車:


//小轎車
public class Car implements Auto{
    @Override
    public void drive(){
        System.out.println(“小轎車啟動了”);
    }
}

//大巴車
public class Bus implements Auto{
    @Override
    public void drive(){
        System.out.println(“大巴車啟動了”);
    }
}

開始“建廠”了,我們實現一個簡單工廠類:


public class AutoFactory{
    //生產汽車
    public Auto produce(String name){
        if("car".equals(name)){
            return new Car();
        } else if("bus".equals(name)){
            return new Bus();
        }
    }
}

一切就緒,我們開始生產汽車了,先生產一輛小轎車:


AutoFactory factory = new AutoFactory();
Auto car = factory.produce("car");
car.drive();

簡單工廠模式實現了生成產品類的程式碼跟具體的產品實現分離,在工廠類中你可以新增所需的生成產品的邏輯程式碼,但是問題來了,這不符合“開放-封閉”原則的,也就是說對擴充套件開放,對修改關閉,如果你要加一個新的汽車型別還需要修改produce方法,為解決這個問題,從而引入了工廠方法模式(Factory Method Pattern)。

工廠方法模式(Factory Method Pattern)

工廠為了擴大市場,現在要開始生產卡車(Truck)了,於是我們設計一輛卡車:


//卡車
public class Truck implements Auto{
    @Override
    public void drive(){
        System.out.println(“卡車啟動了”);
    }
}

如果按照簡單工廠的邏輯,需要修改produce方法(也就是我們要改造已有工廠),這樣會影響已有生產,怎麼辦呢?解決辦法是再新建新的工廠:

首先我們“設計”一個工廠原型(工廠介面):


public interface IAutoFactory{
    //生產汽車
    public Auto produce(String name);
}

然後將原來的工廠簡單改造符合設計好的工廠原型(實現介面即可,所有邏輯不變):


public class AutoFactory implements IAutoFactory{
    //生產汽車
    @Override
    public Auto produce(String name){
        if("car".equals(name)){
            return new Car();
        } else if("bus".equals(name)){
            return new Bus();
        }
    }
}

好的,接下來為了生產卡車,我們要為卡車單獨建廠:


public class TruckAutoFactory implements IAutoFactory{
    //生產卡車
    @Override
    public Auto produce(String name){
        return new Truck();
    }
}

開始生產卡車:


IAutoFactory factory = new TruckAutoFactory();
Auto car = factory.produce(null);
car.drive();

這裡的抽象工廠中,我們為了減少改造成本,在簡單工廠基礎上做最小修改,理論上produce引數可以沒有,然後為小轎車、大巴車和卡車分別建立工廠,分別生產。這樣如果有了新的型別的車,可以不改動之前的程式碼,新建一個“工廠”即可,做到“開放封閉原則”。

雖然看似類變多了,邏輯複雜了,但是這種改造帶來的好處也是顯而易見的:不變動老的程式碼,通過新建工廠類完成新功能的新增,老功能不變,最大限度的避免動了老程式碼的邏輯導致引入新的bug。

工廠方法的結構圖如下:

抽象工廠模式(Abstract Factory Pattern)

我們繼續針對汽車工廠說明,由於接下來工廠需要繼續擴大規模,開始涉足汽車配件,上層決定涉足汽車大燈業務,針對已有車型生產前大燈。但是如果按照工廠方法模式,需要再繼續新建一批工廠,針對每種汽車再建N個工廠,考慮到成本和簡單性,針對對已有汽車工廠改造。

首先“設計”大燈原型:


//大燈
public interface Light {
    //開燈
    public void turnOn();
}

再“設計”小轎車、大巴車和卡車大燈:


//小轎車大燈
public class CarLight implements Light{
    @Override
    public void tunOn(){
        System.out.println(“小轎車大燈亮了”);
    }
}

//大巴車大燈
public class BusLight implements Light{
    @Override
    public void tunOn(){
        System.out.println(“大巴車大燈亮了”);
    }
}

//卡車大燈
public class TruckLight implements Light{
    @Override
    public void tunOn(){
        System.out.println(“卡車大燈亮了”);
    }
}

接下來我們重新“設計”原有的汽車工廠(修改工廠介面或者抽象工廠類)


public interface IAutoFactory{
    //生產汽車
    public Auto produce();
    //生產大燈
    public Light produceLight();
}

好的,改造工廠,首先改造小轎車工廠:


public class CarAutoFactory implements IAutoFactory{
    //生產汽車
    @Override
    public Auto produce(){
        return new Car();
    }

    //生產車燈
    @Override
    public Light produceLight(){
        return new CarLight();
    }
}

改造大巴車工廠:


public class BusAutoFactory implements IAutoFactory{
    //生產汽車
    @Override
    public Auto produce(){
        return new Bus();
    }

    //生產車燈
    @Override
    public Light produceLight(){
        return new BusLight();
    }
}

改造卡車工廠:


public class TruckAutoFactory implements IAutoFactory{
    //生產汽車
    @Override
    public Auto produce(){
        return new Truck();
    }

    //生產車燈
    @Override
    public Light produceLight(){
        return new TruckLight();
    }
}

開始生產:


//生產小轎車和小轎車大燈
IAutoFactory factory = new CarAutoFactory();
Auto car = factory.produce();
car.drive();
Light light = factory.produceLight();
light.turnOn();

//生產大巴車和小大巴車大燈
IAutoFactory factory = new BusAutoFactory();
Auto bus = factory.produce();
bus.drive();
Light light = factory.produceLight();
light.turnOn();

//生產卡車和卡大燈
IAutoFactory factory = new TruckAutoFactory();
Auto truck = factory.produce();
truck.drive();
Light light = factory.produceLight();
light.turnOn();

抽象工廠模式中我們可以定義實現不止一個介面,一個工廠也可以生成不止一個產品類,抽象工廠模式較好的實現了“開放-封閉”原則,是三個模式中較為抽象,並具一般性的模式。

抽象工廠模式示意圖如下:

參考資料

工廠模式在Hutool中的應用

Hutool中,Hutool-db模組為了簡化和抽象連線池的建立,使用了工廠方法模式,首先定義了DSFactory


//資料來源工廠
public abstract class DSFactory {
    //獲取資料來源
    public abstract DataSource getDataSource(String group);
}

然後分別建立了:HikariDSFactoryDruidDSFactoryTomcatDSFactoryDbcpDSFactoryC3p0DSFactory幾種常見連線池的工廠實現,這樣使用者可以很容易的使用對應的連線池工廠建立需要的連線池資料來源(DataSource)。

同樣,使用者也可以自己繼承DSFactory實現抽象方法getDataSource來自定義資料來源。

在此基礎上,對於資料來源工廠的建立,又使用了簡單工廠模式,程式碼如下:


private static DSFactory doCreate(Setting setting) {
    try {
        return new HikariDSFactory(setting);
    } catch (NoClassDefFoundError e) {
        //ignore
    }
    try {
        return new DruidDSFactory(setting);
    } catch (NoClassDefFoundError e) {
        //ignore
    }
    try {
        return new TomcatDSFactory(setting);
    } catch (NoClassDefFoundError e) {
        //ignore
    }
    try {
        return new DbcpDSFactory(setting);
    } catch (NoClassDefFoundError e) {
        //ignore
    }
    try {
        return new C3p0DSFactory(setting);
    } catch (NoClassDefFoundError e) {
        //ignore
    }
    // 預設使用Hutool實現的簡易連線池
    return new PooledDSFactory(setting);
}

通過try的方式,按照優先順序嘗試建立對應連線池的工廠類,如果使用者沒有引入對應連線池庫,就會報NoClassDefFoundError異常,從而嘗試建立下一個連線池工廠,依次類推,直到發現使用者未引入任何連線池庫,則使用Hutool預設的簡單連線池PooledDSFactory。通過這種方式,簡化了使用者對連線池的選擇配置。

原文連結:https://my.oschina.net/looly/blog/1860776