Na dit hoofdstuk kun je:
- bepalen welke classes je überhaupt nodig hebt,
- verantwoordelijkheid goed verdelen (zodat je geen “mega class” krijgt),
- kiezen tussen overerving en compositie met heldere redenen,
- en je begrijpt een paar “best practices” die je code onderhoudbaar maken.
1. Hoe bedenk je classes? Begin bij “dingen” en “regels” #
Een handige manier om classes te vinden:
- Schrijf in gewone taal op wat je systeem doet.
- Onderstreep de zelfstandige naamwoorden → kandidaten voor classes (“dingen”).
- Onderstreep de werkwoorden → kandidaten voor methodes (gedrag).
- Onderstreep regels → invarianten/validatie die bij een object horen.
Voorbeeldzin:
“Een order bevat orderregels en kan betaald worden.”
- dingen:
Order,OrderLine,Payment - gedrag:
add_line(),pay() - regels: “Je kunt niet betalen als er geen regels zijn”, “betaald = kan niet opnieuw”
Dit voorkomt dat je meteen in code schiet zonder structuur.
2) “Een class moet één reden hebben om te veranderen” (SRP, maar simpel uitgelegd) #
Je hoeft SOLID niet te memoriseren. Deze ene zin is vaak genoeg:
Als je class meerdere soorten problemen oplost, wordt hij rommelig.
Voorbeeld van een te brede class #
User die:
- wachtwoorden hasht,
- emails verstuurt,
- database opslaat,
- én permissions checkt.
Dat zorgt voor:
- moeilijk testen,
- veel afhankelijkheden,
- elke kleine wijziging breekt iets anders.
Beter: verantwoordelijkheid splitsen #
User→ alleen user data + regels (state/gedrag)PasswordHasher→ hashing logicEmailSender→ mailUserRepository→ opslag
Praktische test:
Kun je de class in één zin beschrijven zonder “en”?
Als je “en” nodig hebt, is het vaak te veel.
3. Encapsulation: hou state consistent door methodes #
Een veelgemaakte beginnersfout is “van buiten” aan state trekken en duwen:
order.total -= 10
order.status = "paid"PythonDat lijkt makkelijk, maar:
- regels worden omzeild,
- state kan inconsistent worden.
Beter: geef intentie-methodes
order.apply_discount(10)
order.mark_paid()PythonWaarom dit beter is:
- alle checks zitten op één plek,
- je object blijft altijd “geldig”.
Dit is de kern van OOP: objecten bewaken hun eigen regels.
4. Composition over inheritance (maar met echte uitleg) #
Overerving is verleidelijk, maar vaak te rigide.
Compositie is vaak flexibeler:
- Overerving: “ik bén dit”
- Compositie: “ik héb dit / ik gebruik dit”
Waarom compositie vaak wint #
Omdat je gedrag kunt “pluggen” zonder inheritance-explosies.
Voorbeeld: je wil betaalmethodes kunnen wisselen.
Niet:
OrderWithPaypalOrderWithCardOrderWithIdeal
Maar:
class Order:
def __init__(self, payment_provider):
self.payment_provider = payment_provider
def pay(self, amount):
return self.payment_provider.pay(amount)PythonDan kan payment_provider Paypal/Card/iDEAL zijn, zonder subclasses van Order.
Vuistregel:
- Overerving voor echte “is-a” relaties.
- Compositie voor variatie in gedrag (“strategy”), integraties, features.
5. Maak afhankelijkheden expliciet (Dependency Injection “light”) #
Veel code wordt “moeilijk” omdat classes dingen intern aanmaken:
class InvoiceService:
def __init__(self):
self.client = SomeApiClient() # hard-codedPythonProbleem:
- lastig te testen (je zit vast aan echte API),
- lastig te vervangen.
Beter: geef dependencies mee:
class InvoiceService:
def __init__(self, client):
self.client = clientPythonNu kun je:
- in productie een echte client geven,
- in tests een fake/mock client.
Dit is een hele praktische OOP-skill.
6. Kies je “public API” bewust: wat mogen anderen gebruiken? #
Een class is makkelijker te gebruiken als hij een klein, duidelijk setje methodes/attributen heeft.
- Public: wat je “belooft” stabiel te houden
- Intern: wat je later nog wil kunnen aanpassen
Python-conventie:
self._x→ internself.x/ methodes zonder underscore → publiek
Tip: maak je publieke API klein:
- liever 3 duidelijke methodes dan 12 half-overlappende.
7. Houd objecten klein en testbaar (praktisch) #
Een testbare class is meestal:
- klein,
- doet één ding,
- heeft weinig externe afhankelijkheden,
- heeft methodes die deterministisch zijn.
Als je merkt:
- je moet 10 dingen “mocken” om één methode te testen,
dan is je class vaak te druk.
8. Veelvoorkomende “code smells” in OOP (herkennen = voorkomen) #
8.1 God object / Mega class #
Eén class die “alles” weet en doet.
Symptomen:
- 500+ regels
- veel verschillende verantwoordelijkheden
- veel private helper methods
Fix: splits op in kleinere classes/services.
8.2 Te diepe inheritance tree #
A -> B -> C -> D -> E
Je bent constant aan het raden waar gedrag vandaan komt.
Fix: compositie / mixins beperken / flatten.
8.3 “Anemic domain model” #
Classes met alleen data, en alle logica zit in losse functies/services.
Fix: zet domeinregels terug in objecten die de data bezitten (encapsulation).
8.4 Te vroeg abstraheren #
Interfaces, base classes, patterns… terwijl je nog maar 1 variant hebt.
Fix: start simpel. Abstracteer pas als je 2–3 echte varianten hebt.
9. Praktische checklist (kort, maar bruikbaar) #
Als je een class maakt, check:
- Wat is de verantwoordelijkheid (in één zin)?
- Welke regels moet deze class bewaken?
- Welke state hoort echt bij dit object?
- Welke afhankelijkheden kan ik injecteren i.p.v. intern aanmaken?
- Is dit “is-a” (overerving) of “has-a” (compositie)?
- Kan iemand dit object verkeerd gebruiken? (zo ja: extra methods/properties)

