Java設計模式學習——簡單工廠
阿新 • • 發佈:2018-11-13
一. 定義與型別
定義:有工程物件決定創建出哪一種產品類的例項
型別:建立型,但不屬於GOF23中設計模式
二. 適用場景
工廠類負責建立的物件比較少
客戶端(應用層)只知道傳入工廠類的引數,對於如何建立物件(邏輯)不關心
三. 優點與缺點
優點:只需要傳入一個正確的引數,就可以獲取你所需要的物件,而無需知道其建立細節
缺點:工廠類的職責相對過重,增加新的產品需要修改工廠類的判斷邏輯,違背開閉原則
四. coding
/** * @program: designModel * @description: 視訊類 * @author: YuKai Fan * @create: 2018-11-13 16:46 **/ public abstract class Video { public abstract void product(); }
/** * @program: designModel * @description: java視訊類 * @author: YuKai Fan * @create: 2018-11-13 16:47 **/ public class JavaVideo extends Video { public void product() { System.out.println("錄製java課程視訊"); } }
/*** @program: designModel * @description: Python視訊類 * @author: YuKai Fan * @create: 2018-11-13 16:48 **/ public class PythonVideo extends Video{ public void product() { System.out.println("錄製Python課程視訊"); } }
/** * @program: designModel * @description: * @author: YuKai Fan * @create: 2018-11-13 16:48 **/ public class Test { public static void main(String[] args) { /*Video video = new JavaVideo(); video.product();*/ VideoFactory videoFactory = new VideoFactory(); Video java = videoFactory.getVideo("java"); if (java == null) { return; } java.product(); } }
這樣,應用層(客戶端)只需要傳入需要的型別,就可以得到相應的型別視訊,不知道建立的細節,UML類圖為
但是,如果要新增加一個課程的話,就必須在原有的基礎上修改程式碼,這樣違背了設計原則的開閉原則
可以對上面的程式碼進行一定的改進,通過反射的方法來完成型別的選擇
/** * @program: designModel * @description: * @author: YuKai Fan * @create: 2018-11-13 16:50 **/ public class VideoFactory { public Video getVideo(Class c) { Video video = null; try { video = (Video) Class.forName(c.getName()).newInstance(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return video; } /*public Video getVideo(String type) { if ("java".equalsIgnoreCase(type)) { return new JavaVideo(); } else if ("python".equalsIgnoreCase(type)) { return new PythonVideo(); } return null; }*/ }
/** * @program: designModel * @description: * @author: YuKai Fan * @create: 2018-11-13 16:48 **/ public class Test { public static void main(String[] args) { /*Video video = new JavaVideo(); video.product();*/ /*VideoFactory videoFactory = new VideoFactory(); Video java = videoFactory.getVideo("java"); if (java == null) { return; } java.product();*/ VideoFactory videoFactory = new VideoFactory(); Video java = videoFactory.getVideo(JavaVideo.class); if (java == null) { return; } java.product(); } }
這樣的好處是,應用層(客戶端)直接傳入對應的視訊類class即可,不需要修改工廠類的程式碼
五. 原始碼分析 jdk原始碼解析
在jdk中使用簡單工廠的體現:
1.calender.java中的getInstance方法中的createCalender方法
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;
//這裡使用的就是簡單工廠模式,switch-case判斷返回相應的國家日期型別 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-else判斷返回相應的國家日期型別 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; }
這個類的UML為:
jdbc中的載入驅動,獲取連線,也是用的簡單工廠模式