26 Oct 2023
The Open/Closed Principle states that "A software module/class should be open for extension and closed for modification."
In simple terms, once you've written a piece of code (a class or module), you should not have to change that code to add new features or behaviors. Instead, use techniques such as inheritance, interfaces, and abstract classes to create a foundation that can be extended with new functionality without altering the existing code.
Open for extension: This part of the principle states that your software should be able to be extended with new features or functionality without modifying the existing code. You can achieve this by using techniques such as inheritance, interfaces, and abstract classes to create a foundation that can be extended with new functionality.
Closed for modification: This means you shouldn't have to change the existing code when adding new features. Instead, use techniques like inheritance, interfaces, and abstract classes to build a base that can be expanded with new functionality without changing the existing code.
Examples of problems and their solutions that are addressed through the Open-Closed Principle (OCP):
1. Example: Adding New Payment Methods in an E-commerce System
-
Without OCP:
- Problem Description: Without OCP, you directly add conditional checks for each payment method within the payment processing code.
- Pseudo-Code:
// without OCP if (paymentMethod == "CreditCard") { ProcessCreditCardPayment(); } else if (paymentMethod == "PayPal") { ProcessPayPalPayment(); } else if (paymentMethod == "Bitcoin") { ProcessBitcoinPayment(); }
-
OCP Solution:
- Solution Description: The OCP solution involves using a common interface for payment processors and implementing separate classes for each payment method.
- Pseudo-Code:
// OCP solution with a common interface interface PaymentProcessor { ProcessPayment(); } // Implementations for specific payment methods class CreditCardPaymentProcessor implements PaymentProcessor { ProcessPayment() { // Process credit card payment } } class PayPalPaymentProcessor implements PaymentProcessor { ProcessPayment() { // Process PayPal payment } } class BitcoinPaymentProcessor implements PaymentProcessor { ProcessPayment() { // Process Bitcoin payment } }
2. Example: Adding Shape Types to a Drawing Application
-
Without OCP:
- Problem Description: Without OCP, you add conditional checks for each shape type within the drawing application code.
- Pseudo-Code:
// without OCP if (shapeType == "Circle") { DrawCircle(); } else if (shapeType == "Square") { DrawSquare(); } // Add more if needed...
-
OCP Solution:
- Solution Description: The OCP solution involves defining a common interface for shapes and creating separate classes for each shape type.
- Pseudo-Code:
// OCP solution with a common interface interface Shape { Draw(); } // Implementations for specific shapes class Circle implements Shape { Draw() { // Draw a circle } } class Square implements Shape { Draw() { // Draw a square } }
3. Example: Adding Filters to an Image Processing System
-
Without OCP:
- Problem Description: Without OCP, you add conditional checks for each filter type within the image processing code.
- Pseudo-Code:
// without using OCP if (filterType == "Blur") { ApplyBlurFilter(); } else if (filterType == "Sharpen") { ApplySharpenFilter(); } // Add more if needed...
-
OCP Solution:
- Solution Description: The OCP solution involves using a common interface for image filters and implementing individual filter classes.
- Pseudo-Code:
// OCP solution with a common interface interface ImageFilter { Apply(); } // Implementations for specific filters class BlurFilter implements ImageFilter { Apply() { // Apply blur filter } } class SharpenFilter implements ImageFilter { Apply() { // Apply sharpen filter } }
4. Example: Implementing Discount Rules in a Shopping Cart
-
Without OCP:
- Problem Description: Without OCP, you directly add conditional checks for each discount rule within the shopping cart code.
- Pseudo-Code:
// without using OCP if (promotionType == "PercentageDiscount") { ApplyPercentageDiscount(); } else if (promotionType == "BuyOneGetOneFree") { ApplyBuyOneGetOneFree(); } // Add more if needed...
-
OCP Solution:
- Solution Description: The OCP solution involves defining a common interface for discount rules and creating classes for each type of discount rule.
- Pseudo-Code:
// OCP solution with a common interface interface DiscountRule { ApplyDiscount(); } // Implementations for specific discount rules class PercentageDiscount implements DiscountRule { ApplyDiscount() { // Apply percentage discount } } class BuyOneGetOneFree implements DiscountRule { ApplyDiscount() { // Apply buy one get one free discount } }
Example: Creating a Game with Multiple Character Types
-
Without OCP:
- Problem Description: Without OCP, you directly add conditional checks for each character type within the game code, which can become unwieldy as more character types are added.
- Pseudo-Code:
// without OCP if (characterType == "Warrior") { CreateWarriorCharacter(); } else if (characterType == "Mage") { CreateMageCharacter(); }
-
OCP Solution:
- Solution Description: The OCP solution involves using inheritance and a common base class for characters. Each specific character type is implemented as a subclass of the base class, allowing new character types to be added without modifying the existing game code.
- Pseudo-Code:
// Base class for characters using inheritance
class Character {
// Common character attributes and methods
}
// Warrior class inherits from Character
class Warrior extends Character {
// Warrior-specific attributes and methods
void Attack() {
// Implement warrior's attack logic
}
}
// Mage class inherits from Character
class Mage extends Character {
// Mage-specific attributes and methods
void CastSpell() {
// Implement mage's spell-casting logic
}
}
// Usage in the game
Character playerCharacter;
// Creating a warrior
playerCharacter = new Warrior();
playerCharacter.Attack();
// Creating a mage
playerCharacter = new Mage();
playerCharacter.CastSpell();
In this revised pseudo-code, the "extends" keyword is used to indicate that the Warrior
and Mage
classes inherit from the Character
base class, following the principles of object-oriented inheritance.
In all these examples, the Open-Closed Principle is applied by ensuring that you can add new functionality or features without altering the existing code. This promotes maintainability, reduces the risk of introducing bugs, and allows the software to evolve more easily.