|
|
||||||||
|
|||||||||
![]() |
|
|||||||
![]() |
|
|
Onderwerpopties | Zoek in onderwerp | Stem op Onderwerp | Weergavemodus |
|
|
#1 |
|
Fornicatorus Formicidae™
Geregistreerd op: Mar 2005
Locatie: Eastwood City
Berichten: 1.983
|
Vaak is het de beeldvorming die voor het grootste probleem zorgen; OOP (Object
Oriented Programming) vraagt om een iets andere kijk en denkwijze dan traditioneel of proceduriëel programeren. Je moet een klasse (of object) zien als een losse blok logica (een "Black Box") die een bepaalde functionaliteit bevat en die je van buitenaf kunt aansturen zonder dat je hoeft te weten hoe het er van binnen aan toe gaat. Klassen geven je de mogelijkeid om functionele code te scheiden en om meerdere instanties van een klasse te hebben in je programma, zonder dat het een veelvoud aan geheugenruimte kost voor iedere instantie en om code overzichtelijk te houden. Bijkomend voordeel van een klasse is ook dat alle eventuele properties, netjes gegroepeerd zijn en duidelijk onderdeel uitmaken van de klasse en dat deze properties "Dom" kunnen zijn (gewone variabelen) of "Intelligent" (dat ze eerst nog door de klasse een controle of bewerking kunnen ondergaan, alvorens ze daadwerkelijk in de klasse worden opgeslagen/gebruikt) mbv een "Getter" en/of "Setter". Eventuele "Domme" properties kunnen in een later stadium "Intelligent" gemaakt worden, door ze van een "Getter" en/of "Setter" te voorzien, zonder dat je je hele programma overhoop hoeft te halen; het aanpassen van de klasse zelf is voldoende. Neem het volgende voorbeeld: Delphi Code:
het opgetelde, afgetrokken, vermenigvuldigde en gedeelde resultaat en laat deze zien. Verder weinig noemenswaardige code, maar 1 ding valt al direct op: wat zijn die variabelen? Waar dienen ze voor? Met zoiets eenvoudigs als dit is dat makkelijk bij te houden, maar zodra een programma groeit wordt het steeds onoverzichtelijker en zie je door de bomen op een gegeven moment het bos niet meer. Uiteraard kun je de variabelen betere/duidelijkere namen geven, maar je kunt ze ook gewoon groeperen, zodat duidelijk te zien is welke variabelen er bij elkaar horen. Dit doen we met klassen! Een klasse moet je eerst declareren als een Type, zodat de Delphi-compiler hem correct kan klassificeren en registreren in z'n systeem. Dit declareren doen we in het "Interface"-gedeelte van onze code: Delphi Code:
zolang het maar duidelijk aangeeft wat die klasse doet. Volgens de geldende Delphi naamgeving moet een type-declaratie met een hoofdletter "T" beginnen (zonder T ervoor doet de compiler ook niet moeilijk, maar we moeten ons wel een beetje aan de geaccepteerde regels houden natuurlijk). Zorg er overigens wel voor dat je niet een naam bedenkt die al bestaat, dit om verwarring te voorkomen met een reeds bestaande klasse! Oke, dus we noemen onze klasse "TMijnCalculator". Omdat onze klasse geen functionaliteit hoeft te bevatten die al in een andere klasse beschikbaar is kunnen we hem gewoon afleiden van de laagste basis-klasse die er bestaat: TObject. TObject is een klasse die verder weinig om het lijf heeft en maar een paar basis-methods (procedures en functies) heeft om het zaakje te laten werken. Het aangeven dat onze TMijnCalculator een afgeleide is van TObject doe je middels: Delphi Code:
Vervolgens gaan we een aantal variabelen toevoegen aan de klasse die niet beschikbaar zijn voor code buiten de klasse; deze zetten we dan ook in een "Private"-blok. Deze variabelen hebben we nodig voor de werking van onze klasse. Daarna declareren we een aantal functies (of procedures) die nodig zijn om de klasse zijn werk te laten doen, maar we willen niet dat code buiten onze klasse deze functies kunnen uitvoeren, dus plaatsen we ze in een "Protected"-blok. Als laatste zetten we dingen neer die code van buiten de klasse aan mogen spreken, zoals bepaalde openbaar toegankelijke functies, procedures en "Properties". Omdat ze voor code vanaf buiten toegankelijk moeten zijn, zetten we ze in een blok genaamd "Public". Properties kunnen eenvoudige (domme) variabelen zijn of in- en uit-gangen van waarden via een getter en/of setter. Onder properties worden ook eventuele events verstaan (welke eigenlijk gewoon pointers zijn naar een procedure van een bepaald type). Om het einde aan te geven van onze TMijnCalculator-klasse complementeren we de declaratie met "End;" In feite is de klasse-definitie voldoende voor een programmeur om met deze klasse te gaan werken; hij hoeft tenslotte niet te weten wat er in de klasse-code staat, zolang de klasse maar doet wat hij moet doen. Helaas moet de compiler wel eerst de werkende code maken, dus moeten we deze code ook implementeren... Dit doen we in het "Implementation"-gedeelte van onze code: Delphi Code:
machinetaal omzetten (compileren). Met de Constructor TMijnCalculator.Create kunnen we er voor zorgen dat bepaalde variabelen op een standaard waarde worden gezet, of zodat andere klassen die door jouw klasse worden gebruikt, ge-create kunnen worden. Een tegenhanger van Constructor bestaat ook: Destructor Tnaamvandeklasse.Destroy Als een klasse wordt vernietigd, wordt deze destructor aangeroepen zodat je bepaalde acties kunt uitvoeren zoals het destroyen van gebruikte klassen. Overigens zien we bij de TMijnCalculator.GetDelen-functie nog een voordeel bij het maken van klassen: alvorens het resultaat gegeven wordt kun je controleren of de berekening wel mogelijk is (delen door 0 kan namelijk niet). Bij proceduriele code zou je hier midden in de code zelf op moeten controleren en handelen, terwijl in een klasse altijd een geldig antwoord komt (of 0). Om onze unit zelf af te sluiten, moeten we nog het einde aangeven met: Delphi Code:
Vervolgens kunnen we de klasse gaan gebruiken met bijvoorbeeld deze code: Delphi Code:
we hem kunnen refereren, eerst creëren; als we er mee klaar zijn moeten we hem ook weer vrijgeven. Bij het Createn wordt er geheugen vrijgemaakt voor een nieuwe instantie van onze klasse en deze moet weer worden vrijgegeven als we ermee klaar zijn anders krijg je geheugenlekken (en het is gewoon slordig). Vandaar dat je gecreëerde klassen in een Try...Finally...End-constructie moet zetten, om ervoor te zorgen dat je klasse altijd wordt opgeruimd, ook als er tussentijds iets fout mocht gaan (een exceptie bijvoorbeeld). Code tussen Finally...End wordt altijd uitgevoerd, ook als er een exceptie optreed. We pakken er even 1 gecreëerde klasse uit (de anderen zijn zo goed als identiek): Delphi Code:
in de klasse zelf wordt nu eerst intern fValueA en fValueB op 0 gezet. Daarna lopen we het Try-blok in waar we vanuit onze code aan ValueA en ValueB een waarde wordt toegekend (respectivelijk 10 en 20). In de klasse verwijzen deze properties direct naar de interne variabelen fValueA en fValueB, dus hier gebeurt verder weinig spannends... In de ShowMessage op regel 6 wordt vervolgens eerst een complete string samengesteld voor de ShowMessage zelf uit alle losse strings en functies die er als parameter inzitten. Even "Optellen" als voorbeeld nemende (voor de andere properties is de werking gelijk): omdat we een string willen hebben (voor de ShowMessage) en de Optellen- property een single (= float) als uitgang heeft moeten we gebruik maken van de functie FloatToStr() welke in SysUtils zit. Deze FloatToStr vraagt aan onze klasse-instantie (C1.Optellen) wat de waarde is. Onze klasse kijkt in zijn lijst en ziet dat het geen domme property is, maar gebruik maar van een Getter, dus roept hij intern de functie "TMijnCalculator.GetOptellen" aan welke de som van fValueA en fValueB teruggeeft als resultaat. Zo worden alle uitkomsten opgevraagd uit de klasse, omgezet naar strings en als parameter aan ShowMessage gevoerd, welke vervolgens het resultaat laat zien. Uiteraard is bovenstaande een beetje onzinnig en totale overkill, maar het dient alleen maar ter illustratie. ![]() Maar... kunnen we misschien onze klasse toch nog uitbreiden met handige functionaliteit? Jazeker! Kijk maar eens naar wat onze eigen code moet doen in de ShowMessage om er een geldige string uit te halen... We kunnen heel eenvoudig extra functies of properties toevoegen aan onze klasse die in plaats van de standaard Single-waarde ook direct een string-versie ter beschikking stelt: Delphi Code:
onze klasse, zodat ze direct publiekelijk toegankelijk zijn vanaf buiten, maar we kunnen ook de extra stap zetten door ze te implementeren als Getters van properties (en de functies zelf dan in het Private-gedeelte te plaatsen). De klasse komt er dan zo uit te zien: Delphi Code:
string opvragen zoals hieronder: Delphi Code:
Zoals verteld hebben we onze TMijnCalculator-klasse overgeëfd van TObject en TObject heeft van zichzelf ook al een aantal methods in zich zitten voor de basis-functionaliteit. Probeer voor de grap eens deze code: Delphi Code:
![]() Maar we hebben helemaal niet de property of functie TMijnCalculator.ClassName in onze klasse gedeclareert! ![]() Dat klopt... deze is onderdeel van (en wordt geïmplementeert in) TObject en wordt in die klasse (de voorouder van onze klasse dus) uitgevoerd, maar wij hebben daar wel de beschikking over in onze overgeërfde klassen)! ![]() Ik hoop dat mijn uitleg enigszins te begrijpen (en compleet genoeg) is om te gaan experimenteren met eigen klassen. Nog een ding wil ik wel even kwijt: Klassen zijn niet de heilige graal en de oplossing tot alle voorkomende problemen; soms is het beter/handige/korter om gewoon procedurieel te werken, dan om er een complete klasse van te maken. Overweeg dus goed of het inpaken in een klasse wel verantwoord kan worden ![]() Succes! ![]() Peter.
__________________
To C the Basic things in life, use Delphi |
|
|
|
|
|
#2 |
|
Moderateur
Geregistreerd op: Oct 2002
Berichten: 12.760
|
Mooie uitleg! Even losgeweekt uit "Classes, nog te ingewikkeld of?".
__________________
Klik hier! |
|
|
|
![]() |
| Bookmarks |
| Momenteel bekijken: 1 (0 leden en 1 gasten en/of zoekmachine bots) actieve gebruikers dit onderwerp | |
| Onderwerpopties | Zoek in onderwerp |
| Weergavemodus | Stem op dit onderwerp: |
|
|