Open/closed principle states that classes are closed for modifications but open for extensions. Lets say we want to design a payment system where payment can be processed by multiple processors like following:
class Payment {
void pay(paymentMethod) {
switch (paymentMethod) {
case 'PayPal':
break;
case 'Swift':
break;
default:
break;
}
}
}
class PayPal {
void pay() {
//implement payment1
}
}
class Swift {
void pay() {
//implement payment2
}
}
Let's say we implement both payment systems the first time. Now if for some reason any payment system implementation process is changed, should not we have to modify the relevant class? For example, if we implement PayPal and after 2-3 years PayPal's working process is changed, does not modifying the PayPal class break the open/closed principle? If it does what's the solution?
Having that
switch
statement in yourPayment
classes breaks the open/closed principle because it makes the abstract idea of aPayment
tightly coupled to the concrete implementationsPayPal
andSwift
. In order to add a remove a supported payment type, you would have to edit thePayment.pay()
method.A better design uses an
interface
to describe what a payment provider should look like. In this case, that it must have avoid pay()
method.Instead of taking a
paymentMethod
argument as astring
,Payment.pay()
should accept an instance of a class which implements the payment provider interface. It can callpaymentMethod.pay()
to execute the correct function. (Depending on your actual setup, it's probably better to pass this argument to the constructor than to a method).This way it becomes trivially easy to add or remove payment providers because the
Payment
class does not need any knowledge whatsoever about which provider classes exist.