6

源码中的设计模式--工厂模式 - 北漂程序员

 1 year ago
source link: https://www.cnblogs.com/teach/p/16245248.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

本文要解决的几个问题,

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK