Chapter 02
Object-oriented programming and design
Classes, the four pillars, composition and SOLID. The aim is code that stays easy to change, not a list of definitions.
Class and object
Also called instance
- What
- A class defines the data and behaviour of a kind of thing; an object is one instance of it with its own state.
- Why
- Classes let you model a problem as things with clear responsibilities, which keeps related data and logic together.
- Example
- In a library system, Book, Member and Loan are classes; the copy of a novel on the shelf is one Book object.
Watch out
Classes that only hold data while all logic sits in one giant manager class.
Coupling and cohesion
- What
- Cohesion is how closely the parts of one class belong together; coupling is how much one class depends on the internals of others.
- Why
- High cohesion and low coupling are the goal behind almost every design principle: changes stay local and classes are easy to test.
Watch out
A utility class that does ten unrelated things and is imported everywhere.
Encapsulation
Also called information hiding
- What
- Keeping an object's data private and exposing only operations that keep it valid.
- Why
- It protects invariants, the rules that must always hold, and lets you change the inside of a class without breaking its users.
class Account:
def __init__(self):
self._balance = 0
def withdraw(self, amount):
if amount > self._balance:
raise ValueError("insufficient funds")
self._balance -= amountWatch out
A getter and setter for every private field, which exposes the data anyway.
Inheritance
Also called subclass, is-a
- What
- A subclass reuses and extends a parent class, and can be used wherever the parent is expected.
- Why
- It models real "is a" relationships and lets code written for the parent work with every subclass.
Watch out
Inheriting just to share code between things that are not the same kind, creating fragile deep hierarchies.
Polymorphism
Also called overriding, dynamic dispatch
- What
- One call working on many types, each responding in its own way. Overriding is decided at run time; overloading, same name with different parameters, at compile time.
- Why
- It replaces chains of type checks and lets you add a new kind of thing without editing existing code.
class Card:
def pay(self, amount): print("charging card")
class Wallet:
def pay(self, amount): print("debiting wallet")
for method in (Card(), Wallet()):
method.pay(500)Watch out
Writing if kind == "card" branches that polymorphism was meant to remove.
Abstraction and interfaces
Also called abstract class, contract
- What
- Depending on what something does rather than how. An interface names operations without an implementation; an abstract class can also share code and state.
- Why
- It lets you swap implementations, such as a real database or a fake in tests, without touching the code that uses them.
Watch out
An interface for every class "just in case", with only ever one implementation.
Composition
Also called has-a, delegation, composition over inheritance
- What
- Building an object from other objects it holds and delegates to, such as a Car that has an Engine.
- Why
- Behaviour can be combined and swapped at run time without a subclass for every mix, which keeps classes small and loosely coupled.
Watch out
Class explosions like FlyingSwimmingDuck because every feature combination needs a new subclass.
SOLID principles
Also called SRP, LSP, DIP, SOLID, interface segregation principle, Liskov substitution principle, dependency inversion principle, single responsibility principle
- What
- Five design guidelines: single responsibility, open closed, Liskov substitution, interface segregation and dependency inversion.
- Why
- Each one reduces the cost of change. Together they are a shared vocabulary for code reviews.
- Example
- A ReportService that queries the database, formats a PDF and sends email breaks single responsibility; split it into three classes behind small interfaces.
Watch out
Reciting the five names without being able to show a violation and its fix.
Design pattern
Also called Strategy, Observer, Factory, Adapter, Decorator
- What
- A named, reusable solution to a common design problem, such as Strategy for swappable algorithms or Observer for notifying listeners.
- Why
- Patterns give teams a shared vocabulary and help you recognise structures inside the frameworks you already use.
Watch out
Forcing patterns into small programs where a plain function would do.