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