SOLID Principles / Open - closed principles -
Tags: solidprinciples, softwaredevelopment, design
Published: 2024-01-18 13:24:53

## Introduction
Are you concerned with Software Design?
If so, this articles may be valuable information for you!


## What is “Open - closed principle” ?

It’s one of the “SOLID Principles”. It means “Object should be open for extension but closed to modification”.

In other words, the class should be extendable without change itself.

Let’s learning about “Open - closed principle**(referred to below as OCP)**”  with the following code!!

```
Sample Specification
- A Payment system is used on members-only EC Site.
	- Discounted rates based on membership grade
	- Calculate the billing amount of the product with the calculate method of the DiscountManager class
```

## Bad Code

This code against to OCP. If you want to add new membership grade to  `DiscountManager`Class, you will add `case:` of switch.

In that case, you against to “ the class should be extendable without change itself”.

If you do so, you may create a bug in the Class…

```tsx
enum Grade {
  PLATINUM = 'Platinum',
  GOLD = 'Gold',
  SILVER = 'Silver',
  BRONZE = 'Bronze',
}

class Member {
  constructor(public grade: Grade, public totalAmount: number) {}
}

class DiscountManager {
  calculate(member: Member) {
    let discountAmount: number;

    // Discount amount determined by membership grade
    switch (member.grade) {
      case Grade.PLATINUM:
        discountAmount = member.totalAmount * 0.3;
        break;
      case Grade.GOLD:
        discountAmount = member.totalAmount * 0.2;
        break;
      case Grade.SILVER:
        discountAmount = member.totalAmount * 0.1;
        break;
      case Grade.BRONZE:
        discountAmount = member.totalAmount * 0.05;
        break;
    }

    return member.totalAmount - discountAmount;
  }
}

const memberA = new Member(Grade.PLATINUM, 10000);
const memberB = new Member(Grade.GOLD, 15000);
const memberC = new Member(Grade.SILVER, 30000);
const memberD = new Member(Grade.BRONZE, 7000);

const discountManager = new DiscountManager();
console.log(discountManager.calculate(memberA));
console.log(discountManager.calculate(memberB));
console.log(discountManager.calculate(memberC));
console.log(discountManager.calculate(memberD));

```

## Good Code

So then, what should we do?

Create `IMember` as interface and implement it that includes Platinum/Gold/Silver/Bronze classes.

Then, you can delete “switch” in `DiscountManager`

class and it is enough that `DiscountManager` needs to call “calculate()” only.

Also, if you want to add a “Grade”, you can implement it from `IMember`without changing `DiscountManager`. It’s easy.

```
enum Grade {
  PLATINUM = 'Platinum',
  GOLD = 'Gold',
  SILVER = 'Silver',
  BRONZE = 'Bronze',
}

class IMember {
  grade: Grade;
  totalAmount: number;
  calculate: () => number;
}

class Platinum implements IMember {
  private DISCOUNT = 0.3;
  grade = Grade.PLATINUM;

  constructor(public totalAmount: number) {}

  calculate() {
    return this.totalAmount * this.DISCOUNT;
  }
}

class Gold implements IMember {
  private DISCOUNT = 0.2;
  grade = Grade.GOLD;

  constructor(public totalAmount: number) {}

  calculate() {
    return this.totalAmount * this.DISCOUNT;
  }
}

class Silver implements IMember {
  private DISCOUNT = 0.1;
  grade = Grade.SILVER;

  constructor(public totalAmount: number) {}

  calculate() {
    return this.totalAmount * this.DISCOUNT;
  }
}

class Bronze implements IMember {
  private DISCOUNT = 0.05;
  grade = Grade.BRONZE;

  constructor(public totalAmount: number) {}

  calculate() {
    return this.totalAmount * this.DISCOUNT;
  }
}

class DiscountManager {
  calculate(member: IMember) {

    return member.totalAmount - member.calculate();
  }
}

const memberA = new Platinum(10000);
const memberB = new Gold(15000);
const memberC = new Silver(30000);
const memberD = new Bronze(7000);

const discountManager = new DiscountManager();
console.log(discountManager.calculate(memberA));
console.log(discountManager.calculate(memberB));
console.log(discountManager.calculate(memberC));
console.log(discountManager.calculate(memberD));

```


## Share Your Thoughts: We Value Your Feedback!
Thank you for reading this post! Your insights and experiences are a vital part of what makes this community great. I would love to hear from you:

**- What are your thoughts on the topics we discussed?**
**- Do you have any additional insights or experiences to share?**
**- Are there other topics you'd like to see covered in future posts?**

Feel free to leave a comment below or reach out through [Your Contact Method – social media, email, etc.]. Your feedback not only helps me tailor future content to your interests but also sparks meaningful discussions that benefit all of us. Looking forward to your thoughts!