Внедрить зависимость в карту служебных классов

У меня есть класс служебных / констант, который содержит Map<String, Authorizer>, где Authorizer - это интерфейс с несколькими различными реализациями. Я использую карту в разных потоках, чтобы получить объект, содержащий некоторое String (название подхода к авторизации), который затем сопоставляется с конкретным Authorizer, который затем завершает некоторую авторизацию.

Я использую Guice для подключения Authorizer классов, но этот подход не позволяет мне сделать служебный класс (который содержит Map) настоящим служебным классом с пустым частным конструктором. У меня есть забавный обходной путь, который нравится как Guice, так и Checkstyle, но я хочу знать, есть ли способ лучше.

Мой класс полезности:

public final class Constants {
  @Inject
  private Constants() {}

  public static final String AUTH_METHOD_ONE = "Auth1";
  public static final String AUTH_METHOD_TWO = "Auth2";

  @Singleton
  @Inject
  @Getter // For Checkstyle, as AUTH_METHODS isn't a true static final constant
  private static Map<String, Authorizer> authMethods;
}

Мой модуль констант:

public class ConstantsModule extends AbstractModule {
  @Override
  public void configure() {
    requestStaticInjection(Constants.class);
    final MapBinder<String, Authorizer> mapBinder = MapBinder.newMapBinder(binder(), String.class, Authenticator.class);
    mapBinder.addBinding(AUTH_METHOD_ONE).to(MethodOneAuthorizer.class);
    mapBinder.addBinding(AUTH_METHOD_TWO).to(MethodTwoAuthorizer.class);
  }
}

И пример использования:

public class AuthorizationOrchestrator {
  private static Authorizer getAuthorizer(final AuthorizationState state) {
    return state.getMethods().stream()
      .map(AuthorizationApproach::getAuthorizationApproachName)
      .filter(Constants.getAuthMethods().keySet()::contains)
      .findFirst()
      .map(Constants.getAuthMethods()::get)
      .orElse(null);
  }
}

Этот подход также требует использования PowerMock в модульных тестах. Есть ли лучший способ:

  • Сопоставить имена подходов к авторизации классу Authorizer, сохранив сопоставление в одном месте?
  • Использовать класс Constants как истинный служебный класс с public static final Map<String, Authorizer> AUTH_METHODS, но при этом иметь возможность вводить авторизаторы в Map?

person StephenMcMurtry    schedule 08.07.2019    source источник


Ответы (1)


На самом деле нет смысла вводить что-то, что должно быть константой.

Используя статический Constants класс, вы вводите невнедренную зависимость в код, который использует ваш Constants класс, что противоречит всей сути DI, не говоря уже о том, что статические зависимости труднее имитировать в ваших тестах.

Есть несколько других способов сделать это, которые могут вам помочь. В принципе, это одно и то же решение (вставьте Authorizer во что-то, что не является статичным), но у них есть несколько разных нюансов.

  • Используйте композицию, чтобы не использовать служебный класс. Создайте Authorizer реализацию, состоящую из нескольких Authorizer. Составной Authorizer мог бы внутренне делегировать правильную «настоящую» реализацию. Затем вы можете просто ввести одну Authorizer в любом месте, где она нужна. Это может быть невозможно в зависимости от того, как определяется Authorizer контракт.

  • Сохраните логику без какого-либо состояния в служебном классе. Измените подпись статического метода наgetAuthorizer(AuthorizationState state, Map<String, Authorizer> availableAuthorizersByName). Затем вставьте карту AUTH_METHODS непосредственно в классы, которые будут вызывать ваш статический метод getAuthorizer, и передайте карту в качестве одного из аргументов метода.

  • Сделайте getAuthorizer() не статическим. И вставьте карту прямо в экземплярAuthorizationOrchestrator.

person Matthew Pope    schedule 09.07.2019