23种设计模式(2):工厂方法模式

​ 工厂模式根据抽象程度的不同分为三种:简单工厂模式(也叫静态工厂模式)、本文所讲述的工厂方法模式、以及抽象工厂模式。


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的源码和本文,多多理解理解!重在实践与思考。多敲代码。

好了本文就写到这里了,有问题的朋友记得给我流言哦!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值