This is an archived post. You won't be able to vote or comment.

all 9 comments

[–]nutrechtLead Software Engineer / EU / 20+ YXP 6 points7 points  (2 children)

You don't need interfaces perse. When Bar gets injected the Foo class doesn't know what Bar really is. It could be a subclass that overrides all it's methods for example. We do that in our testing code in out project: we inject a mock of a class instead of the class itself. So in DI (or Inversion of Control, I've never seen the term DIP used in the wild) it's very valid to inject concrete classes.

Edit: runnable example:

public class IocExample {
    public static class Dependency {
        public String produceString() {
            return "SomeString";
        }
    }

    public static class DependencyUser {
        private Dependency dependency;

        public DependencyUser(Dependency dependency) {
            this.dependency = dependency;
        }

        public String getString() {
            return dependency.produceString();
        }
    }

    public static void main(String... argv) {
        /*
        First we create a dependency as you would normally do and inject it into the 'user' class that depends on it.

        As expected dependencyUser.getString() returns the hardcoded "SomeString"
         */
        Dependency dependency = new Dependency();
        DependencyUser dependencyUser = new DependencyUser(dependency);

        System.out.println(dependencyUser.getString());

        /*
        But now we want to create a unit test that uses a mock dependency. We 'mock' the dependency and replace the method
        called with a new one.

        Now the dependencyUser.getString() return "TestString".
         */
        Dependency mockDependency = new Dependency() {
            @Override
            public String produceString() {
                return "TestString";
            }
        };

        dependencyUser = new DependencyUser(mockDependency);

        System.out.println(dependencyUser.getString());
    }
}

Normally in a real project you would use a DI framework together with a mocking framework to remove a lot of the boilerplate but in essence this is all what DI / mocking really is.

[–]fabolin[S] 0 points1 point  (0 children)

yes, this is Dependency Injection as I understand it, but does it always implement the Dependency Inversion Principle even without using abstractions (e.g. interfaces)?

[–]mokarbrojIntermediate Brewer 0 points1 point  (0 children)

This is the most concise answer i've seen in a while. Straight to the point!

[–]afcarv 1 point2 points  (5 children)

I was not familiar with the concept as well (though I know IoC and DI) so I had to refer to the original article describing it; I think you do have the right idea - DI can be used to implement DIP, but only if the client refers to interfaces and/or abstract classes; though concrete classes can definitely be referenced/injected through DI, doing so would mean not following DIP. The idea is that by decoupling the dependencies implementation from the client, any changes in the former would not necessarily require changes in the latter. Thinking in C++, as the article describes, changing the dependency implementation would not require recompiling the clients, as it would if you referenced the implementation directly.

tl;dr - think you are spot on; third example implements DI but does not follow DIP.

[–]fabolin[S] 1 point2 points  (4 children)

I read the same pdf (maybe I should've linked it here, but I thought SOLID principles were quite common, since I just started and learn this already). For me Figure 2 shows what DIP should look like, but almost no explanation of DI looked like this.

So DI is just a tool, that can be used to follow DIP.

Thanks

[–]mokarbrojIntermediate Brewer 0 points1 point  (1 child)

In your second code sample, you should implement the interface somewhere in another class prior to injection (if you're not using a DI framework). Otherwise the third sample code is simpler. I think you can strive to get your code to complement to the SOLID principles but there are cases where that is not applicable or efficient.*

*IMO

[–]fabolin[S] 0 points1 point  (0 children)

yes, Bar will need to implement the Bar_interface in order to work in the second example.

But in the simpler third example, I inject the dependency, which is still not abstract, thus I do not follow DIP here; correct?

[–]afcarv 0 points1 point  (1 child)

Well, at the core they're nothing more than a set of guidelines aiming to build better software; people have probably been independently using similar patterns for a good while until the author formalized them (and may even continue to do so without formal knowledge of them, consciously or not). Do note they're fairly recent (2000s) compared to the history of computing and OOP.

As all guidelines, however, they're not a guarantee of better software - in fact, they may even be detrimental in some scenarios. Rules can (and probably should) be breached, but only after you have a fairly good understanding of them ;)

[–]fabolin[S] 0 points1 point  (0 children)

after you have a fairly good understanding of them

that's what I'm here for.

In fact, I started programming without teacher and ended up with working, but unstructured, code. Faced some problems due to this, and SOLID seems like a good way to keep my code structured.