OCP - Open/Close Principle
The Open/Close Principle is the second principle in the Solid Design Principle.
- Software entities should be open for extension but closed for modification.
Today Will discuss about discount calculator system.
Violating OCP:
enum DiscountType {
case seasonal
case loyalty
case nodiscount
}
class DiscountCalculator {
func calculateDiscount(discountType: DiscountType, amount: Double) -> Double {
switch discountType {
case .seasonal:
return amount * 0.1
case .loyalty:
return amount * 0.15
case .nodiscount:
return 0
// Including a default case can handle unexpected cases but won't prompt Xcode to show an error or warning if a new DiscountType is added later. This means new cases could be missed without any compile-time notifications.
default:
return 0
}
}
}
// usage
let discountCalcualtor = DiscountCalculator()
discountCalcualtor.calculateDiscount(discountType: .seasonal, amount: 100)
discountCalcualtor.calculateDiscount(discountType: .loyalty, amount: 100)
discountCalcualtor.calculateDiscount(discountType: .nodiscount, amount: 100)
Issues with Violating OCP:
- Hard to Extend:
- Adding a new discount required modification of the function
calculateDiscount
.
- Adding a new discount required modification of the function
- Increase Complexity:
-
calculateDiscount
becomes complex with multiple discount types.
-
- Difficult Maintenance:
- Changes in discount logic require updates to
calculateDiscount
.
- Changes in discount logic require updates to
Adhering to OCP:
To Adhere to OCP, Use Protocol to confirm the contract between different types of discounts in the application.
protocol Discount {
func apply(amount: Double) -> Double
}
class SeasonalDiscount: Discount {
func apply(amount: Double) -> Double {
return amount * 0.1
}
}
class LoyaltyDiscount: Discount {
func apply(amount: Double) -> Double {
return amount * 0.15
}
}
class DiscountCalculatorOCP {
private var discounts: [Discount]
init(discounts: [Discount]) {
self.discounts = discounts
}
func calculateTotalDiscount(amount: Double) -> Double {
var totalDiscount = 0.0
for discount in discounts {
totalDiscount += discount.apply(amount: amount)
}
return totalDiscount
}
}
// usage
let seasonalDiscount = SeasonalDiscount()
let loyaltyDiscount = LoyaltyDiscount()
let discountCalculatorOCP = DiscountCalculatorOCP(discounts: [seasonalDiscount, loyaltyDiscount])
discountCalculatorOCP.calculateTotalDiscount(amount: 100)
Benefits of Adhering to OCP:
- Improved Maintainability:
- Adding New Discount types doesn't require modifying exising code.
- Enhanced Flexibility:
- Easily extendable with new discount class.
- Greater Reusability:
- Discount logic is reusable across different parts of the application.
Drawbacks:
- More Classes:
- Can lead to many small classes.
- Complex Dependency Management:
- More dependencies to manage.
- Design and Refactoring Overhead:
- Requires more effort to design and refactor.
Mitigating Drawbacks:
- Balanced Approach:
- Apply OCP judiciously, balancing simplicity and extensibility.
- Effective Documentation:
- Clear documentation helps navigate the codebase and understand class responsibilities.
- Use Patterns and Frameworks:
- Design patterns (like Strategy) and dependency management tools can help.
- Team Alignment:
- Ensure the team has a shared understanding of OCP and consistent practices.
- Performance Profiling:
- Profile and optimize performance to manage any overhead introduced by adhering to OCP.
Conclusion:
By understanding and applying the Open/Closed Principle thoughtfully, you can create more maintainable, understandable, and flexible software.
Single Responsibility Principle
Liskov Substitution Principle
Check My GitHub Swift Playground Repo.
Top comments (0)