13. Common Patterns

Prefer composition, inject dependencies, and keep boundaries explicit and testable.

Question: Why is composition often preferred over inheritance?

Answer: Composition is often preferred because it leads to more flexible, decoupled, and reusable code. It models a "has-a" relationship, whereas inheritance models an "is-a" relationship.

Explanation: Inheritance can lead to tight coupling between parent and child classes and rigid hierarchies that are hard to change. Composition allows you to build complex objects by combining smaller, single-responsibility components. This makes it easier to swap out implementations and to test components in isolation.

Question: What is the Repository Pattern?

Answer: The Repository Pattern is a design pattern that mediates between the domain (business logic) and data mapping layers (like an ORM). It provides a collection-like interface for accessing domain objects, abstracting away the underlying data persistence mechanism.

Explanation: Instead of having your business logic directly use SQLAlchemy or another ORM, you define a repository interface (e.g., UserRepository) with methods like get_by_id(user_id) or add(user). The implementation of this repository contains the data access logic. This decouples your business logic from the database, making the code easier to test (you can swap the real repository with an in-memory mock) and easier to maintain (you can change the ORM or database without changing the business logic).

Question: How do you do Dependency Injection (DI) in Python without a framework?

Answer: Pass dependencies explicitly (constructor/function params) and provide factories/providers. In FastAPI, use Depends for declarative DI.

Explanation: DI improves testability and decoupling; avoid global singletons.

Question: When would you use Strategy or Adapter patterns?

Answer: Use Strategy to swap algorithms at runtime via a common interface; use Adapter to make incompatible interfaces work together.