在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);
}
然後分別創建了: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
。通過這種方式,簡化了用戶對連接池的選擇配置。