架构师内功心法,经典框架都在用的工厂模式详解

1、经典框架都在用设计模式解决问题

Spring就是一个把设计模式用的淋漓尽致的经典框架,其实从类的名称就可以看出来,咱们来一一列举一下:设计模式

特别须要说明的是,设计模式历来都不是单个设计模式独立使用的。 在一般状况下,常常是多个设计模式混合使用,你中有我,我中有你。全部的设计模式讲解都会围绕Spring的IOC、AOP、JDBC、MVC来进行展开。设计模式根据设计类型进行分类以下:框架

2、工厂模式详解

2.1 工厂模式的由来

在咱们的现实生活当中,原始社会自给自足(没有工厂)、农耕社会的小做坊(简单工厂,民间酒坊)、工业革命流水线(工厂方法,自产自销)、现代产业链工厂(抽象工厂,富士康)ide

从现实生活联想到咱们项目中的代码一样也是由简而繁一步一步迭代而来的,可是对于调用者确是愈来愈简单化。学习

2.2 简单工厂模式(Simple Factory Pattern)

简单工厂模式是指由一个工厂对象决定建立出哪种产品的实例,但它不属于GOF,23设计模式。优化

参考资料维基百科地址:https://en.wikipedia.org/wiki/Design_Patterns#Patterns_by_Type设计

简单工厂模式适用于工厂类负责建立的对象较少的场景,且客户端只须要传入工厂类的参数,对于如何建立对象的逻辑不须要关系。3d

接下来咱们来举例,以高中学校课程为例。语文、数学、英语等多门学科。咱们能够定义一个课程标准ICourse接口:code

public interface ICourse {

    /**
     * 学习课程
     */
    public void study();
}

建立一个语文课的实现ChineseCourse类:orm

public class ChineseCourse implements ICourse {
    @Override
    public void study() {
        System.out.println("学习语文课");
    }

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

看上面的main方法中,应用层的代码须要依赖ChineseCourse,若是业务扩展,会继续增长MathCourse甚至更多,这样的话客户端的依赖会愈来愈臃肿的。因此咱们须要对建立代码的细节进行隐藏,咱们使用简单工厂模式对代码进行优化。先添加MathCourse类:视频

public class MathCourse implements ICourse {
    @Override
    public void study() {
        System.out.println("学习数学课");
    }
}

建立CourseFactory工厂类:

public class CourseFactory {
    public ICourse create(String name) {
        if("chinese".equals(name)) {
            return new ChineseCourse();
        }else if("math".equals(name)) {
            return new MathCourse();
        }else {
            return null;
        }
    }
}

mian方法调用:

public static void main(String[] args) {
        CourseFactory courseFactory = new CourseFactory();
        courseFactory.create("chinese");
}

客户端调用是简单了,可是咱们的业务继续扩展,须要增长英文课,那么工厂中的create()方法要根据增长的业务每次都修改代码逻辑,不符合开闭原则。所以,咱们还须要对简单工厂进行优化,利用反射技术:

public class CourseFactory {

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

    public static void main(String[] args) {
        CourseFactory courseFactory = new CourseFactory();
        ICourse course = courseFactory.create("com.sfp.ChineseCourse");
        course.study();
    }
}

优化以后,课程不断增长不须要修改CourseFactory中的代码了。可是,方法参数是字符串,可控性有待提升,并且还需进行强制转换。再次修改代码:

public class CourseFactory {

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

    public static void main(String[] args) {
        CourseFactory courseFactory = new CourseFactory();
        ICourse course = courseFactory.create(ChineseCourse.class);
        course.study();
    }
}

简单工厂模式的例子无处不在,如今咱们来看JDK当中的类使用简单工厂模式的例子,例如Calendar类,其中Calendar.getInstance()方法,咱们查看源码具体的实现步骤:

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;
    }

还有一个你们常用的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());
}

简单工厂的缺点:工厂的职责相对太重,不易于扩展过于复杂的代码结构。

2.3 工厂方法模式(Factory Method Pattern)

工厂方法模式是指定义一个建立对象的接口,但让实现这个接口的类来决定实例化哪一个类,工厂方法让类的实例化推迟到子类中进行。 在工厂方法模式中只关心所需产品对应的工厂,无意关注建立细节,而加入新的产品符合开闭原则。

工厂方式模式主要解决产品扩展的问题,根据单一职责原则将职能进行拆分,专人干专事。语文课由语文工厂建立,数据课由数学工厂建立,对工厂自己作一个抽象。示例代码以下:

建立ICourseFactory接口:

public interface ICourseFacotry {

    ICourse create();
}

再分别建立子工厂,ChineseCourseFactory类:

public class ChineseCourseFactory implements ICourseFacotry {
    @Override
    public ICourse create() {
        return new ChineseCourse();
    }
}

MathCourseFactory类:

public class MathCourseFactory implements ICourseFacotry {
    @Override
    public ICourse create() {
        return new MathCourse();
    }
}

执行main方法:

public static void main(String[] args) {
        ICourseFacotry chineseCourseFactory = new ChineseCourseFactory();
        ICourse chineseCourse = chineseCourseFactory.create();
        chineseCourse.study();
        
        ICourseFacotry mathCourseFactory = new MathCourseFactory();
        ICourse mathCourse = mathCourseFactory.create();
        mathCourse.study();
    }

2.4 抽象工厂模式(Abstract Factory Pattern)

抽象工厂模式是指提供一个建立一系列相关或相互依赖对象的接口,无需指定它们具体的类。 客户端(应用层)不依赖于产品实例如何被建立、实现等细节,强调的是一系列相关的产品对象(属于同一产品族)一块儿使用建立对象须要大量重复的代码。须要提供一个产品库的类,全部的产品以一样的接口出现,从而使客户端不依赖于具体实现。

咱们仍是以课程的例子为例,如今是新冠状病毒疫情的时期,高中学生只能在家利用互联网进行在线直播上课,每一个课程不只要提供课程的录播视频,并且还要提供老师的课堂笔记。至关于如今的业务变动为同一个课程不单纯是课程信息,同时要包括录播视频、课堂笔记等才是一个完整的课程。在产品等级中增长两个产品接口Ivideo录播视频和INote课堂笔记。

Ivideo接口:

public interface IVideo {

    void record();
}

INote接口:

public interface INote {

    void edit();
}

建立抽象工厂类CourseFactory类:

public interface CourseFactory {

    IVideo createVideo();

    INote createNote();
}

建立语文课产品族,语文课视频的ChineseVideo类:

public class ChineseVideo implements IVideo {
    @Override
    public void record() {
        System.out.println("录制语文课视频!");
    }
}

建立语文课课堂笔记的ChineseNote类:

public class ChineseNote implements INote {
    @Override
    public void edit() {
        System.out.println("编写语文课笔记!");
    }
}

建立语文课产品族的具体工厂类ChineseCourseFactory:

public class ChineseCourseFactory implements CourseFactory {
    @Override
    public IVideo createVideo() {
        return new ChineseVideo();
    }

    @Override
    public INote createNote() {
        return new ChineseNote();
    }
}

而后再建立数学产品,Math视频MathVideo类:

public class MathVideo implements IVideo {
    @Override
    public void record() {
        System.out.println("录制数学课视频!");
    }
}

建立数学课堂笔记的MathNote类:

public class MathNote implements INote {
    @Override
    public void edit() {
        System.out.println("编写数学课笔记!");
    }
}

建立数学课产品族的具体工厂类MathCourseFactory:

public class MathCourseFactory implements CourseFactory {
    @Override
    public IVideo createVideo() {
        return new MathVideo();
    }

    @Override
    public INote createNote() {
        return new MathNote();
    }

    public static void main(String[] args) {
        MathCourseFactory mathCourseFactory = new MathCourseFactory();
        mathCourseFactory.createNote().edit();
        mathCourseFactory.createVideo().record();
    }
}

上面的案例代码完整的描述了两个产品族语文课程和数学课程,也描述了两个产品等级的视频和课堂笔记。抽象工厂很是完美清晰地描述了这一层复杂的关系。若是咱们再升级扩展产品等级,将课堂做业也加入到课程中,咱们的代码须要从抽象工厂,到具体的工厂都要进行调整,很显然不符合开闭原则。因此抽象工厂也是有缺点的:

一、规定了全部可能被建立的产品集合,产品族中扩展新的产品困难,须要修改抽象工厂的接口。

二、增长了系统的抽象性和理解难度。

3、工厂模式总结

相关文章
相关标签/搜索