Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
April 23, 2021 12:38 am GMT

Dependency Inversion Principle

The Dependency Inversion Principle states that entities must depend on abstractions, not on concretions. High-level modules should not depend on low-level modules. Both should depend on abstraction.

High-level modules should not depend on low-level modules. Both should depend on abstraction.

When we talk about high-level modules we are referring to a class that executes an action implementing a tool or library, and when we talk about low-level modules we are referring to the tools or libraries that are needed to execute an action.

The principle allows for decoupling, which means to separate, disengage or dissociate something from something else. This helps us by reducing dependency and allowing for easier implementations of other tools in the future.

Example

Let's imagine that we have a Candy Store and we are developing the checkout process. In the beginning, we only planned to implement Stripe as our payments processor. Stripe needs for the amount to be passed on as cents to make the transaction. Our classes will look something like this:

//Checkout.jsclass Checkout {  constructor() {    this.paymentProcessor = new Stripe('USD');  }  makePayment(amount) {    //Multiplying by 100 to get the cents    this.paymentProcessor.createTransaction(amount * 100);  }}//Stripe.js//Custom Stripe implementation that calls the Stripe APIclass Stripe {  constructor(currency) {    this.currency = currency;  }  createTransaction(amount) {    /*Call the Stripe API methods*/    console.log(`Payment made for $${amount / 100}`);  }}

Notice that we created a dependency between our Checkout class (high-level module) and Stripe (low-level module), violating the Dependency Inversion Principle. The dependency is especially noticeable when we convert the amount to cents. The Checkout should not care about which payment processor is being used, it only cares about making a transaction.

To decouple these two modules, we would have to implement an intermediary between the checkout and the payment processor, creating an abstraction so that no matter what payment processor we use, the Checkout class will always work with the same method calls. The new PaymentProcessor class will be in charge of adapting everything to payment processor to be used (in this case, Stripe). The intermediary class will have the following code:

//PaymentProcessor.jsclass PaymentProcessor {  constructor(processor, currency) {    this.processor = processor;    this.currency = currency;  }  createPaymentIntent(amount) {    const amountInCents = amount * 100;    this.processor.createTransaction(amountInCents);  }}

As you can see, the createPaymentIntent on the PaymentProcessor class is converting the amount to cents. And now we refactor the Checkout class to implement the abstraction:

//Checkout.jsclass Checkout {  constructor() {    this.paymentProcessor = new PaymentProcessor(new Stripe('USD'), 'USD');  }  makePayment(amount) {    this.paymentProcessor.createPaymentIntent(amount);  }}

Now, if we ever need to change our payment processor, we can do so by passing the new processor instead of Stripe on the Checkout constructor.

Imagine that now we are asked to replace Stripe with another payment processor that does not require for the amount to be converted to cents but on every transaction asks for the currency that's going to be used. The resulting code will be the following:

//Checkout.jsclass Checkout {  constructor() {    this.paymentProcessor = new PaymentProcessor(new BetterProcessor(), 'USD');  }  makePayment(amount) {    this.paymentProcessor.createPaymentIntent(amount);  }}//PaymentProcessor.jsclass PaymentProcessor {  constructor(processor, currency) {    this.processor = processor;    this.currency = currency;  }  createPaymentIntent(amount) {    this.processor.createTransaction(amount, this.currency);  }}//BetterProcessor.jsclass BetterProcessor {  createTransaction(amount, currency) {    console.log(`Payment made for ${amount} ${currency}`);  }}

Notice how on the Checkout we only changed the payment processor to be used on the constructor and the makePayment method remained untouched. We adapted the intermediary class PaymentProcessor to the processor needs.

We removed the dependency between Checkout and the processor used by implementing the intermediary class PaymentProcessor, following the Dependency Inversion Principle.


Original Link: https://dev.to/josuerodriguez98/dependency-inversion-principle-35i

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To