• 源码中的设计模式工厂模式


    本文要解决的几个问题,

    1、什么是工厂模式

    2、工厂模式分为哪几种

    3、工厂模式的优点

    4、源码中有哪些地方使用了工厂模式

    一、模式入场

      看到标题想必小伙伴很好理解,所谓“工厂模式”从字面上就可以理解,比亚迪工厂的作用不就是生产比亚迪汽车的,在java中的工厂模式就是用来产生实例的。现在我有这样一个类,

    Car.java

    package com.my.factory;
    /**
     * 汽车类
     * @date 2022/5/8 11:15
     */
    public class Car {
        //汽车唯一编码
        private String code;
        //汽车型号
        private String model;
    }

    现在要使用Car生成一个具体的实例,那么平时的做法肯定是new了,如下

    Car car=new Car();

      现在有这样一个场景,在很多地方都要使用Car的实例,那么每一次使用都new一次,都是重复的代码从代码规范层面上就不好看,而且这也不符合设计原则。我们是不是可以专门有一个类来生成Car的实例,在需要Car实例的地方只需要调用该类的方法即可,为此有了下面的工具类,

    package com.my.factory.simple;
    
    import com.my.factory.Car;
    /**
     * 简单Car工厂
     * @date 2022/5/8 11:22
     */
    public class SimpleCarFactory {
        public Car productCar(){
            return new Car();
        }
    }

    现在想生成Car,就不再使用new了,我调用工具类就可以了,

    SimpleCarFactory carFactory=new SimpleCarFactory();
    Car car=carFactory.productCar();

      有了SimpleCarFactory工具类就好多了,调用其productCar()方法就给我返回Car实例了,摆脱了new的方式,再也不用被隔壁的小姐姐嘲笑只会new了。

      上面提到的SimpleCarFactory工具类,其实就是工厂模式的一种实现,给它起个名字叫“简单工厂”,《Head first 设计模式》一书中给出的释义是

    简单工厂其实不是一个设计模式,反而比较像是一种编程习惯。但由于经常被使用,所以我们给它一个“Head First Pattern荣誉奖”。

    上面说了简单工厂更像是一种编程习惯,不过这里我也把它看作是工厂模式的一种实现方式。

      在使用了一段SimpleCarFactory类后,有小伙伴提出每次都需要new一个SimpleCarFactory的实例才能调用其productCar()方法,既然是工具类,把productCar()方法声明为static不是更好,的确在设计理念上又进了一步,

    package com.my.factory.simple;
    
    import com.my.factory.Car;
    /**
     * 简单Car工厂
     * @date 2022/5/8 11:22
     */
    public class SimpleCarFactory {
        public static Car productCar(){
            return new Car();
        }
    }

    再使用的时候只需这样用就好了,

    Car car=SimpleCarFactory.productCar();

    上面的这种方式给它起个名字叫“简单静态工厂”,不知不觉中又会了另一种实现,可以和隔壁的小姐姐去炫耀一番了。

    “简单工厂”和“简单静态工厂”都是有一个专门的类来生成实例,区别是后者的方法是静态的。

    二、深入工厂模式

      上面说到的无论是“简单工厂”还是“简单静态工厂”其实本质上都是一样的,都是在一个类中生成类的实例。

    2.1、工厂方法模式

      还是拿上面的汽车工厂的例子来举例,有这样的一个场景,由于汽车订单激增,一个工厂已经无法完成订单了,必须要新建一个工厂来生产汽车,而且每个工厂可以生产不同类型的汽车,现在要对上面的SimpleCarFactory和Car进行改造。假设有两个工厂分别是ConcreteCarFactoryOne和ConcreteCarFactoryTwo,生产的汽车有Biyadiar、XiandaiCar等,现在的类图如下,

    ConcreteCarFactoryOne.java

    package com.my.factory.concrete;
    
    import com.my.factory.BiyadiCar;
    import com.my.factory.ConcreteCar;
    /**
     * 生产比亚迪汽车的工厂
     * @date 2022/5/8 16:17
     */
    public class ConcreteCarFactoryOne extends ConcreteCarFactory {
    
        @Override public ConcreteCar productCar() {
            car = new BiyadiCar();
            car.setCode("1");
            car.setModel("byd");
            return car;
        }
    }

    ConcreteCarFactoryTwo.java

    package com.my.factory.concrete;
    
    import com.my.factory.ConcreteCar;
    import com.my.factory.XiandaiCar;
    
    /**
     * 生产现代汽车的工厂
     * @date 2022/5/8 16:42
     */
    public class ConcreteCarFactoryTwo extends ConcreteCarFactory {
        @Override public ConcreteCar productCar() {
            car = new XiandaiCar();
            car.setCode("2");
            car.setModel("xiandai");
            return car;
        }
    }

    好了,两个工厂类已经完成了,分别生成比亚迪汽车和现代汽车,细心的小伙伴发现一个问题,这两个工厂都有productCar()方法,可不可以抽取出来,答案是必须抽出来,我这里抽取为抽象类,让ConcreteCarFactoryOne和ConcreteCarFactoryTwo分别进行实现,

    ConcreteCarFactory.java

    package com.my.factory.concrete;
    
    import com.my.factory.ConcreteCar;
    
    /**
     * 抽象工厂
     * @date 2022/5/8 16:46
     */
    public abstract class ConcreteCarFactory {
        //要生产的汽车,由子类进行初始化
        protected ConcreteCar car;
    
        //由子类实现该方法
        protected abstract ConcreteCar productCar();
    
        //给汽车喷漆
        public void sprayPaint() {
            System.out.println("给--" + car + "--喷漆");
        }
    }

    ConcreteCarFactoryOne和ConcreteCarFactoryTwo的修改不再贴出,聪明的你肯定知道怎么改。

    另外,对于汽车类这里也抽出了一个公共类,BiyadiCar和XiandaiCai会继承改类,

    ConcreteCar.java

    package com.my.factory;
    
    /**
     * 汽车接口
     * @date 2022/5/8 16:21
     */
    public class ConcreteCar {
        protected String code;
        protected String model;
    
        //省略get/set方法
    }

    BiyadiCar.java

    package com.my.factory;
    
    /**
     * 比亚迪汽车
     * @date 2022/5/8 16:18
     */
    public class BiyadiCar extends ConcreteCar{
        @Override public String toString() {
            return "BiyadiCar{" + "code='" + code + '\'' + ", model='" + model + '\'' + '}';
        }
    }

    XiandaiCar这里就不再给出类似的代码。

    下面看下测试类,

    TestConcreteCarFactory.java

    package com.my.factory.concrete;
    
    import com.my.factory.ConcreteCar;
    
    /**
     * 测试类
     * @date 2022/5/8 16:57
     */
    public class TestConcreteCarFactory {
        public static void main(String[] args) {
            ConcreteCarFactory biyadaCarFactory = new ConcreteCarFactoryOne();
            ConcreteCar biyadiCar = biyadaCarFactory.productCar();
            biyadaCarFactory.sprayPaint();
    
            ConcreteCarFactory xiandaiCarFactory = new ConcreteCarFactoryTwo();
            ConcreteCar xiandaiCar = xiandaiCarFactory.productCar();
            xiandaiCarFactory.sprayPaint();
        }
    }

    测试结果可以看到创建了两个不同的汽车

    给--BiyadiCar{code='1', model='byd'}--喷漆
    给--XiandaiCar{code='2', model='xiandai'}--喷漆

    其UML图如下

    上面便是工厂模式的工厂方法模式的实现,《Head frist 设计模式》一书中对此模式给出的释义是

    工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。

    从上面的UML图中可以很好的理解上面的话,”一个创建对象的接口“这里指的不但但是interface,在本例中定义的则是一个抽象方法。同时类的实例化是在具体的子类中实现的,到底要实例化什么样的类则要根据相应的工厂来决定。

    2.2、抽象工厂模式

      前面我们创建了两个工厂,都是用来生产汽车的,唯一的区别是生产的汽车是不一样的。现在有这样的场景,一个工厂仅生产汽车太浪费资源了,现在新能源是发展的趋势,每个工厂再上一条生产线生产电池吧,为此,上面的工厂需要提供一个接口来生产汽车和电池,这次我们不使用抽象类了,使用接口,

    package com.my.factory.concrete.factory;
    
    import com.my.factory.Battery;
    import com.my.factory.ConcreteCar;
    
    /**
     * 生产汽车和电池的接口
     * @date 2022/5/8 18:46
     */
    public interface ConcreteFactory {
        //生产汽车
        ConcreteCar productCar();
        //生产电池
        Battery productBattery();
    }

    上面的接口中有两个方法一个生产汽车一个生产电池,相应的实现类也必须实现这两个方法。这种一个接口中包含多个生成实例的模式称为”抽象工厂模式“,《Head first 设计模式》一书中给出的释义是,

    抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。

    该释义说的很清楚,注意”家族“二字,说的就是包含一个以上的方法,后续”不需要明确指定具体类“,则是要在具体使用的时候选择合适的实现即可。

    三、追寻源码

    3.1、mybatis中的SqlSessionFactory

    在mybatis中的SqlSessionFactory,便是工厂模式的一种体现,更确切的说是抽象工厂模式,

    在SqlSessionFactory中有openSession方法,且该方法有多个重载,并且还有一个getConfiguration方法,下面看起具体的实现类,

    共有两个实现分别是DefaultSqlSessionFactory和SqlSessionManager,看下openSession()方法在DefaultSqlSesssionFactory中的实现

    最终返回的是一个SqlSession的实现DefaultSqlSession,和上面的工厂模式的UML神奇的类似。

    3.2、Spring中的BeanFactory

    在spring中有BeanFactory接口,

    可以看到该接口中有getBean、getType、getAliases方法,这些都可以作为抽象工厂的证据,小伙伴们说了还有isSingleton、containsBean方法,这些我们说不能作为工厂模式的证据,因为,工厂模式的定义是要生成实例,也就是说工厂模式要创建并返回一个类的实例,而isSingleton、containsBean没有创建实例。看下该接口的实现

    在其实现中有AbstractBeanFactory实现,其getBean方法的一个实现如下,

      由于spring实现的太复杂,这里不再详述。有小伙伴会问没看到在实现中有new啊,的确没有,在spring的实现中是通过反射的方式创建的,我们说生成类的实例不仅只有new的方式,工厂模式的关键在于生成类的实例,而不在于如何生成。

     

    四、总结

      总结下工厂模式的要点,

      1、工厂模式分为简单工厂、简单静态工厂、工厂方法、抽象工厂四种不同的实现;

      2、工厂模式的使用原则在于如何创建类的实例,可以使用new,也可以使用反射、反序列化等方式;

      3、工厂模式摆脱了使用者传统的new的方式,让对象的创建集中在一处,对设计进行了解耦,让使用者不必关心创建对象的细节,只需使用接口;

      今天的分享就到这里了,小伙伴们回想下文章开头提的几个问题都有答案了么,没有的话多读几遍哦。

  • 相关阅读:
    并发编程知识点剖析
    JavaScript 实现留言框
    JavaScript 实现简单的 弹出框关闭框
    网络编程知识点剖析
    css清除浮动的方法
    css盒模型
    CSS的继承性和层叠性
    转载《ionic 热更新 cordova-hot-code-push》
    转《js闭包与内存泄漏》
    前端存储loaclForage
  • 原文地址:https://www.cnblogs.com/teach/p/16245248.html
Copyright © 2020-2023  润新知