Today, we discuss principle #4 of SOLID principles to help write clean code in Python.
Here are the first , second , and third principles if you missed them.
And let’s dive into the fourth design principle (the ‘I’ in SOLID).
We will explain the concept with a real example implemented in Python and shown in a UML diagram to better illustrate the design of the classes and the relationships between them and have a visual understanding of what’s going on.
So what is ISP?
Clients of a class should not be forced to depend on those of its methods that they don’t use.
In the preceding blog
covering LSP, we split one interface (in our case
ISettings ) into
IWritableSettings interfaces. That was
intended so that one could choose a suitable interface based on the
Do you know that this solution was based on the Interface Segregation Principle; the 4th SOLID principle?
The client, mentioned in the definition, can be any part of the system that uses the class. Let’s see ISP in action with a more specific example than the previous one.
Example: E-Commerce Payment Design
Suppose you are developing an e-commerce website. You have a class that
represents the customer’s shopping cart and associated order processing
methods. Let’s define the
IOrderProcessor interface as shown below:
<img src=“https://drive.google.com/uc?export=view&id=1ZzVhnvK2bxulpOh65P26dgl5OyUdabQ0”,alt=“IOrderProcessor class with three methods: validate_card_info(), validate_shipping_address(), and process_order(). Two classes inherit from it: OnlineOrderProcessor and CashOnDeliveryOrderProcessor.",width=“50%">
IOrderProcessor violates ISP. (Designed by Plantuml)
validate_card_info()to validate the credit card information, such as card number, card verification code, and expiration date.
validate_shipping_address()to validate the shipping physical address. This might be necessary to make sure that the destination is in the area that the company can cover.
process_order()to initiate processing the order to be delivered to the destination.
The question now: Why does the above UML show a bad design?
Remember the assumption that you only accept credit card payments. What if that assumption is no longer valid? and the company decides to accept cash-on-delivery payments in some areas.
At first glance, fixing this design might be straightforward as once you
CashOnDeliveryOrderProcessor class you can just raise
NotImplementedError for the credit card functionality
def validate_card_info(obj: CardInfo): raise NotImplementedError()
At this stage, your app may work as expected, but a potential problem may arise in the future.
Let’s assume that for some reason the online-based payments need extra
functionalities. This will force you to modify the
to include the extra methods. You will also need to implement these
newly added methods in the
OnlineOrderProcessor child. Not just
that, you will also need to modify the
child to define the same methods and throw
In other words, the
CashOnDeliveryOrderProcessor will be changed
because of methods it doesn’t need in the first place which is violating
ISP. Also notice that for the implementation of the newly added methods,
you just threw
NotImplementedError which makes the
CashOnDeliveryOrderProcessor violates LSP.
Now, the new design (shown in the figure below) splits the required operations into two separate interfaces:
IOrderProcessorwhich includes only two methods:
process_order(). These two methods are consumed by the
OnlineOrderProcessorclass as well as
IOnlineOrderProcessorwhich has only one method:
validate_card_info(). This interface is implemented only by
Now, if you want to add specific functionalities to how online payments
are processed, you only need to change the
interface which will affect the
OnlineOrderProcessor , not the
CashOnDeliveryOrderProcessor . Thus, the new design conforms to ISP.
<img src=“https://drive.google.com/uc?export=view&id=1IwevolrZQiVgfpOUhD0di3_xwV-GaB6v”,alt=“Two interfaces: IOnlineOrderProcessor with validate_card_info() method, and IOrderProcessor with validate_shipping_address() and process_order() method. There are two classes beneath them. Both CashOnDeliveryOrderProcessor and OnlineOrderProcessor inherit from IOrderProcessor while OnlineOrderProcessor inherit from IOnlineOrderProcessor as well.",width=“50%">
Corrected design of the system. (Designed by Plantuml)
The Interface Segregation principle is applied when you notice that
there is a client of a class that implements a method that is not used.
In our example, we noticed that issue with the
CashOnDeliveryOrderProcessor class which implemented
validate_card_info() that was not needed.
See you in principle #5
- Beginning SOLID Principles and Design Patterns for ASP.NET Developers by Bipin Joshi