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中的加载驱动,获取连接,也是用的简单工厂模式

posted @ 2018-11-13 17:31  MichaelKai  阅读(326)  评论(0编辑  收藏  举报