一、小案例分析 1、功能需求: 實現一個發送信息的功能,要便於擴展與維護。(1)發送信息的工具有很多,比如簡訊、微信、郵件、QQ等。(2)選擇某個工具進行信息發送。 2、小菜雞去實現: (1)定義一個發送工具的父類(介面),並將各種發送工具作為子類(實現類)。(2)定義一個選擇發送工具的類,用於調用 ...
一、小案例分析
1、功能需求:
實現一個發送信息的功能,要便於擴展與維護。
(1)發送信息的工具有很多,比如簡訊、微信、郵件、QQ等。
(2)選擇某個工具進行信息發送。
2、小菜雞去實現:
(1)定義一個發送工具的父類(介面),並將各種發送工具作為子類(實現類)。
(2)定義一個選擇發送工具的類,用於調用發送工具(直接new個子類對象)。
(3)代碼實現:
package creative.pattern.factory.noFactory; import java.util.Scanner; /** * 測試類 * */ public class Demo { public static void main(String[] args) { new SendMessage();// 實例化一個選擇發送工具的類 } } /** * 定義一個發送工具的介面 * */ interface Sender { void sendMessage(); } /** * 定義一個簡訊發送工具,實現介面,重寫方法 * */ class ShortMessageSender implements Sender { @Override public void sendMessage() { System.out.println("發送簡訊"); } } /** * 定義一個微信發送工具,實現介面,重寫方法 * */ class WeChatSender implements Sender { @Override public void sendMessage() { System.out.println("發送微信"); } } /** * 定義一個郵件發送工具,實現介面,重寫方法 * */ class MailSender implements Sender { @Override public void sendMessage() { System.out.println("發送郵件"); } } /** * 定義一個選擇發送工具的類 */ class SendMessage { /** * 用於獲取需要發送信息工具 */ public Sender getSenderType() { System.out.println("輸入發送工具的類型(1-3):"); System.out.println("1:簡訊"); System.out.println("2:微信"); System.out.println("3:郵件"); Scanner scanner = new Scanner(System.in); String senderType = scanner.nextLine(); if ("1".equals(senderType)) { return new ShortMessageSender(); } else if ("2".equals(senderType)) { return new WeChatSender(); } else if ("3".equals(senderType)) { return new MailSender(); } else { return null; } } public SendMessage() { do { Sender sender = getSenderType();// 選擇發送信息的工具 if (sender == null) { System.out.println("歡迎下次使用"); break; } else { sender.sendMessage(); } } while (true); } }
(4)代碼分析:
對於邏輯簡單的代碼,這樣實現沒有問題,但是邏輯稍微複雜一些且需要修改擴展某個地方時,需要改動很多地方。
比如:再增加一個 QQSender,其需要實現Sender介面,並重寫相關方法,然後需要在 SendMessage 類中 去修改相關代碼,這裡違反了開閉原則。若需要增加幾個SendMessage 類,比如SendMessage2、SendMessage3時,同樣需要改動很多代碼。
(5)UML圖:
二、簡單工廠模式(SimpleFactory)
1、什麼是簡單工廠模式:
簡單工廠模式屬於創建型模式,不屬於常見的23種常見模式。簡單的講 簡單工廠模式是由一個工廠對象決定創建出哪一類產品的實例。
2、使用:
(1)簡單工廠模式,定義了一個創建對象的類,然後由這個類來封裝實例化對象的操作。
(2)當需要大量創建某種類或者對象時,可以使用工廠模式。
3、使用簡單工廠模式實現:
(1)定義一個發送工具的父類(介面),並將各種發送工具作為子類(實現類)。
(2)定義一個工廠類,用於調用發送工具。
(2)定義一個選擇發送工具的類,用於調用工廠類。
(3)代碼實現:
package creative.pattern.factory.simpleFactory; import java.util.Scanner; /** * 測試類 * */ public class SimpleFactoryDemo { public static void main(String[] args) { new SendMessage(); } } /** * 定義一個發送工具的介面 * */ interface Sender { void sendMessage(); } /** * 定義一個簡訊發送工具,實現介面,重寫方法 * */ class ShortMessageSender implements Sender { @Override public void sendMessage() { System.out.println("發送簡訊"); } } /** * 定義一個微信發送工具,實現介面,重寫方法 * */ class WeChatSender implements Sender { @Override public void sendMessage() { System.out.println("發送微信"); } } /** * 定義一個郵件發送工具,實現介面,重寫方法 * */ class MailSender implements Sender { @Override public void sendMessage() { System.out.println("發送郵件"); } } /** * 使用簡單工廠模式,管理需要生產的對象 * */ class SimpleFactory { /** * 用於獲取需要發送信息工具 */ public static Sender createSender() { System.out.println("輸入發送工具的類型(1-3):"); System.out.println("1:簡訊"); System.out.println("2:微信"); System.out.println("3:郵件"); Scanner scanner = new Scanner(System.in); String senderType = scanner.nextLine(); if ("1".equals(senderType)) { return new ShortMessageSender(); } else if ("2".equals(senderType)) { return new WeChatSender(); } else if ("3".equals(senderType)) { return new MailSender(); } else { return null; } } } /** * 定義一個選擇發送工具的類 */ class SendMessage { public SendMessage() { do { Sender sender = SimpleFactory.createSender();// 選擇發送信息的工具 if (sender == null) { System.out.println("歡迎下次使用"); break; } else { sender.sendMessage(); } } while (true); } }
(4)代碼分析:
對於上述代碼,SendMessage 只與工廠類SimpleFactory 相關聯,此時需要擴展代碼時,比如擴展QQSender,讓其實現Sender並重寫方法後,在SimpleFactory 中改變相關代碼即可(違反開閉原則),不需要再改動SendMessage 的代碼。
(5)UML圖:
4、使用靜態工廠模式實現(常用形式):
將上例的SimpleFactory 中public Sender createSender()改為 public static Sender createSender()。
調用時,直接使用SimpleFactory.createSender() 即可。
public static Sender createSender() { System.out.println("輸入發送工具的類型(1-3):"); System.out.println("1:簡訊"); System.out.println("2:微信"); System.out.println("3:郵件"); Scanner scanner = new Scanner(System.in); String senderType = scanner.nextLine(); if ("1".equals(senderType)) { return new ShortMessageSender(); } else if ("2".equals(senderType)) { return new WeChatSender(); } else if ("3".equals(senderType)) { return new MailSender(); } else { return null; } }
三、工廠方法模式
1、什麼是工廠方法模式:
在工廠內部定義一個創建對象的抽象方法,由子類去確定要實例化的對象。簡單的講 工廠方法模式將對象實例化的操作推遲到子類去實現。可以看做抽象工廠模式的一個常見類型。
2、使用工廠模式實現:
(1)定義一個發送工具的父類(介面),並將各種發送工具作為子類(實現類)。
(2)定義一個工廠方法介面,並通過工廠實現類去實例化對象。
(3)定義一個選擇發送工具的類,用於調用工廠實現類。
(4)代碼實現:
package creative.pattern.factory.factoryMethod; import java.util.Scanner; /** * 工廠方法模式測試類 * */ public class factoryMethodDemo { public static void main(String[] args) { new SendMessage(); } } /** * 定義一個發送工具的介面 * */ interface Sender { void sendMessage(); } /** * 定義一個簡訊發送工具,實現介面,重寫方法 * */ class ShortMessageSender implements Sender { @Override public void sendMessage() { System.out.println("發送簡訊"); } } /** * 定義一個微信發送工具,實現介面,重寫方法 * */ class WeChatSender implements Sender { @Override public void sendMessage() { System.out.println("發送微信"); } } /** * 定義一個郵件發送工具,實現介面,重寫方法 * */ class MailSender implements Sender { @Override public void sendMessage() { System.out.println("發送郵件"); } } /** * 使用工廠方法模式,讓子類去創建對象 * */ interface FactoryMethod { Sender sendMessage(); } /** * 簡訊工廠類,實現工廠方法類並重寫相關方法 * */ class ShortMessageFactory implements FactoryMethod { @Override public Sender sendMessage() { return new ShortMessageSender(); } } /** * 微信工廠類,實現工廠方法類並重寫相關方法 * */ class WeChatFactory implements FactoryMethod { @Override public Sender sendMessage() { return new WeChatSender(); } } /** * 郵件工廠類,實現工廠方法類並重寫相關方法 * */ class MailFactory implements FactoryMethod { @Override public Sender sendMessage() { return new MailSender(); } } /** * 定義一個選擇發送工具的類 */ class SendMessage { public SendMessage() { do { System.out.println("輸入發送工具的類型(1-3):"); System.out.println("1:簡訊"); System.out.println("2:微信"); System.out.println("3:郵件"); Scanner scanner = new Scanner(System.in); String senderType = scanner.nextLine(); if ("1".equals(senderType)) { FactoryMethod factoryMethod = new ShortMessageFactory(); Sender sender = factoryMethod.sendMessage();// 選擇發送簡訊 sender.sendMessage(); } else if ("2".equals(senderType)) { FactoryMethod factoryMethod = new WeChatFactory(); Sender sender = factoryMethod.sendMessage();// 選擇發送微信 sender.sendMessage(); } else if ("3".equals(senderType)) { FactoryMethod factoryMethod = new MailFactory(); Sender sender = factoryMethod.sendMessage();// 選擇發送郵件 sender.sendMessage(); } else { System.out.println("歡迎下次使用"); break; } } while (true); } }
(5)代碼分析:
SendMessage 類只與FactoryMethod 有關,當擴展新的功能時,比如QQSender,只需創建一個QQFactory,實現FactoryMethod 並重寫其方法即可,調用時無需更改其他代碼(必要的邏輯處理除外,符合開閉原則)。
(6)UML圖:
四、抽象工廠模式
1、什麼是抽象工廠模式
為創建一組相關或相互依賴的對象提供一個介面,而且無需指定他們的具體類。
註:
產品族:是指位於不同產品等級結構中功能相關聯的產品組成的家族。
產品等級結構:可以理解為一個介面或者一個抽象類。
2、抽象工廠模式與工廠方法模式的區別
(1)抽象工廠模式是工廠方法模式的升級版,其針對的是多個產品等級結構,即抽象工廠模式所提供的產品是衍生自不同的介面或抽象類。
(2)工廠方法模式:針對一個產品等級結構,即工廠方法模式衍生自同一個介面或者抽象類。
(3)如下圖所示,ShortMessage 與 WeChat屬於同一個產品等級,ShortMessage 與 ShortMessage2屬於兩個產品等級,為產品族。所以若存在兩個產品等級及以上的情況,即為抽象工廠模式,若是同一個產品等級,則為工廠方法模式。
下圖為工廠方法模式:
下圖為抽象工廠模式:
3、舉例:
在之前案例的基礎上,增加一個功能,微信、簡訊均可以圖片。
(1)定義一個發送工具的父類(介面),並將各種發送工具作為子類(實現類)。
(2)定義一個工廠方法介面,在方法中對產品族進行約束,並通過工廠實現類去實例化對象。
(3)定義一個選擇發送工具的類(測試類),用於調用工廠實現類。
(4)代碼實現:
package creative.pattern.factory.absFactory; /** * 抽象工廠模式測試類 * */ public class AbsFactoryDemo { public static void main(String[] args) { AbsFactory absFactory = new WeChatFactory(); absFactory.getSender().sendMessage(); absFactory.getSender2().sendMessage(); ((WeChatSender2) absFactory.getSender2()).sendPicture(); AbsFactory absFactory2 = new ShortMessageFactory(); absFactory2.getSender().sendMessage(); absFactory2.getSender2().sendMessage(); ((ShortMessageSender2) absFactory2.getSender2()).sendPicture(); } } /** * 定義一個發送工具的介面 * */ interface Sender { void sendMessage(); } /** * 定義一個微信發送工具,實現介面,重寫方法 * */ class WeChatSender implements Sender { @Override public void sendMessage() { System.out.println("使用微信,發送簡訊"); } } /** * 定義一個微信發送工具,實現介面,重寫方法,並新增自己的方法 * */ class WeChatSender2 implements Sender { @Override public void sendMessage() { System.out.println("使用微信,發送簡訊"); } public void sendPicture() { System.out.println("使用微信,發送圖片"); } } /** * 定義一個簡訊發送工具,實現介面,重寫方法 * */ class ShortMessageSender implements Sender { @Override public void sendMessage() { System.out.println("使用簡訊,發送簡訊"); } } /** * 定義一個簡訊發送工具,實現介面,重寫方法,並新增自己的方法 * */ class ShortMessageSender2 implements Sender { @Override public void sendMessage() { System.out.println("使用簡訊,發送簡訊"); } public void sendPicture() { System.out.println("使用簡訊,發送圖片"); } } /** * 抽象工廠模式 * */ interface AbsFactory { Sender getSender(); Sender getSender2(); } /** * 工廠子類,實現抽象工廠類,重寫相關方法, * */ class WeChatFactory implements AbsFactory { @Override public Sender getSender() { return new WeChatSender(); } @Override public Sender getSender2() { return new WeChatSender2(); } } /** * 工廠子類,實現抽象工廠類,重寫相關方法, * */ class ShortMessageFactory implements AbsFactory { @Override public Sender getSender() { return new ShortMessageSender(); } @Override public Sender getSender2() { return new ShortMessageSender2(); } }
(5)代碼分析:
對於產品族,定義在一個介面中,然後通過不同的子類去實現。擴展時,只需要實現介面並重寫相關方法即可,滿足開閉原則。
(6)UML圖:
五、JDK中工廠模式舉例(Calendar)
1、部分源碼
public abstract class Calendar implements Serializable, Cloneable, Comparable<Calendar> { /** * Gets a calendar using the default time zone and locale. The * <code>Calendar</code> returned is based on the current time * in the default time zone with the default * {@link Locale.Category#FORMAT FORMAT} locale. * * @return a Calendar. */ public static Calendar getInstance() { return createCalendar(TimeZone.getDefault(), Locale.getDefault(Locale.Category.FORMAT)); } private static Calendar createCalendar(TimeZone zone, Locale aLocale) { CalendarProvider provider = LocaleProviderAdapter.getAdapter(CalendarProvider.class, aLocale) .getCalendarProvider(); if (provider != null) { try { return provider.getInstance(zone, aLocale); } catch (IllegalArgumentException iae) { // fall back to the default instantiation } } 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; } } } if (cal == null) { // If no known calendar type is explicitly specified, // perform the traditional way to create a Calendar: // create a BuddhistCalendar for th_TH locale, // a JapaneseImperialCalendar for ja_JP_JP locale, or // a GregorianCalendar for any other locales. // NOTE: The language, country and variant strings are interned. if (aLocale.getLanguage() == "th" && aLocale.getCountry() == "TH") { cal = new BuddhistCalendar(zone, aLocale); } else if (aLocale.getVariant() == "JP" && aLocale.getLanguage() == "ja" && aLocale.getCountry() == "JP") { cal = new JapaneseImperialCalendar(zone, aLocale); } else { cal = new GregorianCalendar(zone, aLocale); } } return cal; } }
2、源碼分析
Calendar內部採用簡單工廠模式進行對象的實例化。其根據不同的邏輯判斷條件來選擇實例化具體的對象。
六、總結:
1、工廠模式的意義:
將實例化對象的代碼提取出來,放到一個類(工廠類)裡面進行維護,使其與主項目解耦,提高程式的維護性與擴展性。
2、傳統模式:
直接在需要的地方實例化某對象。擴展代碼時,需要在使用到的地方進行修改,違反了開閉原則。
3、簡單工廠模式:
在需要用到的地方,調用工廠類即可,擴展代碼時,修改工廠類即可,也違反了開閉原則。
(1)簡單工廠模式(普通方法):
使用一個工廠類,在某方法中通過邏輯處理並實例化需要的對象。
(2)簡單工廠模式(靜態方法):
將簡單工廠模式(普通方法)的普通方法改為靜態方法,並通過”類名.方法名“來調用。
(3)簡單工廠模式(多方法):
使用一個工廠類,並通過調用不同的方法去實例化不同的對象。
4、工廠方法模式:
使用一個工廠類介面與多個工廠實現類,在不同的工廠實現類中去實例化不同的對象。擴展代碼時,定義一個工廠實現類,實現工廠類介面並重寫相關方法即可,滿足開閉原則。可以理解為抽象工廠模式的一般形式。
5、抽象工廠模式:
可以理解為工廠方法模式的升級版,其在一個介面中定義了一個產品族的處理(多個方法),子類實現該介面,並重寫相關方法即可,擴展類似於工廠方法模式,滿足開閉原則。