002:Dependency Inversion Principle with pseudo code

002:Dependency Inversion Principle with pseudo code

This is the second article in the Dependency Injection series . If you have not read the first article in the series, have a read here. In this article let us talk about Dependency Inversion Principle which is at the core of Dependency Injection.

Once you want to adopt dependency injection you stumble into another problem: Each of our classes requires dependencies. So how do we construct each and every class? Answer: We not only need to figure out what dependencies they need, we also need to figure out how to instantiate the dependencies. In the first post i mentioned decoupling which is breaking down your code into various modules. In Dependency Inversion, we have two types of modules:

  1. The Low level modules are the dependency modules. These are required by the high level modules.)

  2. The Organizer/ High level modules (can also be referenced to as policy modules): They state what they will need to use from the low level modules.

I gave an example of my restaurant from the first article : We needed a Seats Supplier to supply seats and a Cutlery Supplier to supply spoons and knives. These are low-level, dependency modules and My restaurant is a high-level, organizer/policy-setting module.

Using this principle, dependency relationships established from various modules are independent of each other when it comes to implementation. I can easily swap out the specific suppliers and still get my seats. The same is true for the cutlery suppliers if they cannot deliver.

BUT what on earth does this mean(in English)? Let us delve deeper into the code of our restaurants example.

dependency_Tamrefrank2.jpg

Photo by pisauikan on Unsplash

If i wrote Pseudo code to explain dependency inversion with my restaurants example i would say it as:

Seats Supplier:

package com.my.restaurant

class SeatsSupplier {
 fun supplySeats() {}
}

And a Cutlery Supplier:

package com.my.restaurant

class CutlerySupplier {
 fun supplyCuttlery() {}
}

My restaurant is dependent on seats and cutlery suppliers, hence:

package com.my.restaurant;

class FrankRestaurant {
 private val cutlerySupplier = CutlerySupplier()
 private val seatsSupplier = SeatsSupplier()
 fun implement() {
 seatsSupplier.supplySeats()
 cutlerySupplier.supplyCuttlery()
 }
}

This is what the code looks like without DI.

Class FrankRestaurant is a high-level module, and it depends on low-level modules such as Cutlery Supplier and Seats Supplier .

BUT THIS IS NOT right! why? We are not following the first rule of the dependency inversion principle.

Dependency Inversion (Inversion of control) This is a concept at the root of DI that states that a class should not configure its dependencies statically but should be configured by some other class from outside. A class should concentrate on fulfilling its responsibilities and not on creating objects that it requires to fulfill those responsibilities.

And that's where dependency injection comes into play: it provides the class with the required objects.

Looking at the implement function of class FrankRestaurant, we realize that the methods supplySeats and supplyCuttlery are methods bound to the corresponding classes. Regarding the project scope, those are details since, in both cases, they are forms of suppliers. Thus, the second part of the dependency inversion principle is violated.

Remember, High-level modules, which provide complex logic, should function and remain unaffected by changes in low-level modules, which provide utility features.

what it means=> My restaurant should function regardless of a supplier's inability to deliver.I should be able to go and search for another supplier and not directly depend on one supplier.

To achieve that, we will need to introduce an abstraction layer that decouples the high-level and low-level modules from each other. How do we do this, we shall implement an interface called the Suppliers interface:

Interface in programming: A programming structure/syntax that allows the computer to enforce certain properties on an object.We use it to achieve total abstraction.

package com.my.restaurant

interface Suppliers {
fun supply()
}

By introducing a suppliers interface we have introduced an abstraction.

dependency_injection_series_tamrefrank.jpg

Photo by Tomas Horak on Unsplash

Abstraction is one of the concepts(or Principle) in Object Oriented Programming that states that each object should only expose a high-level way for using it. This should hide internal implementation details. It should only reveal operations relevant for the other objects. Think of a blender: You just place things you want to blend and press a button and blending happens. You only need to know 2 things the rest of the operations that go on under the hood are hidden from you.

When we introduce Abstraction, the Seats Supplier shall be refactored to:

class SeatsSupplier:Suppliers {
override fun supply() {
supplySeats()
}
private fun supplySeats() {}
}

And the Cutlery Supplier shall be refactored to:

class CutlerySupplier:Suppliers {
override fun supply() {
supplyCuttlery()
}
fun supplyCuttlery(){}
}

The next step, in order to tackle the violation of the first part, would be to refactor the FrankRestaurant class so that it will not depend on the SeatsSupplier and the CutlerySupplierclasses. What we will want to achieve is to give us the ability to swap suppliers in case we ever want to.

This will look like:

package com.my.restaurant;
class FrankRestaurant(private val suppliers:List<Suppliers>) {
 fun implement() {
 suppliers.forEach({ d-> d.supply() })
 }
}

The outcome is that the class FrankRestaurant does not depend on lower level modules, but rather abstractions. By depending upon abstractions we're decoupling our implementations from each other.

What this will mean is that if a supplier ever annoys us or prevents us from meeting the launch date, we will easily replace them with another one and my restaurant will still launch.

If they mess up, they are fired! My restaurant has to open, yo!

dependency_tamrefrank-series.gif

Tinder × Chef by ccccccc on dribble

Try think how the PC example the code would look like. Share your answer in the comments section.

Photo by Gerry Roarty on Unsplash