SOLID Principles in Python with Examples
SOLID is an acronym representing five design principles intended to make software designs more understandable, flexible, and maintainable. Here's a brief overview and examples in Python for each principle.
1. Single Responsibility Principle (SRP)
A class should have only one reason to change, meaning it should have only one job.
| class Order:
def __init__(self):
self.items = []
self.quantities = []
self.prices = []
self.status = "open"
def add_item(self, item, quantity, price):
self.items.append(item)
self.quantities.append(quantity)
self.prices.append(price)
# SRP Violation: Adding order processing logic to Order class
# Solution: Separate order processing into another class
class OrderProcessor:
def process_order(self, order):
if order.status == "open":
# Process the order
order.status = "closed"
print("Order processed.")
|
2. Open/Closed Principle (OCP)
Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.
| class Discount:
def __init__(self, customer, price):
self.customer = customer
self.price = price
def give_discount(self):
if self.customer == "fav":
return self.price * 0.2
if self.customer == "vip":
return self.price * 0.4
# OCP Violation: Modifying Discount class each time to add a new customer type
# Solution: Extend Discount class without modifying it
class VIPDiscount(Discount):
def give_discount(self):
return super().give_discount() * 1.2
|
3. Liskov Substitution Principle (LSP)
Objects of a superclass shall be replaceable with objects of its subclasses without affecting the correctness of the program.
| class Bird:
def fly(self):
pass
class Duck(Bird):
def fly(self):
print("Duck flying")
class Ostrich(Bird):
def fly(self):
raise NotImplementedError("Ostrich cannot fly")
# LSP Violation: Ostrich is a Bird but cannot fly
# Solution: Introduce a new class hierarchy
class FlyingBird(Bird):
def fly(self):
pass
class NonFlyingBird(Bird):
pass
|
4. Interface Segregation Principle (ISP)
No client should be forced to depend on methods it does not use.
| from abc import ABC, abstractmethod
class Machine(ABC):
@abstractmethod
def print(self):
pass
@abstractmethod
def scan(self):
pass
# ISP Violation: A simple printer class forced to implement scan method
# Solution: Split the interface
class Printer(ABC):
@abstractmethod
def print(self):
pass
class Scanner(ABC):
@abstractmethod
def scan(self):
pass
|
5. Dependency Inversion Principle (DIP)
High-level modules should not depend on low-level modules. Both should depend on abstractions. Moreover, abstractions should not depend on details. Details should depend on abstractions.
| from abc import ABC, abstractmethod
class Button:
def __init__(self, lamp):
self.lamp = lamp
def toggle(self):
if self.lamp.is_on():
self.lamp.turn_off()
else:
self.lamp.turn_on()
# DIP Violation: Button class directly depends on a specific Lamp class
# Solution: Use an interface to invert the dependency
class Switchable(ABC):
@abstractmethod
def turn_on(self):
pass
@abstractmethod
def turn_off(self):
pass
|
By adhering to these principles, developers can create more maintainable, scalable, and robust systems.