We shouldn’t need to change an interface when adding a new implementer. An interface should suit multiple implementations.
Check out this hypothetical declaration for an IPaymentGateway interface:
public interface IPaymentGateway { PaypalResult Pay(PaypalShoppingCart cart, PaypalCreditCard card); }
Can you spot the problem?
The models are PayPal ones. How is this interface meant to work for a Stripe or Worldpay implementation? Will we convert between PayPal and Stripe models? Potentially we could pull that off—but does it make sense? No, not really.
Worse still, if these PayPal models originate from a PayPal SDK and we are referring to them in our interface, we must reference the PayPal SDK—even when building a Stripe implementation! Not a good look.
It would be worthwhile to design an interface to work for PayPal and Stripe.
Could we not have anticipated the need for our interface to suit various implementations? Yes, of course, we could and should. When designing interfaces, we ought to consider multiple implementers.
We must endeavour to build stability into our interfaces.
Here is a better definition for IPaymentGateway:
public interface IPaymentGateway { PaymentResult Pay(ShoppingCart cart, CreditCard card); }
PaymentResult, ShoppingCart and CreditCard are our models. They are independent of 3rd Party libraries, like the PayPal SDK. Therefore this interface will work for PayPal, Stripe and Worldpay implementations.
Unlike before, this interface is more stable – we are not referring to volatile—changeable—models.
Please design your abstractions to be stable—you don’t need to change them when you have additional implementers.
Design Stable Abstractions
/by Olaf ThielkeDesign Stable Abstractions
Photo by Dušan S. on Unsplash
Previously we explored SOLID’s Dependency Inversion Principle (DIP).
Today we’ll explore one aspect of how the DIP directs our programming:
We shouldn’t need to change an interface when adding a new implementer. An interface should suit multiple implementations.
Check out this hypothetical declaration for an
IPaymentGateway
interface:Can you spot the problem?
The models are PayPal ones. How is this interface meant to work for a Stripe or Worldpay implementation? Will we convert between PayPal and Stripe models? Potentially we could pull that off—but does it make sense? No, not really.
Worse still, if these PayPal models originate from a PayPal SDK and we are referring to them in our interface, we must reference the PayPal SDK—even when building a Stripe implementation! Not a good look.
It would be worthwhile to design an interface to work for PayPal and Stripe.
Could we not have anticipated the need for our interface to suit various implementations? Yes, of course, we could and should. When designing interfaces, we ought to consider multiple implementers.
We must endeavour to build stability into our interfaces.
Here is a better definition for
IPaymentGateway
:PaymentResult
,ShoppingCart
andCreditCard
are our models. They are independent of 3rd Party libraries, like the PayPal SDK. Therefore this interface will work for PayPal, Stripe and Worldpay implementations.Unlike before, this interface is more stable – we are not referring to volatile—changeable—models.
Please design your abstractions to be stable—you don’t need to change them when you have additional implementers.