工厂模式根据抽象程度的不同分为三种:简单工厂模式(也叫静态工厂模式)、本文所讲述的工厂方法模式、以及抽象工厂模式。
1.优点
- 可以使代码结构清晰,有效地封装变化。使得调用者根本无需关心产品的实例化过程,只需依赖工厂即可得到自己想要的产品。
- 对调用者屏蔽具体的产品类。让调用者只关心产品的接口,这样即使我们对产品类变更了具体的实现,对调用者来说没有任何影响。
2.设计四要素(重要)
-
工厂接口:该模式的核心,负责与调用者直接进行交互来提供产品。实际应用中也可使用抽象类代替,效果是一样的。
/** * 工厂接口 * 将工厂的公有行为抽象出来 * * @author suvue * @date 2020/1/10 */ public interface IFactory { /** * 抽象一些工厂行为 */ IProduct createProduct(); }
-
工厂实现:决定实例化产品的各个细节。理论上,需要有多少种产品,就需要有多少个具体的工厂实现。
/** * 默认实现工厂类 * 具体使用中可以根据业务需求,实现不同的工厂类 * * @author suvue * @date 2020/1/10 */ public class DefaultFactory implements IFactory { @Override public IProduct createProduct() { return new DefaultProduct(); } }
-
产品接口:定义产品设计的规范。所以产品实现必须遵循,产品接口定义的优劣直接决定了调用者代码的稳定性。同样地,实际应用中也可以使用抽象类代替,但是最好不要违反里氏替换原则,说直白一点,意思是不要重写抽象类中已经定义好的方法。
/** * 产品接口 * 将产品的公有行为抽象出来 * * @author suvue * @date 2020/1/10 */ public interface IProduct { //例如每个产品,都有制造方式,我们就可以抽取到接口,让实现类自己去实现它 void produceMethod(); }
-
产品实现:实现产品接口的具体类,决定了产品在客户端中的具体行为。
/** * 产品的默认实现 * * @author suvue * @date 2020/1/10 */ public class DefaultProduct implements IProduct{ @Override public void produceMethod() { //此处仅用打印控制台代替具体的实现过程 System.out.println("我是用传统生产方法造出来的默认产品"); } }
-
模拟客户端调用者
/** * 客户端调用者 * * @author suvue * @date 2020/1/10 */ public class Client { public static void main(String[] args) { IFactory factory = new DefaultFactory(); IProduct product = factory.createProduct(); product.produceMethod(); } }
3.经典案例
工厂方法模式最经典的莫过于组装汽车了,假设有一个场景:汽车由发动机,车轮,底盘组成,我们需要组装一批汽车,交给我们的经销商,不用工厂方法模式的话,代码是这样的:
/**
* 发动机
*/
class Engine{
}
/**
* 轮胎
*/
class Tires{
}
/**
* 底盘
*/
class Chassis{
}
class Car{
private Engine engine;
private Tires tires;
private Chassis chassis;
public Car(Engine engine, Tires tires, Chassis chassis) {
this.engine = engine;
this.tires = tires;
this.chassis = chassis;
}
public void success(){
System.out.println("一辆新的小汽车组装完毕!");
}
}
/**
* 不使用工厂方法模式造小汽车
*
* @author suvue
* @date 2020/1/11
*/
public class CommonMethod {
public static void main(String[] args){
Engine engine = new Engine();
Tires tires = new Tires();
Chassis chassis = new Chassis();
Car car = new Car(engine, tires, chassis);
car.success();
}
}
同样的业务,我们看看工厂方法模式的实现代码是怎样的。
interface IFactory {
ICar create();
}
interface ICar {
void success();
}
class BigTruck implements ICar {
private Engine engine;
private Tires tires;
private Chassis chassis;
public BigTruck(Engine engine, Tires tires, Chassis chassis) {
this.engine = engine;
this.tires = tires;
this.chassis = chassis;
}
@Override
public void success() {
System.out.println("一辆崭新的大货车组装完毕");
}
}
class BigTruckFactory implements IFactory {
@Override
public ICar create() {
Engine engine = new Engine();
Tires tires = new Tires();
Chassis chassis = new Chassis();
ICar bigTruck = new BigTruck(engine, tires, chassis);
return bigTruck;
}
}
public class Client {
public static void main(String[] args) {
IFactory factory = new BigTruckFactory();
ICar bigTruck = factory.create();
bigTruck.success();
}
}
上面代码我们模拟了用工厂方法模式组装大货车的过程,最明显的差别就在于,我们的调用方(可以理解为经销商)只调用工厂类就可以拿到自己想要的汽车,而不用关心你这个汽车用的什么牌子的发动机,哪种型号的车轮胎等等。这一点完全遵循的迪米特法则:调用方(经销商)只跟直接朋友(汽车工厂)打交道,不跟陌生人(发动机、轮胎等)交谈。
4.在spring框架中的应用(源码解析)
FatoryBean接口相比于我们上面的IFactory,还是有很大区别的。
IFactory的设计是将所有工厂都有的功能进行抽象,例如每个工厂都可以生产自己的产品、销售产品、销毁产品等。
FactoryBean的设计,是将建造所有工厂的功能进行了抽象,例如 建造一个工厂,都必须有雇佣工程师画设计图、建造团队打地基等这一系列过程。
package org.springframework.beans.factory;
import org.springframework.lang.Nullable;
/**
* If a bean implements this
* interface, it is used as a factory for an object to expose, not directly as a
* bean instance that will be exposed itself.
* 如果一个类实现了这个接口,它将被用作对象工厂暴露给外界,不能作为bean的实例来使用了。
*
* <p>This interface is heavily used within the framework itself, for example for
* the AOP {@link org.springframework.aop.framework.ProxyFactoryBean} or the
* {@link org.springframework.jndi.JndiObjectFactoryBean}. It can be used for
* custom components as well; however, this is only common for infrastructure code.
*
* 上面英文的大致意思就是:我Spring的内部,如AOP等方面都已经用到了FactoryBean了,而且用的还特别好。
* 但是现在应用的方向只限于我的基础组件,以后的拓展空间是大大的!(我觉得带着点吹牛的意思,但是确实很强大)
*/
public interface FactoryBean<T> {
/**
* 这个属性的作用,就是在所有的工厂bean被属性加载器加载后,能标识出类型来
* @since 5.2
*/
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
/**
* Return an instance (possibly shared or independent) of the object
* managed by this factory.
* 获取被这个工厂管理的对象实例,可能是单例,也可能是多例
*/
@Nullable
T getObject() throws Exception;
/**
* Return the type of object that this FactoryBean creates
* 看英文很好理解,就是返回这个工厂类创建出来的产品的类型
*/
@Nullable
Class<?> getObjectType();
/**
* 这个工厂类是否是一个单例
*/
default boolean isSingleton() {
return true;
}
}
FactoryBean的实现类由非常多,除了自己内部实现外,第三方有名的框架如mybatis等都实现了这个类,来达到与Spring框架整合的目的。
那么下面我们就看看SqlSessionFactoryBean的源码吧。
public class SqlSessionFactoryBean
implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
//
private SqlSessionFactory sqlSessionFactory;
//首先实现了我们Spring中的FactoryBean,那么必须实现它的getObject()方法
//果不其然
@Override
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
return this.sqlSessionFactory;
}
}
简单分析一下吧,SqlSessionFactoryBean是一个正常的类,并不是接口或者抽象类,看它重写的getObject()方法,最终返回了一个SqlSessionFactory,那么这又是何方神圣呢,看名字好像是一个工厂了
package org.apache.ibatis.session;
import java.sql.Connection;
/**
* 用来创建一个连接或者数据源的SqlSession
*
* @author Clinton Begin
*/
public interface SqlSessionFactory {
SqlSession openSession();
SqlSession openSession(boolean autoCommit);
//xxx省略下面的代码
}
看SqlSessionFactory的源码,这正与我们的猜测不谋而合,它不但是一个工厂,还是一个接口!这不正是FactoryBean这个顶级工厂所造出来的产品么,只不过这个产品很特殊,它也是一个工厂。感觉像是spring又往上进行了一层抽象。
SqlSessionFactory的实现类如DefaultSqlSessionFactory,SqlSessionManager等都是具体的产品实现了。
这时候再来看一下这段话
IFactory的设计是将所有工厂都有的功能进行抽象,例如每个工厂都可以生产自己的产品、销售产品、销毁产品等。
FactoryBean的设计,是将建造所有工厂的功能进行了抽象,例如 建造一个工厂,都必须有雇佣工程师画设计图、建造团队打地基等这一系列过程。
是不是有点儿理解了呢?
要是还不理解呢,奉上一张图吧!
小结一下吧(感觉自己好啰嗦)
工厂方法模式应用,无外乎这四个要素:工厂接口、工厂实现、产品接口、产品实现
理解不了呢,那就结合spring的源码和本文,多多理解理解!重在实践与思考。多敲代码。
好了本文就写到这里了,有问题的朋友记得给我流言哦!