Introduction
Software design principles are essential guidelines that help developers create maintainable, scalable, and flexible software. Rather than simply writing code that works, good design ensures that software is easy to extend, understand, and test over time.
Some core principles include SOLID, DRY, KISS, and YAGNI. This article focuses on the SOLID principles, showing how they apply in a real-world Python example: designing a notification system that is extensible and testable.
The SOLID Principles Overview
- Single Responsibility Principle (SRP): A class should have only one reason to change.
- Open/Closed Principle (OCP): Software entities should be open for extension, but closed for modification.
- Liskov Substitution Principle (LSP): Subtypes must be substitutable for their base types.
- Interface Segregation Principle (ISP): Clients shouldn’t be forced to depend on interfaces they don’t use.
- Dependency Inversion Principle (DIP): High-level modules shouldn’t depend on low-level modules; both should depend on abstractions.
Real-World Example: Notification System in Python
Imagine a system that sends notifications via multiple channels: Email and SMS. We want to design it so adding new channels (e.g., Push notifications, Webhooks) is easy without modifying existing code.
Step 1: Define an abstraction for notification
from abc import ABC, abstractmethod
class Notifier(ABC):
@abstractmethod
def send(self, message: str, recipient: str):
pass
Step 2: Implement concrete notifiers
class EmailNotifier(Notifier):
def send(self, message: str, recipient: str):
print(f"Sending Email to {recipient}: {message}")
class SMSNotifier(Notifier):
def send(self, message: str, recipient: str):
print(f"Sending SMS to {recipient}: {message}")
Step 3: Create a notification service that uses dependency injection
class NotificationService:
def __init__(self, notifier: Notifier):
self.notifier = notifier
def notify(self, message: str, recipient: str):
self.notifier.send(message, recipient)
Step 4: Usage example
if __name__ == "__main__":
email_notifier = EmailNotifier()
sms_notifier = SMSNotifier()
service = NotificationService(email_notifier)
service.notify("Welcome!", "user@example.com")
service = NotificationService(sms_notifier)
service.notify("Your code is 1234", "+1234567890")
Explanation
- Each notifier class has a single responsibility (SRP).
- Adding new notification channels does not modify existing classes, but extends functionality (OCP).
- NotificationService depends on the abstraction Notifier (DIP), promoting flexibility.
- Different concrete implementations can be swapped without changing the service (LSP).
Top comments (0)