Как создать абстрактную фабрику для многих конкретных типов - PullRequest
0 голосов
/ 02 мая 2019

Предположим, у нас есть интерфейс с именем IAction и множество классов (более 20), реализующих этот интерфейс: ConreteAction1, ConreteAction2 и т. Д. Все эти классы имеют конструкторы с параметрами. Также все эти классы, а также их зависимости, зарегистрированы в модуле 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 создать такую ​​фабричную реализацию более элегантным и расширяемым способом?

1 Ответ

1 голос
/ 02 мая 2019

Для этого вы можете использовать Мультибиндинги .

@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 для агрегирования набора конфигураций, а затем использовать привязку карты для получения нужного поставщика.

...