源码中的设计模式--工厂模式
本文要解决的几个问题,
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的方式,让对象的创建集中在一处,对设计进行了解耦,让使用者不必关心创建对象的细节,只需使用接口;
今天的分享就到这里了,小伙伴们回想下文章开头提的几个问题都有答案了么,没有的话多读几遍哦。