SOLID Principles / Open - closed principles -
shota.n
Posted on January 18, 2024
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…
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!
Posted on January 18, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.