I have a utility/constants class that contains a Map<String, Authorizer> where Authorizer is an interface with a few different implementations. I use the map across different streams to take in an object that contains some String (the name of an Authorization approach) that then maps to a specific Authorizer, which then completes some authorization.
I am using Guice to wire the Authorizer classes, but this approach prevents me from making the utility class (which contains the Map) a true utility class with an empty private constructor. I have a funky-looking workaround that pleases both Guice and Checkstyle, but I want to know if there is a better way.
My Utility Class:
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;
}
My Constants Module:
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);
}
}
And an example usage:
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);
}
}
This approach also requires some PowerMock usage in the Unit Tests. Is there a better way to:
- Map the names of the authorization approaches to an
Authorizerclass while keeping the mapping in one place? - Use the
Constantsclass as a true utility class withpublic static final Map<String, Authorizer> AUTH_METHODSwhile still being able to inject the authorizers into theMap?
It doesn’t really make sense to inject something that is supposed to be a constant.
By using a static
Constantsclass, you are introducing a non-injected dependency into the code that uses yourConstantsclass, which goes against the whole point of DI—not to mention that static dependencies are more difficult to mock in your tests.There’s a few other ways to do this that might work for you. In principle, they’re all the same solution (inject the
Authorizers into something that is not static), but they have a few different nuances.Use composition to avoid needing a utility class. Make an
Authorizerimplementation that is composed of multipleAuthorizers. The compositeAuthorizercould internally delegate to the correct “real” implementation. Then you can just inject a singleAuthorizeranywhere that needs one. This on might not be possible depending on how theAuthorizercontract is defined.Keep the logic without any state in the utility class. Change the signature of the static method to
getAuthorizer(AuthorizationState state, Map<String, Authorizer> availableAuthorizersByName). Then, inject theAUTH_METHODSmap directly into the classes that would be calling your staticgetAuthorizermethod, and pass the map as one of the arguments to the method.Make
getAuthorizer()not static. And inject the map directly into an instance ofAuthorizationOrchestrator.