With interface injection (wikpedia) we have a method to set the dependency on the client as part of an interfase.
public interface ServiceSetter {
public void setService(Service service);
}
Why do we need separate interface for the setter method instead of just having it just in the client class?
-
2I think it might help if you gave a little example. At the moment I'm not sure what you're asking. In particular, what this setter method is.Simon B– Simon B2022年10月06日 16:14:25 +00:00Commented Oct 6, 2022 at 16:14
-
There is really not much relation between DI and interfaces... Note that there are plenty of questions and articles of benefits (and drawbacks) of interfaces - if your actual question is just "What's the benefit of having interface?" that would really be duplicate.Alexei Levenkov– Alexei Levenkov2022年10月06日 16:27:28 +00:00Commented Oct 6, 2022 at 16:27
-
@AlexeiLevenkov I was reading the ways to implement DI on wikipedia, it had one way as Interface Injection.Nishant Chauhan– Nishant Chauhan2022年10月06日 17:22:23 +00:00Commented Oct 6, 2022 at 17:22
-
@NishantChauhan I've edited the question to inline the link and hopefully clarify the question - please review and approve if you find it improving the question, otherwise please edit in the link to wiki yourself into the question.Alexei Levenkov– Alexei Levenkov2022年10月06日 17:38:55 +00:00Commented Oct 6, 2022 at 17:38
-
Note that I read the question as "why we need separate interface when implementing interface injection" which is very different from original title "why we need interface injection" (and what candied_oarnge responded to).Alexei Levenkov– Alexei Levenkov2022年10月06日 17:40:38 +00:00Commented Oct 6, 2022 at 17:40
2 Answers 2
For reference, in case Wikipedia gets edited, the example given is this:
public interface ServiceSetter {
public void setService(Service service);
}
public class Client implements ServiceSetter {
private Service service;
@Override
public void setService(Service service) {
if (service == null) {
throw new InvalidParameterException("service must not be null");
}
this.service = service;
}
}
public class ServiceInjector {
private Set<ServiceSetter> clients;
public void inject(ServiceSetter client) {
this.clients.add(client);
client.setService(new ExampleService());
}
public void switch() {
for (Client client : this.clients) {
client.setService(new AnotherExampleService());
}
}
}
public class ExampleService implements Service {}
public class AnotherExampleService implements Service {}
First of all, I don't like this example. It has many flaws, too numerous to list here. It feels like some abstract theoretical example drafted by some academic that does not realistically reflect real code. However, once I start picking through the bits of code, it does eventually highlight the key components of dependency injection, it just does it in a very confusing way.
ServiceSetter
isn't being used here in the abstract. Its name is actually very concretely referring to the Service
base type implemented by ExampleService
and AnotherExampleService
. If I had a DatabaseContext
class that was used as a dependency, I would analogously create a DatabaseContextSetter
interface to provide the setter method for the DatabaseContext
dependency.
In other words, implementations of the ServiceSetter
interface are publicly stating "I have a dependency on Service
", and it is necessary for them to make that publicly known (I'll explain why in a second)
Having a dependency inherently means that you need to be able to receive that dependency, hence why the interface inherently requires the implementor to implement a setter method.
This code feels like it predates DI containers, and seems to be built in the expectation that you need to roll your own DI service provider. In that perspective, it makes sense why you'd want a marker interface here to figure out a certain consumer's dependencies. This would allow a homebrew DI service provider to do something along the lines of:
Note: I'm using C# because I'm more familiar with it and I'm better able to keep the code both readable and syntactically correct.
public class ServiceProvider
{
public T Create<T>()
{
var t = new T();
if(t is ServiceSetter ss)
ss.setService(this.Create<Service>());
return t;
}
}
I've hardcoded the ServiceSetter
and Service
types in this example, but you could more realistically refactor this to use a collection of registered types (which is how most DI containers work under the hood), e.g. foreach(var registeredType in this.registeredTypes) { ... }
In verbal terms, this logic uses the interface (ServiceSetter
) to:
- Confirm that the current object we're instantiating has a dependency on
Service
(because it implementsServiceSetter
). - Create a
Service
instance - Inject the created
Service
instance into the current object we're instantiating, using thesetService
methods that we know it exposes (since it implements theServiceSetter
interface, which mandates this setter method to exist).
As you can see, the existence of the ServiceSetter
interface is useful in both steps 1 (to identify a consumer) and 3 (ensuring that we have a setter method to inject the dependency into the consumer).
Nowadays, we don't really do dependency injection via method anymore (as far as I've seen), we define our dependencies using the constructor. Constructors can more easily be found (using reflection), and therefore we are already able to answer the questions:
- (Step 1) It is a consumer of
Service
when it has aService
constructor parameter. - (Step 3) The constructor inherently operates as the dependency setter method.
Round this all off, I want to circle back to your core question:
Why do we need separate interface for the setter method instead of just having it just in the client class?
You are correct that dependencies are generally declared by a concrete instance, not an abstract interface. However, as explained above, the interface is mostly likely used here in relation to how a DI service provider would supply those dependencies.
Since we've mostly started using constructor-based DI now, we are in fact registering dependencies in the concrete class (since a constructor is about as personal as it gets to a class and not some abstraction layer on top of it).
I feel like I have about half of this answer. If you have the other half please answer.
Interface Injection is a real thing. It is the unpopular and little understood 3rd main form of Dependency Injection. Martin Fowler explained it to us back in 2004.= = Wikipedia gives us an example of it.= I first studied it years ago. And I tried to explain it here a couple months ago. =
But why do we need it? I've had years of knowing about this at this point and really haven't found a use for it. Maybe that's my own lack of imagination.
What it does is fully remove any knowledge of the service from the client. The client doesn't know how to talk to the service. Doesn't know what service it's getting. Just how to obtain a reference to it.
That's certainly very loose coupling but I've honestly never found the need. From looking at the code I suspect it would cause a fair bit of confusion if you used it. I'm loath to subject a team to that without a clear reason.
If anyone can please provide a clear reason for this I'd love to hear it. I spent a bit of time studying this and I feel like the brain cells I spent on it are going to waste.
-
wait, surely we are always injecting interfaces rather than concrete types?Ewan– Ewan2022年10月06日 17:32:28 +00:00Commented Oct 6, 2022 at 17:32
-
@Ewan look at the code before deciding the name actually makes any sense.candied_orange– candied_orange2022年10月06日 17:33:00 +00:00Commented Oct 6, 2022 at 17:33
-
well i did but still confused tbh.Ewan– Ewan2022年10月06日 17:44:39 +00:00Commented Oct 6, 2022 at 17:44
-
OK reading the MF article, im guessing that this is all before ubiquitous DI containers and people still needed a way to work out which class needed what dependency?Ewan– Ewan2022年10月06日 17:53:38 +00:00Commented Oct 6, 2022 at 17:53
-
@Ewan that'd be a good bet.candied_orange– candied_orange2022年10月06日 17:54:33 +00:00Commented Oct 6, 2022 at 17:54
Explore related questions
See similar questions with these tags.