DEV Community

Cover image for SOLID Principles / Open - closed principles -
shota.n
shota.n

Posted on • Updated on

SOLID Principles / Open - closed principles -

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
Enter fullscreen mode Exit fullscreen mode

Bad Code

This code against to OCP. If you want to add new membership grade to DiscountManagerClass, 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…

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));

Enter fullscreen mode Exit fullscreen mode

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 IMemberwithout 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));

Enter fullscreen mode Exit fullscreen mode

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!

Top comments (0)