設計模式 之 工廠模式

項目源碼:https://gitee.com/Jacob-gitee/DesignMode

個人博客:https://jacob.org.cn

女媧造人的故事

東漢《風俗通》記錄了一則神話故事:“開天辟地,未有人民,女媧搏黃土做人”,講述的內容就是大家非常熟悉的女媧造人的故事。開天辟地之初,大地上并沒有生物,只有蒼茫大地,純粹而潔凈的自然環境,寂靜而又寂寞,于是女媧決定創造一個新物種(即人類)來增加世界的繁榮,怎么制造呢?

別忘了女媧是神仙,沒有辦不到的事情,造人的過程是這樣的:首先,女媧采集黃土捏成人的形狀,然后放到八卦爐中燒制,最后放置到大地上生長,工藝過程是沒有錯的,但是意外隨時都會發生:

第一次烤泥人,感覺應該熟了,往大地上一放,哇,沒烤熟!于是一個白人誕生了!(這也是缺乏經驗的最好證明。)第二次烤泥人,上一次沒烤熟,這次多烤一會兒,放到世間一看,嘿,熟過頭了,于是黑人誕生了!

第三次烤泥人,一邊燒制一邊察看,直到表皮微黃,嘿,剛剛好,于是黃色人種出現了!

這個造人過程是比較有意思的,是不是可以通過軟件開發來實現這個過程呢?古人 云:“三人行,必有我師焉”,在面向對象的思維中,萬物皆對象,是對象我們就可以通過軟件設計來實現。首先對造人過程進行分析,該過程涉及三個對象:女媧、八卦爐、三種不同 膚色的人。女媧可以使用場景類Client來表示,八卦爐類似于一個工廠,負責制造生產產品(即人類),三種不同膚色的人,他們都是同一個接口下的不同實現類,都是人嘛,只是膚 色、語言不同,對于八卦爐來說都是它生產出的產品。分析完畢,我們就可以畫出如圖8-1所示的類圖。

類圖比較簡單,AbstractHumanFactory是一個抽象類,定義了一個八卦爐具有的整體功 能,HumanFactory為實現類,完成具體的任務——創建人類;Human接口是人類的總稱,其 三個實現類分別為三類人種;NvWa類是一個場景類,負責模擬這個場景,執行相關的任務。

我們定義的每個人種都有兩個方法:getColor(獲得人的皮膚顏色)和talk(交談),其 源代碼如代碼清單8-1所示。

image-20200817170437395

/*** @program: DesignMode* @description: 人類總稱	* @author: Jacob* @create: 2020-08-17 15:14**/
public interface Human {//每個人種的皮膚都有相應的顏色public void getColor();//人類會說話public void talk();}

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

/*** @program: DesignMode* @description: 黑色人種* @author: Jacob* @create: 2020-08-17 15:15**/
public class BlackHuman implements Human {@Overridepublic void getColor() {System.out.println("黑色人種的皮膚顏色是黑色的!");}@Overridepublic void talk() {System.out.println("黑人會說話,一般人聽不懂。");}
}
/*** @program: DesignMode* @description: 黃色人種* @author: Jacob* @create: 2020-08-17 15:16**/
public class YellowHuman implements Human {@Overridepublic void getColor() {System.out.println("黃色人種的皮膚顏色是黃色的!");}@Overridepublic void talk() {System.out.println("黃色人種會說話,一般說的都是雙字節。");}
}
/*** @program: DesignMode* @description: 白色人種* @author: Jacob* @create: 2020-08-17 15:17**/
public class WhiteHuman implements Human {@Overridepublic void getColor() {System.out.println("白色人種的皮膚顏色是白色的!");}@Overridepublic void talk() {System.out.println("白色人種會說話,一般說的都是單字節。");}}

所有的人種定義完畢,下一步就是定義一個八卦爐,然后燒制人類。我們想象一下,女 媧最可能給八卦爐下達什么樣的生產命令呢?應該是”給我生產出一個黃色人種 (YellowHuman類)”,而不會是“給我生產一個會走、會跑、會說話、皮膚是黃色的人種”,因為這樣的命令增加了交流的成本,作為一個生產的管理者,只要知道生產什么就可以了,而不需要事物的具體信息。通過分析,我們發現八卦爐生產人類的方法輸入參數類型應該是 Human接口的實現類,這也解釋了為什么類圖上的AbstractHumanFactory抽象類中createHuman 方法的參數為Class類型。

/*** @program: DesignMode* @description:  抽象人類創建工廠* @author: Jacob* @create: 2020-08-17 15:18**/
public abstract class AbstractHumanFactory {public abstract <T extends Human> T createHuman(Class<T> c);}

注意,我們在這里采用了泛型(Generic),通過定義泛型對createHuman的輸入參數產生兩層限制:
● 必須是Class類型;
● 必須是Human的實現類。

其中的"T"表示的是,只要實現了Human接口的類都可以作為參數,泛型是JDK 1.5中的 一個非常重要的新特性,它減少了對象間的轉換,約束其輸入參數類型,對Collection集合下 的實現類都可以定義泛型。

目前女媧只有一個八卦爐,其實現生產人類的方法。

/*** @program: DesignMode* @description: 人類創建工廠* @author: Jacob* @create: 2020-08-17 15:18**/
public class HumanFactory extends AbstractHumanFactory {@Overridepublic <T extends Human> T createHuman(Class<T> c) {Human human = null;try {human = (T)Class.forName(c.getName()).newInstance();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}return (T)human;}
}

人種有了,八卦爐也有了,剩下的工作就是女媧采集黃土,然后命令八卦爐開始生產

/*** @program: DesignMode* @description: 女媧類* @author: Jacob* @create: 2020-08-17 15:21**/
public class NvWa {public static void main(String[] args) {//聲明陰陽八卦爐AbstractHumanFactory YinYangLu = new HumanFactory();//女媧第一次造人,火候不足,于是白人產生了System.out.println("--造出的第一批人是白色人種--");Human whiteHuman = YinYangLu.createHuman(WhiteHuman.class);whiteHuman.getColor();whiteHuman.talk();//女媧第二次造人,火候過足,于是黑人產生了System.out.println("\n--造出的第二批人是黑色人種--");Human blackHuman = YinYangLu.createHuman(BlackHuman.class);blackHuman.getColor();blackHuman.talk();//第三次造人,火候剛剛好,于是黃色人種產生了System.out.println("\n--造出的第三批人是黃色人種--");Human yellowHuman = YinYangLu.createHuman(YellowHuman.class);yellowHuman.getColor();yellowHuman.talk();}
}

人種有了,八卦爐有了,負責生產的女媧也有了,激動人心的時刻到來了,我們運行一下,結果如下所示。

--造出的第一批人是白色人種--
白色人種的皮膚顏色是白色的!
白色人種會說話,一般說的都是單字節。--造出的第二批人是黑色人種--
黑色人種的皮膚顏色是黑色的!
黑人會說話,一般人聽不懂。--造出的第三批人是黃色人種--
黃色人種的皮膚顏色是黃色的!
黃色人種會說話,一般說的都是雙字節。

哇,人類的生產過程就展現出來了!這個世界就熱鬧起來了,黑人、白人、黃人都開始活動了,這也正是我們現在的真實世界。以上就是工廠方法模式。

工廠方法模式的定義

工廠方法模式使用的頻率非常高,在我們日常的開發中總能見到它的身影。其定義為:
Define an interface for creating an object,but let subclasses decide which class to instantiate.Factory Method lets a class defer instantiation to subclasses.(定義一個用于創建對象的接口,讓子類決定實例化哪一個類。工廠方法使一個類的實例化延遲到其子類。)

image-20200817173120736

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

/*** @program: DesignMode* @description: 抽象產品類* @author: Jacob* @create: 2020-08-17 15:32**/
public abstract class Product {//產品類的公共方法public void method1() {//業務邏輯處理System.out.println("Product.method1");}//抽象方法public abstract void method2();}

具體的產品類可以有多個,都繼承于抽象產品類。

/*** @program: DesignMode* @description: 具體產品類 1* @author: Jacob* @create: 2020-08-17 15:33**/
public class ConcreteProduct1 extends Product {@Overridepublic void method1() {//業務邏輯處理System.out.println("ConcreteProduct1.method1()");}@Overridepublic void method2() {//業務邏輯處理System.out.println("ConcreteProduct1.method2()");}
}
/*** @program: DesignMode* @description: 具體產品類 1* @author: Jacob* @create: 2020-08-17 15:33**/
public class ConcreteProduct2 extends Product {@Overridepublic void method2() {//業務邏輯處理System.out.println("ConcreteProduct2.method2()");}
}

抽象工廠類負責定義產品對象的產生。

/*** @program: DesignMode* @description: 抽象工廠類* @author: Jacob* @create: 2020-08-17 15:36**/
public abstract class Creator {/**創建一個產品對象,其輸入參數類型可以自行設置* 通常為String、Enum、Class等,當然也可以為空*/public abstract <T extends Product> T createProduct(Class<T> c);
}

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

/*** @program: DesignMode* @description: 具體工廠類* @author: Jacob* @create: 2020-08-17 15:36**/
public class ConcreteCreator extends Creator {@Overridepublic <T extends Product> T createProduct(Class<T> c) {Product product = null;try {product = (T)Class.forName(c.getName()).newInstance();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}return (T)product;}
}
/*** @program: DesignMode* @description: 場景類* @author: Jacob* @create: 2020-08-17 15:38**/
public class Client {public static void main(String[] args) {Creator creator = new ConcreteCreator();Product product = creator.createProduct(ConcreteProduct1.class);product.method1();product.method2();product = creator.createProduct(ConcreteProduct2.class);product.method1();product.method2();}}

我們運行一下,結果如下所示。

ConcreteProduct1.method1()
ConcreteProduct1.method2()
Product.method1
ConcreteProduct2.method2()

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

工廠方法模式的應用

工廠方法模式的優點

首先,良好的封裝性,代碼結構清晰。一個對象創建是有條件約束的,如一個調用者需要一個具體的產品對象,只要知道這個產品的類名(或約束字符串)就可以了,不用知道創建對象的艱辛過程,降低模塊間的耦合。

其次,工廠方法模式的擴展性非常優秀。在增加產品類的情況下,只要適當地修改具體 的工廠類或擴展一個工廠類,就可以完成“擁抱變化”。例如在我們的例子中,需要增加一個 棕色人種,則只需要增加一個BrownHuman類,工廠類不用任何修改就可完成系統擴展。

再次,屏蔽產品類。這一特點非常重要,產品類的實現如何變化,調用者都不需要關心,它只需要關心產品的接口,只要接口保持不變,系統中的上層模塊就不要發生變化。因為產品類的實例化工作是由工廠類負責的,一個產品對象具體由哪一個產品生成是由工廠類 決定的。在數據庫開發中,大家應該能夠深刻體會到工廠方法模式的好處:如果使用JDBC 連接數據庫,數據庫從MySQL切換到Oracle,需要改動的地方就是切換一下驅動名稱(前提 條件是SQL語句是標準語句),其他的都不需要修改,這是工廠方法模式靈活性的一個直接案例。

最后,工廠方法模式是典型的解耦框架。高層模塊值需要知道產品的抽象類,其他的實現類都不用關心,符合迪米特法則,我不需要的就不要去交流;也符合依賴倒置原則,只依賴產品類的抽象;當然也符合里氏替換原則,使用產品子類替換產品父類,沒問題!

工廠方法模式的使用場景

首先,工廠方法模式是new一個對象的替代品,所以在所有需要生成對象的地方都可以使用,但是需要慎重地考慮是否要增加一個工廠類進行管理,增加代碼的復雜度。

其次,需要靈活的、可擴展的框架時,可以考慮采用工廠方法模式。萬物皆對象,那萬物也就皆產品類,例如需要設計一個連接郵件服務器的框架,有三種網絡協議可供選擇: POP3IMAPHTTP,我們就可以把這三種連接方法作為產品類,定義一個接口如IConnectMail,然后定義對郵件的操作方法,用不同的方法實現三個具體的產品類(也就是連接方式)再定義一個工廠方法,按照不同的傳入條件,選擇不同的連接方式。如此設計, 可以做到完美的擴展,如某些郵件服務器提供了WebService接口,很好,我們只要增加一個產品類就可以了。

再次,工廠方法模式可以用在異構項目中,例如通過WebService與一個非Java的項目交 互,雖然WebService號稱是可以做到異構系統的同構化,但是在實際的開發中,還是會碰到 很多問題,如類型問題、WSDL文件的支持問題,等等。從WSDL中產生的對象都認為是一個產品,然后由一個具體的工廠類進行管理,減少與外圍系統的耦合。

最后,可以使用在測試驅動開發的框架下。例如,測試一個類A,就需要把與類A有關 聯關系的類B也同時產生出來,我們可以使用工廠方法模式把類B虛擬出來,避免類A與類B 的耦合。目前由于JMockEasyMock的誕生,該使用場景已經弱化了,讀者可以在遇到此種 情況時直接考慮使用JMockEasyMock

工廠方法模式的擴展

縮小為簡單工廠模式

我們這樣考慮一個問題:一個模塊僅需要一個工廠類,沒有必要把它產生出來,使用靜 態的方法就可以了,根據這一要求,我們把上例中的AbstarctHumanFactory修改一下。

image-20200817174812028

我們在類圖中去掉了AbstractHumanFactory抽象類,同時把createHuman方法設置為靜態 類型,簡化了類的創建過程,變更的源碼僅僅是HumanFactoryNvWa類,HumanFactory如代如下。

/*** @program: DesignMode* @description:  工廠方法模式的擴展--1. 縮小為簡單工廠模式 人類創建工廠* @author: Jacob* @create: 2020-08-17 15:18**/
public class HumanFactory {public static  <T extends Human> T createHuman(Class<T> c) {Human human = null;try {human = (T)Class.forName(c.getName()).newInstance();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}return (T)human;}
}

HumanFactory類僅有兩個地方發生變化:去掉繼承抽象類,并在createHuman前增加static 關鍵字;工廠類發生變化,也同時引起了調用者NvWa的變化。

/*** @program: DesignMode* @description:  工廠方法模式的擴展--1. 縮小為簡單工廠模式 女媧類* @author: Jacob* @create: 2020-08-17 15:21**/
public class NvWa {public static void main(String[] args) {//女媧第一次造人,火候不足,于是白人產生了System.out.println("--造出的第一批人是白色人種--");Human whiteHuman = HumanFactory.createHuman(WhiteHuman.class);whiteHuman.getColor();whiteHuman.talk();//女媧第二次造人,火候過足,于是黑人產生了System.out.println("\n--造出的第二批人是黑色人種--");Human blackHuman = HumanFactory.createHuman(BlackHuman.class);blackHuman.getColor();blackHuman.talk();//第三次造人,火候剛剛好,于是黃色人種產生了System.out.println("\n--造出的第三批人是黃色人種--");Human yellowHuman = HumanFactory.createHuman(YellowHuman.class);yellowHuman.getColor();yellowHuman.talk();}
}

運行結果沒有發生變化,但是我們的類圖變簡單了,而且調用者也比較簡單,該模式是 工廠方法模式的弱化,因為簡單,所以稱為簡單工廠模式(Simple Factory Pattern),也叫做靜態工廠模式。在實際項目中,采用該方法的案例還是比較多的,其缺點是工廠類的擴展比較困難,不符合開閉原則,但它仍然是一個非常實用的設計模式。

升級為多個工廠類

當我們在做一個比較復雜的項目時,經常會遇到初始化一個對象很耗費精力的情況,所有的產品類都放到一個工廠方法中進行初始化會使代碼結構不清晰。例如,一個產品類有5 個具體實現,每個實現類的初始化(不僅僅是new,初始化包括new一個對象,并對對象設
置一定的初始值)方法都不相同,如果寫在一個工廠方法中,勢必會導致該方法巨大無比,那該怎么辦?

考慮到需要結構清晰,我們就為每個產品定義一個創造者,然后由調用者自己去選擇與哪個工廠方法關聯。我們還是以女媧造人為例,每個人種都有一個固定的八卦爐,分別造出 黑色人種、白色人種、黃色人種。

image-20200817175132053

每個人種(具體的產品類)都對應了一個創建者,每個創建者都獨立負責創建對應的產品對象,非常符合單一職責原則,按照這種模式我們來看看代碼變化。

/*** @program: DesignMode* @description:  工廠方法模式的擴展--2. 升級為多個工廠類 多工廠模式的抽象工廠類* @author: Jacob* @create: 2020-08-17 15:18**/
public abstract class AbstractHumanFactory {public abstract Human createHuman();}

注意 抽象方法中已經不再需要傳遞相關參數了,因為每一個具體的工廠都已經非常明確自己的職責:創建自己負責的產品類對象。

/*** @program: DesignMode* @description: 工廠方法模式的擴展--2. 升級為多個工廠類 黑色人種的創建工廠實現* @author: Jacob* @create: 2020-08-17 16:06**/
public class BlackHumanFactory extends AbstractHumanFactory {@Overridepublic Human createHuman() {return new BlackHuman();}
}
/*** @program: DesignMode* @description: 工廠方法模式的擴展--2. 升級為多個工廠類 白色人種的創建類* @author: Jacob* @create: 2020-08-17 16:05**/
public class whiteHumanFactory extends AbstractHumanFactory {@Overridepublic Human createHuman() {return new WhiteHuman();}
}
/*** @program: DesignMode* @description: 工廠方法模式的擴展--2. 升級為多個工廠類 黃色人種的創建類* @author: Jacob* @create: 2020-08-17 16:02**/
public class YellowHumanFactory extends AbstractHumanFactory {@Overridepublic Human createHuman() {return new YellowHuman();}
}

三個具體的創建工廠都非常簡單,但是,如果一個系統比較復雜時工廠類也會相應地變 復雜。場景類NvWa修改后的代碼如下:

/*** @program: DesignMode* @description:  工廠方法模式的擴展--2. 升級為多個工廠類 女媧類* @author: Jacob* @create: 2020-08-17 15:21**/
public class NvWa {public static void main(String[] args) {//女媧第一次造人,火候不足,于是白人產生了System.out.println("--造出的第一批人是白色人種--");Human whiteHuman = new whiteHumanFactory().createHuman();whiteHuman.getColor();whiteHuman.talk();//女媧第二次造人,火候過足,于是黑人產生了System.out.println("\n--造出的第二批人是黑色人種--");Human blackHuman = new BlackHumanFactory().createHuman();blackHuman.getColor();blackHuman.talk();//第三次造人,火候剛剛好,于是黃色人種產生了System.out.println("\n--造出的第三批人是黃色人種--");Human yellowHuman = new YellowHumanFactory().createHuman();yellowHuman.getColor();yellowHuman.talk();}
}

運行結果還是相同。我們回顧一下,每一個產品類都對應了一個創建類,好處就是創建類的職責清晰,而且結構簡單,但是給可擴展性和可維護性帶來了一定的影響。為什么這么說呢?如果要擴展一個產品類,就需要建立一個相應的工廠類,這樣就增加了擴展的難度。因為工廠類和產品類的數量相同,維護時需要考慮兩個對象之間的關系。

當然,在復雜的應用中一般采用多工廠的方法,然后再增加一個協調類,避免調用者與各個子工廠交流,協調類的作用是封裝子工廠類,對高層模塊提供統一的訪問接口。

替代單例模式

之前講的單例模式以及擴展出的多例模式,并且指出了單例和多例的一些缺點,我們是不是可以采用工廠方法模式實現單例模式的功能呢?單例模式的核心要求就是在內存中 只有一個對象,通過工廠方法模式也可以只在內存中生產一個對象。

image-20200817175919551

非常簡單的類圖,Singleton定義了一個private的無參構造函數,目的是不允許通過new的 方式創建一個對象。

/*** @program: DesignMode* @description: 工廠方法模式的擴展--3. 替代單例模式 單例類* @author: Jacob* @create: 2020-08-17 16:15**/
public class Singleton {//不允許通過new產生一個對象private Singleton() {}public void doSomething() {//業務處理}
}

Singleton保證不能通過正常的渠道建立一個對象,那SingletonFactory如何建立一個單例 對象呢?答案是通過反射方式創建.。

/*** @program: DesignMode* @description: 工廠方法模式的擴展--3. 替代單例模式 單例工程* @author: Jacob* @create: 2020-08-17 16:21**/
public class SingletonFactory {private static Singleton singleton;static {try {Class cl = Class.forName(Singleton.class.getName());//獲得無參構造Constructor constructor = cl.getDeclaredConstructor();//設置無參構造是可訪問的constructor.setAccessible(true);//產生一個實例對象singleton = (Singleton) constructor.newInstance();} catch (Exception e) {//異常處理}}public static Singleton getSingleton() {return singleton;}
}

通過獲得類構造器,然后設置訪問權限,生成一個對象,然后提供外部訪問,保證內存中的對象唯一。當然,其他類也可以通過反射的方式建立一個單例對象,確實如此,但是一個項目或團隊是有章程和規范的,何況已經提供了一個獲得單例對象的方法,為什么還要重
新創建一個新對象呢?除非是有人作惡。

以上通過工廠方法模式創建了一個單例對象,該框架可以繼續擴展,在一個項目中可以 產生一個單例構造器,所有需要產生單例的類都遵循一定的規則(構造方法是private),然后通過擴展該框架,只要輸入一個類型就可以獲得唯一的一個實例。

延遲初始化

何為延遲初始化(Lazy initialization)?一個對象被消費完畢后,并不立刻釋放,工廠類保持其初始狀態,等待再次被使用。延遲初始化是工廠方法模式的一個擴展應用。

image-20200817180148980

ProductFactory負責產品類對象的創建工作,并且通過prMap變量產生一個緩存,對需要 再次被重用的對象保留,ProductConcreteProduct是一個示例代碼。

/*** @program: DesignMode* @description: 工廠方法模式的擴展--4. 延遲初始化 抽象產品類* @author: Jacob* @create: 2020-08-17 15:32**/
public abstract class Product {//產品類的公共方法public void method1() {//業務邏輯處理System.out.println("Product.method1");}//抽象方法public abstract void method2();}
/*** @program: DesignMode* @description: 工廠方法模式的擴展--4. 延遲初始化 具體產品類 1* @author: Jacob* @create: 2020-08-17 15:33**/
public class Product1 extends Product {@Overridepublic void method1() {//業務邏輯處理System.out.println("ConcreteProduct1.method1()");}@Overridepublic void method2() {//業務邏輯處理System.out.println("ConcreteProduct1.method2()");}
}
/*** @program: DesignMode* @description: 工廠方法模式的擴展--4. 延遲初始化 具體產品類 1* @author: Jacob* @create: 2020-08-17 15:33**/
public class Product2 extends Product {@Overridepublic void method2() {//業務邏輯處理System.out.println("ConcreteProduct2.method2()");}
}
/*** @program: DesignMode* @description: 具體工廠類* @author: Jacob* @create: 2020-08-17 15:36**/
public class ProductFactory {private static final Map<String, Product> prMap = new HashMap<>();public static synchronized Product createProduct(String type) {Product product = null;if (prMap.containsKey(type)) {product = prMap.get(type);System.out.println("Map中已經有這個對象");} else {switch (type) {case "product1":product = new Product1();break;case "product2":product = new Product2();break;default:throw new RuntimeException("創建對象異常");}prMap.put(type, product);}return product;}
}
/*** @program: DesignMode* @description: 工廠方法模式的擴展--4. 延遲初始化 場景類* @author: Jacob* @create: 2020-08-17 15:38**/
public class Client {public static void main(String[] args) {Product product = ProductFactory.createProduct("product1");product.method1();product.method2();System.out.println("---------------------------------------------");product = ProductFactory.createProduct("product2");product.method1();product.method2();System.out.println("---------------------------------------------");product = ProductFactory.createProduct("product1");product.method1();product.method2();}}

代碼還比較簡單,通過定義一個Map容器,容納所有產生的對象,如果在Map容器中已 經有的對象,則直接取出返回;如果沒有,則根據需要的類型產生一個對象并放入到Map容器中,以方便下次調用。

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

延遲加載還可以用在對象初始化比較復雜的情況下,例如硬件訪問,涉及多方面的交互,則可以通過延遲加載降低對象的產生和銷毀帶來的復雜性。

最佳實踐

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

學習于:《設計模式之禪》 — 秦小波

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/273568.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/273568.shtml
英文地址,請注明出處:http://en.pswp.cn/news/273568.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

設計模式 之 單例模式

項目源碼&#xff1a;https://gitee.com/Jacob-gitee/DesignMode 個人博客&#xff1a;https://jacob.org.cn 宗旨 Ensure a class has only one instance,and provide a global point of access to it.&#xff08;確保某一個類只有一個實例&#xff0c;而且自行實例化并向整個…

如何實現滑動scrollview上下隱藏

問題描述現在有一個需求&#xff0c;就是一個界面如下ABCA固定在頂部&#xff0c;C固定在底部其中B是一個scrollview(也可能是listview)&#xff0c;要實現&#xff0c;在向上滑動B的時候&#xff0c;A平滑的往上滑&#xff0c;同時C平滑的往下滑&#xff0c;直到消失&#xff…

設計模式 之 抽象工廠模式

項目源碼&#xff1a;https://gitee.com/Jacob-gitee/DesignMode 個人博客 &#xff1a;https://jacob.org.cn 女媧的失誤 工廠模式中講了女媧造人的故事。人是造出來了&#xff0c;世界也熱鬧了&#xff0c;可是低頭一看&#xff0c;都是清一色的類型&#xff0c;缺少關愛、仇…

strip 命令的使用方法

用途 通過除去綁定程序和符號調試程序使用的信息&#xff0c;降低擴展公共對象文件格式&#xff08;XCOFF&#xff09;的對象文件的大小。 語法 strip [ -V ] [ -r [ -l ] | -x [ -l ] | -t | -H | -e | -E ] [ -X {32 |64 |32_64 }] [ -- ] File ... 描…

設計模式 之 模板模式

項目源碼&#xff1a;https://gitee.com/Jacob-gitee/DesignMode 個人博客 &#xff1a;http://jacob.org.cn 女媧的失誤 工廠模式中講了女媧造人的故事。人是造出來了&#xff0c;世界也熱鬧了&#xff0c;可是低頭一看&#xff0c;都是清一色的類型&#xff0c;缺少關愛、仇…

使用Java高速實現進度條

基于有人問到如何做進度條&#xff0c;以下給個簡單的做法&#xff1a; 主要是使用JProgressBar&#xff08;Swing內置javax.swing.JProgressBar&#xff09;和SwingWorker&#xff08;Swing內置javax.swing.SwingWorker&#xff09; 有人肯定會說&#xff0c;不是用線程做的嗎…

Linux 安裝JDK

個人博客 &#xff1a;https://www.siyuan.run CSDN&#xff1a;https://blog.csdn.net/siyuan 微信小程序&#xff1a;思遠Y 安裝時使用到的命令&#xff1a; cd&#xff1a;切換目錄。 eg&#xff1a;cd / mkdir&#xff1a;創建目錄。 eg&#xff1a;mkdir jacob 創建單極目…

Css導航

<div> <ul> <li><a></a></li> <li><a></a></li> <li><a></a></li> .. </ul> </div> <li>中也可包含 <ul> <a></a> <li><a></a>&…

關于js的function.來自百度知道的回答,學習了.

在js中&#xff0c;創建一個函數對象的語法是var myFunction new Function(arg1,…,agrN, body);其中&#xff0c;該函數對象的N個參數放在 函數主體參數body的前面&#xff0c;即函數主體參數必須放在參數列表的最后&#xff0c;也可以無參數new Function(body)。你添加第三個…

Ribbon 支持的9大負載均衡策略

個人博客 &#xff1a;https://www.siyuan.run CSDN&#xff1a;https://blog.csdn.net/siyuan 微信小程序&#xff1a;思遠Y 線性輪詢策略&#xff1a; RoundRibbonRule BaseLoadBalancer 負載均衡器默認采用線性負載輪詢負載均衡策略。 工作流程&#xff1a; RoundRibbonRule…

fedora20開機啟動配置:systemctl

老版fedora中使用chkconfig配置開機啟動&#xff0c;fedora20中&#xff0c;使用chkconfig會出現各種問題。使用systemctl配置。 具體表格如下 轉載于:https://www.cnblogs.com/hh6plus/p/5548083.html

Mysql 字符操作函數相關

常用的字符串函數&#xff1a; 函數說明CONCAT(s1,s2&#xff0c;...)返回一個或多個待拼接的內容&#xff0c;任意一個為NULL則返回值為NULL。CONCAT_WS(x,s1,s2,...)返回多個字符串拼接之后的字符串&#xff0c;每個字符串之間有一個x。SUBSTRING(s,n,len)、MID(s,n,len)兩個…

“cvSnakeImage”: 找不到標識符

1>g:\project\opencv\helloopencv\helloopencv\helloopencv.cpp(74) : error C2065: “CV_VALUE”: 未聲明的標識符1>g:\project\opencv\helloopencv\helloopencv\helloopencv.cpp(74) : error C3861: “cvSnakeImage”: 找不到標識符 增加頭文件 #include <opencv2/l…

Shell 快速入門

個人博客 &#xff1a;https://www.siyuan.run CSDN&#xff1a;https://blog.csdn.net/siyuan 微信小程序&#xff1a;思遠Y 概述 Shell 是一個用 C 語言編寫的程序&#xff0c;它是用戶使用 Linux 的橋梁。Shell 既是一種命令語言&#xff0c;又是一種程序設計語言。 Shell…

Andriod開發 --插件安裝、環境配置、問題集錦

1.用Eclipse搭建Android開發環境和創建第一個Android項目&#xff08;Windows平臺&#xff09; 鏈接閱讀http://www.cnblogs.com/allenzheng/archive/2012/11/10/2762379.html 搭建環境中的不同之處&#xff1a; &#xff08;1&#xff09;我在安裝過程中&#xff0c;在安裝ADT…

《Java 高并發》01 高并發基本概念

基本概念 同步和異步 同步和異步通常是用來形容一次方法調用。 同步方法調用一旦開始&#xff0c;調用者必須等到方法返回才能繼續執行后續操作。 異步方法調用更像一個消息傳遞&#xff0c;一旦開始&#xff0c;方法調用就會立即返回&#xff0c;調用者就可以繼續后續的操…

Android之Http網絡編程(四)

前面幾篇博文簡單的介紹了一些常見的Http的操作&#xff0c;這些操作幾乎都是在新開的線程中進行的網絡請求&#xff0c;并在日志中打印出獲取到的網絡數據。那么&#xff0c;問題來了&#xff01;&#xff08;呃~感覺下一句是藍翔有木有&#xff1f;&#xff09;如何在把獲取到…

《Java 高并發》02 多線程的特性

多線程的三大特性&#xff1a;原子性、可見性和有序性。 原子性 原子性是指一個操作或者多個操作&#xff0c;一旦開始就不會被其他線程干擾&#xff0c;即使是在多個線程一起執行的情況下也不會被干擾。或者不執行。 原子性主要是為了保證數據一致&#xff0c;線程安全問題…

U3D-FSM有限狀態機的簡單設計

http://coder.beitown.com/archives/592 在之前的文章里介紹了一個基礎U3D狀態機框架&#xff08;Unity3D游戲開發之狀態流框架&#xff09;即大Switch的枚舉狀態控制。這種方法雖然容易理解&#xff0c;編程方法也相對簡單&#xff0c;但是弊端是當狀態變得復雜之后&#xff0…

《Java 高并發》04 線程的基本操作

新建線程 新建線程很簡單。只要使用new 關鍵字創建一個線程對象&#xff0c;并且調用 start 方法啟動線程。 Thread t new Thread(); t.start();注意&#xff1a;run 方法不是用來啟動線程。如果調用 run 方法它只會作為普通方法來執行&#xff0c;而不會開啟線程執行。 終止…