Open-Closed Principle (OCP)

Learn about the Open-Closed Principle (OCP) and its benefits. Extend functionality without modifying code, ensuring maintainability and reusability.


Usage

πŸ“ Guideline

Open-Closed Principle: Classes should be closed for modification but open for extension.

The Open-Closed Principle dictates that you shouldn't need to change existing code to add more functionality to a class. Instead, you should be able to extend its behavior without modifying its implementation.

πŸ› οΈ How to Apply

  • Abstraction: Abstract classes and interfaces can define a common contract for different implementations, allowing new functionality to be added by implementing these abstractions. πŸ”‘
  • Inheritance: Use inheritance to create specialized classes that extend a base class, enabling additional functionality to be added without modifying the existing code. 🧬
  • Polymorphism: Utilize polymorphism to accept different implementations of a common interface, promoting extensibility. πŸ”€
  • Composition: Exctend objects using composition rather than modifying existing classes directly, allowing new behavior to be added by combining different components. πŸ”§
  • Dependency Injection: Inject dependencies into classes to allow for flexible component replacement and avoid hard dependencies. πŸ’‰

Pros and Cons

πŸ‘ Pros

  • Enhanced Modularity: Promotes modularity by containing changes within specific classes or modules, reducing the impact on other parts of the codebase. 🧩
  • Maintainability: The principle promotes a design that minimizes modifications to existing code, making it easier to maintain and reducing the risk of introducing bugs. πŸ› οΈ
  • Reusability: By following the Open-Closed Principle, code becomes more reusable as new functionality can be added by extending existing classes or implementing abstractions. ♻️

πŸ‘Ž Cons

  • Increased Complexity: Adhering to the Open-Closed Principle may introduce additional layers of abstraction and complexity, which can be challenging to manage in certain scenarios. πŸ€”
  • Potential Overengineering: Applying the principle too rigidly can lead to over-engineering, resulting in unnecessary abstractions and reduced development speed. 🚧

Examples

❌ Bad

class Order {
  // ...
  
  calculateTotal() {
    // calculate total order cost
  }
  
  applyDiscount() {
    // apply discount based on user type
  }
  
  generateInvoice() {
    // generate invoice for the order
  }
}

βœ… Good

interface Order {
  calculateTotal(): number;
}
 
class RegularOrder implements Order {
  calculateTotal() {
    // calculate total order cost
  }
}
 
class DiscountedOrder implements Order {
  calculateTotal() {
    // calculate total order cost with discount
  }
}
 
class OrderInvoice {
  generate(order: Order) {
    // generate invoice for the order
  }
}

References

🧱 SOLID Principles

SOLID is an acronym for five other class-design principles:

  • Single Responsibility Principle: Encourages classes to have a single responsibility, aligning with the Open-Closed Principle for better code maintainability. 🎯
  • Liskov Substitution Principle: Focuses on substitutability, supporting the Open-Closed Principle by allowing seamless extension with subclass objects. πŸ”„
  • Interface Segregation Principle: Suggests smaller and more focused interfaces, complementing the Open-Closed Principle's emphasis on abstraction and extension. πŸ“¦
  • Dependency Inversion Principle: Promotes depending on abstractions, aligning with the Open-Closed Principle's goal of extending functionality without modifying existing code. πŸ”§
  • Don't Repeat Yourself (DRY): DRY principle reduces code duplication, helping adhere to the Open-Closed Principle by minimizing unnecessary modifications. ♻️