首页 > 解决方案 > 如何为许多具体类型创建抽象工厂

问题描述

假设我们有一个名为 的接口IAction,并且有很多类(超过 20 个)实现了这个接口:ConreteAction1ConreteAction2等。所有这些类都有带参数的构造函数。此外,所有这些类以及它们的依赖项都在 Dagger 模块中注册(作为瞬态对象或单例)。我们的任务是实现(或自动生成)抽象工厂实现接口,类似于:

public interface IActionFactory{
    IAction createByClass(Class clazz); // create action by type
    // or
    IAction createByName(String name); // create action by custom name
}

我知道如何使用 Dagger 对Provider<T>注释的支持来实现这个工厂:

public class ActionFactory implements IActionFactory{

    @Inject Provider<ConcreteAction1> concreteAction1Provider;
    @Inject Provider<ConcreteAction2> concreteAction2Provider;
    (...)

    @Override
    public IAction createByClass(Class clazz){

        if(ConcreteAction1.class.equals(clazz)){
            return concreteAction1Provider.get()
        }

        if(ConcreteAction2.class.equals(clazz)){
            return concreteAction2Provider.get()
        }
        (...)
    }

    @Override
    public IAction createByName(String name){

        if(name.equals("name_1")){
            return concreteAction1Provider.get()
        }

        if(name.equals("name_2")){
            return concreteAction2Provider.get()
        }
        (...)
    }
}

不幸的是,这种方法涉及大量样板代码(我有 20 多个具体类),并且每次创建另一个IAction接口实现时都必须修改上面的工厂(违反 Open-Close 原则)。

Dagger 中是否有任何其他方式可以以更优雅和可扩展的方式创建这样的工厂实现?

标签: javadagger-2dagger

解决方案


您可以为此使用多重绑定

@Module public interface ActionModule {
  @Binds @IntoMap @ClassKey(ConcreteAction1.class)
  IAction bindActionClass1(ConcreteAction1 action1);

  @Binds @IntoMap @ClassKey(ConcreteAction2.class)
  IAction bindActionClass2(ConcreteAction2 action2);

  // ...

  @Binds @IntoMap @StringKey("name_1")
  IAction bindActionName1(ConcreteAction1 action1);

  @Binds @IntoMap @StringKey("name_2")
  IAction bindActionName2(ConcreteAction2 action2);

  // ...
}

public class ActionFactory implements IActionFactory{
  @Inject Map<Class<?>, Provider<IAction>> classActionFactory;
  @Inject Map<String, Provider<IAction>> stringActionFactory;

  @Override
  public IAction createByClass(Class<?> clazz) {
    // TODO: handle missing entries gracefully
    return classActionFactory.get(clazz).get();
  }

  @Override
  public IAction createByName(String name) {
    return stringActionFactory.get(name).get();
  }
}

在这一点上,主要的困难是你要绑定每个动作两次,每张地图一次。如果这是一个问题,您可以使用 Set 绑定来聚合一组配置,然后使用映射绑定来检索正确的 Provider。


推荐阅读