top of page

Open/closed principle

Updated: Jul 27, 2023

Continuing from my previous post about the Single Responsibility Principle as part of SOLID, next on the list is the Open/closed principle (the ‘O’ in SOLID), or OCP. Again, implementing this principle will help to write cleaner code. From a high-level view, Bertrand Meyer says “software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification”. So, what exactly does this mean?

What is it?

Your class (or software module) should be open for extension for when there are changes in requirements and so new behaviour can be added. Your class should also be closed for modification, as in you can change the behaviour of the class without needing to change the exiting code. The idea is that you want to make changes to classes by adding to it or building on top of it (through use of subclassing, polymorphism, inheritance etc.) rather than modifying the any existing code.

Why use it?

Change is inevitable as requirements change constantly and your code must be flexible to deal with this change. But when we do want to change the code, we want the impact to be minimal on the rest of the code base. If you need to change an existing code module to modify the behaviour, then that module and all it’s dependencies will need to be retested and re-compiled and re-deployed to production, creating a large surface area of change. On the other hand, if you need to create a new class that then inherits from a base class, and only this new class needs to be tested (which has no dependencies as it’s a new class), then the impact will be much smaller.

When to use it?

Although the OCP is an excellent rule of thumb, you need to have balance of when to use it. When starting new code, you should ideally follow the KISS principle, where you want to try to keep things simple. If a software module changes once, then accept it; if it changes again, then refactor it to follow OCP. The reason behind this is that OCP will bring with it added complexity; you need to know when that complexity is worth it, i.e. when software modules are likely to change.

Example

Below is an adapted example I’ve seen in code where the Open Closed Principle should be applied:


public class Validator {
  public void Validate(RegistrationModel model) {
    ValidateName(model);
    ValidatePostCode(model);
  }
  public void ValidateName(RegistrationModel model) {
    if (string.IsNullOrEmpty(model.Name)) {
      throw new Exception();
    }
  }
  public void ValidatePostCode(RegistrationModel model) {
    if (!IsValidPostCode(model.PostCode)) {
      throw new Exception();
    }
  }
  static bool IsValidPostCode(string postcode) {
    return Regex.IsMatch(postcode, "POSTCODE_REGEX");
  }
}
public class RegistrationModel {
  public string Name {
    get;
    set;
  }
  public string PostCode {
    get;
    set;
  }
  public string PhoneNumber {
    get;
    set;
  }
}

We have a model, which we want to perform some validation on. We have separated our validation checks ValidateName and ValidatePostCode into nice simple SRP methods. The class Validator is Open for extension, i.e. we can add new validation by adding a new method which is called in the validate method. But it is not closed for modification, because we will need to modify the Validator class and the Validate method to add new validation, so this is breaking the OCP. We need to refactor this class so we can add new validation to it, without touching the existing code.


 public class RefactoredValidator {
  public void Validate(RegistrationModel model, IList & lt; ValidatorItem & gt; validators) {
    foreach(var validatorItem in validators) {
      validatorItem.Validate(model);
    }
  }
}
public abstract class ValidatorItem {
  public abstract void Validate(RegistrationModel model);
}
public class NameValidator: ValidatorItem {
  public override void Validate(RegistrationModel model) {
    if (string.IsNullOrEmpty(model.Name)) {
      throw new Exception();
    }
  }
}
public class PostCodeValidator: ValidatorItem {
  public override void Validate(RegistrationModel model) {
    if (!IsValidPostCode(model.PostCode)) {
      throw new Exception();
    }
  }
  static bool IsValidPostCode(string postcode) {
    return Regex.IsMatch(postcode, "POSTCODE_REGEX");
  }
}

Our refactored Validate method now accepts a list of validators, which all implement an abstract method Validate. This Validate method will be called on each of the validators during validation.

Now if wanted to add new validation for the PhoneNumber field, we wouldn’t have to touch any of the existing code. All we would need to do is create a new ValidatorItem class for the PhoneNumber and then pass that into the Validate method of the Validator class.

Real World Example

Current example of public code that does follow the Open Closed Principle would be bootstrap.css files. These files are closed for modification because you don’t need to edit the source code, i.e. the bootstrap.css file to make changes, instead you can override it’s CSS to make your changes.

TL;DR

The OCP can help to minimize bugs and help maintainability when there’s a need to change code. But similarly to the Single Responsibility Principle, OCP will come with its own problems; you need to know when to use it as it will bring with it added complexity. Therefore, try to only apply OCP where the code is likely to change.

1 view0 comments

Recent Posts

See All

I'm a lead software developer currently working at AG Grid in London.

Technologies I'm currently focused on include Salesforce, .NET Core, Angular, SQL, React and Azure.

Other than that, in my spare time I watch the Arsenal at the Emirates, work on side projects or watch sports. Oh, and I'm also a part-time body builder.

You can contact me at vh@viqas.co.uk

profile.jpg

About Viqas Hussain

bottom of page