1

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?

Alexei Levenkov
  • 312
  • 1
  • 10
  • 2
    I 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 Oct 06 '22 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 Oct 06 '22 at 16:27
  • @AlexeiLevenkov I was reading the ways to implement DI on [wikipedia](https://en.wikipedia.org/wiki/Dependency_injection#Interface_injection), it had one way as Interface Injection. – Nishant Chauhan Oct 06 '22 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 Oct 06 '22 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 Oct 06 '22 at 17:40
  • Interface injection works in a language without reflection, but with a "can I cast this to an interface type" thing. For example, C++ or any object model based on COM. – Sebastian Redl Mar 06 '23 at 08:28

2 Answers2

1

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:

  1. Confirm that the current object we're instantiating has a dependency on Service (because it implements ServiceSetter).
  2. Create a Service instance
  3. Inject the created Service instance into the current object we're instantiating, using the setService methods that we know it exposes (since it implements the ServiceSetter 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 a Service 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).

Flater
  • 44,596
  • 8
  • 88
  • 122
-1

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.

candied_orange
  • 102,279
  • 24
  • 197
  • 315