Java设计模式学习——简单工厂
一. 定义与类型
定义:有工程对象决定创建出哪一种产品类的实例
类型:创建型,但不属于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中的加载驱动,获取连接,也是用的简单工厂模式