Solid Design Principles In Javascript (Part 2) — Open-Closed Principle / Liskov Substitution Principle
Tags: javascript, typescript, designpattern, soliddesign
Published: 2022-02-10 13:09:44


__Design is beautiful when it is simple__

Hello readers, in my previous article i talked about solid design patterns and covered the first principle (**Single Responsibility Principle**). In this article we focus on the second and third principle which is the **Open Closed Principle** and the **Liskov Substitution Principle**.

If you haven’t read part 1 of this article, don’t worry you can read it here.

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

You can also access the full code example on my GitHub
https://github.com/Caleb-Mantey/solid-design-principles-in-js

In our previous article we had some code that looked like this:

### Mailer

```javascript
class Mailer{
        constructor(mail, mailerFormats){
            this.mail = mail
            this.mailerFormats = mailerFormats
            this.smtpService = new MailerSmtpService()
        }         

        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(){
           this.smtp_con = this.smtp_service_connection()
        } 
       
        send (mail){
            this.smtp_con.send(mail)
            // can easily change to be this if a service requires    this implementation - smtp_con.deliver(mail)
        }     
   
         smtp_service_connection(){
            // Connects to smtp service
        }
    }
```

### 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;
        }
    }
```

Looking at the code above we are not doing anything much, we have just separated the logic for sending emails into separate classes.

The code above is doing the following.

- A class that connects to the smtp service (**MailerSmtpService**)
- 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**)

From the code above we can simply call the `Mailer` class and pass some required properties to its constructor method (mail, mailerformats) which will be used to setup our mail.

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

Now lets see how we can make this code even better with the open-closed principle.

## Open-closed Principle

> This principle states that a class must be open for extension but close to modification.

This principle focus on the fact that a class must be easily extended without changing the contents of the class. If we follow this principle well we can actually change the behaviour of our class without ever touching any original piece of code. This also means if a Developer named _Fred_ works on a certain feature and another Developer named _Kwame_ wants to add some changes, then _Kwame_ should be able to do that easily by extending on the features _Fred_ has already provided.
Let’s take our `MailerSmtpService` class in the first example and let’s make it support this principle.

### MailerSmtpService — ( Initial Implementation)

This is our initial implementation for the `MailerSmtpService`. Nothing fancy here yet. All we are doing is connecting to an smtp service in the constructor method and storing the result of the connection in `this.smtp_con`, then we provide a `send` method that takes a `mail` as an argument and sends an email.
But we have a problem here. Let’s say we want to change the smtp service provider. We will have to come to our `MailerSmtpService` class and implement the new smtp service here. However we can do better and use the **open-closed principle** to make our code more maintainable and even provide the option for switching smtp service providers without touching any piece of existing code.

```javascript
class MailerSmtpService{
        constructor(){
           this.smtp_con = this.smtp_service_connection()
        }

        send (mail){
            this.smtp_con.send(mail)
            // can also be this.smtp_con.deliver(mail)
        }

        smtp_service_connection(){
            // Connects to smtp service
        }
}
```

### MailerSmtpService — ( Enhanced Version)

Now in order to support the **open-closed principle**, we will remove the `smtp_service_connection` method from our `MailerSmtpService` class and rather we pass the method as a parameter in the `MailerSmtpService` constructor, then in a subclass (`PostMarkSmtpService` and `SendGridSmtpService`) that inherits from `MailerSmtpService` we call the constructor method of the base class with `super(() => {})` then we pass a method which handles the smtp connection depending on the smtp provider in use. Also we override the send method in the parent class (`MailerSmtpService` ) and each of the child classes(`PostMarkSmtpService` and `SendGridSmtpService`) implement their custom versions of the send method.

```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)
        }
}
```

In our mailer class we can now create a new `PostMarkSmtpService` or `SendGridSmtpService` in our app and we can easily keep extending to support different smtp service by inheriting from the `MailerSmtpService` class.

```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)))
        }
}
```

With this implementation a developer can keep extending the `MailerSmtpService` to support more mailing services without modifying the existing logic in the `MailerSmtpService` class.

This is the **open-closed principle** at work.

## Liskov Substitution principle

The next principle is the **Liskov substitution principle** it is easier to understand this principle because we have already implemented it in our code example above.

This principle states that

> Derived or child classes must be substitutable for their base or parent classes.

This means that a parent class should be easily substituted by the child classes without blowing up the application. This principle can be seen in the example above where we created a parent class called `MailerSmtpService` and we had two child classes called `PostMarkSmtpService` and `SendGridSmtpService`. You can observe that the child classes where used as substitute for the parent class with ease.

For example with typescript we can infer the type of `PostMarkSmtpService` and `SendGridSmtpService` to be their parent class `MailerSmtpService` and the application will still work without any errors.

```typescript
mailerSmtp: MailerSmtpService = new MailerSmtpService();
postmarkMailerSmtp: MailerSmtpService = new PostMarkSmtpService();
sendgridMailerSmtp: MailerSmtpService = new SendGridSmtpService();
```

Thanks for your time. Give me a follow or a like if you loved this article.
Watch out for the final part (part 3) of this series where we talk about the last two principles (**Interface Segregation Principle** and **Dependency Inversion**)