一、工廠設(shè)計(jì)模式介紹
在日常開(kāi)發(fā)中扛拨,凡是需要生成復(fù)雜對(duì)象的地方耘分,都可以嘗試考慮使用工廠模式來(lái)代替。
復(fù)雜對(duì)象指的是類的構(gòu)造函數(shù)參數(shù)過(guò)多等對(duì)類的構(gòu)造有影響的情況,因?yàn)轭惖臉?gòu)造過(guò)于復(fù)雜求泰,如果直接在其他業(yè)務(wù)類內(nèi)使用央渣,則兩者的耦合過(guò)重,后續(xù)業(yè)務(wù)更改渴频,就需要在任何引用該類的源代碼內(nèi)進(jìn)行更改芽丹,光是查找所有依賴就很消耗時(shí)間了,更別說(shuō)要一個(gè)一個(gè)修改了枉氮。
按實(shí)際業(yè)務(wù)場(chǎng)景劃分,工廠模式有 3 種不同的實(shí)現(xiàn)方式暖庄,分別是簡(jiǎn)單工廠模式聊替、工廠方法模式和抽象工廠模式。
二培廓、簡(jiǎn)單工廠模式
1.什么是簡(jiǎn)單工廠模式
我們把被創(chuàng)建的對(duì)象稱為“產(chǎn)品”惹悄,把創(chuàng)建產(chǎn)品的對(duì)象稱為“工廠”。如果要?jiǎng)?chuàng)建的產(chǎn)品不多肩钠,只要一個(gè)工廠類就可以完成泣港,這種模式叫“簡(jiǎn)單工廠模式”。
在簡(jiǎn)單工廠模式中創(chuàng)建實(shí)例的方法通常為靜態(tài)(static)方法价匠,因此簡(jiǎn)單工廠模式(Simple Factory Pattern)又叫作靜態(tài)工廠方法模式(Static Factory Method Pattern),本文給的案例沒(méi)有將方法改為靜態(tài)方法当纱,讀者可以自行嘗試。
2.簡(jiǎn)單工廠模式的結(jié)構(gòu)
簡(jiǎn)單工廠模式的主要角色如下:
- 簡(jiǎn)單工廠(SimpleFactory):是簡(jiǎn)單工廠模式的核心踩窖,負(fù)責(zé)實(shí)現(xiàn)創(chuàng)建所有實(shí)例的內(nèi)部邏輯坡氯。工廠類的創(chuàng)建產(chǎn)品類的方法可以被外界直接調(diào)用,創(chuàng)建所需的產(chǎn)品對(duì)象洋腮。
- 抽象對(duì)象(Product):是簡(jiǎn)單工廠創(chuàng)建的所有對(duì)象的父類箫柳,負(fù)責(zé)描述所有實(shí)例共有的公共接口。
-
具體對(duì)象(Television啥供、Fan悯恍、Computer):是簡(jiǎn)單工廠模式的創(chuàng)建目標(biāo)煤杀。
image.png
3.簡(jiǎn)單工廠模式的實(shí)現(xiàn)
public abstract class Product {
private String name;
/**
* 實(shí)現(xiàn)功能
*/
public abstract void doFunction();
public void setName(String name) {
this.name = name;
}
}
public class Computer extends Product {
@Override
public void doFunction() {
System.out.println("我可以用來(lái)打電腦游戲");
}
}
public class Fan extends Product {
@Override
public void doFunction() {
System.out.println("我可以用來(lái)吹風(fēng)");
}
}
public class Television extends Product {
@Override
public void doFunction() {
System.out.println("我可以用來(lái)看電視");
}
}
public class SimpleFactory {
private static final String COMPUTER = "computer";
private static final String FAN = "fan";
private static final String TELEVISION = "television";
public Product createProduct(String type){
Product product = null;
if (FAN.equals(type)) {
product = new Fan();
product.setName(type);
} else if (TELEVISION.equals(type)) {
product = new Television();
product.setName(type);
} else if (COMPUTER.equals(type)) {
product = new Computer();
product.setName(type);
}
return product;
}
}
4.簡(jiǎn)單工廠模式優(yōu)缺點(diǎn)與應(yīng)用場(chǎng)景
優(yōu)點(diǎn):
1)工廠類包含必要的邏輯判斷疲酌,可以決定在什么時(shí)候創(chuàng)建哪一個(gè)產(chǎn)品的實(shí)例∧孛客戶端可以免除直接創(chuàng)建產(chǎn)品對(duì)象的職責(zé)贷屎,很方便的創(chuàng)建出相應(yīng)的產(chǎn)品窒百。工廠和產(chǎn)品的職責(zé)區(qū)分明確。
2)客戶端無(wú)需知道所創(chuàng)建具體產(chǎn)品的類名豫尽,只需知道參數(shù)即可篙梢。
3)也可以引入配置文件,在不修改客戶端代碼的情況下更換和添加新的具體產(chǎn)品類美旧。缺點(diǎn):
1)簡(jiǎn)單工廠模式的工廠類單一渤滞,負(fù)責(zé)所有產(chǎn)品的創(chuàng)建贬墩,職責(zé)過(guò)重,一旦異常妄呕,整個(gè)系統(tǒng)將受影響陶舞。且工廠類代碼會(huì)非常臃腫,違背高聚合原則绪励。
2)使用簡(jiǎn)單工廠模式會(huì)增加系統(tǒng)中類的個(gè)數(shù)(引入新的工廠類)肿孵,增加系統(tǒng)的復(fù)雜度和理解難度
3)系統(tǒng)擴(kuò)展困難,一旦增加新產(chǎn)品不得不修改工廠邏輯疏魏,在產(chǎn)品類型較多時(shí)停做,可能造成邏輯過(guò)于復(fù)雜應(yīng)用場(chǎng)景
對(duì)于產(chǎn)品種類相對(duì)較少的情況,考慮使用簡(jiǎn)單工廠模式大莫。使用簡(jiǎn)單工廠模式的客戶端只需要傳入工廠類的參數(shù)蛉腌,不需要關(guān)心如何創(chuàng)建對(duì)象的邏輯,可以很方便地創(chuàng)建所需產(chǎn)品只厘。
三烙丛、工廠方法模式
1.什么是工廠方法模式
前面我們介紹了簡(jiǎn)單工廠模式,提到了簡(jiǎn)單工廠模式違背了開(kāi)閉原則羔味,而“工廠方法模式”是對(duì)簡(jiǎn)單工廠模式的進(jìn)一步抽象化河咽,其好處是可以使系統(tǒng)在不修改原來(lái)代碼的情況下引進(jìn)新的產(chǎn)品,即滿足開(kāi)閉原則赋元。
工廠方法模式(Factory Method Pattern)也被稱為多態(tài)工廠模式库北,其定義了一個(gè)創(chuàng)建某種產(chǎn)品的接口,但由子類決定要實(shí)例化的產(chǎn)品是哪一個(gè)们陆,從而把產(chǎn)品的實(shí)例化推遲到子類寒瓦。
2.工廠方法設(shè)計(jì)模式的結(jié)構(gòu)
工廠方法模式的主要角色如下:
- 抽象工廠(AbstractFactory):提供了創(chuàng)建產(chǎn)品的接口,調(diào)用者通過(guò)它訪問(wèn)具體工廠的工廠方法 crateProduct() 來(lái)創(chuàng)建產(chǎn)品坪仇。
- 具體工廠(FanFactory杂腰、ComputerFactory、TelevisionFactory):主要是實(shí)現(xiàn)抽象工廠中的抽象方法椅文,完成具體產(chǎn)品的創(chuàng)建喂很。
- 抽象產(chǎn)品(Product):定義了產(chǎn)品的規(guī)范,描述了產(chǎn)品的主要特性和功能皆刺。
-
具體產(chǎn)品(Televison少辣、Fan、Computer):實(shí)現(xiàn)了抽象產(chǎn)品角色所定義的接口羡蛾,由具體工廠來(lái)創(chuàng)建漓帅,它同具體工廠之間一一對(duì)應(yīng)。
image.png
3.工廠方法模式的實(shí)現(xiàn)
public abstract class Product {
private String name;
/**
* 實(shí)現(xiàn)功能
*/
public abstract void doFunction();
public void setName(String name) {
this.name = name;
}
}
public class Computer extends Product {
@Override
public void doFunction() {
System.out.println("我可以用來(lái)打電腦游戲");
}
}
public class Fan extends Product {
@Override
public void doFunction() {
System.out.println("我可以用來(lái)吹風(fēng)");
}
}
public class Television extends Product {
@Override
public void doFunction() {
System.out.println("我可以用來(lái)看電視");
}
}
public interface AbstractFactory {
Product createProduct();
}
public class ComputerFactory implements AbstractFactory{
@Override
public Product createProduct() {
return new Computer();
}
}
public class FanFactory implements AbstractFactory{
@Override
public Product createProduct() {
return new Fan();
}
}
public class TelevisionFactory implements AbstractFactory{
@Override
public Product createProduct() {
return new Television();
}
}
4.工廠方法模式優(yōu)缺點(diǎn)與應(yīng)用場(chǎng)景
優(yōu)點(diǎn):
1)用戶只需要知道具體工廠的名稱就可得到所要的產(chǎn)品,無(wú)須知道產(chǎn)品的2具體創(chuàng)建過(guò)程忙干。
2)靈活性增強(qiáng)器予,對(duì)于新產(chǎn)品的創(chuàng)建,只需多寫一個(gè)相應(yīng)的工廠類捐迫。
3)典型的解耦框架乾翔。高層模塊只需要知道產(chǎn)品的抽象類,無(wú)須關(guān)心其他實(shí)現(xiàn)類施戴,滿足迪米特法則反浓、依賴倒置原則和里氏替換原則。缺點(diǎn):
1)類的個(gè)數(shù)容易過(guò)多赞哗,增加復(fù)雜度
2)增加了系統(tǒng)的抽象性和理解難度
3)抽象產(chǎn)品只能生產(chǎn)一種產(chǎn)品雷则,此弊端可使用[抽象工廠模式解決。應(yīng)用場(chǎng)景:
1)客戶只知道創(chuàng)建產(chǎn)品的工廠名懈玻,而不知道具體的產(chǎn)品名巧婶。
2)創(chuàng)建對(duì)象的任務(wù)由多個(gè)具體子工廠中的某一個(gè)完成乾颁,而抽象工廠只提供創(chuàng)建產(chǎn)品的接口涂乌。
3)客戶不關(guān)心創(chuàng)建產(chǎn)品的細(xì)節(jié),只關(guān)心產(chǎn)品的品牌
三英岭、抽象工廠模式
1.什么是抽象工廠模式
本節(jié)要介紹的抽象工廠模式將考慮多等級(jí)產(chǎn)品的生產(chǎn)湾盒,將同一個(gè)具體工廠所生產(chǎn)的位于不同等級(jí)的一組產(chǎn)品稱為一個(gè)產(chǎn)品族,圖1所示的是海爾工廠和 TCL 工廠所生產(chǎn)的電視機(jī)與空調(diào)對(duì)應(yīng)的關(guān)系圖诅妹。
抽象工廠模式是工廠方法模式的升級(jí)版本罚勾,工廠方法模式只生產(chǎn)一個(gè)等級(jí)的產(chǎn)品,而抽象工廠模式可生產(chǎn)多個(gè)等級(jí)的產(chǎn)品吭狡。
抽象工廠(AbstractFactory)模式的定義:是一種為訪問(wèn)類提供一個(gè)創(chuàng)建一組相關(guān)或相互依賴對(duì)象的接口尖殃,且訪問(wèn)類無(wú)須指定所要產(chǎn)品的具體類就能得到同族的不同等級(jí)的產(chǎn)品的模式結(jié)構(gòu)。
2.抽象工廠模式的結(jié)構(gòu)
工廠方法模式的主要角色如下:
- 抽象工廠(AbstractFactory):提供了創(chuàng)建產(chǎn)品的接口划煮,調(diào)用者通過(guò)它訪問(wèn)具體工廠的工廠方法來(lái)創(chuàng)建產(chǎn)品送丰。
- 具體工廠(TclFactory、GreeFactory):主要是實(shí)現(xiàn)抽象工廠中的創(chuàng)造產(chǎn)品的多個(gè)抽象方法弛秋,完成某個(gè)品牌的具體產(chǎn)品的創(chuàng)建器躏。
- 抽象產(chǎn)品:定義了產(chǎn)品的規(guī)范,描述了產(chǎn)品的主要特性和功能蟹略。
-
具體產(chǎn)品:實(shí)現(xiàn)了抽象產(chǎn)品角色所定義的內(nèi)容登失。
image.png
3.抽象工廠模式的實(shí)現(xiàn)
public interface IFan {
void blow();
}
public interface ITelevision {
void watch();
}
public class GreeFan implements IFan {
@Override
public void blow() {
System.out.println("吹格力電風(fēng)扇");
}
}
public class TclFan implements IFan{
@Override
public void blow() {
System.out.println("吹Tcl電風(fēng)扇");
}
}
public class GreeTelevision implements ITelevision{
@Override
public void watch() {
System.out.println("看格力電視機(jī)");
}
}
public class TclTelevision implements ITelevision {
@Override
public void watch() {
System.out.println("看TCL電視機(jī)");
}
}
public abstract class AbstractFactory {
public void init() {
System.out.println("都要執(zhí)行的通用的工廠方法");
}
protected abstract IFan createFan();
protected abstract ITelevision createTelevision();
}
public class GreeFactory extends AbstractFactory{
@Override
protected IFan createFan() {
super.init();
return new GreeFan();
}
@Override
protected ITelevision createTelevision() {
super.init();
return new GreeTelevision();
}
}
public class TclFactory extends AbstractFactory{
@Override
protected IFan createFan() {
super.init();
return new TclFan();
}
@Override
protected ITelevision createTelevision() {
super.init();
return new TclTelevision();
}
}
4.抽象工廠模式優(yōu)缺點(diǎn)與應(yīng)用場(chǎng)景
優(yōu)點(diǎn)
1)抽象工廠模式除了具有工廠方法模式的優(yōu)點(diǎn)外,還可以在類的內(nèi)部對(duì)產(chǎn)品族中相關(guān)聯(lián)的多等級(jí)產(chǎn)品共同管理挖炬,而不必專門引入多個(gè)新的類來(lái)進(jìn)行管理揽浙。
2)當(dāng)需要產(chǎn)品族時(shí),抽象工廠可以保證客戶端始終只使用同一個(gè)產(chǎn)品的產(chǎn)品組。
3)抽象工廠增強(qiáng)了程序的可擴(kuò)展性捏萍,當(dāng)增加一個(gè)新的產(chǎn)品族時(shí)太抓,不需要修改原代碼,滿足開(kāi)閉原則令杈。缺點(diǎn)
1)當(dāng)產(chǎn)品族中需要增加一個(gè)新的產(chǎn)品時(shí)走敌,所有的工廠類都需要進(jìn)行修改。增加了系統(tǒng)的抽象性和理解難度逗噩。應(yīng)用場(chǎng)景
跟工廠方法模式應(yīng)用場(chǎng)景類似掉丽,只是更加細(xì)化了。
五异雁、工廠設(shè)計(jì)模式在實(shí)際中的應(yīng)用
1.工廠設(shè)計(jì)模式在mybatis源碼中的應(yīng)用
源碼詳見(jiàn):cn.bugstack.mybatis.session.SqlSessionFactory
public interface SqlSessionFactory {
SqlSession openSession();
}
源碼詳見(jiàn):cn.bugstack.mybatis.session.defaults.DefaultSqlSessionFactory
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
@Override
public SqlSession openSession() {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
TransactionFactory transactionFactory = environment.getTransactionFactory();
tx = transactionFactory.newTransaction(configuration.getEnvironment().getDataSource(), TransactionIsolationLevel.READ_COMMITTED, false);
// 創(chuàng)建執(zhí)行器
final Executor executor = configuration.newExecutor(tx);
// 創(chuàng)建DefaultSqlSession
return new DefaultSqlSession(configuration, executor);
} catch (Exception e) {
try {
assert tx != null;
tx.close();
} catch (SQLException ignore) {
}
throw new RuntimeException("Error opening session. Cause: " + e);
}
}
}
- 工廠模式:簡(jiǎn)單工廠捶障,是一種創(chuàng)建型設(shè)計(jì)模式,其在父類中提供一個(gè)創(chuàng)建對(duì)象的方法纲刀,允許子類決定實(shí)例對(duì)象的類型项炼。
-
場(chǎng)景介紹:
SqlSessionFactory
是獲取會(huì)話的工廠,每次我們使用 Mybatis 操作數(shù)據(jù)庫(kù)的時(shí)候示绊,都會(huì)開(kāi)啟一個(gè)新的會(huì)話锭部。在會(huì)話工廠的實(shí)現(xiàn)中負(fù)責(zé)獲取數(shù)據(jù)源環(huán)境配置信息、構(gòu)建事務(wù)工廠面褐、創(chuàng)建操作SQL的執(zhí)行器拌禾,并最終返回會(huì)話實(shí)現(xiàn)類。 -
同類設(shè)計(jì):
SqlSessionFactory
展哭、ObjectFactory
湃窍、MapperProxyFactory
、DataSourceFactory
2.工廠設(shè)計(jì)模式在slf4j源碼中的應(yīng)用
我們來(lái)看一下slf4j中是如何應(yīng)用工廠方法模式的:
首先slf4j定義一個(gè)抽象工廠ILoggerFactory
public interface ILoggerFactory {
Logger getLogger(String var1);
}
接著定義了一個(gè)抽象產(chǎn)品Logger:
public interface Logger {
... ...
}
public class SubstituteLogger implements Logger {
private final String name;
private volatile Logger _delegate;
private Boolean delegateEventAware;
private Method logMethodCache;
private EventRecodingLogger eventRecodingLogger;
private Queue<SubstituteLoggingEvent> eventQueue;
private final boolean createdPostInitialization;
public SubstituteLogger(String name, Queue<SubstituteLoggingEvent> eventQueue, boolean createdPostInitialization) {
this.name = name;
this.eventQueue = eventQueue;
this.createdPostInitialization = createdPostInitialization;
}
)
具體工廠有很多匪傍,都實(shí)現(xiàn)了抽象工廠您市,這里展示其中一個(gè)SubstituteLoggerFactory
public class SubstituteLoggerFactory implements ILoggerFactory {
boolean postInitialization = false;
final Map<String, SubstituteLogger> loggers = new HashMap<String, SubstituteLogger>();
final LinkedBlockingQueue<SubstituteLoggingEvent> eventQueue = new LinkedBlockingQueue<SubstituteLoggingEvent>();
synchronized public Logger getLogger(String name) {
SubstituteLogger logger = loggers.get(name);
if (logger == null) {
logger = new SubstituteLogger(name, eventQueue, postInitialization);
loggers.put(name, logger);
}
return logger;
}
}
具體工廠用于生產(chǎn)具體的產(chǎn)品,SubstituteLogger就是一種具體的產(chǎn)品役衡,從上邊的代碼中就可以看到茵休,SubstituteLoggerFactory工廠在生產(chǎn)SubstituteLogger具體產(chǎn)品時(shí),做了一定的處理映挂。而真正使用時(shí)泽篮,只需要通過(guò)getLogger方法就可以得到具體產(chǎn)品,當(dāng)然除了工廠設(shè)計(jì)模式這里還用到了單例設(shè)計(jì)模式柑船,這就是設(shè)計(jì)模式的魅力帽撑。