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

作者: RunAlgorithm | 来源:发表于2019-05-13 22:48 被阅读0次

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 默认提供的,也可以自定义实现。
  • 运行时 Dubbo 会通过 ExtensionLoader 读取配置文件,动态载入所需要的实现类。

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);
}

具体产品:

  • DubboRegistry,Dubbo 自己实现的注册中心。
  • MulticastRegistry,无中心节点的注册中心。
  • RedisRegistry,Redis 实现的注册中心。
  • ZookeeperRegistry,基于 Zookeeper 的注册中心。

具体工厂:

  • DubboRegistryFactory,生产 DubboRegistry。
  • MulticastRegistryFactory,生产 MulticastRegistry。
  • RedisRegistryFactory,生产 RedisRegistry。
  • ZookeeperRegistryFactory,生产 ZookeeperRegistry。

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

Dubbo Registryfactory

3.3 其他

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

  • Dubbo 的 CacheFactory,缓存工厂。
  • Dubbo 的 MonitorFactory,监视器工厂。

4. 特点

4.1. 优势

  • 创建和使用分离:使用者无需关系复杂的创建细节。
  • 工厂决定产品:工厂可以自主选择希望返回的产品。
  • 易于修改:修改产品和工厂不影响到其他的工厂。
  • 易于扩展:新产品直接创建新的工厂类,扩展性好。

4.2. 缺点

  • 类膨胀:如果产品数量多,对应的工厂也多,造成类的膨胀。

    注意控制类的数量,如果产品数量繁多,想办法控制一下数量,或者直接使用简单工厂模式。

  • 难理解:增加了一层抽象,会对理解带来难度。在进行反射等操作的复杂度会上升。

相关文章

网友评论

    本文标题:创建和使用分离——工厂方法模式

    本文链接:https://www.haomeiwen.com/subject/timdaqtx.html