23种设计模式(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();
     }
    }

典型应用

工厂方法模式最经典的莫过于组装汽车了,假设有一个场景:汽车由发动机,车轮,底盘组成,我们需要组装一批汽车,交给我们的经销商,不用工厂方法模式的话,代码是这样的:

/**
 * 发动机
 */
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();
 }
}

上面代码我们模拟了用工厂方法模式组装大货车的过程,最明显的差别就在于,我们的调用方(可以理解为经销商)只调用工厂类就可以拿到自己想要的汽车,而不用关心你这个汽车用的什么牌子的发动机,哪种型号的车轮胎等等。这一点完全遵循的迪米特法则:调用方(经销商)只跟直接朋友(汽车工厂)打交道,不跟陌生人(发动机、轮胎等)交谈。

在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的设计,是将建造所有工厂的功能进行了抽象,例如 建造一个工厂,都必须有雇佣工程师画设计图、建造团队打地基等这一系列过程。

是不是有点儿理解了呢?

要是还不理解呢,奉上一张图吧!

工厂方法模式.png

小结一下吧(感觉自己好啰嗦)

工厂方法模式应用,无外乎这四个要素:工厂接口、工厂实现、产品接口、产品实现

理解不了呢,那就结合spring的源码和本文,多多理解理解!重在实践与思考。多敲代码。

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

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容