Clients should not be forced to depend on interfaces they do not use. This means that we should split interfaces into smaller, more specific ones, so that clients only need to depend on the interfaces that they actually use.
Let's refactor our PaymentHandler
example to demonstrate ISP. We'll introduce a new method specifically for captcha
operations that are applicable with CardPayment
and PayPalPayment
. However, the CryptoPayment
class does not support captcha, in turn, breaking the Interface Segregation Principle, by enforcing the captcha
method on all payment handlers.
from abc import ABC, abstractmethod
class Checkout:
def __init__(self):
self.products = []
self.quantity = []
self.price = []
self.status = "pending"
class PaymentHandler(ABC):
@abstractmethod
def pay(self, checkout: Checkout):
pass
@abstractmethod
def captcha(self, checkout: Checkout, code: str):
pass
class CardPayment(PaymentHandler):
def __init__(self, code: str):
self.security_token = code
self.pass_captcha = False
def pay(self, checkout: Checkout):
print("Processing card payment...")
checkout.status = "paid"
def captcha(self, checkout: Checkout, code: str):
print("Captcha verification for card payment...")
self.pass_captcha = True
class PayPalPayment(PaymentHandler):
def __init__(self, code: str):
self.security_token = code
self.pass_captcha = False
def pay(self, checkout: Checkout):
print("Processing PayPal payment...")
checkout.status = "paid"
def captcha(self, checkout: Checkout, code: str):
print("Captcha verification for card payment...")
self.pass_captcha = True
class CryptoPayment(PaymentHandler):
def __init__(self, token: str):
self.fingerprint_token = token
def pay(self, checkout: Checkout):
print("Processing cryptocurrency payment...")
checkout.status = "paid"
def captcha(self, checkout: Checkout):
raise Exception("Not supported for Crypto payments")
checkout = Checkout()
crypto_processor = CryptoPayment()
crypto_processor.captcha(checkout)
To fix this, we can split the PaymentHandler
interface into two separate interfaces: PaymentHandler
and CaptchaVerification
. This way, clients can depend on the interfaces they actually use, adhering to the Interface Segregation Principle.
from abc import ABC, abstractmethod
class Checkout:
def __init__(self):
self.products = []
self.quantity = []
self.price = []
self.status = "pending"
class PaymentHandler(ABC):
@abstractmethod
def pay(self, checkout: Checkout):
pass
class CaptchaVerification(PaymentHandler):
@abstractmethod
def captcha(self, checkout: Checkout, code: str):
pass
class CardPayment(CaptchaVerification):
def __init__(self, code: str):
self.security_token = code
self.pass_captcha = False
def pay(self, checkout: Checkout):
print("Processing card payment...")
checkout.status = "paid"
def captcha(self, checkout: Checkout, code: str):
print("Captcha verification for card payment...")
self.pass_captcha = True
class PayPalPayment(CaptchaVerification):
def __init__(self, code: str):
self.security_token = code
self.pass_captcha = False
def pay(self, checkout: Checkout):
print("Processing PayPal payment...")
checkout.status = "paid"
def captcha(self, checkout: Checkout, code: str):
print("Captcha verification for card payment...")
if code == self.security_token:
self.pass_captcha = True
class CryptoPayment(PaymentHandler):
def __init__(self, token: str):
self.fingerprint_token = token
def pay(self, checkout: Checkout):
print("Processing cryptocurrency payment...")
checkout.status = "paid"
checkout = Checkout()
paypal_processor = PayPalPayment("900913")
paypal_processor.captcha(checkout, "d^rkM4tt£r")
paypal_processor.pay(checkout)
crypto_processor = CryptoPayment("0xdeadbeef")
crypto_processor.pay(checkout)