美文网首页设计模式就该这样学
彻底说透简单工厂那些你没有关注过的细节

彻底说透简单工厂那些你没有关注过的细节

作者: Tom弹架构 | 来源:发表于2021-11-10 14:01 被阅读0次

    本文节选自《设计模式就该这样学》

    1 使用简单工厂模式封装产品创建细节

    接下来看代码,还是以创建一门网络课程为例。假设有Java架构、大数据、人工智能等课程,已经形成了一个生态。我们可以定义一个课程标准ICourse接口。

    
    public interface ICourse {
        /** 录制视频 */
        public void record();
    }
    
    

    创建一个Java课程的实现类JavaCourse。

    
    public class JavaCourse implements ICourse {
        public void record() {
            System.out.println("录制Java课程");
        }
    }
    
    

    客户端调用代码如下。

    
    public static void main(String[] args) {
        ICourse course = new JavaCourse();
        course.record();
    }
    
    

    由上面代码可知,父类ICourse指向子类JavaCourse的引用,应用层代码需要依赖JavaCourse。如果业务扩展,则继续增加PythonCourse,甚至更多,那么客户端的依赖会变得越来越臃肿。因此,我们要想办法把这种依赖减弱,把创建细节隐藏。虽然在目前的代码中,创建对象的过程并不复杂,但从代码设计角度来讲不易于扩展。因此,用简单工厂模式对代码进行优化。首先增加课程PythonCourse类。

    
    public class PythonCourse implements ICourse {
        public void record() {
            System.out.println("录制Python课程");
        }
    }
    
    

    然后创建CourseFactory工厂类。

    
    public class CourseFactory {
        public ICourse create(String name){
            if("java".equals(name)){
                return new JavaCourse();
            }else if("python".equals(name)){
                return new PythonCourse();
            }else {
                return null;
            }
        }
    }
    
    

    最后修改客户端调用代码。

    
    public class SimpleFactoryTest {
        public static void main(String[] args) {
            CourseFactory factory = new CourseFactory();
            factory.create("java");
        }
    }
    
    

    当然,为了调用方便,可将CourseFactory的create()方法改为静态方法,其类图如下图所示。

    file

    客户端调用虽然简单了,但如果业务继续扩展,要增加前端课程,则工厂中的create()方法就要随着产品链的丰富每次都要修改代码逻辑,这不符合开闭原则。因此,我们可以采用反射技术继续对简单工厂模式进行优化,代码如下。

    
    public class CourseFactory {
        public ICourse create(String className){
            try {
                if (!(null == className || "".equals(className))) {
                    return (ICourse) Class.forName(className).newInstance();
                }
            }catch (Exception e){
                e.printStackTrace();
            }
            return null;
        }
    }
    
    

    客户端调用代码修改如下。

    
    public static void main(String[] args) {
            CourseFactory factory = new CourseFactory();
            ICourse course = factory.create("com.gupaoedu.vip.pattern.factory.simplefactory.JavaCourse");
            course.record();
    }
    
    

    优化之后,产品不断丰富,不需要修改CourseFactory中的代码。但问题是,方法参数是字符串,可控性有待提升,而且还需要强制转型。继续修改代码。

    
    public ICourse create(Class<? extends ICourse> clazz){
        try {
            if (null != clazz) {
                return clazz.newInstance();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
    
    

    优化客户端测试代码。

    
    public static void main(String[] args) {
        CourseFactory factory = new CourseFactory();
        ICourse course = factory.create(JavaCourse.class);
        course.record();
    }
    
    

    最后来看如下图所示的类图。

    file

    2 简单工厂模式在JDK源码中的应用

    简单工厂模式在JDK源码中无处不在,例如Calendar类,看Calendar.getInstance()方法。下面打开的是Calendar的具体创建类。

    
    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) {
            }
        }
    
        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 (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;
    }
    
    

    3 简单工厂模式在Logback源码中的应用

    在大家经常使用的Logback中,可以看到LoggerFactory中有多个重载的方法getLogger()。

    
    public static Logger getLogger(String name) {
        ILoggerFactory iLoggerFactory = getILoggerFactory();
        return iLoggerFactory.getLogger(name);
    }
    
    public static Logger getLogger(Class clazz) {
        return getLogger(clazz.getName());
    }
    
    

    关注『 Tom弹架构 』回复“设计模式”可获取完整源码。

    【推荐】Tom弹架构:收藏本文,相当于收藏一本“设计模式”的书

    本文为“Tom弹架构”原创,转载请注明出处。技术在于分享,我分享我快乐!
    如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。关注『 Tom弹架构 』可获取更多技术干货!

    相关文章

      网友评论

        本文标题:彻底说透简单工厂那些你没有关注过的细节

        本文链接:https://www.haomeiwen.com/subject/fdobzltx.html