0

In my job I work with C# (although the language is irrelevant for my question, and now I'd like to focus on Android) and we usually inject interfaces and not the actual classes, so I was wondering what are the real benefits of that?

As far as I know, I can understand interfaces (if no DI is taking part) as a way of not tying class methods to fixed implementations of their "dependencies", for example, we could decide to change the database engine so, using interfaces, the repository methods could be easily changed adjusting code to the new engine and the services accessing the repository would stay unaltered.

Given that, and please correct me if I'm wrong, I cannot see any real benefits of injecting interfaces. I mean, we already have DI so if we inject we are already decoupling and giving the possibility of altering the dependency as per our needs (injecting one implementation or another) then why injecting interfaces?

9
  • 1
  • 3
    Maybe this is nitpicking but you can't inject an interface. You can only inject an object that implements an interface. I think what you mean to say is that the object receiving the injection depends on the interface and not the concrete implementation. The reason for this is the Liskov Substitution Principle and SOLID.
    – John Wu
    Jul 22, 2022 at 20:14
  • You are right @John Whu. Please, put it as an answer and I'll mark it as correct. Jul 22, 2022 at 20:36
  • It's a pitty you downvoted me just because there is a similar question @Greg Burghardt. Maybe I missed it when looking for help, or whatever. You could have just put the link without downvoting, but it's your right. Is funny because many times I post great questions and no one upvote, but one question that is considered non appropriate for whatever reason is enough for being downvoted. In the end, this is a perfectly correct question. I'm not asking about how to cook a chicken in the stove. Jul 22, 2022 at 20:53
  • The down-vote wasn't me. I do not typically down-vote possible duplicates unless the question looks like someone didn't do any research or put any time into it. I don't think that about your question, though. Jul 22, 2022 at 21:10

4 Answers 4

0

Typically we inject objects. But, well, interface injection is actually a thing.

One thing to understand, an interface is different than an interface (the keyword). This confusion is brought to you by the creators of Java who decided they wanted to support multiple inheritance after all and were to lazy/stuck-with-old-code to redesign how an abstract class worked. And so the interface was born.

So if what you're trying to ask is if you have to code against interface rather than a perfectly extendable class then no. You do not have to slap interfaces on everything. Unless you want multiple inheritance to work in those languages that need this hack.

Anyway, that's not an injection. That's what you're coding against.

This is interface injection:

// Service setter interface.
public interface ServiceSetter {
    public void setService(Service service);
}

// Client class
public class Client implements ServiceSetter {
    // Internal reference to the service used by this client.
    private Service service;

    // Set the service that this client is to use.
    @Override
    public void setService(Service service) {
        this.service = service;
    }
}

// Injector class
public class ServiceInjector {
    Set<ServiceSetter> clients;
    public void inject(ServiceSetter client) {
        clients.add(client);
        client.setService(new ServiceFoo());
    }
    public void switchToBar() {
        for (Client client : clients) {
            client.setService(new ServiceBar());
        }
    }
}

// Service classes
public class ServiceFoo implements Service {}
public class ServiceBar implements Service {}

wikipedia.org - Dependency Injection - Interface Injection

What does that get you? Your dependencies have no idea what their clients are. Will you ever need that? I always value keeping things from knowing things that they don't need to know. Honestly though, I've never had good reason to do this. But it is called interface injection.

6
  • Just stumbled upon this; I might be wrong, but it strikes me that this weirdly named "Interface Injection" thing doesn't really belong to the Wikipedia article on DI - at least, not under the title "Types of dependency injection". Cause, this is not a distinct type of dependency injection, this is just setter injection + some client-side dependency management code around it. Basically, in the code above - the Service classes are the dependencies of the Client (that just uses setter injection), and the Client itself is used by some orchestration code that requires something with a setService Jul 25, 2022 at 20:35
  • Aaah, the name comes from this article by Martin Fowler; it's unfortunate that he chose that particular name, I find its confusing when encountered out of context. Jul 25, 2022 at 20:48
  • 2
    @FilipMilovanović I'll admit it's an unfortunate name. And I'll be damned if I've ever found it useful. But I read about it and now it's stuck in my brain. Probably where the names of old girl friends should have been. Sigh. Jul 25, 2022 at 21:42
  • The description of the pattern/technique on Wikipedia is also very confusing, because their example doesn't actually demonstrate what they are talking about (e.g. where they say "the dependency must do something in addition to simply passing back a reference to itself [... e.g.] reference-counting"). In Fowler's article, the scheme is something like: every abstract dependency comes along with a corresponding injector interface (really, a factory or a provider for the dependency), so that you can dynamically chose/configure what happens at the injection time. 1/2 Jul 25, 2022 at 22:07
  • 1
    @FilipMilovanović what truly scares me is someday I may find use for it. And then I’ll have to explain it. Jul 25, 2022 at 22:16
1

Okay, so let's start from the world where you use class types as your injected dependency types.

public class MyDependency {}

public class MyService
{
    public MyService(MyDependency myDependency)
    {
       //...
    }
}

You're absolutely correct that this works just as well in terms of the real production code, assuming that that class adequately covers all the possible implementations of that dependency.

And even if multiple classes exist, you could still let those inherit from the same base class instead of the same interface, so you'd still be using class-based injection.

public class MyDependency {}
public class MyDependencyOne : MyDependency {}
public class MyDependencyTwo : MyDependency {}

public class MyService
{
    public MyService(MyDependency myDependency)
    {
       //...
    }
}

Production code wise, all good. But we also want to write tests. Specifically, when I call MyService.UploadUser(user), I want to make sure that MyService correctly combines the first and last name of the user and sends that as a single string to the dependency (which you can imagine to be a database repository).

Clearly, my unit test is going to use a real MyService object. But I don't actually want to upload real data to a real database, and that's what MyDependency precisely does in this scenario. Since the constructor for MyService demands a MyDependency, I can't avoid needing to supply a real MyDependency.

It would be a lot better if I could inject a fake MyDependency, sort of a secret agent that pretends to be the real thing but does not actually talk to the database and instead just tracks what it's being told.

But the MyService code should be written in a way that it works agnostic of getting a real/fake dependency. I'm referring to the type of the injected dependency here. So the question becomes: can we achieve this using a class type?

There's no shared logic between the real and fake dependency, so there's no benefit to inheritance. The only thing that the real and fake dependency share is that they have the same public interface (hint hint), their internals are completely different.

public interface IMyDependency 
{
    void SaveUserName(string fullName);
}

public class MyRealDependency : IMyDependency { /* same code as before */ }

public class MyFakeDependency : IMyDependency 
{
    // Snitch list which captures all values sent to the secret agent
    public List<string> CapturedValues { get; } = new();

    public void SaveUserName(string fullName)
    {
        this.CapturedValues.Add(fullName);
    }
}

public class MyService
{
    public MyService(MyDependency myDependency)
    {
       //...
    }
}

When you look at MyFakeDependency, you can see that it collects and records anything that is sent to it. This means I can later check what was sent to it, and this is something I can use in my tests. For example:

public void Concatenates_first_and_last_name_of_user()
{
    var user = new User("Rick", "Sanchez");
    var fakeDependency = new MyFakeDependency();
    var service = new MyService(fakeDependency);

    service.UploadUser(user);

    // Confirm that MyService did call IMyDependency.SaveUserName
    fakeDependency.CapturedValues.Should().HaveCount(1);

    // Confirm that the sent name is first + last name
    fakeDependency.CapturedValues[0].Should().Be("Rick Sanchez");
}

An interface is, in a very oversimplified way to put it, a base class which only allows you to define public signatures (methods and properties), but not implementations (method bodies).

Specifically in C#, you could also use an abstract class to the same effect as an interface here. However, C# does not allow multiple inheritance. A class can only inherit one abstract base class, but it can implement ("inherit") as many interfaces as it likes.
Because of this, in cases where you do not need any shared implementation logic (method bodies), an interface is a better choice over an abstract class.

Ask yourself how you would've done it using a class-type as the injected dependency. You'll find that you need to define an implementationless base class that covers both the fake and real dependency, and that's precisely what an interface is. Can you do it with a class on a technical level? Sure. But you're really just using it as a pseudo-interface.

0

There's the situation where a class implements an interface and nothing else. However, if you write software for MacOS / iOS, it is very, very common that you implement an interface in a quite unrelated class.

For example, if the user changes the contents of a textfield, you'd want to check if the change is allowed, you'd want to check if they hit the "return" key which might cause some action etc., and you write the code for this not in a textfield, but in a class which controls the textfield (and other fields). So an interface is declared by the TextField class, it is implemented by a quite unrelated class, and an instance of that class injected into a TextField instance to handle user interactions.

In the TextField class, you most definitely want an interface (for example because you have no idea whatsoever which class is going to implement it). And it's quite likely that you inject a concrete class instance. And that you have a dozen other classes that are injected into other TextField instances.

0

The payoff comes when you start testing. Typically you create concrete classes with very limited functionality that implement the interface. For instance a dummy repository class may be coded to return a known values for it's getPasswordHash(userId) method. This allows testing the calling code without a backing database.

A second situation where an interface is highly desirable is when you write a library class that requires a callback method from the library user. It is easy to inject a dependency that implements your interface but much harder to have the caller supply a concrete class that extends your concrete class. Many OO languages do not support multiple inheritance, so don't use up the one chance at inheritance when there is a good alternative.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.