Open/Closed Principle

Principle

Classes should be open for extension but closed for modification.

This principle sounds like a riddle! It essentially means design your application in a way to keep existing code from breaking when you implement new features.

  • "Open to extension" means you can add new features.
  • "Closed to modification" means the existing code (before adding a new feature) does not have to be modified when you added the new features.

In other words, new functionality does not require a rewrite of existing code. If you aren't modifying the existing code, you know you aren't breaking any of it.1

Let's be real for a moment: of course, you will be modifying your code when you add a new feature to your software. The principle says, design the code in a way that any future addition will require small modification of the existing code (so the risk of breaking existing features is minimized).

Okay! how? Well, one possibility is that all (the bulk of) the new functionality is contained in the newly added class(es). Here is a simple example: suppose you are building an application that involves online shopping with the following partial UML diagram:

Moreover, assume the methods in Shipping are implemented for ground shipping. Now your business expands overseas and you want to add a new method of shipping (e.g. Air Shipping). How would you go about updating the software?

Well, one approach would involve updating the existing Shipping class to accommodate the new shipping method. For instance, a getShippingCost() cost method would be updated as:

public double getShippingCost(Order order, String shipping) {
    if ("ground".equals(shipping)) {
        // calculate the total cost for Ground shipping
    } else if ("air".equals(shipping)) {
        // calculate the total cost for Air shipping
    }
}

This makes for bloated methods, increased complexity, and a higher risk to break the current functionality of the Shipping class. The Open/Closed principle suggests keeping the Shipping class closed to modification but open to extension. A design that adheres to this principle could use inheritance:

In the above design, the AirShipping would override the implementation of getShippingCost accordingly. Since you are not changing anything in the Shipping class, you will not be breaking any of the features before the "Air shipping" was introduced.

Aside: I claim the following is yet a better design. I leave it to you as an exercise to explain why!


1 As with the Single Responsibility Principle, this principle is applied to all software entities (classes, modules, functions, etc.)