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

本文要解决的几个问题,

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

 

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

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

发表评论

相关文章