摘要:又稱為多態(tài)性工廠模式或虛擬構造子模式。簡單工廠模式簡單工廠模式簡單工廠模式又稱為靜態(tài)工廠方法模式,它屬于類創(chuàng)建型模式。多態(tài)性設計工廠方法模式之所以又被稱為多態(tài)工廠模式,是因為所有的具體工廠類都具有同一抽象父類。
點擊進入我的博客 2.1 簡單工廠模式 2.1.1 工廠模式的幾種形態(tài)
工廠模式主要用一下幾種形態(tài):
簡單工廠(Simple Factory):專門定義一個類來負責創(chuàng)建其他類的實例,被創(chuàng)建的實例通常都具有共同的父類。它又稱為靜態(tài)工廠方法模式。
工廠方法(Factory Method):提前定義用于創(chuàng)建對象的接口,讓子類決定實例化具體的某一個類,即在工廠和產品中間增加接口,工廠不再負責產品的創(chuàng)建,由接口針對不同條件返回具體的類實例,由具體類實例去實現(xiàn)。又稱為多態(tài)性工廠模式或虛擬構造子模式。
抽象工廠(Abstract Factory):抽象工廠模式是指當有多個抽象角色時,使用的一種工廠模式。抽象工廠模式可以向客戶端提供一個接口,使客戶端在不必指定產品的具體的情況下,創(chuàng)建多個產品族中的產品對象。又稱為工具箱模式。
2.1.2 簡單工廠模式簡單工廠模式(Simple Factory Pattern)又稱為靜態(tài)工廠方法(Static Factory Method)模式,它屬于類創(chuàng)建型模式。
在簡單工廠模式中,可以根據自變量的不同返回不同類的實例。
簡單工廠模式專門定義一個類來負責創(chuàng)建其他類的實例,被創(chuàng)建的實例通常都具有共同的父類。
工廠類(Creator)角色:擔任這個角色的是工廠方法模式的核心,含有與應用緊密相關的商業(yè)邏輯。工廠類在客戶端的直接調用下創(chuàng)建產品對象,它往往由一個具體Java類實現(xiàn)。
抽象產品(Product)角色:擔任這個角色的類是工廠方法模式所創(chuàng)建的對象的父類,或它們共同擁有的接口。抽象產品角色可以用一個Java接口或者Java抽象類實現(xiàn)。
具體產品(Concrete Product)角色:工廠方法模式所創(chuàng)建的任何對象都是這個角色的實例,具體產品角色由一個具體Java 類實現(xiàn)。
abstract class Fruit {} class Apple extends Fruit {} class Banana extends Fruit {} class FruitFactory { public static Fruit newInstance(Class extends Fruit> clz) { try { return clz.newInstance(); } catch (Exception e) { throw new RuntimeException(e); } } }
每個工廠類可以有多于一個的工廠方法,分別負責創(chuàng)建不同的產品對象。比如java.text.DateFormat類是其子類的工廠類,而它就提供了多個靜態(tài)工廠方法。
在有些情況下,工廠角色可以由抽象產品角色扮演。典型的應用就是java.text.DateFormat類,一個抽象產品類同時是子類的工廠。
如果抽象產品角色已經被忽略,而工廠角色就可以與具體產品角色合并。換言之,一個產品類為自身的工廠。
class ConcreteProduct { public static ConcreteProduct factory() { return new ConcreteProduct(); } }2.1.3 簡單工廠模式與其他模式的關系
單例模式使用了簡單工廠模式。換言之,單例類具有一個靜態(tài)工廠方法提供自身的實例。
單例模式并不是簡單工廠模式的退化情形,單例模式要求單例類的構造方法是私有的,從而客戶端不能直接將之實例化,而必須通過這個靜態(tài)工廠方法將之實例化
單例類自身是自己的工廠角色。換言之,單例類自己負責創(chuàng)建自身的實例。
單例類使用一個靜態(tài)的屬性存儲自己的惟一實例 ,工廠方法永遠僅提供這一個實例。
多例模式是對單例模式的推廣。多例模式與單例模式的共同之處在于它們都禁止外界直接將之實例化,同時通過靜態(tài)工廠方法向外界提供循環(huán)使用的自身的實例。它們的不同在于單例模式僅有一個實例,而多例模式則可以有多個實例。
多例模式往往具有一個聚集屬性,通過向這個聚集屬性登記已經創(chuàng)建過的實例達到循環(huán)使用實例的目的。一般而言,一個典型的多例類具有某種內部狀態(tài),這個內部狀態(tài)可以用來區(qū)分各個實例;而對應于每一個內部狀態(tài),都只有一個實例存在。
根據外界傳入的參量,工廠方法可以查詢自己的登記聚集,如果具有這個狀態(tài)的實例已經存在,就直接將這個實例提供給外界;反之,就首先創(chuàng)建一個新的滿足要求的實例,將之登記到聚集中,然后再提供給客戶端。
單例和多例模式使用了一個屬性或者聚集屬性來登記所創(chuàng)建的產品對象, 以便可以通過查詢這個屬性或者聚集屬性找到和共享已經創(chuàng)建了的產品對象。這就是備忘錄模式的應用。
簡單工廠模式所創(chuàng)建的對象往往屬于一個產品等級結構,這個等級結構可以是MVC模式中的視圖(View);而工廠角色本身可以是控制器(Controller)。一個MVC 模式可以有一個控制器和多個視圖,如上圖所示。
上圖中的Controller(控制器)也就是工廠角色,它負責創(chuàng)建產品View(視圖)。
如果系統(tǒng)需要有多個控制器參與這個過程的話,簡單工廠模式就不適用了,應當考慮使用工廠方法模式。
2.1.4 簡單工廠模式的優(yōu)點和缺點工廠類含有必要的判斷邏輯,可以決定在什么時候創(chuàng)建哪一個產品類的實例,客戶端可以免除直接創(chuàng)建產品對象的責任,而僅僅“消費”產品;簡單工廠模式通過這種做法實現(xiàn)了對責任的分割,它提供了專門的工廠類用于創(chuàng)建對象。
客戶端無需知道所創(chuàng)建的具體產品類的類名,只需要知道具體產品類所對應的參數(shù)即可,對于一些復雜的類名,通過簡單工廠模式可以減少使用者的記憶量。
通過引入配置文件,可以在不修改任何客戶端代碼的情況下更換和增加新的具體產品類,在一定程度上提高了系統(tǒng)的靈活性。
由于工廠類集中了所有產品創(chuàng)建邏輯,一旦不能正常工作,整個系統(tǒng)都要受到影響。
使用簡單工廠模式將會增加系統(tǒng)中類的個數(shù),在一定程序上增加了系統(tǒng)的復雜度和理解難度。
系統(tǒng)擴展困難,一旦添加新產品就不得不修改工廠邏輯,在產品類型較多時,有可能造成工廠邏輯過于復雜,不利于系統(tǒng)的擴展和維護。
簡單工廠模式由于使用了靜態(tài)工廠方法,造成工廠角色無法形成基于繼承的等級結構。
工廠類負責創(chuàng)建的對象比較少,由于創(chuàng)建的對象較少,不會造成工廠方法中的業(yè)務邏輯太過復雜。
客戶端只知道傳入工廠類的參數(shù),對于如何創(chuàng)建對象不關心;客戶端既不需要關心創(chuàng)建細節(jié),甚至連類名都不需要記住,只需要知道類型所對應的參數(shù)。
2.2 工廠方法模式 2.2.1 工廠方法模式簡介工廠方法模式是類的創(chuàng)建模式,又叫做虛擬構造子模式或多態(tài)性工廠模式。
在工廠方法模式中,核心的工廠類不再負責所有的產品的創(chuàng)建,而是將具體創(chuàng)建的工作交給子類去做。該核心類成為一個抽象工廠角色,僅負責給出具體工廠子類必須實現(xiàn)的接口,而不接觸哪一個產品類應當被實例化這種細節(jié)。
2.2.2 工廠方法的結構抽象工廠(Creator)角色:擔任這個角色的是工廠方法模式的核心,它是與應用程序無關的。任何在模式中創(chuàng)建對象的工廠類必須實現(xiàn)這個接口。在實際的系統(tǒng)中,這個角色也常常使用抽象類實現(xiàn)。
具體工廠(Concrete Creator)角色:擔任這個角色的是實現(xiàn)了抽象工廠接口的具體JAVA類。具體工廠角色含有與業(yè)務密切相關的邏輯,并且受到應用程序的調用以創(chuàng)建導出類。
抽象產品(Product)角色:工廠方法模式所創(chuàng)建的對象的超類,也就是所有產品對象的共同父類或共同擁有的接口。在實際的系統(tǒng)中,這個角色也常常使用抽象類實現(xiàn)。
具體產品(Concrete Product)角色:這個角色實現(xiàn)了抽象產品(Product)角色所聲明的接口,工廠方法模式所創(chuàng)建的每一個對象都是某個具體產品角色的實例。
abstract class Fruit {} abstract class FruitFactory { public abstract Fruit newInstance(); } class Apple extends Fruit {} class Banana extends Fruit {} class AppleFactory extends FruitFactory { @Override public Fruit newInstance() { return new Apple(); } } class BananaFactory extends FruitFactory { @Override public Fruit newInstance() { return new Banana(); } }2.2.3 工廠方法模式的細節(jié)
java.util.Collection接口繼承來Iterable接口,所有其子類都必須實現(xiàn)Iterator
抽象工廠角色和抽象產品角色都可以選擇由Java接口或者Java抽象類來實現(xiàn)。
如果具體工廠角色由共同的邏輯,那么這些共同的邏輯就可以向上移動到抽象工廠角色中,這也意味著抽象工廠角色應該由抽象類實現(xiàn);反之就應當由接口實現(xiàn)。
抽象工廠角色可以規(guī)定出多于一個的工廠方法,從而使具體工廠角色實現(xiàn)這些不同的工廠方法。
隱藏細節(jié):在工廠方法模式中,工廠方法用來創(chuàng)建客戶所需要的產品,同時還向客戶隱藏了哪種具體產品類將被實例化這一細節(jié),用戶只需要關心所需產品對應的工廠,無須關心創(chuàng)建細節(jié),甚至無須知道具體產品類的類名。
多態(tài)性設計:工廠方法模式之所以又被稱為多態(tài)工廠模式,是因為所有的具體工廠類都具有同一抽象父類。基于工廠角色和產品角色的多態(tài)性設計是工廠方法模式的關鍵,它能夠使工廠可以自主確定創(chuàng)建何種產品對象,而如何創(chuàng)建這個對象的細節(jié)則完全封裝在具體工廠內部。
完全符合開閉原則:在系統(tǒng)中加入新產品時,無須修改抽象工廠和抽象產品提供的接口,無須修改客戶端,也無須修改其他的具體工廠和具體產品,而只要添加一個具體工廠和具體產品就可以了。
類數(shù)量太多:在添加新產品時,需要編寫新的具體產品類,而且還要提供與之對應的具體工廠類,系統(tǒng)中類的個數(shù)將成對增加,在一定程度上增加了系統(tǒng)的復雜度,有更多的類需要編譯和運行,會給系統(tǒng)帶來一些額外的開銷。
系統(tǒng)的抽象性和復雜性:由于考慮到系統(tǒng)的可擴展性,需要引入抽象層,在客戶端代碼中均使用抽象層進行定義,增加了系統(tǒng)的抽象性和理解難度,且在實現(xiàn)時可能需要用到DOM、反射等技術,增加了系統(tǒng)的實現(xiàn)難度。
一個類不知道它所需要的對象的類:在工廠方法模式中,客戶端不需要知道具體產品類的類名,只需要知道所對應的工廠即可,具體的產品對象由具體工廠類創(chuàng)建;客戶端需要知道創(chuàng)建具體產品的工廠類。
一個類通過其子類來指定創(chuàng)建哪個對象:在工廠方法模式中,對于抽象工廠類只需要提供一個創(chuàng)建產品的接口,而由其子類來確定具體要創(chuàng)建的對象,利用面向對象的多態(tài)性和里氏代換原則,在程序運行時,子類對象將覆蓋父類對象,從而使得系統(tǒng)更容易擴展。
動態(tài)指定:將創(chuàng)建對象的任務委托給多個工廠子類中的某一個,客戶端在使用時可以無須關心是哪一個工廠子類創(chuàng)建產品子類,需要時再動態(tài)指定,可將具體工廠類的類名存儲在配置文件或數(shù)據庫中。
2.2.4 工廠方法模式與其他模式工廠方法模式和簡單工廠模式在結構上的不同很明顯。工廠方法模式的核心是一個抽象工廠類,而簡單工廠模式把核心放在一個具體類上。
如果系統(tǒng)需要加入一個新的導出類型,那么所需要的就是向系統(tǒng)中加入一個這個導出類以及所對應的工廠類。沒有必要修改客戶端,也沒有必要修改抽象工廠角色或者其他已有的具體工廠角色。對于增加新的導出類型而言,這個系統(tǒng)完全支持“開-閉原則”。
工廠方法模式常常與模版方法模式一起聯(lián)合使用。原因其實不難理解:第一,兩個模式都是基于方法的,工廠方法模式是基于多態(tài)性的工廠方法的,而模版方法模式是基于模版方法和基本方法的;第二,兩個模式都將具體工作交給子類。工廠方法模式將創(chuàng)建工作推延給子類,模版方法模式將剩余邏輯交給子類。
工廠方法模式總是涉及到兩個等級結構中的對象,而這兩個等級結構可以分別是MVC中的控制器和試圖。一個MVC模式可以有多個控制器和多個視圖。
如果系統(tǒng)內只需要一個控制器,那么可以簡化為簡單工廠模式。
享元模式使用了帶有循環(huán)邏輯的工廠方法。
2.3 抽象工廠模式 2.3.1 抽象工廠模式簡介抽象工廠模式是所有形態(tài)的工廠模式中最為抽象和具有一般性的形態(tài)。
“抽象”來自“抽象產品角色”,“抽象工廠”就是抽象產品角色的工廠。
抽象工廠模式與工廠方法模式最大的區(qū)別在于,工廠方法模式針對的是一個產品等級結構,而抽象工廠模式則需要面對多個產品等級結構。
2.3.2 抽象工廠方式結構抽象工廠(Creator)角色:擔任這個角色的是抽象方法模式的核心,它是與應用程序無關的。
具體工廠(Concrete Creator)角色:具體工廠角色含有與業(yè)務密切相關的邏輯,并且受到應用程序的調用以創(chuàng)建導出類。
抽象產品(Product)角色:抽象方法模式所創(chuàng)建的對象的超類,也就是所有產品對象的共同父類或共同擁有的接口。
具體產品(Concrete Product)角色:抽象工廠模式所創(chuàng)建的每一個對象都是某個具體產品角色的實例。
2.3.3 抽象工廠方式細節(jié)一個系統(tǒng)不應當依賴于產品類實例如何被創(chuàng)建、組合和表達的細節(jié)。這對于所有形態(tài)的工廠模式都是重要的;
一個系統(tǒng)的產品有多于一個的產品族,而系統(tǒng)只消費其中某一族的產品;
同屬于同一個產品族的產品是在一起使用的,這一約束必須要在系統(tǒng)的設計中體現(xiàn)出來;
系統(tǒng)提供一個產品類的庫,所有的產品以同樣的接口出現(xiàn),從而使客戶端不依賴于實現(xiàn)。
隔離了具體類的生成,使得用戶不需要知道什么被創(chuàng)建了。
當一個產品族中的多個對象被設計成一起工作時,它能夠保證客戶端始終只使用同一個產品族中的對象。
抽象工廠的接口確定了可以被創(chuàng)建的產品集合,所以難以擴展抽象工廠以生成新種類的產品。
2.3.4 三種工廠模式總結下面例子中,手機、電腦是抽象產品,蘋果、三星等是工廠。
抽象產品叫手機
具體產品是蘋果手機、三星手機
工廠有一個生產手機的方法,可以根據傳入品牌是蘋果還是三星決定生產哪個品牌的手機
抽象產品叫手機
具體產品是蘋果手機、三星手機
抽象工廠叫手機工廠
具體工廠是蘋果手機工廠和三星手機工廠,分別生產蘋果手機和三星手機
抽象產品叫手機、電腦
具體產品是蘋果手機、蘋果電腦、三星手機、三星電腦
抽象工廠叫手機電腦工廠,有兩個方法分別是生產手機和生產電腦
具體工廠是蘋果工廠和三星工廠,蘋果工廠的兩個方法分別生產蘋果手機和蘋果電腦,三星工廠的兩個方法分別生產三星手機和三星電腦
2.4 單例模式單例模式確保某個類只有一個實例,而且自行實例化并向整個系統(tǒng)提供這個實例。
2.4.1 單例模式細節(jié)私有化構造方法!
保證一個類僅有一個實例,并提供一個訪問它的全局訪問點。
主要解決一個全局使用的類頻繁地創(chuàng)建與銷毀的問題。
屬性文件
Java.lang.Runtime對象
在內存中只有一個實例,減少內存開支,特別是一個對象需要頻繁地創(chuàng)建銷毀時。
單例模式可以避免對資源的多重占用,例如一個寫文件操作,由于只有一個實例存在內存中,避免對同一個資源文件的同時寫操作。
單例模式可以在系統(tǒng)設置全局的訪問點,優(yōu)化和共享資源訪問,例如,可以設計一個單例類,負責所有數(shù)據表的映射處理。
由于私有化了構造方法,所以不能繼承
與單一職責原則沖突,一個類應該只關心內部邏輯,而不關心外面怎么樣來實例化。
特別要注意單例對象如果持有Context,那么容易引發(fā)內存泄漏,此時需要注意傳遞給單例對象的context,最好是Application Context
2.4.2 餓漢式與懶漢式加載類的時候比較慢
運行時獲得對象的速度比較快
它從加載到應用結束會一直占用資源。
class EagerSingleton { // 創(chuàng)建單例類對象 private static EagerSingleton instance = new EagerSingleton(); // 構造方法私有化 private EagerSingleton() {} // 獲取可用對象 public static EagerSingleton getInstance() { return instance; } }
是運行時獲得對象的速度比較慢
加載類的時候比較快
它在整個應用的生命周期只有一部分時間在占用資源。
class LazySingleton { // 聲明單例類對象 private static LazySingleton instance; // 構造方法私有化 private LazySingleton() {} // 獲取可用對象 public static synchronized LazySingleton getInstance() { if(null == instance) { instance = new LazySingleton(); } return instance; } }2.4.3 懶漢式與雙重檢查成例
由于2.4.2懶漢式代碼中,直接對整個getInstance()方法進行了同步處理,可能會導致一些性能問題,于是有了下面的改進方法,通過雙重檢查和同步代碼塊的形式來處理懶漢式的并發(fā)問題。但要注意的是,這在Java語言中可能是問題的,之所以是可能有問題,是因為不同Java版本的內存模型不同。
在第一次檢查時,可能會有多個線程同時到達(1)處。假設線程1和線程2都到達(1)進行第一次檢查,此時instance為null,兩個線程都通過第一次檢查
然后由于同步代碼塊加鎖,只能有一個線程獲取鎖。線程1獲取鎖并向下繼續(xù)執(zhí)行,此時instance仍然為null,于是執(zhí)行(5)初始化instance = new Singleton(),然后線程1執(zhí)行完畢釋放鎖。
然后線程2獲取鎖,此時第二次檢查判斷instance不為null,所以線程2不會進行初始化,直接退出,返回已經初始化好的instance。
以上步驟聽起來是沒有問題的,但問題出在instance = new Singleton()這一句話并不是原子操作!
class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() throws Exception { if(null == instance) { // (1)第一次檢查 // (2)這里會有多個線程同時到達 synchronized(Singleton.class) { // 同步代碼塊加鎖 // (3)此處只能是單線程 if (null == instance) { // (4)第二次檢查 instance = new Singleton(); // (5)初始化instance } } } return instance; } }
為展示問題出現(xiàn)的原因,假設代碼行instance =new Singleton();執(zhí)行了下列偽代碼:
mem = allocate(); // (1)為單例對象分配內存空間. instance = mem; // (2)注意,instance引用現(xiàn)在已經不是null,但還未初始化 ctorSingleton(instance); // (3)為單例對象通過instance調用構造函數(shù)
上述偽代碼中,執(zhí)行的順序可能是(1)(3)(2),此時不會導致上述問題;但如果(1)(2)(3)的執(zhí)行過程,則可能在線程1執(zhí)行到(2)時,CPU開始執(zhí)行線程2,此時恰好線程2執(zhí)行到第一次檢查,獲取到的是一個不為null但尚未初始化的值,此時程序會拋出錯誤。
在高版本的JDK中,使用volatile關鍵字可以保證不會產生上述問題。被volatile所修飾的變量的值不會被本地線程緩存,所有對該變量的讀寫都是直接操作共享內存來實現(xiàn),從而確保多個線程能正確的處理該變量。
該關鍵字可能會屏蔽掉虛擬機中的一些代碼優(yōu)化,所以其運行效率可能不是很高。
class Singleton { private static volatile Singleton instance; }
class Singleton { private Singleton() {} private static class Holder { static Singleton instance = new Singleton(); } public static Singleton getInstance() { // 外圍類能直接訪問內部類(不管是否是靜態(tài)的)的私有變量 return Holder.instance; } }
單例模式與雙重檢測
雙重檢查的缺陷
用happen-before規(guī)則重新審視DCL
2.5 多例模式多例模式實際上就是單例模式的推廣,多例類可以有多個實例,多例類必須自己創(chuàng)建、管理自己的實例,并向外界提供自己的實例。
多例模式分為有上限多例類和無上限多例類,無上限多例類要通過集合來實現(xiàn)。
建造者模式(Builder Pattern)使用多個簡單的對象一步一步構建成一個復雜的對象。它提供了一種創(chuàng)建對象的最佳方式。
2.6.1 建造者結構Builder(抽象建造者):可以是一個抽象類或一個接口,規(guī)范產品對象的各個組成部分的建造。
ConcreteBuilder(具體建造者):它實現(xiàn)了Builder接口,給出一步一步的創(chuàng)建產品實例的操作,然后提供一個方法返回創(chuàng)建好的復雜產品對象。
Product(產品角色):如果是單個產品類,那么就是一個具體的產品;如果是多個產品類,那么就是一個抽象的類或接口。
ConcreteProduct(具體產品):當多個產品類時,繼承抽象Product,也就是具體的要建造的復雜對象。值得注意的是,這些產品類不一定會有共同的接口。
Director(指揮者):它復雜安排復雜對象的建造次序,指揮者與抽象建造者之間存在關聯(lián)關系,可以在Director的方法中調用建造者對象的部件構造與裝配方法,完成建造復雜對象的任務。
2.6.2 建造者模式細節(jié)一個產品通常有不同的組成成分作為產品的零件,不同的產品可以有不同的零件,建造產品的過程是建造零件的過程。建造者模式將產品的結構和產品的零件建造過程對外隱藏起來,把對建造過程進行指揮的責任和具體建造零件的責任分割開來,達到責任劃分和封裝的目的。
主要解決在開發(fā)過程中,有時需要創(chuàng)建一個復雜對象,通常由多個部分的子對象構成;由于復雜對象的多樣性,這個復雜對象的各個部分經常面臨著劇烈的變化,但是將它們組合在一起的算法需要保持穩(wěn)定。
肯德基的產品很多,需要組成“套餐”。
Java的StringBuilder
省略抽象建造者:如果只需要一個具體建造者,則可以省略抽象建造者。
省略指揮者:可以在具體建造者里邊直接構造具體產品。
合并具體建造者和具體產品:在產品本身就是自己的建造者。
良好的封裝性
具體建造類之間獨立,擴展性好
如果產品比較多,可能會有很多的建造類。
2.6.3 肯德基套餐案例public class Waiter { public static void main(String[] args) { KFCBuilder builder = new MexicanTwisterBuilder(); builder.buildBeverage(); builder.buildHamburger(); builder.buildSnack(); KFCCombo combo = builder.getCombo(); } } // 套餐接口 abstract class KFCCombo { private String hamburger; private String beverage; private String snack; // getters & setters } // 墨西哥雞肉卷套餐 class MexicanTwisterCombo extends KFCCombo {} // Builder接口 interface KFCBuilder { void buildHamburger(); void buildBeverage(); void buildSnack(); KFCCombo getCombo(); } class MexicanTwisterBuilder implements KFCBuilder { private KFCCombo combo = new MexicanTwisterCombo(); @Override public void buildHamburger() { combo.setHamburger("Mexican Twister"); } @Override public void buildBeverage() { combo.setBeverage("Pepsi Cola"); } @Override public void buildSnack() { combo.setSnack("Hot Wings"); } @Override public KFCCombo getCombo() { return combo; } }2.6.4 builder內部類
如果一個類有很多屬性,此時為此類寫一個Builder內部類,來輔助建造該類。
class Phone { private String screen; private String camera; private String cpu; private String battery; public static Builder builder() { return new Builder(); } public static class Builder { private Phone phone = new Phone(); public Builder screen(String screen) { phone.screen = screen; return this; } public Builder camera(String camera) { phone.camera = camera; return this; } public Builder cpu(String cpu) { phone.cpu = cpu; return this; } public Builder battery(String battery) { phone.battery = battery; return this; } public Phone build() { return phone; } } }2.6.5 與其他模式的關系
抽象工廠模式實現(xiàn)對產品家族的創(chuàng)建,一個產品家族是這樣的一系列產品:具有不同分類維度的產品組合,采用抽象工廠模式則是不需要關心構建過程,只關心什么產品由什么工廠生產即可。
而建造者模式則是要求按照規(guī)定建造產品,它的主要目的是通過組裝零配件而產生一個新產品。
換言之,抽象工廠模式在更加具體的維度上,而建造模式在一個更加宏觀的維度上。
事實上建造模式是策略模式的一種特殊情況,這兩種模式的卻別在于用意不同。
建造模式適應于為客戶端一點一點地建造新的對象。
策略模式的目的是為算法提供抽象的接口。
2.7 原始模型模式原始模型模式通過給一個原型對象來指明所要創(chuàng)建的對象的類型,然后用復制這個原型對象的辦法創(chuàng)建出更多同類型的對象。
2.7.1 原型模式結構
這種模式涉及到三個角色:
客戶(Client)角色:客戶類提出創(chuàng)建對象的請求
抽象原型(Prototype)角色:這是一個抽象角色,此角色給出所以的具體原型類所需的接口。
具體原型(Concrete Prototype):被復制的對象。
2.7.2 原型模式細節(jié)用原型實例指定創(chuàng)建對象的種類,并且通過拷貝這些原型創(chuàng)建新的對象。
創(chuàng)建新對象成本較大(例如初始化時間長,占用CPU多或占太多網絡資源),新對象可以通過復制已有對象來獲得,如果相似對象,則可以對其成員變量稍作修改。
系統(tǒng)要保存對象的狀態(tài),而對象的狀態(tài)很小。
需要避免使用分層次的工廠類來創(chuàng)建分層次的對象,并且類的實例對象只有一個或很少的組合狀態(tài),通過復制原型對象得到新實例可以比使用構造函數(shù)創(chuàng)建一個新實例更加方便。
當創(chuàng)建對象的實例較為復雜的時候,使用原型模式可以簡化對象的創(chuàng)建過程,通過復制一個已有的實例可以提高實例的創(chuàng)建效率。
擴展性好,由于原型模式提供了抽象原型類,在客戶端針對抽象原型類進行編程,而將具體原型類寫到配置文件中,增減或減少產品對原有系統(tǒng)都沒有影響。
原型模式提供了簡化的創(chuàng)建結構,工廠方法模式常常需要有一個與產品類等級結構相同的工廠等級結構,而原型模式不需要這樣,圓形模式中產品的復制是通過封裝在類中的克隆方法實現(xiàn)的,無需專門的工廠類來創(chuàng)建產品。
可以使用深克隆方式保存對象的狀態(tài),使用原型模式將對象復制一份并將其狀態(tài)保存起來,以便在需要的時候使用(例如恢復到歷史某一狀態(tài)),可輔助實現(xiàn)撤銷操作。
需要為每一個類配置一個克隆方法,而且該克隆方法位于類的內部,當對已有類進行改造的時候,需要修改代碼,違反了開閉原則。
在實現(xiàn)深克隆時需要編寫較為復雜的代碼,而且當對象之間存在多重簽到引用時,為了實現(xiàn)深克隆,每一層對象對應的類都必須支持深克隆,實現(xiàn)起來會比較麻煩。
2.7.3 Java的Cloneclone()方法返回的對象叫做原始對象的克隆體。一個克隆對象的基本特性必須是:
a.clone()!=a,這也就意味著克隆對象和原始對象在java中是兩個不同的對象。
a.clone().getClass == a.getClass(),克隆對象與原對象類型相同
a.clone.equals(a),也就是說克隆對象完完全全是原始對象的一個拷貝。此條件是非必需的。
Object類沒有實現(xiàn)該接口,所以用戶如果沒有主動實現(xiàn)該接口時,調用clone()方法會報錯CloneNotSupportedException。
實現(xiàn)Cloneable接口,這是步驟的關鍵之處。
重寫clone()方法,并聲明為public,因為Object的該方法是protected的。
調用super.clone()來獲取新的克隆對象。在運行時刻,Object中的clone()識別出你要復制的是哪一個對象,然后為此對象分配空間,并進行對象的復制,將原始對象的內容一一復制到新對象的存儲空間中。
class A implements Cloneable { @Override public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } } }2.7.4 深復制和淺復制
被復制對象的所有變量都含有與原來的對象相同的值,而所有的對其他對象的引用仍然指向原來的對象。換言之,淺復制僅僅復制所考慮的對象,而不復制它所引用的對象。
Object.clone()是淺復制。
被復制對象的所有變量都含有與原來的對象相同的值,除去那些引用其他對象的變量。那些引用其他對象的變量將指向被復制過的新對象,而不再是原有的那些被引用的對象。換言之,深復制把要復制的對象所引用的對象都復制了一遍。
深復制要深入到多少層是一個不易確定到問題,需要特別注意
深復制的過程中可能出現(xiàn)循環(huán)引用到問題,需要小心處理
把對象寫到流里的過程是串行化(Serilization)過程,但是在Java程序師圈子里又非常形象地稱為“冷凍”或者“腌咸菜(picking)”
把對象從流中讀出來的并行化(Deserialization)過程則叫做 “解凍”或者“回鮮(depicking)”過程。
應當指出的是,寫在流里的是對象的一個拷貝,而原對象仍然存在于JVM里面,因此“腌咸菜”的只是對象的一個拷貝,Java咸菜還可以回鮮。
利用這個特性,在Java語言里深復制一個對象,可以先使對象實現(xiàn)Serializable接口,然后把對象(實際上只是對象的一個拷貝)寫到一個流里,再從流里讀出來,便可以克隆對象。
這樣做的前提是對象以及對象內部所有引用到的對象都是可串行化的,否則,就需要仔細考察那些不可串行化的對象可否設成transient,從而將之排除在復制過程之外。
public Object deepClone() throws Exception { //將對象寫到流里 ByteArrayOutputStream bo = new ByteArrayOutputStream(); ObjectOutputStream oo = new ObjectOutputStream(bo); oo.writeObject(this); //從流里讀出來 ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray()); ObjectInputStream oi = new ObjectInputStream(bi); return(oi.readObject()); }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/72502.html
摘要:我們分三篇文章來總結一下設計模式在中的應用,這是第一篇創(chuàng)建型模式。二提煉設計模式的幾個原則開閉原則模塊應對擴展開放,而對修改關閉。工廠模式實現(xiàn)定義一個用于創(chuàng)建對象的接口,讓子類決定實例化哪一個類。設計模式的第一部分,創(chuàng)建型模式就總結完了。 我們分三篇文章來總結一下設計模式在PHP中的應用,這是第一篇創(chuàng)建型模式。一、設計模式簡介 首先我們來認識一下什么是設計模式: 設計模式是一套被反復使...
摘要:抽象工廠目的創(chuàng)建一系列相關或依賴的對象,而不指定它們的具體類。這個模式是一個真正的設計模式,因為它遵循了依賴反轉原則眾所周知這個代表了真正的面向對象程序設計。 【搬運于GitHub開源項目DesignPatternsPHP】 項目地址:戳我 1、創(chuàng)建型設計模式 在軟件工程中,創(chuàng)建型設計模式承擔著對象創(chuàng)建的職責,嘗試創(chuàng)建適合程序上下文的對象,對象創(chuàng)建設計模式的產生是由于軟件工程設計的問...
摘要:當然,除了讓我們顯得更加專業(yè)之外,在自己所學習或者工作的項目中,適當合理的使用設計模式,能夠給項目帶來很大的好處。 簡單說兩句 本文首發(fā)公眾號【一名打字員】 對不住各位老鐵了,年前說好要更幾波JAVA的東西,又偷懶了,沒辦法,在這里用小錘錘偷偷錘了自己幾下。由于工作原因,更新時間不定,各位老鐵有問題可以私聊我哈。 對于初學者或者是正在向中高級的Java程序猿(打字員)來說,時刻梳理自己...
摘要:利用工廠方法模式,請求者發(fā)出請求,而不具體創(chuàng)建產品。正是因為這個原因,使用工廠方法模式可以簡化復雜的創(chuàng)建過程,關鍵就在于它在維持一個公共接口。 創(chuàng)建型設計模式 包括以下五種: 抽象工廠 生成器 工廠方法 原型 單例 我們選擇工廠方法和原型模式作為將用PHP實現(xiàn)的創(chuàng)建型設計的例子工廠方法模式是這5個設計模式中唯一的一種類設計模式原型模式屬于對象類模式,可以使用PHP_clone方法實...
摘要:維基百科在軟件工程中,創(chuàng)建型設計模式是用于解決對象創(chuàng)建機制,嘗試在指定場景下使用合理的方式來創(chuàng)建對象的設計模式。維基百科說建造者模式是一種對象創(chuàng)建軟件設計模式,其目的是找到一種解決方案,以解決可伸縮構造函數(shù)的反模式。 1.創(chuàng)建型設計模式2.結構型設計模式3.行為型設計模式 創(chuàng)建型設計模式 簡而言之 創(chuàng)建型設計模式關注的是如何實例化一個或者一組相關的對象。 維基百科 在軟件工程中,創(chuàng)建型...
閱讀 2045·2023-04-26 02:23
閱讀 1789·2021-09-03 10:30
閱讀 1351·2019-08-30 15:43
閱讀 1191·2019-08-29 16:29
閱讀 530·2019-08-29 12:28
閱讀 2332·2019-08-26 12:13
閱讀 2169·2019-08-26 12:01
閱讀 2400·2019-08-26 11:56