Exploring Practical Design Patterns: Connecting Theory to the Real World
Introduction
Design patterns serve as reusable solutions to recurring software design problems, fostering maintainable, scalable, and efficient code. In this blog post, we'll delve into three design patterns, discussing their real-world connections and providing examples to illustrate their practical utility.
Singleton Pattern
The Singleton pattern ensures a class has only one instance and offers a global point of access to that instance. This is handy when managing a shared resource or maintaining a consistent configuration across an application.
Real-world Connection: Database Connection Pooling
Suppose you're developing a web app that needs database connectivity. Creating new database connections with every request can be wasteful. By implementing the Singleton pattern, you can establish a single instance of a database connection pool that's shared throughout the app, minimizing connection overhead.
Example:
class DatabaseConnectionPool:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
# Initialize connection pool
return cls._instance
# Usage
connection_pool_1 = DatabaseConnectionPool()
connection_pool_2 = DatabaseConnectionPool()
print(connection_pool_1 is connection_pool_2) # Outputs: True
Observer Pattern
The Observer pattern creates a one-to-many relationship between objects, allowing changes in one object (subject) to update all its dependents (observers). This is invaluable when various parts of a system must respond coherently to changes.
Real-world Connection: Event Handling in GUI Applications
Imagine a GUI app where different UI elements react to user interactions or data updates. The Observer pattern can help ensure changes in one part of the interface are swiftly reflected in other parts without tightly coupling them.
Most of the commonly used frameworks on the UI side use this pattern extensively.
Example:
class Subject:
def __init__(self):
self._observers = []
def attach(self, observer):
self._observers.append(observer)
def notify(self, message):
for observer in self._observers:
observer.update(message)
class Observer:
def update(self, message):
# Update UI element with the new message
pass
# Usage
button = UIElement()
textbox = UIElement()
subject = Subject()
subject.attach(button)
subject.attach(textbox)
# When subject's state changes
subject.notify("New data arrived")
Strategy Pattern
The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. This enables clients to select an algorithm from the family at runtime, without altering the code that uses the algorithm.
Real-world Connection: Payment Processing
Suppose you're building an e-commerce platform with multiple payment methods. Using the Strategy pattern, you can encapsulate each payment method as a strategy. This allows you to easily switch between payment methods without altering the core codebase.
Similar use cases can exist in integrating multiple Email/SMS third parties or using different AI models to get inference.
Example:
class PaymentStrategy:
def process_payment(self, amount):
raise NotImplementedError
class CreditCardPayment(PaymentStrategy):
def process_payment(self, amount):
# Process payment using credit card
pass
class PayPalPayment(PaymentStrategy):
def process_payment(self, amount):
# Process payment using PayPal
pass
# Usage
payment_method = CreditCardPayment()
order = Order(100.0)
order.process_order(payment_method)
Conclusion
Design patterns act as guides for addressing common software design challenges. Singleton, Observer, and Strategy patterns are just a few of many available. Incorporating these patterns into your development process can result in more effective and flexible applications. I am currently reading a book on design patterns - Design Patterns Explained by Alan Shalloway. Will share more such reads in future. Subscribe to receive the updates.
Ending with a quote -
We are what we repeatedly do. Excellence, then, is not an act, but a habit - Aristotle