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

本文要解决的几个问题,

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的方式,让对象的创建集中在一处,对设计进行了解耦,让使用者不必关心创建对象的细节,只需使用接口;

 

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

《源码中的设计模式--工厂模式》

    原文作者:北漂程序员
    原文地址: https://www.cnblogs.com/teach/p/16245248.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞