簡單工廠模式並不屬於 GoF 23 個經典設計模式,但一般將它做爲學習其餘工廠模式的基礎,它的設計思想很簡單,其基本流程以下:html
首先將須要建立的各類不一樣對象(例如各類不一樣的 Chart 對象)的相關代碼封裝到不一樣的類中,這些類稱爲具體產品類,而將它們公共的代碼進行抽象和提取後封裝在一個抽象產品類中,每個具體產品類都是抽象產品類的子類;而後提供一個工廠類用於建立各類產品,在工廠類中提供一個建立產品的工廠方法,該方法能夠根據所傳 入的參數不一樣建立不一樣的具體產品對象;客戶端只需調用工廠類的工廠方法並傳入相應的參數便可獲得一個產品 對象。java
簡單工廠模式定義以下:數據庫
簡單工廠模式(Simple Factory Pattern):定義一個工廠類,它能夠根據參數的不一樣返回不一樣類的實例,被創 建的實例一般都具備共同的父類。由於在簡單工廠模式中用於建立實例的方法是靜態(static)方法,所以簡單工 廠模式又被稱爲靜態工廠方法(Static Factory Method)模式,它屬於類建立型模式。編程
簡單工廠模式的要點在於:當你須要什麼,只須要傳入一個正確的參數,就能夠獲取你所須要的對象,而無須知道其建立細節。簡單工廠模式結構比較簡單,其核心是工廠類的設計,其結構如圖所示:設計模式
• Factory(工廠角色):工廠角色即工廠類,它是簡單工廠模式的核心,負責實現建立全部產品實例的內部邏 輯;工廠類能夠被外界直接調用,建立所需的產品對象;在工廠類中提供了靜態的工廠方法 factoryMetho d(),它的返回類型爲抽象產品類型 Product。函數
• Product(抽象產品角色):它是工廠類所建立的全部對象的父類,封裝了各類產品對象的公有方法,它的 引入將提升系統的靈活性,使得在工廠類中只需定義一個通用的工廠方法,由於全部建立的具體產品對象都 是其子類對象。學習
• ConcreteProduct(具體產品角色):它是簡單工廠模式的建立目標,全部被建立的對象都充當這個角色的 某個具體類的實例。每個具體產品角色都繼承了抽象產品角色,須要實如今抽象產品中聲明的抽象方法。ui
在簡單工廠模式中,客戶端經過工廠類來建立一個產品類的實例,而無須直接使用new關鍵字來建立對象,它是工廠模式家族中最簡單的一員。this
在使用簡單工廠模式時,首先須要對產品類進行重構,不能設計一個一應俱全的產品類,而需根據實際狀況設計 一個產品層次結構。spa
(1) 工廠類包含必要的判斷邏輯,能夠決定在何時建立哪個產品類的實例,客戶端能夠免除直接建立產品對象的職責,而僅僅「消費」產品,簡單工廠模式實現了對象建立和使用的分離。
(2) 客戶端無須知道所建立的具體產品類的類名,只須要知道具體產品類所對應的參數便可,對於一些複雜的類名,經過簡單工廠模式能夠在必定程度減小使用者的記憶量。
(3) 經過引入配置文件,能夠在不修改任何客戶端代碼的狀況下更換和增長新的具體產品類,在必定程度上提升了 系統的靈活性。
(1) 因爲工廠類集中了全部產品的建立邏輯,職責太重,一旦不能正常工做,整個系統都要受到影響。
(2) 使用簡單工廠模式勢必會增長系統中類的個數(引入了新的工廠類),增長了系統的複雜度和理解難度。
(3) 系統擴展困難,一旦添加新產品就不得不修改工廠邏輯,在產品類型較多時,有可能形成工廠邏輯過於復 雜,不利於系統的擴展和維護。
(4) 簡單工廠模式因爲使用了靜態工廠方法,形成工廠角色沒法造成基於繼承的等級結構。
(1) 工廠類負責建立的對象比較少,因爲建立的對象較少,不會形成工廠方法中的業務邏輯太過複雜。
(2) 客戶端只知道傳入工廠類的參數,對於如何建立對象並不關心。
簡單工廠模式雖然簡單,但存在一個很嚴重的問題。當系統中須要引入新產品時,因爲靜態工廠方法經過所傳入參數的不一樣來建立不一樣的產品,這一定要修改工廠類的源代碼,將違背「開閉原則」,如何實現增長新產品而不影響已有代碼?工廠方法模式應運而生,本文將介紹第二種工廠模式——工廠方法模式。
在工廠方法模式中,咱們再也不提供一個統一的工廠類來建立全部的產品對象,而是針對不一樣的產品提供不一樣的工廠,系統提供一個與產品等級結構對應的工廠等級結構。
工廠方法模式(Factory Method Pattern):定義一個用於建立對象的接口,讓子類決定將哪個類實例化。工廠方法模式讓一個類的實例化延遲到其子類。工廠方法模式又簡稱爲工廠模式(Factory Pattern),又可稱做虛擬構造器模式(Virtual Constructor Pattern)或多態工廠模式(Polymorphic Factory Pattern)。工廠方法模式是一種 類建立型模式。
工廠方法模式提供一個抽象工廠接口來聲明抽象工廠方法,而由其子類來具體實現工廠方法,建立具體的產品對象。工廠方法模式結構如圖所示:
• Product(抽象產品):它是定義產品的接口,是工廠方法模式所建立對象的超類型,也就是產品對象的公 共父類。
• ConcreteProduct(具體產品):它實現了抽象產品接口,某種類型的具體產品由專門的具體工廠建立,具 體工廠和具體產品之間一一對應。
• Factory(抽象工廠):在抽象工廠類中,聲明瞭工廠方法(Factory Method),用於返回一個產品。抽象工 廠是工廠方法模式的核心,全部建立對象的工廠類都必須實現該接口。
• ConcreteFactory(具體工廠):它是抽象工廠類的子類,實現了抽象工廠中定義的工廠方法,並可由客戶 端調用,返回一個具體產品類的實例。
//日誌記錄器接口:抽象產品 interface Logger { public void writeLog(); } //數據庫日誌記錄器:具體產品 class DatabaseLogger implements Logger { public void writeLog() { System.out.println("數據庫日誌記錄。"); } } //文件日誌記錄器:具體產品 class FileLogger implements Logger { public void writeLog() { System.out.println("文件日誌記錄。"); } } //日誌記錄器工廠接口:抽象工廠 interface LoggerFactory { public Logger createLogger(); } //數據庫日誌記錄器工廠類:具體工廠 class DatabaseLoggerFactory implements LoggerFactory { public Logger createLogger() { //鏈接數據庫,代碼省略 //建立數據庫日誌記錄器對象 Logger logger = new DatabaseLogger(); //初始化數據庫日誌記錄器,代碼省略 return logger; } } //文件日誌記錄器工廠類:具體工廠 class FileLoggerFactory implements LoggerFactory { public Logger createLogger() { //建立文件日誌記錄器對象 Logger logger = new FileLogger(); //建立文件,代碼省略 return logger; } }
客戶端
class Client { public static void main(String args[]) { LoggerFactory factory; Logger logger; factory = new FileLoggerFactory(); //可引入配置文件實現 logger = factory.createLogger(); logger.writeLog(); } }
實際使用的時候,可使用反射技術
(1) 在工廠方法模式中,工廠方法用來建立客戶所須要的產品,同時還向客戶隱藏了哪一種具體產品類將被實例化這一細節,用戶只須要關心所需產品對應的工廠,無須關心建立細節,甚至無須知道具體產品類的類名。
(2) 基於工廠角色和產品角色的多態性設計是工廠方法模式的關鍵。它可以讓工廠能夠自主肯定建立何種產品對象,而如何建立這個對象的細節則徹底封裝在具體工廠內部。工廠方法模式之因此又被稱爲多態工廠模式,就正是由於全部的具體工廠類都具備同一抽象父類。
(3) 使用工廠方法模式的另外一個優勢是在系統中加入新產品時,無須修改抽象工廠和抽象產品提供的接口,無須修改客戶端,也無須修改其餘的具體工廠和具體產品,而只要添加一個具體工廠和具體產品就能夠了,這樣,系統 的可擴展性也就變得很是好,徹底符合「開閉原則」。
(1) 在添加新產品時,須要編寫新的具體產品類,並且還要提供與之對應的具體工廠類,系統中類的個數將成對增長,在必定程度上增長了系統的複雜度,有更多的類須要編譯和運行,會給系統帶來一些額外的開銷。
(2) 因爲考慮到系統的可擴展性,須要引入抽象層,在客戶端代碼中均使用抽象層進行定義,增長了系統的抽象性和理解難度,且在實現時可能須要用到 DOM、反射等技術,增長了系統的實現難度。
(1) 客戶端不知道它所須要的對象的類。在工廠方法模式中,客戶端不須要知道具體產品類的類名,只須要知道所對應的工廠便可,具體的產品對象由具體工廠類建立,可將具體工廠類的類名存儲在配置文件或數據庫中。
(2) 抽象工廠類經過其子類來指定建立哪一個對象。在工廠方法模式中,對於抽象工廠類只須要提供一個建立產品的 接口,而由其子類來肯定具體要建立的對象,利用面向對象的多態性和里氏代換原則,在程序運行時,子類對象 將覆蓋父類對象,從而使得系統更容易擴展。
1)屏蔽對象的細節,面向對象的規範有一點就是別人知道你的東西越少越好。
2)對象的建立和使用解耦。
系統所提供的工廠生產的具體產品並非一個簡單的對象,而是多個位於不一樣產品等級結構、屬於不一樣類型的 具體產品時就可使用抽象工廠模式。抽象工廠模式是全部形式的工廠模式中最爲抽象和最具通常性的一種形 式。抽象工廠模式與工廠方法模式最大的區別在於,工廠方法模式針對的是一個產品等級結構,而抽象工廠模式 須要面對多個產品等級結構,一個工廠等級結構能夠負責多個不一樣產品等級結構中的產品對象的建立。當一個工廠等級結構能夠建立出分屬於不一樣產品等級結構的一個產品族中的全部對象時,抽象工廠模式比工廠方法模式更 爲簡單、更有效率。抽象工廠模式示意圖如圖所示:
在圖中,每個具體工廠能夠生產屬於一個產品族的全部產品,例如生產顏色相同的正方形、圓形和橢圓形,所生產的產品又位於不一樣的產品等級結構中。若是使用工廠方法模式,圖所示結構須要提供 15 個具體工廠,而使用 抽象工廠模式只須要提供 5 個具體工廠,極大減小了系統中類的個數。
//界面皮膚工廠接口:抽象工廠 interface SkinFactory { public Button createButton(); public TextField createTextField(); public ComboBox createComboBox(); } //Spring皮膚工廠:具體工廠 class SpringSkinFactory implements SkinFactory { public Button createButton() { return new SpringButton(); } public TextField createTextField() { return new SpringTextField(); } public ComboBox createComboBox() { return new SpringComboBox(); } }
(1) 抽象工廠模式隔離了具體類的生成,使得客戶並不須要知道什麼被建立。因爲這種隔離,更換一個具體工廠就變得相對容易,全部的具體工廠都實現了抽象工廠中定義的那些公共接口,所以只需改變具體工廠的實例,就能夠在某種程度上改變整個軟件系統的行爲。
(2) 當一個產品族中的多個對象被設計成一塊兒工做時,它可以保證客戶端始終只使用同一個產品族中的對象。
(3) 增長新的產品族很方便,無須修改已有系統,符合「開閉原則」。
增長新的產品等級結構麻煩(例如在一族中新添加一個組件),須要對原有系統進行較大的修改,甚至須要修改抽象層代碼,這顯然會帶來較大的不便,違背了「開閉原則」。
(1) 一個系統不該當依賴於產品類實例如何被建立、組合和表達的細節,這對於全部類型的工廠模式都是很重要的,用戶無須關心對象的建立過程,將對象的建立和使用解耦。
(2) 系統中有多於一個的產品族,而每次只使用其中某一產品族。能夠經過配置文件等方式來使得用戶能夠動態改 變產品族,也能夠很方便地增長新的產品族。
(3) 屬於同一個產品族的產品將在一塊兒使用,這一約束必須在系統的設計中體現出來。同一個產品族中的產品能夠 是沒有任何關係的對象,可是它們都具備一些共同的約束,如同一操做系統下的按鈕和文本框,按鈕與文本框之 間沒有直接關係,但它們都是屬於某一操做系統的,此時具備一個共同的約束條件:操做系統的類型。
(4) 產品等級結構穩定,設計完成以後,不會向系統中增長新的產品等級結構或者刪除已有的產品等級結構。
爲了節約系統資源,有時須要確保系統中某個類只有惟一一個實例,當這個惟一實例建立成功以後,咱們沒法再建立一個同類型的其餘對象,全部的操做都只能基於這個惟一實例。爲了確保對象的惟一性,咱們能夠經過單例模式來實現,這就是單例模式的動機所在。
原型模式(Prototype Pattern):使用原型實例指定建立對象的種類,而且經過拷貝這些原型建立新的對象。原型模式是一種對象建立型模式。
原型模式的工做原理很簡單:將一個原型對象傳給那個要發動建立的對象,這個要發動建立的對象經過請求原型對象拷貝本身來實現建立過程。因爲在軟件系統中咱們常常會遇到須要建立多個相同或者類似對象的狀況,所以原型模式在真實開發中的使用頻率仍是很是高的。原型模式是一種「另類」的建立型模式,建立克隆對象的工廠就是原型類自身,工廠方法由克隆方法來實現。
須要注意的是經過克隆方法所建立的對象是全新的對象,它們在內存中擁有新的地址,一般對克隆所產生的對象進行修改對原型對象不會形成任何影響,每個克隆對象都是相互獨立的。經過不一樣的方式修改能夠獲得一系列類似但不徹底相同的對象。
//工做週報WeeklyLog:具體原型類,考慮到代碼的可讀性和易理解性,只列出部分與模式相關的核心代碼 @Data class WeeklyLog implements Cloneable { private String name; private String date; private String content; //克隆方法clone(),此處使用Java語言提供的克隆機制 public WeeklyLog clone() { Object obj = null; try { obj = super.clone(); return (WeeklyLog) obj; } catch (CloneNotSupportedException e) { System.out.println("不支持複製!"); return null; } } }
在淺克隆中,若是原型對象的成員變量是值類型,將複製一份給克隆對象;若是原型對象的成員變量是引用類型,則將引用對象的地址複製一份給克隆對象,也就是說原型對象和克隆對象的成員變量指向相同的內存地址。簡單來講,在淺克隆中,當對象被複制時只複製它自己和其中包含的值類型的成員變量,而引用類型的成員對象並無複製,如圖所示:
Object的clone方法使用的就是淺克隆
在深克隆中,不管原型對象的成員變量是值類型仍是引用類型,都將複製一份給克隆對象,深克隆將原型對象的全部引用對象也複製一份給克隆對象。簡單來講,在深克隆中,除了對象自己被複制外,對象所包含的全部成員變量也將複製,如圖所示:
在 Java 語言中,若是須要實現深克隆,能夠經過序列化(Serialization)等方式來實現。序列化就是將對象寫 到流的過程,寫到流中的對象是原有對象的一個拷貝,而原對象仍然存在於內存中。經過序列化實現的拷貝不只能夠複製對象自己,並且能夠複製其引用的成員對象,所以經過序列化將對象寫到一個流中,再從流裏將其讀出來,能夠實現深克隆。須要注意的是可以實現序列化的對象其類必須實現 Serializable 接口,不然沒法實現序列化操做。下面咱們使用深克隆技術來實現工做週報和附件對象的複製,因爲要將附件對象和工做週報對象都寫入流中,所以兩個類均須要實現 Serializable 接口。
(1) 當建立新的對象實例較爲複雜時,使用原型模式能夠簡化對象的建立過程,經過複製一個已有實例能夠提升新 實例的建立效率。
(2) 擴展性較好,因爲在原型模式中提供了抽象原型類,在客戶端能夠針對抽象原型類進行編程,而將具體原型類 寫在配置文件中,增長或減小產品類對原有系統都沒有任何影響。
(3) 原型模式提供了簡化的建立結構,工廠方法模式經常須要有一個與產品類等級結構相同的工廠等級結構,而原 型模式就不須要這樣,原型模式中產品的複製是經過封裝在原型類中的克隆方法實現的,無須專門的工廠類來創 建產品。
(4) 可使用深克隆的方式保存對象的狀態,使用原型模式將對象複製一份並將其狀態保存起來,以便在須要的時 候使用(如恢復到某一歷史狀態),可輔助實現撤銷操做。
(1) 須要爲每個類配備一個克隆方法,並且該克隆方法位於一個類的內部,當對已有的類進行改造時,須要修改源代碼,違背了「開閉原則」
(2) 在實現深克隆時須要編寫較爲複雜的代碼,並且當對象之間存在多重的嵌套引用時,爲了實現深克隆,每一層對象對應的類都必須支持深克隆,實現起來可能會比較麻煩。
建造者模式是較爲複雜的建立型模式,它將客戶端與包含多個組成部分(或部件)的複雜對象的建立過程分離,客戶端無須知道複雜對象的內部組成部分與裝配方式,只須要知道所需建造者的類型便可。它關注如何一步一步建立一個的複雜對象,不一樣的具體建造者定義了不一樣的建立過程,且具體建造者相互獨立,增長新的建造者很是方便,無須修改已有代碼,系統具備較好的擴展性。
建造者模式定義以下:
建造者模式(Builder Pattern):將一個複雜對象的構建與它的表示分離,使得一樣的構建過程能夠建立不一樣的 表示。建造者模式是一種對象建立型模式。
建造者模式一步一步建立一個複雜的對象,它容許用戶只經過指定複雜對象的類型和內容就能夠構建它們,用戶不須要知道內部的具體構建細節。建造者模式結構如圖所示:
在建造者模式的定義中提到了複雜對象,那麼什麼是複雜對象?簡單來講,複雜對象是指那些包含多個成員屬性的對象,這些成員屬性也稱爲部件或零件,如汽車包括方向盤、發動機、輪胎等部件,電子郵件包括髮件人、收件人、主題、內容、附件等部件,一個典型的複雜對象類代碼示例以下:在建造者模式的定義中提到了複雜對象,那麼什麼是複雜對象?簡單來講,複雜對象是指那些包含多個成員屬性的對象,這些成員屬性也稱爲部件或零件,如汽車包括方向盤、發動機、輪胎等部件,電子郵件包括髮件人、收件人、主題、內容、附件等部件,一個典型的複雜對象類代碼示例以下:
package cn.x5456.builderpattern; /** * @author x5456 */ public class BuilderPattern { class Product { private String partA; //定義部件,部件能夠是任意類型,包括值類型和引用類型 private String partB; private String partC; public String getPartA() { return partA; } public void setPartA(String partA) { this.partA = partA; } public String getPartB() { return partB; } public void setPartB(String partB) { this.partB = partB; } public String getPartC() { return partC; } public void setPartC(String partC) { this.partC = partC; } } abstract class Builder { //建立產品對象 protected Product product = new Product(); public abstract void buildPartA(); public abstract void buildPartB(); public abstract void buildPartC(); //返回產品對象 public Product getResult() { return product; } } // 指揮者類。在指揮類中控制產品組件構造順序 // 該類主要有兩個做用: // 一方面它隔離了客戶與建立過程;(至關於咱們去買電腦,不用咱們組裝,而是Director幫咱們組裝) // 另外一方面它控制產品的建立過程,包括某個buildPartX() 方法是否被調用以多個 buildPartX() 方法調用的前後次序等。 class Director { private Builder builder; public Director(Builder builder) { this.builder = builder; } public void setBuilder(Builder builder) { this.builder = builder; } //產品構建與組裝方法 public Product construct() { builder.buildPartA(); builder.buildPartB(); builder.buildPartC(); return builder.getResult(); } } public void func() { Builder builder = new ConcreteBuilder(); //可經過配置文件+反射實現 Director director = new Director(builder); Product product = director.construct(); } }
在建造者模式中,客戶端只需實例化指揮者類,指揮者類針對抽象建造者編程,客戶端根據須要傳入具體的建造 者類型,指揮者將指導具體建造者一步一步構造一個完整的產品(逐步調用具體建造者的 buildX() 方法),相同 的構造過程能夠建立徹底不一樣的產品。在遊戲角色實例中,若是須要更換角色,只須要修改配置文件,更換具體角色建造者類便可;若是須要增長新角色,能夠增長一個新的具體角色建造者類做爲抽象角色建造者的子類,再修改配置文件便可,原有代碼無須修改,徹底符合「開閉原則」。
abstract class ActorBuilder { protected Actor actor = new Actor(); public abstract void buildType(); public abstract void buildSex(); public abstract void buildFace(); public abstract void buildCostume(); public abstract void buildHairstyle(); public Actor construct() { this.buildType(); this.buildSex(); this.buildFace(); this.buildCostume(); this.buildHairstyle(); return actor; } }
調用方法也要修改:
ActorBuilder ab;
ab = (ActorBuilder)XMLUtil.getBean();
Actor actor;
actor = ab.construct();
建造者模式除了逐步構建一個複雜產品對象外,還能夠經過 Director 類來更加精細地控制產品的建立過程,例如增長一類稱之爲鉤子方法(HookMethod)的特殊方法(經過Director)來控制是否對某個 buildPartX() 的調用。
鉤子方法的返回類型一般爲 boolean 類型,方法名通常爲 isXXX(),鉤子方法定義在抽象建造者類中。例如咱們 能夠在遊戲角色的抽象建造者類 ActorBuilder 中定義一個方法 isBareheaded(),用於判斷某個角色是否爲「光 頭(Bareheaded)」,在 ActorBuilder 爲之提供一個默認實現,其返回值爲 false,代碼以下所示:
(1) 在建造者模式中,客戶端沒必要知道產品內部組成的細節,將產品自己與產品的建立過程解耦,使得相同的建立 過程能夠建立不一樣的產品對象。
(2) 每個具體建造者都相對獨立,而與其餘的具體建造者無關,所以能夠很方便地替換具體建造者或增長新的具 體建造者,用戶使用不一樣的具體建造者便可獲得不一樣的產品對象。因爲指揮者類針對抽象建造者編程,增長新的具體建造者無須修改原有類庫的代碼,系統擴展方便,符合「開閉原則」
(3) 能夠更加精細地控制產品的建立過程。將複雜產品的建立步驟分解在不一樣的方法中,使得建立過程更加清晰,也更方便使用程序來控制建立過程。
(1) 建造者模式所建立的產品通常具備較多的共同點,其組成部分類似,若是產品之間的差別性很大,例如不少組成部分都不相同,不適合使用建造者模式,所以其使用範圍受到必定的限制。
(2) 若是產品的內部變化複雜,可能會致使須要定義不少具體建造者類來實現這種變化,致使系統變得很龐大,增長系統的理解難度和運行成本。
(1) 須要生成的產品對象有複雜的內部結構,這些產品對象一般包含多個成員屬性。
(2) 須要生成的產品對象的屬性相互依賴,須要指定其生成順序。
(3) 對象的建立過程獨立於建立該對象的類。在建造者模式中經過引入了指揮者類,將建立過程封裝在指揮者類中,而不在建造者類和客戶類中。
(4) 隔離複雜對象的建立和使用,並使得相同的建立過程能夠建立不一樣的產品。