在Java(或者叫作面嚮對象語言)的世界中,工廠模式被普遍應用於項目中,也許你並無據說過,不過也許你已經在使用了。 簡單來講,工廠模式的出現源於增長程序序的可擴展性,下降耦合度。之因此叫作工廠模式,是用工廠生產產品來形象的比喻代碼中生產對象的過程。整體來講,工廠模式分爲如下幾種:html
咱們模擬一種場景,有一家汽車廠(AutoFactory)要生產汽車,如今主要生產小轎車(Car)和大巴車(Bus),那用代碼模擬以下:java
首先「設計」一個汽車原型(定義汽車接口),這個接口體現了全部汽車的共性:設計模式
public interface Auto { //全部汽車均可以被駕駛 public void drive(); }
接下來咱們「設計」兩種汽車:小轎車和大巴車:ide
//小轎車 public class Car implements Auto{ @Override public void drive(){ System.out.println(「小轎車啓動了」); } }
//大巴車 public class Bus implements Auto{ @Override public void drive(){ System.out.println(「大巴車啓動了」); } }
開始「建廠」了,咱們實現一個簡單工廠類:ui
public class AutoFactory{ //生產汽車 public Auto produce(String name){ if("car".equals(name)){ return new Car(); } else if("bus".equals(name)){ return new Bus(); } } }
一切就緒,咱們開始生產汽車了,先生產一輛小轎車:.net
AutoFactory factory = new AutoFactory(); Auto car = factory.produce("car"); car.drive();
簡單工廠模式實現了生成產品類的代碼跟具體的產品實現分離,在工廠類中你能夠添加所需的生成產品的邏輯代碼,可是問題來了,這不符合「開放-封閉」原則的,也就是說對擴展開放,對修改關閉,若是你要加一個新的汽車類型還須要修改produce方法,爲解決這個問題,從而引入了工廠方法模式(Factory Method Pattern)。設計
工廠爲了擴大市場,如今要開始生產卡車(Truck)了,因而咱們設計一輛卡車:code
//卡車 public class Truck implements Auto{ @Override public void drive(){ System.out.println(「卡車啓動了」); } }
若是按照簡單工廠的邏輯,須要修改produce方法(也就是咱們要改造已有工廠),這樣會影響已有生產,怎麼辦呢?解決辦法是再新建新的工廠:htm
首先咱們「設計」一個工廠原型(工廠接口):對象
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。
工廠方法的結構圖以下:
咱們繼續針對汽車工廠說明,因爲接下來工廠須要繼續擴大規模,開始涉足汽車配件,上層決定涉足汽車大燈業務,針對已有車型生產前大燈。可是若是按照工廠方法模式,須要再繼續新建一批工廠,針對每種汽車再建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-db模塊爲了簡化和抽象鏈接池的建立,使用了工廠方法模式,首先定義了DSFactory
:
//數據源工廠 public abstract class DSFactory { //獲取數據源 public abstract DataSource getDataSource(String group); }
而後分別建立了:HikariDSFactory
、DruidDSFactory
、TomcatDSFactory
、DbcpDSFactory
、C3p0DSFactory
幾種常見鏈接池的工廠實現,這樣用戶能夠很容易的使用對應的鏈接池工廠建立須要的鏈接池數據源(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
。經過這種方式,簡化了用戶對鏈接池的選擇配置。