抽象工厂模式_设计模式系列—抽象工厂模式
前言
- 23種設計模式速記
- 單例(singleton)模式
- 工廠方法(factory method)模式
23種設計模式快速記憶的請看上面第一篇,前面說完了工廠方法模式,我們發現工廠方法模式存在一個嚴重的問題:一個具體工廠只能創建一類產品 ,而實際過程中一個工廠往往需要生產多類產品。為了解決上述問題,可以使用抽象工廠模式,本篇和大家一起來學習抽象工廠模式相關內容。
模式定義
抽象工廠模式,即Abstract Factory Pattern,提供一個創建一系列相關或相互依賴對象的接口,而無須指定它們具體的類;具體的工廠負責實現具體的產品實例。
抽象工廠模式是工廠方法模式的升級版本,工廠方法模式只生產一個等級的產品,而抽象工廠模式可生產多個等級的產品。
解決的問題
每個工廠只能創建一類產品
即工廠方法模式的缺點
模式組成
抽象工廠模式同工廠方法模式一樣,也是由抽象工廠、具體工廠、抽象產品和具體產品等 4 個要素構成,但抽象工廠中方法個數不同,抽象產品的個數也不同。現在我們來分析其基本結構和實現方法。
使用步驟
實例說明
實例概況
- 背景:隔壁老王有兩間塑料加工廠(A廠僅生產容器類產品;B廠僅生產模具類產品);隨著客戶需求的變化,A廠所在地的客戶需要也模具類產品,B廠所在地的客戶也需要容器類產品;
- 沖突:沒有資源(資金+租位)在當地分別開設多一家注塑分廠
- 解決方案:在原有的兩家塑料廠里增設生產需求的功能,即A廠能生產容器+模具產品;B廠間能生產模具+容器產品。
即抽象工廠模式
使用步驟
步驟1: 創建抽象工廠類,定義具體工廠的公共接口
abstract?class?Factory{????public?abstract?AbstractProduct?ManufactureContainer();????public?abstract?AbstractProduct?ManufactureMould();}步驟2: 創建抽象產品族類 ,定義具體產品的公共接口;
abstract?class?AbstractProduct{????public?abstract?void?method1();}步驟3: 創建抽象產品類 ,定義具體產品的公共接口;
//容器產品抽象類abstract?class?ContainerProduct?extends?AbstractProduct?{????@Override????public?abstract?void?method1();}//模具產品抽象類abstract?class?MouldProduct?extends?AbstractProduct?{????@Override????public?abstract?void?method1();}步驟4: 創建具體產品類(繼承抽象產品類), 定義生產的具體產品;
//容器產品A類class?ContainerProductA?extends?ContainerProduct{????@Override????public?void?method1()?{????????System.out.println("生產出了容器產品A");????}}//容器產品B類class?ContainerProductB?extends?ContainerProduct{????@Override????public?void?method1()?{????????System.out.println("生產出了容器產品B");????}}//模具產品A類class?MouldProductA?extends?MouldProduct{????@Override????public?void?method1()?{????????System.out.println("生產出了模具產品A");????}}//模具產品B類class?MouldProductB?extends?MouldProduct{????@Override????public?void?method1()?{????????System.out.println("生產出了模具產品B");????}}步驟5:創建具體工廠類(繼承抽象工廠類),定義創建對應具體產品實例的方法;
//A廠?-?生產模具+容器產品class?FactoryA?extends?Factory{????@Override????public?AbstractProduct?ManufactureContainer()?{????????return?new?ContainerProductA();????}????@Override????public?AbstractProduct?ManufactureMould()?{????????return?new?MouldProductA();????}}//B廠?-?生產模具+容器產品class?FactoryB?extends?Factory{????@Override????public?AbstractProduct?ManufactureContainer()?{????????return?new?ContainerProductB();????}????@Override????public?AbstractProduct?ManufactureMould()?{????????return?new?MouldProductB();????}}步驟6:客戶端通過實例化具體的工廠類,并調用其創建不同目標產品的方法創建不同具體產品類的實例
public?class?AbstractFactoryPattern?{????public?static?void?main(String[]?args){????????FactoryA?mFactoryA?=?new?FactoryA();????????FactoryB?mFactoryB?=?new?FactoryB();????????//A廠當地客戶需要容器產品A????????mFactoryA.ManufactureContainer().method1();????????//A廠當地客戶需要模具產品A????????mFactoryA.ManufactureMould().method1();????????//B廠當地客戶需要容器產品B????????mFactoryB.ManufactureContainer().method1();????????//B廠當地客戶需要模具產品B????????mFactoryB.ManufactureMould().method1();????}}輸出結果
生產出了容器產品A生產出了模具產品A生產出了容器產品B生產出了模具產品B優點
- 可以在類的內部對產品族中相關聯的多等級產品共同管理,而不必專門引入多個新的類來進行管理。
- 當需要產品族時,抽象工廠可以保證客戶端始終只使用同一個產品的產品組。
- 抽象工廠增強了程序的可擴展性,當增加一個新的產品族時,不需要修改原代碼,滿足開閉原則。
缺點
當產品族中需要增加一個新的產品時,所有的工廠類都需要進行修改。增加了系統的抽象性和理解難度。
這是因為抽象工廠接口中已經確定了可以被創建的產品集合,如果需要添加新產品,此時就必須去修改抽象工廠的接口,這樣就涉及到抽象工廠類的以及所有子類的改變,這樣也就違背了“開發——封閉”原則。
對于新的產品族符合開-閉原則;對于新的產品種類不符合開-閉原則,這一特性稱為開-閉原則的傾斜性。
應用場景
程序需要處理不同系列的相關產品,但是您不希望它依賴于這些產品的具體類時,可以使用抽象工廠。
源碼中的應用
#JDKjava.sql.Connectionjava.sql.Driver#?mybatisSqlSessionFactoryjava.sql.Connection
public?interface?Connection??extends?Wrapper,?AutoCloseable?{?//...??????????//返回普通的sql執行器????????Statement?createStatement()?throws?SQLException;??//返回具有參數化預編譯功能的sql執行器?????PreparedStatement?prepareStatement(String?sql)?throws?SQLException;?????//返回可以執行存儲過程的sql執行器????????CallableStatement?prepareCall(String?sql)?throws?SQLException;???????//...?????????}從上面的注釋就可以看出,這就是典型的抽象工廠接口,描述了不同的產品等級Statement、PreparedStatement、CallableStatement,它們都位于抽象接口Statement產品等級結構中。我們可以繼續尋找該抽象工廠接口的實現類。
以Mysql為例,可以找到Mysql對這個工廠接口的實現類ConnectionImpl,ConnectionImpl并不是直接實現了java.sql.Connection,而是通過實現自己擴展的MySQLConnection接口,該接口也是間接繼承了java.sql.Connection
public?class?ConnectionImpl?extends?ConnectionPropertiesImpl?implements?MySQLConnection?{?//...?public?java.sql.Statement?createStatement()?throws?SQLException?{????????return?createStatement(DEFAULT_RESULT_SET_TYPE,?DEFAULT_RESULT_SET_CONCURRENCY);????}????public?java.sql.PreparedStatement?prepareStatement(String?sql)?throws?SQLException?{????????return?prepareStatement(sql,?DEFAULT_RESULT_SET_TYPE,?DEFAULT_RESULT_SET_CONCURRENCY);????}?????public?java.sql.CallableStatement?prepareCall(String?sql)?throws?SQLException?{????????return?prepareCall(sql,?DEFAULT_RESULT_SET_TYPE,?DEFAULT_RESULT_SET_CONCURRENCY);????}??//...}以createStatement為例,跟蹤其調用代碼可以看到StatementImpl這個類就是實現了java.sql.Statement的具體產品類
public?java.sql.Statement?createStatement(int?resultSetType,?int?resultSetConcurrency)?throws?SQLException?{????checkClosed();????StatementImpl?stmt?=?new?StatementImpl(getMultiHostSafeProxy(),?this.database);????stmt.setResultSetType(resultSetType);????stmt.setResultSetConcurrency(resultSetConcurrency);????return?stmt;}SqlSessionFactory
/**?*?Creates?an?{@link?SqlSession}?out?of?a?connection?or?a?DataSource?*??*?@author?Clinton?Begin?*/public?interface?SqlSessionFactory?{??SqlSession?openSession();??SqlSession?openSession(boolean?autoCommit);??SqlSession?openSession(Connection?connection);??SqlSession?openSession(TransactionIsolationLevel?level);??SqlSession?openSession(ExecutorType?execType);??SqlSession?openSession(ExecutorType?execType,?boolean?autoCommit);??SqlSession?openSession(ExecutorType?execType,?TransactionIsolationLevel?level);??SqlSession?openSession(ExecutorType?execType,?Connection?connection);??Configuration?getConfiguration();}SqlSessionFactory也是抽象工廠接口,Configuration和SqlSession都是在不同的產品等級上。通過IDEA工具可以通過UML圖清晰得看到SqlSessionFactory的工廠實現類
以上兩個例子都和數據庫操作相關,同樣使用了抽象工廠模式。在jdbc中,客戶端通過Connection工廠獲取到Statement產品對象,然后通過該對象進行增刪改查操作,對于mybatis這種數據庫操縱框架而言(底層也是封裝了jdbc api)有異曲同工,通過SeqSessionFactory工廠獲取到SqlSession產品對象,然后進行增刪改查操作。
PS:以上代碼提交在 Github :
https://github.com/Niuh-Study/niuh-designpatterns.git
文章持續更新,可以微信搜一搜「 一角錢技術 」第一時間閱讀, 本文 GitHub org_hejianhui/JavaStudy 已經收錄,歡迎 Star。
總結
以上是生活随笔為你收集整理的抽象工厂模式_设计模式系列—抽象工厂模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 两阶段聚类_挑子学习笔记:
- 下一篇: 基因表达式编程gep_基因表达式编程GE