Java架构算法设计模式和编程理论Java 杂谈

创建和使用分离——工厂方法模式

2019-05-13  本文已影响0人  RunAlgorithm

1. 定义

工厂方法模式(Factory Method Pattern):定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。工厂方法模式又简称为工厂模式(Factory Pattern),又可称作虚拟构造器模式(Virtual Constructor Pattern)或多态工厂模式(Polymorphic Factory Pattern)。工厂方法模式是一种类创建型模式。

和简单工厂模式一样也是创建和使用分离。

区别在于,简单工厂模式所有的产品都由一个工厂生产。这样的话导致产品和工厂耦合,新增和减少产品都要修改工厂类,不符合开闭原则。

工厂方法模式一个产品对应一个工厂,不同的产品提供不同的工厂。这样新产品只需要创建新的工厂,易于扩展,符合开闭原则。

2. 设计

工厂模式主要角色:

可以看到,这里和简单工厂的区别在于,不仅产品有一个抽象类,工厂也增加了一个抽象类。

类图如下:

工厂方法模式-类图

抽象产品:

public interface IProduct {

    void sayHello();
}

抽象工厂:

public interface IFactory {

    IProduct getProduct();
}

具体产品 A:

public class ProductA implements IProduct {
    public void sayHello() {
        System.out.println("A hello.");
    }
}

具体工厂 A,用来生产具体产品 A:

public class FactoryA implements IFactory {

    public IProduct getProduct() {
        return new ProductA();
    }
}

使用的地方,需要产品 A,使用工厂 A 来获取。需要产品 B,使用工厂 B 来获取:

public class TestFactoryMethod {

    public static void main(String[] args) {

        // 创建产品 A
        IFactory factory = new FactoryA();
        IProduct product = factory.getProduct();
        product.sayHello();

        // 创建产品 B
        factory = new FactoryB();
        product = factory.getProduct();
        product.sayHello();
    }
}

如果需要增加产品 C,不用修改其他工厂类,创建具体产品 C 和工厂 C 即可,很容易进行扩展。

3. 应用

上面提供的模型比较简单。

工厂模式往往应用于复杂对象的创建。在创建对象的同时,还可以进行对象的初始化、资源和环境的配置等。

工厂方法模式的工厂,还可以使用配置文件来声明。

3.1. Spring 的 FactoryBean

Spring 的 FactoryBean 是工厂模式的典型应用。

定义如下:

public interface FactoryBean<T> {

    ...
    
    T getObject() throws Exception;

    ...
}

工厂方法为 getObject

对于复杂对象的构建,如果需要读取资源、配置,需要进行特殊的初始化等,可以实现对应的工厂。比如连接数据库、连接网络、创建对象池等等。

很多开源框架会使用 FactoryBean 来生成对应的实例。

Spring 使用工厂来生成对应的对象,这个解决了 Spring 对复杂对象的构建难题。

比如 MyBatis 框架的 SqlSessionFactoryBean,用来创建 SqlSessionFactory 对象。整个 SqlSessionFactory 的创建非常复杂,需要读取资源文件和配置。SqlSessionFactoryBean 使得这个复杂的创建流程对使用者透明。

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {

  ...
  
  @Override
  public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }

    return this.sqlSessionFactory;
  }
  
  @Override
  public void afterPropertiesSet() throws Exception {
    ...
    this.sqlSessionFactory = buildSqlSessionFactory();
  }

  ... 

  protected SqlSessionFactory buildSqlSessionFactory() throws IOException {

    Configuration configuration;

    ..

    return this.sqlSessionFactoryBuilder.build(configuration);
  }
  
  ...
}

3.2. Dubbo 的 RegistryFactory

Dubbo 有一个很强大的地方,就是可以交给使用者自由地选择组件。

比如:

等等

具体的方式是:

Dubbo 的 RegistryFactory 是注册中心的生产工厂,也是框架的一个 SPI 扩展组件。

创建注册中心时,需要将复杂的创建过程和使用进行隔离,然后又希望能够生产不同的产品,在添加新产品的时候,不想修改到旧代码。

这里应用了工厂方法模式,用一句话描述工厂方法,就是创建和使用分离+一种产品。

抽象产品 Registry。

// 注册中心 = 节点 + 服务
public interface Registry extends Node, RegistryService {
}

// 服务节点抽象
public interface Node {

    URL getUrl();

    boolean isAvailable();

    void destroy();
}

// 注册服务抽象
public interface RegistryService {

    void register(URL url);

    void unregister(URL url);

    void subscribe(URL url, NotifyListener listener);

    void unsubscribe(URL url, NotifyListener listener);

    List<URL> lookup(URL url);

}

抽象工厂 RegistryFactory:

@SPI("dubbo")
public interface RegistryFactory {

    @Adaptive({"protocol"})
    Registry getRegistry(URL url);
}

具体产品:

具体工厂:

通过工厂方法,屏蔽了注册中心复杂的构建细节。然后在配置文件指定具体的工厂,通过 SPI 机制加载,可以让 Dubbo 动态选择具体的注册中心,而不用改到源码,扩展性极强。

Dubbo Registryfactory

3.3 其他

还有很多框架使用到工厂方法,这里举例:

4. 特点

4.1. 优势

4.2. 缺点

上一篇下一篇

猜你喜欢

热点阅读