In today’s instalment, we’ll inspect the Open-Closed Principle (OCP). It’s the second of the SOLID Principles – the O in the SOLID acronym refers to the OCP. Here is a high-level explanation of what the SOLID Principles are trying to accomplish.
The OCP is one of my favourite SOLID Principles. It promotes the building of pluggable systems; i.e. systems where we may replace modules and components by counterparts offering similar, but different capabilities.
The Open-Closed Principle:
A software artefact should be open for extension
but closed for modification.
The OCP means that we should aim to structure or software in such a manner that when we need to extend it, we can write new code rather than modify existing code.
I think this makes a lot of sense. If every time we want to add a minor extension to our system, we must make significant changes in our existing code, then there is a substantial flaw in the system architecture. The OCP encourages us to structure systems in such a way that we rarely need to touch existing code – ideally never. That ideal is not achievable. There always come times when we must change code. However, at least we can aim for minimal code changes.
The OCP directly counteracts software rigidity. A rigid system is one that is difficult to change. If we follow the OCP and manage to write only new code for a given requirement, then how does rigidity affect us? It doesn’t – we have sidestepped that snakepit.
The OCP underpins pluggable systems. In a pluggable system, we can easily unplug existing functionality by plugging in new functionality.
I have found that the OCP is often best explained in terms of violations and how to fix them. Say, we have this code in our application:
var paypal= new PaypalPaymentGateway(paypalConfig); paypal.Pay(paypalCart, creditCard);
If we wanted to use a different payment gateway, Stripe, then we have little choice but to replace the code to suit Stripe:
var stripe = new StripePaymentGateway(stripeConfig); stripe.BasicPay(stripeCreditCard, creditCart);
We had to change these calls; we had to modify existing code. We are not complying with the OCP.
Can we do better? It would be useful if we could have a payment gateway object and it could be either a PaypalPaymentGateway or StripePaymentGateway.
It is possible. Firstly, let’s create an interface, IPaymentGateway, which has a Pay() method:
public interface IPaymentGateway { void Pay(ShoppingCart cart, CreditCard card); }
And both the PaypalPaymentGateway and StripePaymentGateway classes implement IPaymentGateway.
Now the code to pay using either of our payment gateways becomes
PaymentGateway.Pay(shoppingCart, creditCard);
Reference PaymentGateway is declared as IPaymentGateway.
We have made it so the payment gateway is pluggable into the calling code. None of this code knows anything about either Paypal or Stripe! Making it so that it works for yet another payment gateway, say, Braintree, will mean writing a BraintreePaymentGateway adapter class which implements IPaymentGateway.
(For the curious reader, here is a more detailed and dynamic version of this payment gateway example.)
That’s it for today. We’ll delve further into the OCP tomorrow.
The Open-Closed Principle
/by Olaf ThielkeThe Open-Closed Principle
In today’s instalment, we’ll inspect the Open-Closed Principle (OCP). It’s the second of the SOLID Principles – the O in the SOLID acronym refers to the OCP. Here is a high-level explanation of what the SOLID Principles are trying to accomplish.
The OCP is one of my favourite SOLID Principles. It promotes the building of pluggable systems; i.e. systems where we may replace modules and components by counterparts offering similar, but different capabilities.
The OCP means that we should aim to structure or software in such a manner that when we need to extend it, we can write new code rather than modify existing code.
I think this makes a lot of sense. If every time we want to add a minor extension to our system, we must make significant changes in our existing code, then there is a substantial flaw in the system architecture. The OCP encourages us to structure systems in such a way that we rarely need to touch existing code – ideally never. That ideal is not achievable. There always come times when we must change code. However, at least we can aim for minimal code changes.
The OCP directly counteracts software rigidity. A rigid system is one that is difficult to change. If we follow the OCP and manage to write only new code for a given requirement, then how does rigidity affect us? It doesn’t – we have sidestepped that snakepit.
The OCP underpins pluggable systems. In a pluggable system, we can easily unplug existing functionality by plugging in new functionality.
I have found that the OCP is often best explained in terms of violations and how to fix them. Say, we have this code in our application:
If we wanted to use a different payment gateway, Stripe, then we have little choice but to replace the code to suit Stripe:
We had to change these calls; we had to modify existing code. We are not complying with the OCP.
Can we do better? It would be useful if we could have a payment gateway object and it could be either a PaypalPaymentGateway or StripePaymentGateway.
It is possible. Firstly, let’s create an interface, IPaymentGateway, which has a Pay() method:
And both the PaypalPaymentGateway and StripePaymentGateway classes implement IPaymentGateway.
Now the code to pay using either of our payment gateways becomes
PaymentGateway.Pay(shoppingCart, creditCard);
Reference PaymentGateway is declared as IPaymentGateway.
We have made it so the payment gateway is pluggable into the calling code. None of this code knows anything about either Paypal or Stripe! Making it so that it works for yet another payment gateway, say, Braintree, will mean writing a BraintreePaymentGateway adapter class which implements IPaymentGateway.
(For the curious reader, here is a more detailed and dynamic version of this payment gateway example.)
That’s it for today. We’ll delve further into the OCP tomorrow.