工廠模式應用很是之廣,在JDK
底層源碼以及各大主流框架中隨處可見,通常以Factory
結尾命名的類,好比Mybatis
中的SqlSessionFactory
,Spring
中的BeanFactory
等,都是工廠模式的典型表明。java
簡單工廠模式又稱爲靜態工廠模式,屬於設計模式中的建立型模式。簡單工廠模式經過對外提供一個靜態方法來統一爲類建立實例,目的是實現類與類之間解耦:客戶端不須要知道這個對象是如何被穿建立出來的,只須要調用簡單工廠模式的方法來統一建立就能夠了,從而明確了各個類的職責。git
簡單工廠模式,以生產汽車輪胎爲例。github
public class Tire { /** * 通用屬性 */ private String common; }
包含通用屬性外還有本身的特有屬性
public class TireForBenz extends Tire{ Tire tire; /** * 特有屬性 */ private String benz; public TireForBenz() { this.benz = "獲得 Benz 輪胎"; } @Override public String toString() { return "["+this.benz +"]"; } }
包含通用屬性外還有本身的特有屬性
public class TireForBwm extends Tire{ Tire tire; /** * 特有屬性 */ private String bwm; public TireForBwm() { this.bwm = "獲得 Bwm 輪胎"; } @Override public String toString() { return "["+this.bwm +"]"; } }
public interface TireFactory { Tire produceTire(); }
重寫生產輪胎的方法返回奔馳型輪胎。
public class BenzTireFactory implements TireFactory { /** * 生產奔馳輪胎 */ @Override public Tire produceTire() { System.out.println("奔馳輪胎生產中。。。"); return new TireForBenz(); } }
重寫生產輪胎的方法返回寶馬型輪胎。
public class BwmTireFactory implements TireFactory { /** * 生產寶馬輪胎 */ @Override public TireForBwm produceTire() { System.out.println("寶馬輪胎生產中。。。"); return new TireForBwm(); } }
經過傳入的品牌名稱調用相應產線生產相應品牌的輪胎
public class SimpleFactoryMode { public static TireFactory produceCar(String name) { if ("BenzTireFactory".equals(name)) { return new BenzTireFactory(); } if ("BwmTireFactory".equals(name)) { return new BwmTireFactory(); } return null; } }
客戶端經過工廠類獲取實例對象。
@Test public void simpleFactoryModeTest() { // 造奔馳輪胎 TireFactory benz = SimpleFactoryMode.produceCar("BenzTireFactory"); if (null != benz) { benz.produceTire(); }else { System.out.println("工廠暫時沒法生產奔馳輪胎"); } // 造寶馬輪胎 TireFactory bwm = SimpleFactoryMode.produceCar("BwmTireFactory"); if (null != bwm) { bwm.produceTire(); }else { System.out.println("工廠暫時沒法生產寶馬輪胎"); } // 造本田汽輪胎(工廠無該方法) TireFactory honda = SimpleFactoryMode.produceCar("Honda"); if (null != honda) { honda.produceTire(); }else { System.out.println("工廠暫時沒法生產本田輪胎"); } }
奔馳輪胎生產中。。。 寶馬輪胎生產中。。。 工廠暫時沒法生產本田輪胎
該方式確實能完成不一樣品牌的輪胎生產,可是,有個問題:方法參數是字符串,可控性有待提高。設計模式
不要經過傳入的字符串來判斷須要建立對象,而是客戶端想要建立什麼對象,只須要傳入具體的實現類就能夠了,而後經過
Java
的反射來建立對象。
public static TireFactory produceCar(Class<? extends TireFactory> clazz) { try { // 經過Java的反射來建立對象 return clazz.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; }
每次建立對象都是經過反射來建立的,因此在性能上是有必定的損耗。框架
public void simpleFactoryModeUpgradeTest() { // 造奔馳輪胎 TireFactory benzTire = SimpleFactoryMode.produceCar(BenzTireFactory.class); TireForBenz benz = (TireForBenz) benzTire.produceTire(); System.out.println(benz.toString()); // 造寶馬輪胎 TireFactory bwmTire = SimpleFactoryMode.produceCar(BwmTireFactory.class); TireForBwm bwm = (TireForBwm) bwmTire.produceTire(); System.out.println(bwm.toString()); }
奔馳輪胎生產中。。。 [獲得 Benz 輪胎] 寶馬輪胎生產中。。。 [獲得 Bwm 輪胎]
簡單工廠模式確實在必定程度上實現代碼的解耦,而這種解耦的特色在於,這種模式將對象的建立和使用分離。這種模式的本質在於經過一個傳入的參數,作if...else
判斷,來達到返回不一樣類型對象的目的。缺點也很明顯,不符合開閉原則(好比新增一個保時捷輪胎的生產,除了須要增長實體和生產方法,還須要修改工廠類SimpleFactoryMode.java
)。所以,若是須要增長新的類型,就不得不去修改原來的代碼,違反開閉原則。ide
那就是它把全部對象的建立都集中在同一個工廠類裏面了,所以,當新增一個新對象時,必然會須要修改這個共享工廠類,違反開閉原則天然不可避免。post
既然問題關鍵在於,全部對象的建立都跟這個惟一的工廠類耦合了,那我每一個對象各自都配置一個單獨的工廠類,這個工廠類只建立各自類型的對象,那這樣不就解決耦合的問題了嗎?性能
工廠方法模式是指定義一個建立對象的接口,但讓實現這個接口的類來決定實例化哪一個類。工廠方法讓類的實例化推遲到子類中進行。在工廠方法模式中用戶只須要關心所需產品對應的工廠,無須關心建立細節,並且加入新的產品符合開閉原則。測試
工廠方法模式,以生產發動機爲例。優化
public class Engine { /** * 型號 */ private String common; }
包含通用屬性外還有本身的特有屬性
public class EngineForBenz extends Engine{ Engine engine; /** * 特有屬性 */ private String benz; public EngineForBenz() { this.benz = "獲得 Benz 發動機"; } @Override public String toString() { return "["+this.benz +"]"; } }
包含通用屬性外還有本身的特有屬性
public class EngineForBwm extends Engine{ Engine engine; /** * 特有屬性 */ private String bwm; public EngineForBwm() { this.bwm = "獲得 Bwm 發動機"; } @Override public String toString() { return "["+this.bwm +"]"; } }
public interface EngineFactory<T> { Engine produceEngine(); }
public class BenzEngineFactory implements EngineFactory<EngineForBenz> { /** * 生產奔馳發動機 */ @Override public Engine produceEngine() { System.out.println("奔馳發動機生產中。。。"); return new EngineForBenz(); } }
public class BwmEngineFactory implements EngineFactory<EngineForBwm> { /** * 生產寶馬發動機 */ @Override public Engine produceEngine() { System.out.println("寶馬發動機生產中。。。"); return new EngineForBwm(); } }
@Test public void factoryModeTest() { // 造奔馳發動機 EngineFactory car = new BenzEngineFactory(); EngineForBenz benz = (EngineForBenz) car.produceEngine(); System.out.println(benz.toString()); // 造寶馬發動機 EngineFactory carFactory = new BwmEngineFactory(); EngineForBwm bwm = (EngineForBwm) carFactory.produceEngine(); System.out.println(bwm.toString()); }
奔馳發動機生產中。。。 [獲得 Benz 發動機] 寶馬發動機生產中。。。 [獲得 Bwm 發動機]
工廠方法模式輕鬆解決了簡單工廠模式的問題,符合開閉原則。在上面例子中,當須要新增一個保時捷汽車,此時只須要提供一個對應的EngineForBSJ.java
實現produceEngine()
方法便可,對於原先代碼再不須要作任何修改。
可是每一個類型的對象都會有一個與之對應的工廠類。若是對象的類型很是多,意味着會須要建立不少的工廠實現類,形成類數量膨脹,對後續維護帶來一些麻煩。
抽象工廠模式出現,就是爲了解決上述工廠方法模式存在的問題,能夠當作是工廠方法模式的升級。
工廠方法模式建立的對象其實歸根到底都是同一類對象。以汽車生產爲例,不管是輪胎仍是發動機,都是汽車生產的一部分,都是屬於汽車生產的過程。以下圖:
由上圖咱們能夠發現,雖然分爲奔馳車和寶馬車,可是從工廠方法角度,他們都屬於汽車這一類別,這就致使了須要單獨爲每個零件指定各自的工廠類,從而致使了類數量膨脹的問題。
既然這樣,咱們能夠把每類汽車指定一個工廠,而後再讓不一樣產線去生產他須要的產品,以下圖
這樣當每一類物品組件數量特別多,能夠把它稱爲產品族。抽象工廠模式就是爲了建立一系列以產品族爲單位的對象,這樣在須要建立大量系列對象時能夠大大提升開發效率,下降維護成本。
由於奔馳輪胎/寶馬輪胎/奔馳發動機/寶馬發動機的實體在前面已經建立過,這裏就直接用了。
該類已包含輪胎/發動機生產,具體實體鍵一/二中相關實體。
public interface CarFactory { /** * 準備生產 */ void init(); /** * 生產輪胎 * @return */ Tire produceTire(); /** * 生產發動機 * @return */ Engine produceEngine(); }
public class BenzCarFactory implements CarFactory{ @Override public void init() { System.out.println("----------------------- 奔馳汽車準備生產 -----------------------"); } @Override public Tire produceTire() { System.out.println("正在生產奔馳輪胎"); return new TireForBenz(); } @Override public Engine produceEngine() { System.out.println("正在生產奔馳發動機"); return new EngineForBenz(); } }
public class BwmCarFactory implements CarFactory{ @Override public void init() { System.out.println("----------------------- 寶馬汽車準備生產 -----------------------"); } @Override public Tire produceTire() { System.out.println("正在生產寶馬輪胎"); return new TireForBwm(); } @Override public Engine produceEngine() { System.out.println("正在生產寶馬發動機"); return new EngineForBwm(); } }
@Test public void abstractFactoryModeTest() { // 生產奔馳整車的零部件 CarFactory benz = new BenzCarFactory(); benz.init(); TireForBenz benzTire = (TireForBenz) benz.produceTire(); System.out.println(benzTire.toString()); EngineForBenz benzEngine = (EngineForBenz) benz.produceEngine(); System.out.println(benzEngine.toString()); // 生成寶馬整車的零部件d CarFactory bwm = new BwmCarFactory(); bwm.init(); TireForBwm bwmTire = (TireForBwm) bwm.produceTire(); System.out.println(bwmTire.toString()); EngineForBwm bwmEngine = (EngineForBwm) bwm.produceEngine(); System.out.println(bwmEngine.toString()); }
----------------------- 奔馳汽車準備生產 ----------------------- 正在生產奔馳輪胎 [獲得 Benz 輪胎] 正在生產奔馳發動機 [獲得 Benz 發動機] ----------------------- 寶馬汽車準備生產 ----------------------- 正在生產寶馬輪胎 [獲得 Bwm 輪胎] 正在生產寶馬發動機 [獲得 Bwm 發動機]
既然說抽象工廠模式是工廠方法模式的升級,那到底升級了啥?
實際上是由原來的單一產品的生產升級成爲了系列產品的生產。設想一下,假設上面汽車的例子中,每一品牌汽車中就只生產一種部件,好比就只生產發動機,不生產輪胎等其餘組件了,以下圖
發現了什麼沒有?抽象工廠模式竟然轉變爲咱們以前講過的工廠方法模式了!換句話說,當你的產品族中只生產一種產品的時候,你的抽象工廠模式其實已經退化爲工廠方法模式了。反過來講,當生產多種產品時,工廠方法模式就進化爲抽象工廠模式。
抽象工廠模式在建立大量系列對象時能夠大大提升開發效率,就是爲生產產品族而生的,而對於生產單一產品卻無能爲力。
若是須要添加一個新的產品族,那就簡單了,好比新增一個保時捷汽車,那就只須要添加一個保時捷汽車的工廠實現類就行了,並不會對原有的代碼形成任何影響。
可是,若是假設在汽車中,我須要再加一個組件,好比倒車影像,怎麼操做?你須要在CarFactory
接口中添加返回倒車影像對象的接口。這一加不得了了......全部品牌汽車實現類所有須要修改並追加該方法的實現,違反了開閉原則。
建立大量系列對象時能夠大大提升開發效率,下降維護成本。
工廠模式的三種形式都介紹完了,那咱們實際開發中該如何去選擇呢?