Open-Source Internship opportunity by OpenGenus for programmers. Apply now.
In this article, we have explored the concept of Dependency inversion principle in Code Design and explained the concept with an example of Payment System.
Table of content
- Introduction
- Examples without using Dependency inversion principle
- Example using Dependency inversion principle
Introduction -
Dependency Inversion Principle is one of the S.O.L.I.D. principles in Object Oriented Programming (OOP), Its main goal is about minimizing coupling or make loose coupling between different modules and components of program.
Robert C. Martin’s definition of the Dependency Inversion Principle consists of two parts:
- High-level modules should not depend on low-level modules. Both should depend on abstractions.
- Abstractions should not depend on details. Details should depend on abstractions.
In easy words both high-level and low-level modules should depend on abstraction rather than high level depending on low level and each dependency in design should target an abstract class or interface instead of targeting a concrete class.
So basically dependency inversion principle helps us to couple different software modules loosely so that changes in one module doesn't affect the other module which is the major goals of application developers to minimize interdependence(coupling) between different modules or components of the system.Otherwise if a system is tightly coupled then change in one module will also demand changes in the modules which are dependent on that module which subsequently in later stage create a crash in system or application.
Examples without using Dependency inversion principle -
Let us understand it by example of a payment processing program in js -
- without any abstract method a normal payment program may look like-
with Stripe Api
class store{
constructor(user){
this.stripe= new this.stripe(user)
}
purchasebook(quantity){
this.stripe.makepayment(quantity*100)
}
purchaselaptop(quantity){
this.stripe.makepayment(quantity*100)
}
}
class stripe{
constructor(user){
this.user = user
}
makepayment(amountinrupee)
{
console.log(` ${this.user} made payment of $${amountinrupee} with stripe `)
}
}
const store =new store{'rahul'}
store.purchasebook(5)
store.purchaselaptop(3)
Now lets we didn't want to use stripe as our payment processor provider so now we want to use paypal now we have to again alter our whole class according to paypal function and API's e.g.
with paypal Api
class store{
constructor(user){
this.paypal = new paypal()
this.user = user
}
purchasebook(quantity){
this.paypal.makepayment(this.user, quantity*100)
}
purchaselaptop(quantity){
this.paypal.makepayment(this.user, quantity*1000)
}
}
class paypal{
makepayment(user,amountinrupee)
{
console.log(` ${user} made payment of $${amountinrupee} with paypal `)
}
}
const store =new store{'rahul'}
store.purchasebook(5)
store.purchaselaptop(3)
So even in such a small program we have to alter whole code of classes so as to transform payment system from one type to other , now imagine a program of thousands line of code and hundreds of classes how much time and effort and work is required to do transformation from one payment system to other.
This is where Dependency inversion principle comes in handy it gives use method to use abstract classes and hide base classes from being altered each time a implementation is changed.
Example using Dependency inversion principle -
With an abstract method payment processor, we hide our main Store class from outer implementations and now we have to only deal with implementation methods used in our program and not to alter our base class store each time a payment service provider changed.
it can be illustrated by following code.
class store{
constructor(paymentprocessor){
this.paymentprocessor= new paymentprocessor(user)
}
purchasebook(quantity){
this.paymentprocessor.pay(quantity*100)
}
purchaselaptop(quantity){
this.paymentprocessor.pay(quantity*1000)
}
}
class stripepaymentprocessor{
constructor(user){
this.stripe = new stripe(user)
}
pay(amountinrupee)
{
this.stripe.makepayment(amountinrupee *100)
}
}
class paypalpaymentprocessor{
constructor(user){
this.user = user
this.paypal = new paypal()
}
pay(amountinrupee)
{
this.paypal.makepayment(this.user, amountinrupee *100)
}
}
class stripe{
constructor(user){
this.user = user
}
makepayment(amountinrupee)
{
console.log(` ${this.user} made payment of $${amountinrupee} with stripe `)
}
}
//WHILE USING STRIPE
const store =new store ( new stripepaymentprocessor ('rahul'))
store.purchasebook(5)
store.purchaselaptop(3)
//WHILE USING PAYPAL
const store =new store ( new paypalpaymentprocessor ('rahul'))
store.purchasebook(5)
store.purchaselaptop(3)
So from above examples we can see that using abstract classes we just need to change a single line of code for changing a payment service provider instead of changing th whole base class.
With this article at OpenGenus, you must have a strong understanding of Dependency inversion principle.