What is Dependency Inversion ?

Dependency Inversion is a very simple concept : “if a class B depends on a class A, you can revert this dependency by generating a contract that class A must implement”. Thus you became responsible for this contract and you do not depend anymore on anyone because they will have to implement your contract and not you…

Let’s take an example in real life : you have a car and you want an insurance. You could be dependent of your insurance if you want to but you could also establish a list of what you want from your insurance and look at many insurances to see if they answer all that is in your list. This way you can change your insurance whenever you want and/or you can choose whatever insurance you want. Of course, in real life your insurance will want you to be dependent on it (they will add feature in the contract that will make you dependent and/or that other insurances do not have, this way they can not be replaced). In software engineering, you should be responsible for your contract (interface) and the things that you will use should implement that contract (or you could extend them so they implement your contract). You should not change/create your code with the constraints of what you are using.

Let’s take an example in real code :

	class ClassA {
		var name: String
		init(name: String) {
			self.name = name
		}
		func printName() {
			print("My Name is: " + name)
		}
	}

	class ClassB {
		var a :ClassA
		init(a : ClassA) {
			self.a = a
			a.printName()
		}
	}

	var b = ClassB(a: ClassA(name: "Dependency Inversion"))

In here, we can see that ClassB depends on ClassA (you can say : “ClassB is using ClassA”). Thus if I change something on ClassA then ClassB is at risk. For example, I could decide to change the function printName and then ClassB will break immediately. This could work and you could deal with this without any problem but it generates different problems like :

  • if ClassA depends on something else (like a framework for example), then ClassB also depends on that…
  • if ClassA needs an asynchronous value, then ClassB will also need this value…
  • if ClassA can not have expected values, then ClassB will not be testable correctly because ClassA can not be expected…
  • if ClassA crashes, then ClassB will…
  • if ClassA is done by a bad developer, then ClassB will suffer from it…
  • etc…
ClassB depends on Class A
Ref. 1

In UML, the dependency is represented by an arrow pointing from ClassB to ClassA, like in figure Ref.1.

You can break this by using Dependency Inversion. How does this work ?

ClassB will create a contract and if ClassA wants to be used by ClassB, it will have to implement that contract.

In languages where you have interface or protocol you will use them to define the contract, in other languages you could use Abstract Classes (Classes that are never instanced but always subclassed). In Swift for example the code will look like this.

    class ClassA: ClassBProtocol {
	    var name: String
	    init(name: String) {
	        self.name = name
	    }
	    func printName() {
	        print("My Name is: " + name)
	    }
	}

	protocol ClassBProtocol {
	    func printName()
	}

	class ClassB {
	    var a :ClassBProtocol
	    init(a : ClassBProtocol) {
	        self.a = a
	        a.printName()
	    }
	}

Dependency in action
Ref-2

You can also represent it in UML like in figure Ref-2. The blank arrow generally represents the “implement” link (at least I represent it with an empty arrow – sometimes the same as using “inheritance” or “isA” relationship).

We clearly see that the arrow is reversed (the one that was pointing to ClassA is now pointing to the “contract”). At least this is how I like to see the dependency inversion (it reverts the arrow).

Now we can develop ClassB without using ClassA. We can test it, we can use a mock until ClassA is completely done, we can apply changes if ClassA becomes obsolete, we are not forced to use an unneeded framework (thus we can defer the use of a framework), etc.

To me, if there’s one thing to know when starting to develop, it should be Dependency Inversion. Unfortunately, this is generally something that you learn at school, then forget and then wish to recover later (but generally too late).