一、設計模式的分類總的來說,設計模式可以分為三大類:創建型模式、結構型模式、行為型模式,具體如下圖:二、工廠模式工廠模式分為簡單工廠模式、工廠方法模式和抽象工廠模式。其
2021-06-19 14:45:38
總的來說,設計模式可以分為三大類:創建型模式、結構型模式、行為型模式,具體如下圖:
工廠模式分為簡單工廠模式、工廠方法模式和抽象工廠模式。其中簡單工廠模式並不屬於23種設計模式,但並不影響它的廣泛使用。在JDK的源碼當中,就存在著許多這樣的例子。
我們先來看一段程式碼:
public static void main(String[] args) { // 日曆類 Calendar calendar = Calendar.getInstance(); SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("當前時間為:" + simpleDateFormat.format(calendar.getTime())); calendar.add(Calendar.HOUR,2); System.out.println("當前時間加了兩個小時後,時間是: " + simpleDateFormat.format(calendar.getTime()));}
這段程式碼,大家應該比較熟悉,通過對Calendar的一系列操作,打印出當前時間和當前時間加兩個小時後的時間,這裡我們來看看結果:
結果正和我們想象的一樣,兩次打印出來的時間相隔兩個小時。但我們今天的重點是Calendar calendar = Calendar.getInstance()這段程式碼,通過getInstance()方法拿到了Calendar類的例項。來看看具體的原始碼:
public static Calendar getInstance(){ return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT)); }// 程式碼不全,有興趣的朋友可以去看JDK源碼 private static Calendar createCalendar(TimeZone zone, Locale aLocale){ // 中間的程式碼省略..... Calendar cal = null; if (aLocale.hasExtensions()) { String caltype = aLocale.getUnicodeLocaleType("ca"); if (caltype != null) { switch (caltype) { case "buddhist": cal = new BuddhistCalendar(zone, aLocale); break; case "japanese": cal = new JapaneseImperialCalendar(zone, aLocale); break; case "gregory": cal = new GregorianCalendar(zone, aLocale); break; } } } // 中間的程式碼省略..... return cal; }
可以看出,getInstance()方法裡面呼叫了createCalendar()方法來得到Calendar類的例項,最後返回給呼叫者。而createCalendar()方法中通過switch(){case}的判斷來返回所對應的Calendar類的例項,這其實就是簡單工廠模式的一種應用。
看完簡單工廠模式在JDK中的應用之後,我們來設計一下自己的例子:
小明家新開了一家小工廠,接了一單生意,幫助海爾(Haier)集團生產冰箱,並需要設計相應的方案。小明本身也是程式設計師出身,思考一會後就寫出了下面的程式碼:
/** * 冰箱 */public interface IFridge { // 生產冰箱 public void createFridge();}
/** * 海爾 */public class Haier implements IFridge { @Override public void createFridge() { System.out.println("生產海爾冰箱..."); }}
客戶端呼叫程式碼:
public static void main(String[] args) { IFridge iFridge = new Haier(); iFridge.createFridge();}
看上面的程式碼,父類IFridge類指向子類Haier類的引用,應用層需要依賴於Haier。如果業務擴展,後續增加格力(Gree)甚至更多,那麼客戶端這裡的程式碼會越來越臃腫。所以,我們要想辦法將這種依賴減弱,將創建IFridge物件的細節隱藏掉。我們用簡單工廠模式優化一下:
創建Gree格力類
/** * 格力 */public class Gree implements IFridge { @Override public void createFridge() { System.out.println("生產格力冰箱..."); }}
創建FridgeFactory工廠類
/** * 冰箱工廠 */public class FridgeFactory { // 創建對應的 IFridge 例項 public static IFridge createFridge(String name){ if ("haier".equals(name)){ return new Haier(); } else if ("gree".equals(name)){ return new Gree(); } return null; }}
修改客戶端呼叫的程式碼:
public static void main(String[] args) { // 海爾 IFridge haier = FridgeFactory.createFridge("haier"); haier.createFridge(); // 格力 IFridge gree = FridgeFactory.createFridge("gree"); gree.createFridge();}
這樣來看,雖然程式碼多了,但維護起來以及擴展起來就方便很多,來看一看類圖:
當然,上面的FridgeFactory程式碼中依舊有些問題,如果我們需要增加生產美的(Midea)冰箱,那麼我們就需要去修改createFridge()方法的程式碼,顯然違背了開閉原則,我們來改造一下:
修改FridgeFactory工廠類
/** * 冰箱工廠 */public class FridgeFactory { // 創建對應的 IFridge 例項 public static IFridge createFridge(String className){ try { if (null != className && !"".equals(className)){ // 反射 return (IFridge)Class.forName(className).newInstance(); } }catch (Exception e){ e.printStackTrace(); } return null; }}
修改客戶端呼叫的程式碼
public static void main(String[] args) { // com.xxx.Haier 換成 自己 項目中 Haier 所在的位置 海爾 IFridge haier = FridgeFactory.createFridge("com.xxx.Haier"); haier.createFridge(); // com.xxx.Gree 換成 自己 項目中 Gree 所在的位置 格力 IFridge gree = FridgeFactory.createFridge("com.xxx.Gree"); gree.createFridge();}
優化之後,我們再也不需要隨著業務的提升而去修改FridgeFactory類中的程式碼了。但是依舊有一個問題,createFridge()方法中的參數是字元串,如果有人亂填怎麼辦,那不就報錯了,所以再來優化一下:
修改FridgeFactory工廠類
/** * 冰箱工廠 */public class FridgeFactory { // 創建對應的 IFridge 例項 public static IFridge createFridge(Class<? extends IFridge> clazz){ try { if (clazz != null){ return clazz.newInstance(); } }catch (Exception e){ e.printStackTrace(); } return null; }}
修改客戶端呼叫的程式碼
public static void main(String[] args) { // 海爾 FridgeFactory.createFridge(Haier.class).createFridge(); // 格力 FridgeFactory.createFridge(Gree.class).createFridge();}
再來看一下類圖:
簡單工廠模式雖然好用,但也有它的侷限性:工廠類的職責過重,不利於擴展更為複雜產品結構。
定義一個創建物件的介面,但讓實現這個介面的類來決定例項化哪個類,工廠方法讓類的例項化推遲到子類中進行。
在工廠方法模式中使用者只需要關心所需產品對應的工廠,無須關心創建細節,而且加入新的產品符合開閉原則。
隨著小明家新工廠的生意火爆,各類的訂單都紛湧而至,各個牌子的廠家都想讓小明家的工廠生產冰箱,小明無奈只能開了分工廠,並根據客戶的品牌名給工廠取了對應的名字,其中海爾工廠生產海爾的冰箱,格力工廠生產格力的冰箱,美的工廠生產美的的冰箱。用程式碼演化就是下面這般:
IFridgeFactory類介面
public interface IFridgeFactory { public IFridge createIFridge();}
海爾
// 海爾 工廠public class HaierFactory implements IFridgeFactory { @Override public IFridge createIFridge() { return new Haier(); }}
格力
// 格力 工廠public class GreeFactory implements IFridgeFactory { @Override public IFridge createIFridge() { return new Gree(); }}
美的
/** * 美的 */public class Midea implements IFridge { @Override public void createFridge() { System.out.println("生產美的冰箱..."); }}
// 美的public class MideaFactory implements IFridgeFactory { @Override public IFridge createIFridge() { return new Midea(); }}
客戶端呼叫:
public static void main(String[] args) { // 格力 new GreeFactory().createIFridge().createFridge(); // 海爾 new HaierFactory().createIFridge().createFridge(); // 美的 new MideaFactory().createIFridge().createFridge();}
這裡其實就是細化了工廠,將業務拆分,利用了設計模式原則中的單一職責原則,讓每個品牌對應工廠只幹一件事,不去摻和其他品牌的事情。來看一看類圖:
工廠方法模式適用於一下場景:
工廠方法模式也有缺點:
定義:提供一個創建一系列相關或者相互依賴物件的介面,無需指定他們具體的類。
這個定義讀起來相當的拗口,很抽象,不好理解。還是和上面的例子結合來說明:
在生產完一批冰箱並上市售賣之後,美的、格力、海爾等公司非常滿意,慢慢的將自己家的空調、熱水器也交給小明家的工廠去生產了。小明為此在對應的品牌工廠有開闢了對應的生產裝置的空間(這裡為了大家看的方便,我將所有的程式碼都放上去):
冰箱、空調、熱水器介面
// 冰箱public interface IFridge { // 生產冰箱 public void createFridge();}// 空調public interface IAirConditioner { // 生產空調 public void createAirConditioner();}// 熱水器public interface IWaterHeater { // 生產熱水器 public void createWaterHeater();}
海爾
/** * 海爾 冰箱 */public class HaierFridge implements IFridge{ @Override public void createFridge() { System.out.println("生產海爾冰箱..."); }}// 海爾 空調public class HaierAirConditioner implements IAirConditioner { @Override public void createAirConditioner() { System.out.println("生產海爾空調..."); }}// 海爾熱水器public class HaierWaterHeater implements IWaterHeater { @Override public void createWaterHeater() { System.out.println("生產海爾熱水器..."); }}
格力
/** * 格力 冰箱 */public class GreeFridge implements IFridge { @Override public void createFridge() { System.out.println("生產格力冰箱..."); }}// 格力 空調public class GreeAirConditioner implements IAirConditioner { @Override public void createAirConditioner() { System.out.println("生產格力空調..."); }}// 格力熱水器public class GreeWaterHeater implements IWaterHeater { @Override public void createWaterHeater() { System.out.println("生產格力熱水器..."); }}
美的
/** * 美的 冰箱 */public class MideaFridge implements IFridge{ @Override public void createFridge() { System.out.println("生產美的冰箱..."); }}// 美的 空調public class MideaAirConditioner implements IAirConditioner { @Override public void createAirConditioner() { System.out.println("生產美的空調..."); }}// 美的熱水器public class MideaWaterHeater implements IWaterHeater { @Override public void createWaterHeater() { System.out.println("生產美的熱水器..."); }}
工廠介面
12345678public interface IFactory { // 冰箱 public IFridge createIFridge(); // 空調 public IAirConditioner createIConditioner(); // 熱水器 public IWaterHeater createIWaterHeater();}
海爾工廠
// 海爾 工廠public class HaierFactory implements IFactory { // 冰箱 @Override public IFridge createIFridge() { return new HaierFridge(); } // 空調 @Override public IAirConditioner createIConditioner() { return new HaierAirConditioner(); } // 熱水器 @Override public IWaterHeater createIWaterHeater() { return new HaierWaterHeater(); }}
格力工廠
// 格力public class GreeFactory implements IFactory { // 冰箱 @Override public IFridge createIFridge() { return new GreeFridge(); } // 空調 @Override public IAirConditioner createIConditioner() { return new GreeAirConditioner(); } // 熱水器 @Override public IWaterHeater createIWaterHeater() { return new GreeWaterHeater(); }}
美的工廠
public class MideaFactory implements IFactory { // 冰箱 @Override public IFridge createIFridge() { return new MideaFridge(); } // 空調 @Override public IAirConditioner createIConditioner() { return new MideaAirConditioner(); } // 熱水器 @Override public IWaterHeater createIWaterHeater() { return new MideaWaterHeater(); }}
客戶端呼叫
public static void main(String[] args) { // 海爾工廠 HaierFactory haierFactory = new HaierFactory(); haierFactory.createIFridge().createFridge(); haierFactory.createIConditioner().createAirConditioner(); haierFactory.createIWaterHeater().createWaterHeater(); // 格力工廠 GreeFactory greeFactory = new GreeFactory(); greeFactory.createIFridge().createFridge(); greeFactory.createIConditioner().createAirConditioner(); greeFactory.createIWaterHeater().createWaterHeater(); // 美的工廠 MideaFactory mideaFactory = new MideaFactory(); mideaFactory.createIFridge().createFridge(); mideaFactory.createIConditioner().createAirConditioner(); mideaFactory.createIWaterHeater().createWaterHeater();}
類圖
從上面一大堆的程式碼,尤其是類圖,我們可以很明顯的感覺到,抽象工廠可以完美清晰的描述海爾、格力、美的三個品牌的冰箱、空調、熱水器的龐大體系。但也正因為如此,抽象工廠給我們的視覺衝擊有些大,能很明顯的感覺到系統的複雜性、抽象性以及系統的極難擴展性;並且這裡還隱藏著一個違背開閉原則的問題:
在工廠介面IFactory類中,如果在日後的產品升級當中,需要增加生產洗衣機的業務,那這裡修改之後,所有實現IFactory介面的類都需要變動,很大程度增加了系統的不穩定性。
也正因為如此,在實際的業務開發中,我們不應該有著強烈的強迫症和潔癖,認為一個系統的結構設計必須要完美的符合各種原則。要結合實際的業務去思考,如果系統結構的等級更新不頻繁的話,不遵守某些原則也是有必要性的,畢竟所有的技術都是為業務而服務的。
作者:蔡小明
連結:https://www.cnblogs.com/caimm/p/14899740.html
相關文章
一、設計模式的分類總的來說,設計模式可以分為三大類:創建型模式、結構型模式、行為型模式,具體如下圖:二、工廠模式工廠模式分為簡單工廠模式、工廠方法模式和抽象工廠模式。其
2021-06-19 14:45:38
IT之家 6 月 19 日訊息 OPPO 官方在今日公佈了其 618 終極戰報,618 期間 OPPO 全品類線上銷量同比增長超 125%,其中,Reno 數字系列手機銷量同比增長超 400%。作為本次 618 活動
2021-06-19 14:44:06
又是一年618,不知道這個618你有沒有為自己或者家人剁手幾款手機呢?在選擇一款手機的時候,不同人有著不同的思考維度,但唯一的共同點就是價格和配置了。當然了,群眾的眼睛是雪亮的
2021-06-19 14:25:04
最新訊息爆料:移動全新智慧終端品牌NZONE將釋出成立以來第一款手機S7,有渠道爆料將成為除華為自身以外的第一個國產科技手機品牌首發出廠搭載鴻蒙OS2.0作業系統的手機品牌。01
2021-06-19 14:23:56
曉查 發自 凹非寺量子位 報道 | 公眾號 QbitAILSTM之父又發新博文了。這位Jürgen Schmidhuber,幾乎每隔一段時間就出來回顧AI歷史,拋出一些極具爭議的觀點,這次也不例外。△ J
2021-06-19 14:22:59
父親使用的手機,外觀配色和設計一定要彰顯自身的氣度。RedmiNote10Pro提供了幻青配色,不僅是經典的顏色,還極具質感。機身背板覆蓋有目前最強康寧大猩猩玻璃,清澈通透、更加抗摔
2021-06-19 14:04:14