Should a class have a "has-a" relationship with a configuration class? - dependency-injection

I have seen some questions about wrapping a set of configuration properties in a class, but not how to actually use it. Given a configuration class (omitting getters and setters for brevity):
class ServiceConfiguration {
private String foo;
}
Should the service class use the configuration directly?
static class SomeServiceB {
private ServiceConfiguration configuration;
public SomeServiceB(ServiceConfiguration configuration) {
this.configuration = configuration;
}
public void printIt() {
System.out.println(configuration.getFoo());
}
}
Or should it only be concerned with the actual value of foo? For example:
static class SomeServiceA {
private String foo;
public SomeServiceA(String foo) {
this.foo = foo;
}
public void printIt() {
System.out.println(foo);
}
}
I would argue that the SomeServiceA is superior because of the lower coupling and separation of concerns.

Both solutions are correct. The important part is that a class only takes a dependency on configuration values that it actually uses.
This means that injecting an ApplicationConfiguration object that holds all configuration values, or allows access to all application configuration values is a bad idea, because this makes it unclear what configuration values the class actually uses and postponing the reading of configuration values can cause configuration mistakes to only pop up very late. The mantra is: fail-fast.
Injecting an object that encapsulates the values a class needs is typically more convenient than injecting all those values in separate constructor arguments. This is basically the Parameter Object refactoring.
Another advantage of wrapping configuration values (even if it is just one) into a Parameter Object, is that it allows a DI Container to auto-wire this class. This is especially useful when the class has dependencies other than configuration values. For instance:
class SomeServiceB {
public SomeServiceB(IDep1 d1, IDep2 d2, SomeServiceConfig config) {
}
}
Primitive values can't easily be auto-wired by a DI Container, because values like strings and integers are ambigious: you might have multiple instances of these values with total different meanings. For instance, you might have both a connection string and a file path. Both are strings.
It is important though to make the configuration objects immutable to communicate clearly that they can't be changed. You should typically construct them at application startup and register them as singleton.

Related

Inject vs Injectable vs Custom Providers

I’m having difficulty trying to figure out when to use what. I understand DI and have done this for years, but within NestJS it seems like there are things missing from the docs that would help me understand why some assigned properties go missing from an injected item.
We have a logger that gets injected via a custom provider.
From looking at the docs, they'll add #Inject during the constructor area, but if I'm injecting via a custom provider, why use #Inject?
Equally, why use #Injectable if we have custom providers handling all the injection setup for the module?
Where the question comes up is that filename is getting lost among other injected services where sometimes when the logger gets executed in one service, the filename is the same, while other times the service being different, it will retain the filename from the ctor name of a different service.
Anyway, I don't understand why to use one set up over another.
Update: I found we follow the LifeCycle on some Services, but I find the logger doesn't retain at the time of execution.
//custom-provider
export ClientProvider = {
provide: Client,
userFactory: (logger: Logger): Client => {
return new Client(logger);
},
inject: [Logger.getToken()]
};
//client
export class Client{
private readonly logger;
constructor(logger:Logger){
this.logger = logger;
if (this.logger) this.logger.fileName = this.constructor.name;
}
}
//logger
export class Logger {
static getToken(): string { return 'the-token-key'; }
private filename: string;
constructor(filename: string){
this.filename = filename;
}
}
//cat-service
export class CatService implements OnModuleInit {
private readonly logger;
constructor(#Inject(Logger.getToken()) private logger:Logger){
this.logger = logger;
}
onModuleInit() {
if (this.logger) this.logger.fileName = this.constructor.name;
}
}
So, high level, Nest naturally uses metadata reflected from Typescript to know what to bind and where. Normally, this metadata is things like Class Names that exist at runtime, which is why you cannot use interfaces.
#Injectable() tells Nest that it should look for the metadata provided in this class's constructor and find the correct providers. This is how Nest does most of the DI, as mentioned before.
#Inject() tells Nest, "Hey, I know this says it is a Logger class, but really inject the provider with the injection token I tell you", which is useful for things like using specific instances of, say, a Logger class.
With a custom provider, #Injectable() is not necessary if you use a factory, as you have shown, but it would be if you were to use a class instead, because factories return the instance of the value to be provided, whereas with useClass you are returning a class definition that Nest will need to instantiate.
The only reason I could see your filename being overwritten is that providers are singleton scope between modules, but if the same provider is in the providers array then it should be a new instance each time, so maybe something with that is acting a little wonky

Guice multiple implementations, parameterized constructor with dependencies

I'm struggling with a particular dependency injection problem and I just can't seem to figure it out. FYI: I'm new to guice, but I have experience with other DI frameworks - that's why I believe this shouldn't be to complicated to achieve.
What am I doing:
I'm working on Lagom multi module project and using Guice as DI.
What I would like to achieve:
Inject multiple named instances of some interface implementation (lets' call it publisher, since it will publishing messages to kafka topic) to my service.
This 'publisher' has injected some Lagom and Akka related services (ServiceLocator, ActorSystem, Materializer, etc..).
Now I would like to have two instances of such publisher and each will publish messages to different topic (So one publisher instance per topic).
How would I achieve that?
I have no problem with one instance or multiple instances for the same topic, but if I want to inject different topic name for each instance I have a problem.
So my publisher implementation constructor looks like that:
#Inject
public PublisherImpl(
#Named("topicName") String topic,
ServiceLocator serviceLocator,
ActorSystem actorSystem,
Materializer materializer,
ApplicationLifecycle applicationLifecycle) {
...
}
If I want to create one instance I would do it like this in my ServiceModule:
public class FeedListenerServiceModule extends AbstractModule implements ServiceGuiceSupport {
#Override
protected void configure() {
bindService(MyService.class, MyServiceImpl.class);
bindConstant().annotatedWith(Names.named("topicName")).to("topicOne");
bind(Publisher.class).annotatedWith(Names.named("publisherOne")).to(PublisherImpl.class);
}
}
How would I bind multiple publishers each for it's own topic?
I was playing around with implementing another private module:
public class PublisherModule extends PrivateModule {
private String publisherName;
private String topicName;
public PublisherModule(String publisherName, String topicName) {
this.publisherName = publisherName;
this.topicName = topicName;
}
#Override
protected void configure() {
bindConstant().annotatedWith(Names.named("topicName")).to(topicName);
bind(Publisher.class).annotatedWith(Names.named(publisherName)).to(PublisherImpl.class);
}
}
but this led me nowhere since you can't get injector in you module configuration method:
Injector injector = Guice.createInjector(this); // This will throw IllegalStateException : Re-entry is not allowed
injector.createChildInjector(
new PublisherModule("publisherOne", "topicOne"),
new PublisherModule("publisherTwo", "topicTwo"));
The only solution which is easy and it works is that I change my PublisherImpl to abstract, add him abstract 'getTopic()' method and add two more implementations with topic override.
But this solution is lame. Adding additional inheritance for code reuse is not exactly the best practice. Also I believe that Guice for sure must support such feature.
Any advises are welcome.
KR, Nejc
Don't create a new Injector within a configure method. Instead, install the new modules you create. No child injectors needed—as in the PrivateModule documentation, "Private modules are implemented using parent injectors", so there's a child injector involved anyway.
install(new PublisherModule("publisherOne", "topicOne"));
install(new PublisherModule("publisherTwo", "topicTwo"));
Your technique of using PrivateModule is the one I'd go with in this situation, particularly given the desire to make the bindings available through binding annotations as you have it, and particularly if the full set of topics is known at runtime. You could even put the call to install in a loop.
However, if you need an arbitrary number of implementations, you may want to create an injectable factory or provider to which you can pass a String set at runtime.
public class PublisherProvider {
// You can inject Provider<T> for all T bindings in Guice, automatically, which
// lets you configure in your Module whether or not instances are shared.
#Inject private final Provider<ServiceLocator> serviceLocatorProvider;
// ...
private final Map<String, Publisher> publisherMap = new HashMap<>();
public Publisher publisherFor(String topicName) {
if (publisherMap.containsKey(topicName)) {
return publisherMap.get(topicName);
} else {
PublisherImpl publisherImpl = new PublisherImpl(
topicName, serviceLocatorProvider.get(), actorSystemProvider.get(),
materializerProvider.get(), applicationLifecycleProvider.get());
publisherMap.put(topicName, publisherImpl);
return publisherImpl;
}
}
}
You'd probably want to make the above thread-safe; in addition, you can avoid the explicit constructor call by using assisted injection (FactoryModuleBuilder) or AutoFactory, which will automatically pass through explicit parameters like topicName while injecting DI providers like ServiceLocator (which hopefully has a specific purpose, because you may not need much service-locating within a DI framework anyway!).
(Side note: Don't forget to expose your annotated binding for your PrivateModule. If you don't find yourself injecting your topicName anywhere else, you might also consider using individual #Provides methods with the assisted injection or AutoFactory approach above, but if you expect each Publisher to need a differing object graph you might choose the PrivateModule approach anyway.)
Guice's approach to dependency injection is that the DI framework complements your instantiation logic, it doesn't replace it. Where it can, it will instantiate things for you, but it doesn't try to be too clever about it. It also doesn't confuse configuration (topic names) with dependency injection - it does one thing, DI, and does that one thing well. So you can't use it to configure things, the way you can with Spring for example.
So if you want to instantiate an object with two different parameters, then you instantiate that object with two different parameters - ie, you invoke new twice. This can be done by using provider methods, which are documented here:
https://github.com/google/guice/wiki/ProvidesMethods
In your case, it might look something like adding the following method to your module:
#Provides
#Named("publisherOne")
#Singleton
Publisher providePublisherOne(ServiceLocator serviceLocator,
ActorSystem actorSystem,
Materializer materializer,
ApplicationLifecycle applicationLifecycle) {
return new PublisherImpl("topicOne", serviceLocator,
actorSystem, materializer, applicationLifecycle);
}
Also, you probably want it to be a singleton if you're adding a lifecycle hook, otherwise you could run into memory leaks each time you add a new hook every time it's instantiated.

I want ClassA to get particular a particular instance of ClassB, but can't modify either class directly

Say I have two JdbcTemplates, one for "employee_database" and one for "customer_database". Say a class EmployeeDAO requires the former as a constructor dependency, and CustomerDAO requires the latter. If I were writing these classes myself, I'd do
public class EmployeeDAO {
public EmployeeDAO(#Named("employee") JdbcTemplate employeeJdbcTemplate)
and
bind(JdbcTemplate.class).annotatedWith(Names.named("employee")).toInstance(employeeJdbcTemplateInstance);
And likewise for CustomerDAO
But I can't modify EmployeeDAO to add the Named annotation to the constructor parameters.
What's the canonical way to insure the DAO objects get their respective JdbcTemplates in this scenario without having to instantiate them myself?
In a sense, this is similar to the "robot legs problem", as you're trying to create similar-but-slightly-different trees of objects. In the eponymous problem, you're using a reusable Leg object that receives a #Left Foot and a #Right Foot as needed; in this problem, you're similarly varying the binding of an inner object (JdbcTemplate) based on an outer object's (DAO's) context.
A "cheap way" is to use #Provides methods, which is a particularly low-cost solution if your consumer DAOs have few dependencies and are unlikely to change frequently. Naturally, creating a full Provider would also work too, but this syntax works just fine for most cases.
public class YourModule extends AbstractModule {
#Override public void configure() {}
#Provides EmployeeDao createEmployeeDao( // Name doesn't matter.
#Named("employeeJdbcTemplate") JdbcTemplate employeeTemplate,
Dep2 dep2,
Provider<Dep3> dep3Provider) {
return new EmployeeDao(employeeTemplate, dep2, dep3Provider);
}
}
If the dep list is long, deps change frequently, or multiple classes depend on a JdbcTemplate, then private modules may be the way to go.
install(new PrivateModule() {
#Override public void configure() {
bind(JdbcTemplate.class).toInstance(employeeJdbcTemplate);
expose(EmployeeDao.class);
}
});
The example above uses an anonymous inner class, but you could also create a named class (either top-level or nested) that accepts a JdbcTemplate instance and a DAO class literal, and call it like so:
install(new DaoModule(employeeTemplate, EmployeeDao.class));
install(new DaoModule(customerTemplate, CustomerDao.class));

Configuring StructureMap to generate and "remember" a constructor param literal

I have a StructureMap config that looks something like:
cfg.For<ICacheOrder>().Use<CacheOrder>().Ctor<int>().Is(context => LoginHelper.LoginID);
cfg.For<ICacheProduct>().Use<CacheProduct>().Ctor<int>().Is(context => LoginHelper.LoginID);
cfg.For<ISQLOrder>().Use<SQLOrder>().Ctor<int>().Is(context => LoginHelper.LoginID);
cfg.For<ISQLProduct>().Use<SQLProduct>().Ctor<int>().Is(context => LoginHelper.LoginID);
Via constructor injection, a chain of objects can be created, with some needing an int LoginID that is determined at the time of creation. The static LoginHelper determines the LoginID.
Presently in my config, LoginHelper is called for every created object. Is there a way, perhaps via StructureMap's IContext, for LoginID to be "remembered" and only determined once within a chain of creation?
I know that I could refactor and create an ILogin interface/concrete that StructureMap could construct and cache - but I'd prefer my various layers to be concerned only with a simple int LoginID.
Although it's okay to inject primitive configuration values in your services, when you repetitively inject that same primitive into multiple services, you are missing an abstraction.
This is clearly the case with your configuration; you are missing an abstraction.
The solution is to let those services depend on an abstraction rather than a primitive value. For instance:
public interface ICurrentUser
{
int LoginID { get; }
}
And you can create a rather simple implementation as follows:
public class CurrentUserImpl : ICurrentUser
{
public CurrentUserImpl()
{
this.LoginID = LoginHelper.LoginID;
}
public int LoginID { get; private set; }
}
This means that you will have to change the constructors of CacheOrder, CacheProduct, SQLOrder and SQLProduct, but when you do this, your configuration gets much more maintainable:
cfg.For<ICacheOrder>().Use<CacheOrder>();
cfg.For<ICacheProduct>().Use<CacheProduct>();
cfg.For<ISQLOrder>().Use<SQLOrder>();
cfg.For<ISQLProduct>().Use<SQLProduct>();
The problem of "remembering a param literal" now goes away immediately, because we can now register the ICurrentUser as follows:
cfg.For<ICurrentUser>().Use<CurrentUserImpl>();
The default lifecycle in Structure Map is per request (per object graph) so the same instance is injected into all objects in a single object graph.
Another option is to register it using the HttpContext lifecycle, but this of course only works when running an ASP.NET web application.

How to inject with Guice when there are two different constructors?

Total Guice noob here, have read a few articles and seen the intro video, that's about it.
Here's my simplified old code that I'm trying to "guicifiy". Can't quite figure out how to, since (as far as I understand), I can only #inject-annotate one of the two constructors? How can a calling class create the one or the other instance? Or will I have to refactor this somehow?
public class MyDialog extends JDialog {
public MyDialog( JFrame parent, <other parameters...> ) {
super( parent );
}
public MyDialog( JDialog parent, <other parameters...>) {
super( parent );
}
}
You can only inject into the one ctor.
Depending on how this class is being used, you could:
Inject a factory into the client code with two "new" methods.
Roll all the arguments into one ctor and pass null when not required.
How can a calling class create the one or the other instance?
This suggests that the calling classes will want multiple instances of MyDialog? Then you need to use a hand-rolled factory (Assisted Inject can handle this for you if you only had one ctor). I don't know the details of what you are up to and I'm likely repeating what you already know but as a blanked statement I'd suggest also extracting an interface from MyDialog and have the factory return them. This way you can fake MyDialog in tests.
Constructor injection is very clean. mlk is right, saying that you can inject into one constructor only.
What you can do is use method injection:
public class Smt {
private int a;
private Cereal cereal;
private Personality personality;
private ignition;
public Smt() {
this.a = 5;
}
public Smt(int a) {
this.a = a;
}
#Inject
public void setup(#CiniMini Cereal cereal, #Rastafarian Personality personality,
Ignition ignition) {
this.cereal = cereal;
this.personality = personality;
this.ignition = ignition;
}
}
What Guice will do is call your class' setup class method and provide all the injections. Then you do the same thing as in the constructor--assign the objects to your class' attributes.
I agree with the previous comments.
Just an additional hint: constructor injection is supposed to provide all dependencies a class needs. As mlk says, one approach could be to annotate the constructor with most arguments and then refactor the other one to call the former by passing null values where needed.
Additionally, Guice 3.0 supports the so called Constructor Bindings which allow the programmer to specify which constructor to use. See here for more details.

Resources