https://javaedge.blog.csdn.net/article/details/103932174

  • 所牽涉源代碼地址

https://github.com/Wasabi1234...git

0 簡單工廠案例






JDK 應用實例

日曆類



迭代器

Collection 接口就至關於 VideoFactory

至關於各類具體的工廠,如 JavaVideoFactory
父類接口,子類實現
Itr 就是具體產品 JavaVideo
github

工廠應用

爲解決 url 協議擴展使用


Launcher#Factory靜態類

logback 應用

LoggerFactory#getLogger(String name)

JDBC實例

直接註冊 MySQL 驅動



返回值是一個抽象類,必有一子類實現其,看一下


經過間接繼承此處理器


這其中URLStreamHandler就至關於各類抽象產品,而其實現類即各類具體的產品
URLStreamHandlerFactory就至關於 VideoFactory
而以下 Factory 就至關於如 JavaVideoFactory/PythonVideoFactory
數據庫

Logback實例



1 工廠方法模式案例

1.1 簡單工廠的升級




對造人過程進行分析,該過程涉及三個對象:女媧、八卦爐、三種不一樣膚色的人segmentfault

  • 女媧可使用場景類Client表示
  • 八卦爐相似於一個工廠,負責製造生產產品(即人類)
  • 三種不一樣膚色的人,他們都是同一個接口下的不一樣實現類,對於八卦爐來講都是它生產出的產品

女媧造人類圖

  • 接口Human是對人類的總稱,每一個人種都至少具備兩個方法

  • 黑色人種

  • 黃色人種

  • 白色人種

全部人種定義完畢,下一步就是定義一個八卦爐,而後燒製人類設計模式

最可能給八卦爐下達什麼樣的生產命令呢?
應該是給我生產出一個黃色人種(YellowHuman類)
而不會是給我生產一個會走、會跑、會說話、皮膚是黃色的人種
由於這樣的命令增長了交流的成本,做爲一個生產的管理者,只要知道生產什麼就能夠了,而不須要事物的具體信息

在這裏採用了泛型,經過定義泛型對createHuman的輸入參數產生兩層限制
● 必須是Class類型
● 必須是Human的實現類
其中的T表示,只要實現了Human接口的類均可以做爲參數緩存

只有一個八卦爐,其實現生產人類的方法
服務器

人種有了,八卦爐也有了,剩下的工做就是女媧採集黃土,而後命令八卦爐開始生產
網絡

人種有了,八卦爐有了,負責生產的女媧也有了
運行一下,結果以下所示

以上就是工廠方法模式框架

2 定義

  • 官方定義

Define an interface for creating an object,but let subclasses decide which class to instantiate.Factory Method lets a class defer instantiation to subclasses
定義一個用於建立對象的接口,讓子類決定實例化哪個類。工廠方法使一個類的實例化延遲到其子類
ide

工廠方法模式的通用類圖
在工廠方法模式中,抽象產品類Product負責定義產品的共性,實現對事物最抽象的定義;Creator爲抽象建立類,也就是抽象工廠,具體如何建立產品類是由具體的實現工廠ConcreteCreator完成的。工廠方法模式的變種較多,咱們來看一個比較實用的通用源碼。

  • 抽象產品類

具體的產品類能夠有多個,都繼承於抽象產品類

  • 具體產品類


  • 抽象工廠類

負責定義產品對象的產生

  • 具體工廠類

具體如何產生一個產品的對象,是由具體的工廠類實現的

  • 場景類


該通用代碼是一個比較實用、易擴展的框架,讀者能夠根據實際項目須要進行擴展

3 應用

3.1 優勢


  • 良好的封裝性,代碼結構清晰

一個對象建立是有條件約束的,如一個調用者須要一個具體的產品對象,只要知道這個產品的類名(或約束字符串)就能夠了,不用知道建立對象的艱辛過程,下降模塊間的耦合

  • 工廠方法模式的擴展性很是優秀

在增長產品類的狀況下,只要適當地修改具體的工廠類或擴展一個工廠類,就能夠完成「擁抱變化」
例如在咱們的例子中,須要增長一個棕色人種,則只須要增長一個BrownHuman類,工廠類不用任何修改就可完成系統擴展。

  • 屏蔽產品類

這一特色很是重要,產品類的實現如何變化,調用者都不須要關心,它只須要關心產品的接口,只要接口保持不變,系統中的上層模塊就不要發生變化
由於產品類的實例化工做是由工廠類負責的,一個產品對象具體由哪個產品生成是由工廠類決定的
在數據庫開發中,你們應該可以深入體會到工廠方法模式的好處:若是使用JDBC鏈接數據庫,數據庫從MySQL切換到Oracle,須要改動的地方就是切換一下驅動名稱(前提條件是SQL語句是標準語句),其餘的都不須要修改,這是工廠方法模式靈活性的一個直接案例。

  • 典型的解耦框架

高層模塊值須要知道產品的抽象類,其餘的實現類都不用關心
符合迪米特法則,我不須要的就不要去交流
也符合依賴倒置原則,只依賴產品類的抽象
固然也符合里氏替換原則,使用產品子類替換產品父類,沒問題!

3.2 缺點


3.3 適用場景


工廠方法模式是new一個對象的替代品

在全部須要生成對象的地方均可以使用,可是須要慎重地考慮是否要增長一個工廠類進行管理,增長代碼的複雜度

須要靈活的、可擴展的框架時

萬物皆對象,那萬物也就皆產品類
例如須要設計一個鏈接郵件服務器的框架,有三種網絡協議可供選擇:POP三、IMAP、HTTP
咱們就能夠把這三種鏈接方法做爲產品類,定義一個接口如IConnectMail
而後定義對郵件的操做方法
用不一樣的方法實現三個具體的產品類(也就是鏈接方式)
再定義一個工廠方法,按照不一樣的傳入條件,選擇不一樣的鏈接方式
如此設計,能夠作到完美的擴展,如某些郵件服務器提供了WebService接口,很好,咱們只要增長一個產品類就能夠了

異構項目

例如經過WebService與一個非Java的項目交互,雖然WebService號稱是能夠作到異構系統的同構化,可是在實際的開發中,仍是會碰到不少問題,如類型問題、WSDL文件的支持問題,等等。從WSDL中產生的對象都認爲是一個產品,而後由一個具體的工廠類進行管理,減小與外圍系統的耦合。

使用在測試驅動開發的框架下

例如,測試一個類A,就須要把與類A有關聯關係的類B也同時產生出來,咱們可使用工廠方法模式把類B虛擬出來,避免類A與類B的耦合。目前因爲JMock和EasyMock的誕生,該使用場景已經弱化了,讀者能夠在遇到此種狀況時直接考慮使用JMock或EasyMock

4 擴展

工廠方法模式有不少擴展,並且與其餘模式結合使用威力更大,下面將介紹4種擴展。

4.1 縮小爲簡單工廠模式

咱們這樣考慮一個問題:一個模塊僅須要一個工廠類,沒有必要把它產生出來,使用靜態的方法就能夠了,根據這一要求,咱們把上例中的AbstarctHumanFactory修改一下
簡單工廠模式類圖
咱們在類圖中去掉了AbstractHumanFactory抽象類,同時把createHuman方法設置爲靜態類型,簡化了類的建立過程,變動的源碼僅僅是HumanFactory和NvWa類

  • 簡單工廠模式中的工廠類

待考證

HumanFactory類僅有兩個地方發生變化

  • 去掉繼承抽象類
  • createHuman前增長static關鍵字

工廠類發生變化,也同時引發了調用者NvWa的變化
簡單工廠模式中的場景類
運行結果沒有發生變化,可是咱們的類圖變簡單了,並且調用者也比較簡單,該模式是工廠方法模式的弱化,由於簡單,因此稱爲簡單工廠模式(Simple Factory Pattern),也叫作靜態工廠模式
在實際項目中,採用該方法的案例仍是比較多的

  • 其缺點

工廠類的擴展比較困難,不符合開閉原則,但它仍然是一個很是實用的設計模式。

4.2 升級爲多個工廠類

當咱們在作一個比較複雜的項目時,常常會遇到初始化一個對象很耗費精力的狀況,全部的產品類都放到一個工廠方法中進行初始化會使代碼結構不清晰
例如,一個產品類有5個具體實現,每一個實現類的初始化(不只僅是new,初始化包括new一個對象,並對對象設置必定的初始值)方法都不相同,若是寫在一個工廠方法中,勢必會致使該方法巨大無比,那該怎麼辦?

考慮到須要結構清晰,咱們就爲每一個產品定義一個創造者,而後由調用者本身去選擇與哪一個工廠方法關聯
咱們仍是以女媧造人爲例,每一個人種都有一個固定的八卦爐,分別造出黑色人種、白色人種、黃色人種
多個工廠類的類圖

  • 每一個人種(具體的產品類)都對應了一個建立者,每一個建立者都獨立負責建立對應的產品對象,很是符合單一職責原則,看看代碼變化

多工廠模式的抽象工廠類
抽象方法中已經再也不須要傳遞相關參數了,由於每個具體的工廠都已經很是明確本身的職責:建立本身負責的產品類對象。

  • 黑色人種的建立工廠實現

  • 黃色人種的建立類

  • 白色人種的建立類

三個具體的建立工廠都很是簡單,可是,若是一個系統比較複雜時工廠類也會相應地變複雜。

  • 場景類NvWa修改後的代碼

運行結果仍是相同
每個產品類都對應了一個建立類,好處就是建立類的職責清晰,並且結構簡單,可是給可擴展性和可維護性帶來了必定的影響。爲何這麼說呢?若是要擴展一個產品類,就須要創建一個相應的工廠類,這樣就增長了擴展的難度。由於工廠類和產品類的數量相同,維護時須要考慮兩個對象之間的關係。

固然,在複雜的應用中通常採用多工廠的方法,而後再增長一個協調類,避免調用者與各個子工廠交流,協調類的做用是封裝子工廠類,對高層模塊提供統一的訪問接口。

4.3 替代單例模式

單例模式的核心要求就是在內存中只有一個對象,經過工廠方法模式也能夠只在內存中生產一個對象
工廠方法模式替代單例模式類圖
Singleton定義了一個private的無參構造函數,目的是不容許經過new的方式建立一個對象
單例類
Singleton保證不能經過正常的渠道創建一個對象

  • 那SingletonFactory如何創建一個單例對象呢?

反射~
負責生成單例的工廠類
經過得到類構造器,而後設置private訪問權限,生成一個對象,而後提供外部訪問,保證內存中的對象惟一

以上經過工廠方法模式建立了一個單例對象,該框架能夠繼續擴展,在一個項目中能夠產生一個單例構造器,全部須要產生單例的類都遵循必定的規則(構造方法是private),而後經過擴展該框架,只要輸入一個類型就能夠得到惟一的一個實例。

3.4 延遲初始化(Lazy initialization)

一個對象被消費完畢後,並不馬上釋放,工廠類保持其初始狀態,等待再次被使用
延遲初始化是工廠方法模式的一個擴展應用
![延遲初始化的通用類圖
](http://upload-images.jianshu....
ProductFactory負責產品類對象的建立工做,而且經過prMap變量產生一個緩存,對須要再次被重用的對象保留
延遲加載的工廠類
經過定義一個Map容器,容納全部產生的對象,若是在Map容器中已經有的對象,則直接取出返回;若是沒有,則根據須要的類型產生一個對象並放入到Map容器中,以方便下次調用。

延遲加載框架是能夠擴展的,例如限制某一個產品類的最大實例化數量,能夠經過判斷Map中已有的對象數量來實現,這樣的處理是很是有意義的,例如JDBC鏈接數據庫,都會要求設置一個MaxConnections最大鏈接數量,該數量就是內存中最大實例化的數量。

延遲加載還能夠用在對象初始化比較複雜的狀況下,例如硬件訪問,涉及多方面的交互,則能夠經過延遲加載下降對象的產生和銷燬帶來的複雜性。

4 最佳實踐

工廠方法模式在項目中使用得很是頻繁,以致於不少代碼中都包含工廠方法模式
該模式幾乎盡人皆知,但不是每一個人都能用得好。熟能生巧,熟練掌握該模式,多思考工廠方法如何應用,並且工廠方法模式還能夠與其餘模式混合使用(例如模板方法模式、單例模式、原型模式等),變化出無窮的優秀設計,這也正是軟件設計和開發的樂趣所在。

本文由博客一文多發平臺 OpenWrite 發佈!
本站公眾號
   歡迎關注本站公眾號,獲取更多信息