Open/Closed Principle and Backward Compatibility

Ali Ashoori
Level Up Coding
Published in
4 min readJan 28, 2020

--

Background: There are already plenty of resources around the web, articles, and books explaining S.O.L.I.D principles and coding best practices, but I couldn’t stop myself from sharing a short example about the concept that I greatly have a passion about, S.O.L.I.D principles and design patterns. So, here is another example of the Open/Closed Principle which indeed was part of a coding challenge interview I had from one of the Banks so it shows the importance of it — let’s nail it once for all.

Introduction: The principle states that a class should be open to extensions but closed to modifications. This especially helps when the requirements or the rules change frequently or at least are probable to be. Conforming to OCP will protect your code against backward compatibility concerns. In other words, if the new requirements must be handled by a class, then there’s no need to modify it rather we would only need to introduce a subclass for it which will then introduce the new logic independently. Purely simple, now we want to take a closer look at this through an example.

The case study demonstrates processing a payment request with attention to the value of the property amount. This is about having four different gateways of Default, Regular, Priority, and Premium — each of which processes the payment differently based on the value of the amount coming from the payload request. Here’s how these should process the payment:

  • Default — values from 1 to 50 having a service cost of 15%
  • Regular — values from 50 to 100 having a service cost of 10%
  • Priority — values from 100 to 300 having a service cost of 5%
  • Premium — value above 300 no service cost considered

Simple but interesting enough to kick off implementation. First, we will see how the logic can be implemented naively and then after how we can protect our code against backward compatibility issues using OCP.

The Naive Approach

Figure 1 — Naive approach with three explicit If’s and one implicit for values greater than 300

The Better Approach with attention to OCP

We now need to abstract away these if statements so that we will stop modifying the code with any new changes introduced besides that we need to test each of the conditions separately. Remember that in a real-world example these conditions could be much complicated and more lengthy than what is shown in Figure 1 which means we will need to have a better strategy to handle backward compatibility as well as modular coding practices. We will do it together through the following steps.

Step 1 — We create an interface that would the only point of contact for any consumer of our function above. This will give us a starting point for abstracting away the logic and separate them.

Figure 2 — The interface responsible to be communicating with the clients

Step 2 — Now we define the gateway interface which comes with two operations one to see if the incoming request matches the criteria and the second operation would be the actual payment processor.

Figure 3 — Payment Gateway interface with Is Match method to resolve proper payment processor

Step 3 — Now it’s time to implement the four different gateways that earlier mentioned each carries a different logic against the amount value.

Figure 4 — Payment Gateways hosting the actual logics independently (ready for Unit Testing)

Step 4 — And finally, the Payment Processor class which is responsible to process the payment coming from the request. This class will host all of the gateway rules and applies the one which accurately meets the required conditions.

Note: Adding the gateways itself could be also refactored using reflections so that every single time a new rule gets added to the system a developer would not need to manually add that in the Payment Processor or any other module which depends on them. But for the sake of simplicity and focusing on the subject it has been ignored.

Figure 5 — The Payment Processor class (no if statement used anymore — Open for extension Closed to modification)

Awesome, isn’t it? I like this piece of code that amazingly abstract away all of those if statements into independent separate classes ready for unit testing. As it is shown, our Payment Processor class is now open to extension as we can add endless payment gateways and closed to modifications as we won’t need to make any changes in this class regardless of the number of gateways that might be added.

And here is a dummy class showing how the caller would invoke the code above:

Figure 6 — The consumer class which invokes the calculations

Hopefully, now this proves how easy it is to implement OCP and with a bit of practice and challenge to apply it also in real-world scenarios.

Happy coding!

--

--