Solid Design Principles In Javascript (Part 3) — Interface Segregation Principle & Dependency Inversion
Tags: javascript, typescript, designpatterns, soliddesignprinciples
Published: 2022-02-11 12:25:16


__Good design is obvious. Great design is transparent.__

Welcome back folks, hope you have been enjoying this series so far. This is the final part of the series. In the previous article we looked at the second and third solid design principle (**Open Closed Principle** and the **Liskov Substitution Principle**). In this article we focus on the last two principles which are the **Interface Segregation Principle** and **Dependency Inversion**.

If you haven’t read part 1 or part 2 of this article, you can access it here.

- **Part 1:** https://dev.to/calebmantey/solid-design-principles-in-javascript-part-1-single-responsibility-principle-4k0c

- **Part 2:** https://dev.to/calebmantey/solid-design-principles-in-javascript-part-2-open-closed-principle-liskov-substitution-principle-2h63

You can also access the full code example on my GitHub
https://github.com/Caleb-Mantey/solid-design-principles-in-js
Now lets recap what our code looks like from (part 1 & 2)

### Mailer

```javascript
class Mailer{
        constructor(mail, mailerFormats){
            this.mail = mail
            this.mailerFormats = mailerFormats
            this.smtpService = new PostMarkSmtpService()
            // OR this.smtpService = new SendGridSmtpService()
        }    
    
        send(){
            // Loops through mail formats and calls the send method
            this.mailerFormats.forEach((formatter) => 
            this.smtpService.send(formatter.format(this.mail)))
        }
}
```

### MailerSmtpService

```javascript
class MailerSmtpService{
        constructor(smtp_connection = () => {
            //connects to default smtp service
        }){
           this.smtp_con = smtp_connection()
        }   
     
        send (mail){
            this.smtp_con.send(mail)
        }
}
```

### PostMarkSmtpService

```javascript
class PostMarkSmtpService extends MailerSmtpService {
        constructor(){
           super(() => {
                // Connects to postmark smtp service
            })
        }    
    
        send (mail){
            this.smtp_con.send(mail)
        }
}
```

### SendGridSmtpService

```javascript
class SendGridSmtpService extends MailerSmtpService {
        constructor(){
            super(() => {
                // Connects to sendgrid smtp service
            })
        }      
  
        send (mail){
            this.smtp_con.deliver(mail)
        }
}
```

### HtmlFormatter

```javascript
class HtmlFormatter{
        constructor(){
        }        
        
        format(mail){
             // formats to html version of mail
             mail = `<html>
            <head><title>Email For You</title></head>
            <body>${mail}</body>
            </html>`;            
            return mail;
        }
}
```

### TextFormatter

```javascript
class TextFormatter{
        constructor(){
        }   
             
        format(mail){
             // formats to text version of mail
             mail = "Email For You \n" + mail;          
             return mail;
        }
}
```

The code above is doing the following.

- A base class that sets up the behaviour of an smtp service (**MailerSmtpService**)
- A child class that inherits from **MailerSmtpService** class and connects to an smtp service (**PostMarkSmtpService**)
- A child class that inherits from **MailerSmtpService** class and connects to an smtp service (**SendGridSmtpService**)
- A class that formats our mail in text (**TextFormatter**)
- A class that formats our mail in html (**HtmlFormatter**)
- A class responsible for sending the mail (**Mailer**)

This code is tied together in our **Mailer** class when it is instantiated and can be used to send an email like this.

```javascript
const mailer = new Mailer(“hello kwame”, [new HtmlFormatter(), new TextFormatter()])
mailer.send();
```

This is the implementation we had from part 1 and 2 of this article. To follow along i recommend you read part 1 and 2 if you haven’t read it. You can do so by visiting the links provided at the beginning of this text.

## Interface Segregation Principle

This principle states that

> Do not force any client to implement an interface which is irrelevant to them.

This principle is similar to the `single responsibility principle` but applies to interfaces. It is usually referred to as the first principle of interfaces. Since javascript does not support interfaces we will implement it with typescript to get a better understanding. Let’s take our first example where we had the `HtmlFormatter` and `TextFormatter` class which formats our email and do some few changes.

### IFormatter

```typescript
export interface IFormatter {
        format(mail: string): string
        custom_styles(): string
}
```

### HtmlFormatter

```typescript
class HtmlFormatter implements IFormatter {

       format(mail: string) {
            // sends html version of mail
            mail = `<html>
            <head>
            <title>Email For You</title>
            ${this.custom_styles()}
            </head>
            <body>${mail}</body>
            </html>`;
            return mail;
        }

        custom_styles(): string {
            return "<style>body{background-color: blue}</style>"
        }
}
```

### TextFormatter

```typescript
class TextFormatter implements IFormatter {

        format(mail: string) {
            // sends text version of mail
            mail = "Text Version \n" + mail;
            return mail;
        }

        custom_styles(): string {
            return ""
        }
}
```

Now with typescript included, we have to declare the return types for our functions and the data type for our variables. We also have the ability to create an interface like we do in languages like c# and java.

With this features available to us, we have added an interface (`IFormatter`) that exposes two functions (`format` and `custom_styles`). Our `TextFormatter` and `HtmlFormatter` class are also implementing this interface’s (`IFormatter`) methods. This will make sure we have the `format` and `custom_styles` methods been implemented in both our `TextFormatter` and `HtmlFormatter` class. If the `format` and `custom_styles` methods are not present in any class that implements the `IFormatter` interface, our application will throw an error. But, there is a problem here because the `custom_styles` method is only needed in the `HtmlFormatter` class to help in styling the html document. However since both the `TextFormatter` and `HtmlFormatter` class are using the same interface (`IFormatter`) they both have to implement the same methods(`custom_styles` and `format`) forcing as to write an empty `custom_styles` method for the `TextFormatter` class.

Now lets see a better approach:

### IStyles

```typescript
export interface IStyles {
        custom_styles(): string
}
```

### IFormatter

```typescript
export interface IFormatter {
        format(mail: string): string
}
```

### HtmlFormatter

```typescript
class HtmlFormatter implements IFormatter, IStyles {
       format(mail: string) {
            // sends html version of mail
            mail = `<html>
            <head>
            <title>Email For You</title>
            ${this.custom_styles()}
            </head>
            <body>${mail}</body>
            </html>`;
            return mail;
        }

        custom_styles(): string {
            return "<style>body{background-color: blue}</style>"
        }
}
```

### TextFormatter

```typescript
class TextFormatter implements IFormatter {
        format(mail: string) {
            // sends text version of mail
            mail = "Text Version \n" + mail;
            return mail;
        }
}
```

Now you can see from the code refactor we have a new interface `IStyles` as well as our previous interface `IFormatter`. Also the `HtmlFormatter` class implements both the `IStyles` and `IFormatter` interface whiles the `TextFormatter` class implements only the `IFormatter` interface. This now makes our code cleaner and ensures the right methods are been implement in the classes that needs them. Now our `TextFormatter` class does not need to implement the `custom_styles` method since we have removed the `custom_styles` method from the `IFormatter` interface to a new interface (`IStyles`). This makes our code more maintainable and scalable. This is the **Interface Segregation Principle** at work.

## Dependency Inversion Principle

This principle is divided into two parts and it states that

> High-level modules/classes should not depend on low-level modules/classes. Both should depend on abstractions.
> Abstractions should not depend on details. Details should depend on abstractions.

The above lines simply state that if a high level module or class will be dependent more on a low-level module or class, then your code would have tight coupling and if you try to make a change in one class it can break another class. It is always better to abstract the code to make classes loosely coupled as much as you can. This makes maintaining the code easy.

There’s a common misunderstanding that dependency inversion is simply another way to say dependency injection. However, the two are not the same.

In our previous example we created two new interfaces `IStyles` and `IFormatter` which where implemented in the `TextFormatter` and `HtmlFormatter` class. 
Now lets see how these classes can be used with abstraction in the example below:

### Mailer

```typescript
class Mailer {
    mail: string;
    mailerFormats: Array<IFormatter>; // abstraction
    smtpService: MailerSmtpService;

    constructor(mail: string, mailerFormats: Array<IFormatter>/*abstraction*/) {
        this.mail = mail;
        this.mailerFormats = mailerFormats;
        this.smtpService = new SendGridSmtpService();
    }

    send() {
        // Loops through mail formats and calls the send method
        this.mailerFormats.forEach((formatter) =>
        this.smtpService.send(formatter.format(this.mail))
        );
    }
}
```

Now lets look at the refactor of the `Mailer` class from our first example (the first principle - **Single responsibility principle**). You can see we now have a `mailerFormats` property which takes an array of `IFormatter` objects (`mailerFormats: Array<IFormatter>;`). This means any class that implements the `IFormatter` interface can be stored in this array. Also our `Mailer` class doesn't need to know about what formatter we are going to use. All it cares about is the formatter is implementing an `IFormatter` interface and it has a `format` method which we can call when the need arises. This will allow our `Mailer` class to be loosely coupled with our `HtmlFormatter` and `TextFormatter` class.
Our `Mailer` class is now depending an abstraction(`IFormatter`) of the `HtmlFormatter` and `TextFormatter` class.

Thanks for your time. This brings us to the end of this series. If you find my content interesting and want to learn more please like and give me a follow.

I will be posting a lot of articles on different topics in the coming weeks, so if you don’t want to miss out then keep your eye on this space to stay updated.

You can also follow me on my github.
https://github.com/Caleb-Mantey/