Interface Segregation Principle
Explaining “I” in Solid principles
Interface Segregation Principle (ISP) in the context of a food delivery application like Zomato. The Interface Segregation Principle states that no client should be forced to depend on methods it does not use. In other words, it’s better to have several smaller, specific interfaces rather than one large, general-purpose interface.
Here’s an example demonstrating the Interface Segregation Principle:
```python
from abc import ABC, abstractmethod
# Before applying ISP
class RestaurantInterface(ABC):
@abstractmethod
def accept_order(self, order):
pass
@abstractmethod
def prepare_food(self, order):
pass
@abstractmethod
def package_order(self, order):
pass
@abstractmethod
def handle_delivery(self, order):
pass
@abstractmethod
def process_payment(self, order):
pass
@abstractmethod
def handle_table_reservation(self, reservation):
pass
# This forces all restaurants to implement methods they might not need,
# like handle_delivery for dine-in only restaurants or handle_table_reservation for delivery-only restaurants.
# After applying ISP
class OrderProcessingInterface(ABC):
@abstractmethod
def accept_order(self, order):
pass
@abstractmethod
def prepare_food(self, order):
pass
@abstractmethod
def package_order(self, order):
pass
class DeliveryInterface(ABC):
@abstractmethod
def handle_delivery(self, order):
pass
class PaymentInterface(ABC):
@abstractmethod
def process_payment(self, order):
pass
class ReservationInterface(ABC):
@abstractmethod
def handle_table_reservation(self, reservation):
pass
# Now we can create more specific restaurant types
class DeliveryRestaurant(OrderProcessingInterface, DeliveryInterface, PaymentInterface):
def accept_order(self, order):
print(“Accepting delivery order”)
def prepare_food(self, order):
print(“Preparing food for delivery”)
def package_order(self, order):
print(“Packaging order for delivery”)
def handle_delivery(self, order):
print(“Handling delivery”)
def process_payment(self, order):
print(“Processing payment for delivery order”)
class DineInRestaurant(OrderProcessingInterface, PaymentInterface, ReservationInterface):
def accept_order(self, order):
print(“Accepting dine-in order”)
def prepare_food(self, order):
print(“Preparing food for dine-in”)
def package_order(self, order):
print(“Plating order for dine-in”)
def process_payment(self, order):
print(“Processing payment for dine-in order”)
def handle_table_reservation(self, reservation):
print(“Handling table reservation”)
# Usage
delivery_restaurant = DeliveryRestaurant()
dine_in_restaurant = DineInRestaurant()
order = {“items”: [“Pizza”, “Soda”]}
reservation = {“time”: “19:00”, “guests”: 4}
delivery_restaurant.accept_order(order)
delivery_restaurant.handle_delivery(order)
dine_in_restaurant.accept_order(order)
dine_in_restaurant.handle_table_reservation(reservation)
```
In this refactored version:
1. We’ve broken down the large `RestaurantInterface` into smaller, more focused interfaces: `OrderProcessingInterface`, `DeliveryInterface`, `PaymentInterface`, and `ReservationInterface`.
2. We then create specific restaurant types (`DeliveryRestaurant` and `DineInRestaurant`) that implement only the interfaces they need.
Benefits of this approach:
1. Flexibility: Restaurants can implement only the interfaces that are relevant to their business model. A delivery-only restaurant doesn’t need to implement table reservation methods.
2. Maintainability: Changes to one aspect (e.g., delivery process) don’t affect unrelated parts of the system.
3. Readability: It’s clearer what capabilities each type of restaurant has by looking at the interfaces it implements.
4. Extensibility: It’s easier to add new types of restaurants or new capabilities without modifying existing code.
5. Testability: You can mock or stub smaller interfaces more easily in unit tests.
This structure allows Zomato to handle various types of restaurants more efficiently. For example:
- A cloud kitchen might implement `OrderProcessingInterface`, `DeliveryInterface`, and `PaymentInterface`.
- A fine dining restaurant might implement all interfaces.
- A food truck might implement only `OrderProcessingInterface` and `PaymentInterface`.
By adhering to the Interface Segregation Principle, the system becomes more flexible and can easily accommodate different types of food service businesses, which is crucial for a platform like Zomato that deals with a diverse range of restaurants and food service providers.
**Interface Segregation Principle (ISP) in Software Development: Simplified Explanation**
**Real-World Use Case: Ola (Ride-Sharing App)**
Imagine you are developing a ride-sharing app like Ola. Here are some of the main features you need to handle:
1. **Ride Booking:** Booking different types of rides.
2. **Ride Tracking:** Tracking the location of the ride.
3. **Payment Processing:** Handling payments and transactions.
4. **User Feedback:** Collecting feedback and ratings from users.
Interface Segregation Principle in Ola Software:
1. **Base Interface:**
— Define a base interface `Ride` that provides a common method for booking rides.
```python
from abc import ABC, abstractmethod
class Ride(ABC):
@abstractmethod
def book(self, user, destination):
pass
```
2. **Specific Interfaces:**
— Create smaller, more specific interfaces for additional functionalities.
```python
class Trackable(ABC):
@abstractmethod
def track_location(self):
pass
class Payable(ABC):
@abstractmethod
def process_payment(self, amount):
pass
class Feedbackable(ABC):
@abstractmethod
def collect_feedback(self, feedback):
pass
```
3. **Implementing Specific Interfaces:**
— Implement these interfaces in classes where they are relevant.
```python
class MiniRide(Ride, Trackable):
def book(self, user, destination):
print(f”Mini ride booked for {user} to {destination}.”)
def track_location(self):
print(“Tracking Mini ride location.”)
class SedanRide(Ride, Trackable, Payable):
def book(self, user, destination):
print(f”Sedan ride booked for {user} to {destination}.”)
def track_location(self):
print(“Tracking Sedan ride location.”)
def process_payment(self, amount):
print(f”Processing payment of {amount} for Sedan ride.”)
class PrimeRide(Ride, Trackable, Payable, Feedbackable):
def book(self, user, destination):
print(f”Prime ride booked for {user} to {destination}.”)
def track_location(self):
print(“Tracking Prime ride location.”)
def process_payment(self, amount):
print(f”Processing payment of {amount} for Prime ride.”)
def collect_feedback(self, feedback):
print(f”Collecting feedback for Prime ride: {feedback}”)
```
4. **Using the Interfaces:**
— Use the classes and their specific functionalities without forcing them to implement methods they don’t need.
```python
def book_ride(ride: Ride, user, destination):
ride.book(user, destination)
def track_ride(ride: Trackable):
ride.track_location()
def process_ride_payment(ride: Payable, amount):
ride.process_payment(amount)
def collect_ride_feedback(ride: Feedbackable, feedback):
ride.collect_feedback(feedback)
# Example usage
user = “John Doe”
destination = “Airport”
amount = 100
feedback = “Great ride!”
mini_ride = MiniRide()
sedan_ride = SedanRide()
prime_ride = PrimeRide()
book_ride(mini_ride, user, destination)
track_ride(mini_ride)
book_ride(sedan_ride, user, destination)
track_ride(sedan_ride)
process_ride_payment(sedan_ride, amount)
book_ride(prime_ride, user, destination)
track_ride(prime_ride)
process_ride_payment(prime_ride, amount)
collect_ride_feedback(prime_ride, feedback)
```
**Why Interface Segregation Principle is Important:**
- **Reduced Complexity:** Classes and modules are simpler because they implement only the interfaces they need.
- **Improved Flexibility:** Changes to one interface do not affect clients that do not use it.
- **Better Maintainability:** Smaller interfaces are easier to understand, implement, and modify.
**Analogy:**
Think of ISP like having different specialists in a hospital. Instead of one doctor handling all medical tasks (surgery, diagnosis, therapy), there are specialists for each task (surgeon, diagnostician, therapist). Each specialist focuses on their specific job, making the hospital more efficient and effective. Similarly, in software, each class should implement only the interfaces it needs, making the system more efficient and maintainable.
By following the Interface Segregation Principle in your Ola ride-sharing app, you ensure that each class and module is focused and relevant, making the codebase cleaner and easier to manage.