1

I have been studying also S.O.L.I.D. and watched this video:
https://www.youtube.com/watch?v=huEEkx5P5Hs

01:45:30 into the video he talks about the Dependency Inversion Principle and I am scratching my head? I had to simplify it (if possible) to get it through this thick scull of mine and here is what I came up with. Code on the marked My_modified_code my version, code marked Original DIP video version. Can I accomplish the same with the latter code?

Original:

namespace simple.main
{
     class main
    {
        static void Main()
        {
            FirstClass FirstClass = new FirstClass(new OtherClass());
            FirstClass.Method();
            Console.ReadKey();

            //tempClass temp = new OtherClass();
            //temp.Method();
        }
    }

     public class FirstClass
     {
         private tempClass _LastClass;
         public FirstClass(tempClass tempClass)//ctor
         {
             _LastClass = tempClass;
         }

         public void Method()
         {
             _LastClass.Method();
         }
     }

    public abstract class tempClass{public abstract void Method();}

    public class LASTCLASS : tempClass
    {
        public override void Method()
        {
            Console.WriteLine("\nHello World!");
        }
    }

    public class OtherClass : tempClass
    {
        public override void Method()
        {
            Console.WriteLine("\nOther World!");
        }
    }
}

My_modified_code:

namespace simple.main
{
     class main
    {
        static void Main()
        {
            //FirstClass FirstClass = new FirstClass(new OtherClass());
            //FirstClass.Method();
            //Console.ReadKey();

            tempClass temp = new OtherClass();
            temp.Method();
        }
    }

     //public class FirstClass
     //{
     //    private tempClass _LastClass;
     //    public FirstClass(tempClass tempClass)//ctor
     //    {
     //        _LastClass = tempClass;
     //    }

     //    public void Method()
     //    {
     //        _LastClass.Method();
     //    }
     //}

    public abstract class tempClass{public abstract void Method();}

    public class LASTCLASS : tempClass
    {
        public override void Method()
        {
            Console.WriteLine("\nHello World!");
        }
    }

    public class OtherClass : tempClass
    {
        public override void Method()
        {
            Console.WriteLine("\nOther World!");
        }
    }
  • I can't watch the video now, but to me, the original is already Dependency Inverted enough. The First class already depends on abstractions instead of concrete class. – Euphoric Nov 04 '14 at 08:52
  • 1
    Better suited for http://codereview.stackexchange.com/ ? – guillaume31 Aug 24 '15 at 09:25
  • 1
    @guillaume31 perhaps (though it would have required some significant editing). However, it isn't possible to migrate something older than 60 days without extraordinary involvement from SE for extraordinarily good questions. –  Aug 24 '15 at 13:16

1 Answers1

6

Congratulations on studying S.O.L.I.D. These are some of my favorite principles.

The Video

01:45:30 is not when the video deals with The Dependency Inversion Principle (that's 1:26:38). It's not even when it deals with Dependency Injection (that's 1:32:33 - accepting a dependency and 1:33:30 - injecting a dependency). 01:45:30 is when it deals with Dependency Injection Frameworks.

Your Code

Going from Original to My_modified_code, you remove FirstClass and call Method() on the OtherClass instance stored in temp as a tempClass. This is a refactoring since it does not change the behavior of the program (except for the commented out Console.ReadKey()). What is different is that rather than achieving polymorphism by composition and delegation in FirstClass (much like the Strategy Pattern does) you are now achieving polymorphism by subclassing and inheritance under tempClass (much like the Template Pattern).

What you've lost is the level of indirection that FirstClass was providing. FirstClass could have suppressed the Method() call, could have changed what instance Method() was called on at any time, and could even have called something else entirely.

You've also lost your one and only Dependency Injection. OtherClass was being injected into FirstClass as a tempClass in main before. Now main is handling it directly.

But other than that, yeah it's behavior is pretty much the same. Understand that the intent here wasn't to write the simplest code that behaves as you want. It's to write code that will withstand changes without breaking.

Dependency Inversion Principle

This is about both high and low level modules/classes not depending on concrete classes, or details, whatever you want to call them. This is why we see new only in main away from your client classes. Instead they depend on abstractions like tempClass.

The reason for this is to protect against change. If details aren't known then details can change without affecting anything else. Anything you know about you have to care about.

Dependency Injection

This is how the details find their way into the client classes that don't know them except through their abstractions.

We used to just call injections "parameter passing" or "passing a reference around" but few people understood why it was important to do that. So Fowler invented a new word for it in an attempt to (well, to sell books, but also) make clear that this is prefered to clients constructing things themselves.

Construction

Your simple example has all the construction happen in main. It's important to know it doesn't have to only occur here. You can have many classes dedicated to constructing and injecting dependencies into clients. What is key is that clients should not know about them.

There are many creational patterns that can help with this. The original Gang of Four creational patterns are a start but we've come a long way since then. From Josh Bloch's builder pattern to Fluent Interfaces to internal Domain Specific Languages. It's changed the way we test, and the way we write SQL queries. These new creational patterns are also part of why Java 8 almost looks like a new language.

Tap into this power and you can hand tune the construction of your clients to suit your needs. If, instead, you'd rather throw a completely generic construction solution at the problem then read on.

Dependency Injection Frameworks - Rant

Contrary to popular belief, and much marketing propaganda, Dependency Injection Frameworks do not help you invert dependencies (a good design does that). They do not accept dependency injections (your client code, written in your native language, does that). No, what Dependency Injection Frameworks actually do for you is solve the construction problem.

They can solve this by making you push all knowledge of your client object graph into xml configuration files or by making you decorate your classes with (sometimes proprietary) noise that lets you autowire based on class name or type or whatever. Which to chose is the classic Configuration vs Convention debate. Do you want absolute control or do you not want to have to think?

The only thing a Dependency Injection Framework (DIF) has to do with Dependency Injection(DI) is communication. DI is how a DIF communicates the work it's done for your client classes as it builds them. We call this communication the injection.

In other words, DI is what the framework expects YOU to support and accept in your clients. You can support and accept DI with or without a framework. If you don't, the framework won't do you much good. Well, the framework might offer some hacky workarounds, but other than that...

Constructor, Setter, and Interface Injection are the three classic forms of injection that you don't need any proprietary framework magic to use. Your language can do them on it's own. The video used constructor injection at 1:32:33.

There are other forms of injection (annotations and reflection tricks come to mind), many of them proprietary. If you're not careful you can find yourself writing code that is, ironically, dependent on your particular Dependency Injection Framework. A situation that the Framework authors wouldn't mind at all. But your boss might.

The lesson of this rant is that using a Dependency Injection Framework is no substitute for understanding Dependency Injection (DI), or the Dependency Inversion Principle (DIP). Really, they're just a handy way to construct objects, and certainly not the only one.

candied_orange
  • 108,538
  • I like your rant. One note on autowire, it tell me that DIF is used in a bad way. Dependency sometime is needed and it does not make sens to let the client decide how to wire dependency. Okay it is good to let client decide how to wire the dependent part but it has the cost of making your code less readable and it is a cost that most of the time does not worth it. I rather prefer start KISS and refactor once I know my client need the dependency to be change. – mathk Aug 24 '15 at 08:55
  • @mathk thanks. If you can refactor clients and ignore the open closed principle, say because the client isn't published, then yes you can do that. Remember that this also harms testability if your testing tool can't hack it's way in anyway. Rather than always depending on refactoring I suggest learning the difference between newables and injectables. Consider this, this, and this – candied_orange Aug 24 '15 at 09:44
  • I have been working with smalltalk for quite a will and now doing mainstream language(Java, C#, ...) Having saying that I think mainstream language have all the same issue regarding DI. They do not have a clean reification of class concept and force people over-specifying the wiring. This is a reason why you have so many trouble writing test and having to add Factory class. Smalltalk does not suffer this. Note that I am not advocating for smalltalk other language does have similar concept. Just that it is possible to have a different view point that mainstream language will not show you. – mathk Aug 24 '15 at 09:57
  • Good answer, but as a small nitpick I think the word "framework" isn't appropriate for most IoC containers. See frameworks vs libraries. Your app isn't hosted within the container; you use the container as a library to help with constructing your objects – Alex Aug 24 '15 at 10:06
  • @AlexFoxGill very true. I would only point out that my rant wasn't aimed at such library based IoC containers. Part of what the rant is actually trying to say that you don't have to drink the cool aid and can treat such frameworks as you would a library. Uncle Bob said it best here – candied_orange Aug 24 '15 at 10:22
  • Just wanted to point out that your paragraph starting with "We used to just call injections "parameter passing"" is a bit misleading. Not all parameters are dependencies. Equating the two can confuse somebody trying to suss out the nuances of Dependency Injection. – Eric King Aug 24 '15 at 16:58
  • @EricKing well color me confused then. I thought it was that not all dependencies were parameters. Some are evil hidden dependencies. Exactly what parameters are not dependencies then? If I need an int passed to me aren't I depending on that int? – candied_orange Aug 24 '15 at 21:51
  • A dependency is a module with behavior that another module depends on to get it's work done. A dependency has behavior, which can be abstracted. The dependency can be provided as a parameter, but it doesn't have to be. A parameter is an input to a subroutine. That input may be a dependency, but it may be a simple variable without behavior. I would not consider the parameters for a subroutine such as add(int x, int y) as dependencies. – Eric King Aug 24 '15 at 22:05
  • @EricKing The paragraph you are questioning doesn't even mention dependencies (which were around long before Fowler). It mentions injections. So I don't really see how you'd like me to fix it. I was attempting to demystify the injection mechanism. But since we're comparing notes: To me a dependency is something needed. I'll grant that someone might create a parameter for something that isn't needed but I'd think them a bit silly. As for modules: so a String is a dependency but an int is not? As for abstracted behavior: That's a requirement of DIP or the Strategy Pattern but not DI. – candied_orange Aug 25 '15 at 04:29