Zie Scherp Scherper - Hoofdstuk 9
Na dit hoofdstuk kan je:
uitleggen waarom OOP code modulair en onderhoudsvriendelijk maakt
het verschil tussen een klasse en een object beschrijven
abstractie en encapsulatie situeren (het black-box-principe)
een klasse maken met methoden en instantievariabelen
access modifiers (public/private) correct gebruiken
objecten hun staat laten beschermen via properties (full en auto)
werken met de ingebouwde DateTime-klasse
Tot nu toe programmeerden we gestructureerd (procedureel): loops, methoden, beslissingen.
Gestructureerd
werkt prima, maar grote projecten verzanden in spaghetti
OOP
bouwt hierop voort, maar maakt code modulair, leesbaar en onderhoudsvriendelijk
Alles draait voortaan rond klassen en objecten.
Toen ik OOP kreeg, in C++, was dat pas in het
derde jaar
bij de ingenieurs. Jij krijgt het in je eerste.
Paratrooper-reboot D&D over “het internet” MFC32, iemand?
Waarschuwing
Twee balletjes? Dan moet je bijna elke lijn verdubbelen (bal2X, v2X, …). Bij honderd balletjes wordt het een ramp.
We stoppen de data en het gedrag van een balletje samen in één klasse.
Tip
Honderd balletjes? Een array van Balletje en twee loopjes. Zo simpel.
De logica zit in de objecten zelf. Elk object is verantwoordelijk voor zijn eigen gedrag, op basis van zijn eigen interne toestand.
In de Main zeg je gewoon:
“update jezelf”
bal1.Update();
“teken jezelf”
bal1.TekenOpScherm();
Duizend mammoeten en sabeltandtijgers! Ik doe een voorspelling: van alle hoofdstukken wordt dit het hoofdstuk waar je het meest je tanden op stukbijt.
Hou vol, geef niet te snel op, en kom hier geregeld terug.
Klasse
een blauwdruk die het gedrag en de toestand van objecten beschrijft
Object
een instantie van die klasse, met een eigen toestand en identiteit
Tip
Het grondplan van een huis is de klasse. De tien huizen die je ermee bouwt zijn de objecten: elk met eigen bakstenen en rolluiken.
Een object heeft:
Gedrag
beschreven door de methoden van de klasse
Toestand
bepaald door zijn instantievariabelen en properties
Identiteit
een unieke naam zodat andere objecten ermee kunnen werken
Tip
Zoek in een opgave naar de zelfstandige naamwoorden: dat zijn vaak je klassen.
Het black-box-principe, denk aan een auto:
Encapsulatie
alle onderdelen die samenhoren bundel je tot één geheel
Abstractie
naar buiten toe vereenvoudig je de complexiteit tot een interface (stuur, pedalen, dashboard)
Hoe minder de buitenwereld moet weten om met een object te werken, hoe beter.
De vier pijlers van OOP:
A Abstractie
P Polymorfisme
hoofdstuk 16
I Inheritance / overerving
hoofdstuk 13
E Encapsulatie
Opmerking
Steve Jobs over objecten: “They encapsulate complexity, and the interfaces to that complexity are high level.”
Een klassenaam begint met een hoofdletter
Zet elke klasse in een apart .cs-bestand (Project - Add class…)
VS zet er de access modifier internal voor
Objecten maak je met het new keyword (zoals eerder bij Random):
new
maakt het object in het geheugen
geeft het adres terug
Tip
Klassen zijn dus gewoon een nieuw, krachtiger datatype: variabelen die meerdere waarden én methoden bundelen.
Waarschuwing
Een null-object tóch gebruiken geeft een NullReferenceException: dé klassieker bij beginnende OOP’ers. Je vergat new.
Geen static voor methoden in zo’n klasse
public maakt de methode bruikbaar voor de buitenwereld
private
enkel zichtbaar binnen de klasse zelf (de standaard als je niets schrijft)
public
iedereen kan eraan
internal
enkel binnen het huidige project
protected
voor overerving (later)
Belangrijk
Zet altijd expliciet een access modifier. Niets schrijven betekent private, maar dat expliciet maken leest veel duidelijker.
Interne hulpcode scherm je af zodat anderen ze niet per ongeluk aanroepen. Binnen de klasse mag je ze wél gebruiken:
Elke instantie houdt zijn eigen toestand bij in een instantievariabele:
Verjong je het ene object, dan blijft het andere ongemoeid: elk object heeft zijn eigen geboorteJaar.
Ik zag je denken: “zal ik die instantievariabele eens public maken?” Niet doen. Simpel.
Een public instantievariabele laat de buitenwereld om het even welke waarde toekennen (adil.geboortejaar = -12000;) en breekt zo je klasse.
Belangrijk
Toegang regel je via properties en methoden, nooit via een publieke instantievariabele.
Een methode kan illegale waarden tegenhouden:
Een object beschermt zichzelf: de buitenwereld kan zijn werking niet zomaar om zeep helpen.
Darth Vader is een SithLord met een geheime naam en een hoeveelheid energie (nooit onder 0):
Tip
Properties zijn de moderne C#-manier om de interne staat gecontroleerd in en uit te lezen, in plaats van losse methoden.
Een property is altijd public en begint met een hoofdletter
In de set zit de waarde in het keyword value
Write-only
geen get (bv. een pincode)
Read-only
geen set
Read-only met private set
enkel intern aanpasbaar
Transformerend
berekent data in de get
Tip
Pas, ook binnen de klasse, je instantievariabele aan via de property, zodat je controles niet omzeilt.
Een read-only property die data berekent (geen eigen instantievariabele):
Tip
Geen extra controle nodig? Dan volstaat een auto-property, zonder zichtbare instantievariabele:
Tip
Begin vaak met auto-properties; vervang ze door full properties zodra je controle of transformatie nodig hebt. (prop + tab + tab in VS.)
DateTime.Now is een static property
new DateTime(...) gebruikt een constructor (hoofdstuk 11) om het object meteen in te stellen
AddDays, AddHours, AddYears, … geven telkens een nieuw DateTime-object terug
Properties zoals Month en DayOfWeek zijn read-only
Twee DateTime-objecten aftrekken geeft een TimeSpan:
Optellen van twee datums kan niet
Aftrekken wel
Bouw een mini-fitness-app waarin een sporter zijn trainingen bijhoudt. Een klasse Loper met:
Naam
AantalLopen, TotaleAfstand en TotaleTijd (alleen leesbaar naar buiten)
een methode RegistreerTraining(afstand, tijd)
Tip
Gebruik properties met begrenzing (clamping): een negatieve afstand of tijd weiger je in de set. Zo beschermt het object zichzelf, precies de kern van dit hoofdstuk.
We doorliepen vier fasen:
DateTime als voorproefje van een krachtige klasse OOP bundelt data en gedrag in klassen; objecten zijn de concrete instanties (met new)
Een object beheert zijn eigen toestand; instantievariabelen blijven privé
Properties geven gecontroleerde toegang: full (met get/set/value) of auto
Vergeet je new, dan is je object null en krijg je een NullReferenceException
DateTime toont hoe krachtig een goed ontworpen klasse is
Tip
Volgende halte: geheugenmanagement en uitzonderingen (exceptions).
Kennisclips
Hoe doen andere programmeertalen hetzelfde? En een korte zoek-de-fout, om zo kritisch mogelijk naar code te leren kijken.
Dat alle code in een klasse moet, deelt C# met Java. C# zit aan het strenge eind.
Python en JavaScript zijn losser: daar mag je gewoon losse code en functies schrijven. OOP is daar een keuze, geen verplichting.
In C bestaat het begrip klasse zelfs niet.
Java kent geen properties: daar schrijf je met de hand een aparte getter en setter:
Python zit dichter bij C# met de @property-decorator: methoden die je van buiten gebruikt alsof het een gewone variabele is.
In beide gevallen verberg je de instantievariabele en regel je de toegang.
Onderstaande Python-klasse wil de energie afschermen tegen negatieve waarden, maar de check werkt niet. Wat loopt er mis?
Opmerking
In de setter staat self.energie = waarde in plaats van self._energie = waarde. Daardoor roept de setter zichzelf opnieuw aan (de property heet immers energie) en krijg je oneindige recursie. Je moet de achterliggende variabele _energie toewijzen, niet de property zelf. Hetzelfde gevaar bestaat in C# wanneer je in een set per ongeluk de property aanspreekt in plaats van de instantievariabele.
Een handige samenvattende poster (Opgelet: gemaakt m.b.v. ChatGPT Image Gen 2).