OOP Design Patterns in Python
Object-Oriented Programming (OOP) is a fundamental concept in Python that allows developers to build scalable and reusable code. Design patterns are reusable solutions to common software design problems, helping us structure our code efficiently. Python supports various OOP design patterns, categorized into three main types:
- Creational Patterns – Focus on object creation mechanisms.
- Structural Patterns – Focus on class and object composition.
- Behavioral Patterns – Focus on communication between objects.
Creational Design Patterns
1. Singleton Pattern
The Singleton pattern ensures that only one instance of a class is created and provides a global access point to that instance.
How it works: We override the __new__
method in the class to check if an instance already exists. If an instance exists, we return the existing instance instead of creating a new one.
class Singleton:
_instance = None # Class attribute to store the instance
def __new__(cls):
if cls._instance is None: # Check if an instance already exists
cls._instance = super().__new__(cls) # Create a new instance
return cls._instance # Return the single instance
# Creating objects
obj1 = Singleton()
obj2 = Singleton()
# Checking if both objects are the same
print(obj1 is obj2) # Output: True
Output
True
Since obj1
and obj2
reference the same instance, they are the same object.
Structural Design Patterns
Adapter Pattern
The Adapter pattern allows two incompatible interfaces to work together by acting as a bridge between them.
How it works: We create an adapter class that translates requests from one interface to another, allowing two incompatible classes to communicate.
# Class with an incompatible interface
class OldPrinter:
def old_print(self):
return "Old Printer Output"
# Adapter class that makes OldPrinter compatible with new requirements
class PrinterAdapter:
def __init__(self, old_printer):
self.old_printer = old_printer
def print_text(self):
return self.old_printer.old_print()
# Using the Adapter
old_printer = OldPrinter()
adapter = PrinterAdapter(old_printer)
print(adapter.print_text()) # Output: Old Printer Output
Output
Old Printer Output
The adapter allows the OldPrinter
class to be used with a new interface, making it compatible with modern systems.
Behavioral Design Patterns
Observer Pattern
The Observer pattern establishes a subscription mechanism where objects (observers) get notified when another object (subject) changes.
How it works: The Subject
maintains a list of observers and notifies them of any changes. Observers register themselves to receive updates.
# Subject (Observable)
class NewsPublisher:
def __init__(self):
self.subscribers = []
def subscribe(self, subscriber):
self.subscribers.append(subscriber)
def unsubscribe(self, subscriber):
self.subscribers.remove(subscriber)
def notify(self, news):
for subscriber in self.subscribers:
subscriber.update(news)
# Observer
class Subscriber:
def __init__(self, name):
self.name = name
def update(self, news):
print(f"{self.name} received news: {news}")
# Example usage
publisher = NewsPublisher()
subscriber1 = Subscriber("Arjun")
subscriber2 = Subscriber("Ram")
publisher.subscribe(subscriber1)
publisher.subscribe(subscriber2)
publisher.notify("New Design Patterns Article Published!")
Output
Arjun received news: New Design Patterns Article Published!
Ram received news: New Design Patterns Article Published!
Both Arjun
and Ram
receive the news because they subscribed to the NewsPublisher
. The observer pattern allows multiple objects to stay updated without tight coupling.
Conclusion
Design patterns help us write clean, efficient, and scalable code in Python. By using the right design pattern, we can:
- Reduce code duplication.
- Improve code readability and maintainability.
- Ensure flexibility in software architecture.
Understanding these patterns will help you write better Python applications and design software in a structured way.