Constructor Patterns
Common Constructor Design Patterns
1. Basic Constructor Pattern
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
2. Factory Method Pattern
class DatabaseConnection:
def __init__(self, host, port):
self.host = host
self.port = port
@classmethod
def from_config(cls, config):
return cls(config['host'], config['port'])
Constructor Pattern Comparison
Pattern |
Description |
Use Case |
Simple Constructor |
Direct attribute assignment |
Basic object creation |
Factory Method |
Flexible object instantiation |
Complex object creation |
Singleton |
Ensure single instance |
Resource management |
Dependency Injection |
External dependency management |
Modular design |
Singleton Pattern Implementation
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
Dependency Injection Pattern
class Logger:
def __init__(self, handler):
self.handler = handler
def log(self, message):
self.handler.write(message)
Constructor Flow Visualization
graph TD
A[Constructor Called] --> B{Pattern Type}
B --> |Simple| C[Direct Initialization]
B --> |Factory| D[Complex Creation Logic]
B --> |Singleton| E[Instance Checking]
B --> |Dependency| F[External Dependencies]
Advanced Constructor Techniques
Composition Over Inheritance
class EmailService:
def __init__(self, smtp_client):
self.smtp_client = smtp_client
class NotificationSystem:
def __init__(self, email_service):
self.email_service = email_service
LabEx Recommended Patterns
- Prefer composition to inheritance
- Use factory methods for complex object creation
- Implement dependency injection
- Keep constructors simple and focused
Type-Checked Constructor
class User:
def __init__(self, username: str, email: str):
if not isinstance(username, str):
raise TypeError("Username must be a string")
self.username = username
self.email = email
Lazy Initialization
class ExpensiveResource:
def __init__(self):
self._data = None
@property
def data(self):
if self._data is None:
self._data = self._load_data()
return self._data
def _load_data(self):
## Expensive data loading logic
pass
Constructor Anti-Patterns to Avoid
- Overloading constructors with too many responsibilities
- Creating complex initialization logic
- Ignoring type checking and validation
- Tight coupling between classes