Clean code
  • Overview
    • Introduction
    • Why is clean code so important?
    • What Is Clean Code?
    • How to write clean code?
    • Conventions
  • The key principles of clean code
    • Meaningful Names
    • Functions
    • Classes
    • Comments
    • Error Handling
      • Exception handling best practices
    • Unit Tests
    • Formatting
    • Objects and Data Structures
    • Simple Design Rules
    • Concurrency
    • Code Smells
  • Building Maintainable Software
    • Write Short Units of Code
    • Write Simple Units of Code
    • Write Code Once
    • Keep Unit Interfaces Small
    • Write Clean Code
    • Automate Tests
  • Bonus
    • SOLID Principle
      • SRP - Single Responsibility Principle
      • OCP - Open-Closed Principle
      • LSP - Liskov Substitution Principle
      • ISP - Interface Segregation Principle
      • DIP - Dependency Inversion Principle
    • LoD Principle
    • YAGNI Principle
    • DRY Principle
    • Fail Fast principle
    • Hollywood Principle
    • Library vs Framework
    • Coupling and Cohesion
    • AOP - Aspect-Oriented Programming
      • Building an AOP framework
    • OOP Design Pattern
    • Technical Dept
    • How to learn software Design and Architecture - Roadmap
    • Microservcies
      • Defining the scope of a microservice
      • Step-by-Step: How to Identify Over-Scoped Microservices
      • Benefits of Grouping or Consolidating Microservices
      • A practical step-by-step plan to consolidate microservice
Powered by GitBook
On this page
  1. Bonus
  2. SOLID Principle

OCP - Open-Closed Principle

Objects or entities should be open for extension, but closed for modification.

The Open-Closed principle (OCP) states that classes should be open for extension but closed for modification. In doing so, the extension allows us to implement new functionality to the module, we stop ourselves from modifying existing code and causing potential new bugs.

Example

To understand this principle, let's look at an example like this:

class UserService {

    public void create(User user) {
        if (user.getPassword().length() >= 8) {
            // do something
        }
    }    

    public void update(User user) {
        if (user.getPassword().length() >= 8) {
            // do something
        }
    }
}

With the above example, if we put the validation inside the logic, we can get the following problem:

  • Adding a new validation we have to modify a lot.

  • Hard to test, we have to test both the logic and the validation of the implementation.

Now, we move the validation to different classes for processing. Then if we want to change another way of validating for the user, just change it in the class validator or create a new instance of the validator.

class UserService {
    private Validator validator;
 
    public UserService(Validator validator) { // Open for extension
        this.validator = validator;
    }
 
    public void save() {
        if (validator.isValid(user)) {
            // do something
        }
    }
    
    public void update() {
        if (validator.isValid(user)) {
            // do something
        }
    }
}
 
interface Validator {
    boolean isValid(User user);
}
 
class UserValidator1 implements Validator {
    @Override
    public boolean isValid(User user) {
        return user.getPassword().length() >= 8;
    }
}

// Open for extension 
class UserValidator2 implements Validator {
    @Override
    public boolean isValid(User user) {
        return isNotBlacklist(user.getUsername()) && isStrongPassword(user.getPassword());
    }
    
    private boolean isNotBlacklist(String username) {
    }
    
    private boolean isStrongPassword(String password) {
    }
}
 
public class OCPExample {
    public static void main(String[] args) {
        UserService userService1 = new UserService(new UserValidator1());
        UserService userService2 = new UserService(new UserValidator2());
    }
}

Doing it this way, we have followed the Single Responsibility Principle (we have moved the additional responsibility to another class, each class only does one thing). Now, we don't have to modify the logic class. If we want to add another way to validate the data, we just need to create a new class and inject it into the constructor.

The Open-Closed Principle can also be achieved in a variety of ways: using inheritance or using compositional design patterns such as Strategy Pattern.

PreviousSRP - Single Responsibility PrincipleNextLSP - Liskov Substitution Principle

Last updated 2 years ago