Advanced OOP
Dependency Injection
Loose coupling through external dependency provision
Interview: Software design — tests understanding of DI principles, testability, and Python-specific DI patterns
Dependency Injection (DI) is a design pattern where objects receive their dependencies from external sources rather than creating them internally. This promotes loose coupling, testability, and flexibility. In Python, DI is typically implemented through constructor parameters, though more sophisticated approaches exist.
Types of Injection
- Constructor injection: Dependencies passed to
__init__(most common in Python) - Setter injection: Dependencies set via properties/methods after creation
- Interface injection: Dependencies passed through a specific method
Benefits
- Testability: Inject mocks/stubs for unit testing
- Flexibility: Swap implementations without changing code
- Decoupling: Classes don't know about concrete dependencies
- Configuration: Dependencies configured at startup, not hardcoded
Interview Tip
Python's dynamic typing makes DI natural — you can inject any object with the right interface. No need for heavy DI frameworks like in Java. Constructor injection with Protocol type hints is the most Pythonic approach.
Use Cases
Unit testing: injecting mock objects for isolated tests
Configuration: swapping database/cache implementations per environment
Plugin architectures: loading different implementations at runtime
Microservices: injecting different transport layers (HTTP, gRPC)
Application bootstrapping: wiring dependencies at startup
Common Mistakes
Hardcoding dependencies inside __init__ instead of accepting them as parameters
Creating overly complex DI containers for simple applications
Not using Protocols/ABCs to define dependency interfaces
Injecting too many dependencies (class has too many responsibilities)
Using global singletons instead of proper dependency injection