NLDelphi logo

Apada
Start Forum Nieuws Artikelen Links E mail Statistieken
NLDelphi

Ga Terug   NLDelphi > Tips voor je collega's > Tiphoek

Antwoord
 
Onderwerpopties Zoek in onderwerp Stem op Onderwerp Weergavemodus
Oud 11-Jul-09, 20:18   #1
VideoRipper
Fornicatorus Formicidae™
 
VideoRipper's Avatar
 
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:
  1. Procedure Berekenen1;
  2. Var
  3.   A1,           // Variabelen voor 1e waarden
  4.   A2,
  5.   A3,
  6.   A4,
  7.   B1,           // Variabelen voor 2e waarden
  8.   B2,
  9.   B3,
  10.   B4 : Single;
  11.   O  : Single;  // Optellen
  12.   A  : Single;  // Aftrekken
  13.   V  : Single;  // Vermenigvuldigen
  14.   D  : Single;  // Delen
  15. Begin
  16.   A1 := 10;
  17.   B1 := 20;
  18.   A2 := 30;
  19.   B2 := 40;
  20.   A3 := 50;
  21.   B3 := 60;
  22.   A4 := 70;
  23.   B4 := 80;
  24.  
  25.   O := B1 + A1;
  26.   A := B1 - A1;
  27.   V := B1 * A1;
  28.   D := B1 / A1;
  29.   ShowMessage('A = ' + FloatToStr(A1) + ', B = ' + FloatToStr(B1) + #13#10 +
  30.               'Optellen = ' + FloatToStr(O) + #13#10 +
  31.               'Aftrekken = ' + FloatToStr(A) + #13#10 +
  32.               'Vermenigvuldigen = ' + FloatToStr(V) + #13#10 +
  33.               'Delen = ' + FloatToStr(D));
  34.  
  35.   O := B2 + A2;
  36.   A := B2 - A2;
  37.   V := B2 * A2;
  38.   D := B2 / A2;
  39.   ShowMessage('A = ' + FloatToStr(A2) + ', B = ' + FloatToStr(B2) + #13#10 +
  40.               'Optellen = ' + FloatToStr(O) + #13#10 +
  41.               'Aftrekken = ' + FloatToStr(A) + #13#10 +
  42.               'Vermenigvuldigen = ' + FloatToStr(V) + #13#10 +
  43.               'Delen = ' + FloatToStr(D));
  44.  
  45.   O := B3 + A3;
  46.   A := B3 - A3;
  47.   V := B3 * A3;
  48.   D := B3 / A3;
  49.   ShowMessage('A = ' + FloatToStr(A3) + ', B = ' + FloatToStr(B3) + #13#10 +
  50.               'Optellen = ' + FloatToStr(O) + #13#10 +
  51.               'Aftrekken = ' + FloatToStr(A) + #13#10 +
  52.               'Vermenigvuldigen = ' + FloatToStr(V) + #13#10 +
  53.               'Delen = ' + FloatToStr(D));
  54.  
  55.   O := B4 + A4;
  56.   A := B4 - A4;
  57.   V := B4 * A4;
  58.   D := B4 / A4;
  59.   ShowMessage('A = ' + FloatToStr(A4) + ', B = ' + FloatToStr(B4) + #13#10 +
  60.               'Optellen = ' + FloatToStr(O) + #13#10 +
  61.               'Aftrekken = ' + FloatToStr(A) + #13#10 +
  62.               'Vermenigvuldigen = ' + FloatToStr(V) + #13#10 +
  63.               'Delen = ' + FloatToStr(D));
  64. End;
Bovenstaande procedure neemt een 4-tal getallenparen en berekent vervolgens
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:
  1. Unit BerekenUnit; // Naam van deze unit
  2.  
  3. Interface // Eerst gaan we het uitleggen aan buitenstaande code hoe deze
  4.           // unit (en zijn types) aangesproken dient te worden
  5. Uses
  6.   SysUtils;  // Hier zetten we de units neer die we nodig hebben voor onze eigen
  7.             // code (bijvoorbeeld de functie FloatToStr die in SysUtils zit)
  8. type
  9.   TMijnCalculator = Class(TObject)
  10.   Private
  11.     fValueA : Single;
  12.     fValueB : Single;
  13.   Protected
  14.     Function GetOptellen : Single;
  15.     Function GetAftrekken : Single;
  16.     Function GetVermenigvuldigen : Single;
  17.     Function GetDelen : Single;
  18.   Public
  19.     Constructor Create;
  20.     Property ValueA                   : Single Read fValueA Write fValueA;
  21.     Property ValueB                   : Single Read fValueB Write fValueB;
  22.     Property Optellen                 : Single Read GetOptellen;
  23.     Property Aftrekken                : Single Read GetAftrekken;
  24.     Property Vermenigvuldigen         : Single Read GetVermenigvuldigen;
  25.     Property Delen                    : Single Read GetDelen;
  26.   End;
Allereerst geven we de klasse een naam, dit mag iedere willekeurge naam zijn,
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:
  1. TMijnCalculator = Class(TObject)
Let op: er staat geen punt-komma (; ) achter! Dit omdat de type-declaratie nog niet klaar is.

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:
  1. Implementation
  2.  
  3. Function TMijnCalculator.GetOptellen : Single;
  4. Begin
  5.   Result := fValueB + fValueA;
  6. End;
  7.  
  8. Function TMijnCalculator.GetAftrekken : Single;
  9. Begin
  10.   Result := fValueB - fValueA;
  11. End;
  12.  
  13. Function TMijnCalculator.GetVermenigVuldigen : Single;
  14. Begin
  15.   Result := fValueB * fValueA;
  16. End;
  17.  
  18. Function TMijnCalculator.GetDelen : Single;
  19. Begin
  20.   If (fValueA <> 0) Then
  21.     Result := fValueB / fValueA
  22.   Else
  23.     Result := 0;
  24. End;
  25.  
  26. Constructor TMijnCalculator.Create;
  27. Begin
  28.   Inherited Create;
  29.   fValueA := 0;
  30.   fValueB := 0;
  31. End;
Met deze code maken we de klasse af en kan de compiler het resultaat naar
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:
  1. End.

Vervolgens kunnen we de klasse gaan gebruiken met bijvoorbeeld deze code:
Delphi Code:
  1. Uses
  2.   BerekenUnit;
  3.  
  4. Procedure Berekenen2;
  5. Var
  6.   C1,
  7.   C2,
  8.   C3,
  9.   C4 : TMijnCalculator;
  10. begin
  11.   C1 := TMijnCalculator.Create;
  12.   Try
  13.     C1.ValueA := 10;
  14.     C1.ValueB := 20;
  15.  
  16.     C2 := TMijnCalculator.Create;
  17.     Try
  18.       C2.ValueA := 30;
  19.       C2.ValueB := 40;
  20.  
  21.       C3 := TMijnCalculator.Create;
  22.       Try
  23.         C3.ValueA := 50;
  24.         C3.ValueB := 60;
  25.  
  26.         C4 := TMijnCalculator.Create;
  27.         Try
  28.           C4.ValueA := 70;
  29.           C4.ValueB := 80;
  30.  
  31.           ShowMessage('A = ' + FloatToStr(C1.ValueA) + ', B = ' + FloatToStr(C1.ValueB) + #13#10 +
  32.                       'Optellen = ' + FloatToStr(C1.Optellen) + #13#10 +
  33.                       'Aftrekken = ' + FloatToStr(C1.Aftrekken) + #13#10 +
  34.                       'Vermenigvuldigen = ' + FloatToStr(C1.Vermenigvuldigen) + #13#10 +
  35.                       'Delen = ' + FloatToStr(C1.Delen));
  36.  
  37.           ShowMessage('A = ' + FloatToStr(C2.ValueA) + ', B = ' + FloatToStr(C2.ValueB) + #13#10 +
  38.                       'Optellen = ' + FloatToStr(C2.Optellen) + #13#10 +
  39.                       'Aftrekken = ' + FloatToStr(C2.Aftrekken) + #13#10 +
  40.                       'Vermenigvuldigen = ' + FloatToStr(C2.Vermenigvuldigen) + #13#10 +
  41.                       'Delen = ' + FloatToStr(C2.Delen));
  42.  
  43.           ShowMessage('A = ' + FloatToStr(C3.ValueA) + ', B = ' + FloatToStr(C3.ValueB) + #13#10 +
  44.                       'Optellen = ' + FloatToStr(C3.Optellen) + #13#10 +
  45.                       'Aftrekken = ' + FloatToStr(C3.Aftrekken) + #13#10 +
  46.                       'Vermenigvuldigen = ' + FloatToStr(C3.Vermenigvuldigen) + #13#10 +
  47.                       'Delen = ' + FloatToStr(C3.Delen));
  48.  
  49.           ShowMessage('A = ' + FloatToStr(C4.ValueA) + ', B = ' + FloatToStr(C4.ValueB) + #13#10 +
  50.                       'Optellen = ' + FloatToStr(C4.Optellen) + #13#10 +
  51.                       'Aftrekken = ' + FloatToStr(C4.Aftrekken) + #13#10 +
  52.                       'Vermenigvuldigen = ' + FloatToStr(C4.Vermenigvuldigen) + #13#10 +
  53.                       'Delen = ' + FloatToStr(C4.Delen));
  54.  
  55.         Finally
  56.           C4.Free;
  57.         End;
  58.       Finally
  59.         C3.Free;
  60.       End;
  61.     Finally
  62.       C2.Free;
  63.     End;
  64.   Finally
  65.     C1.Free;
  66.   End;
  67. end;
Om een klasse te kunnen gebruiken moeten we, naast een variabele waarmee
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:
  1. Begin
  2.   C1 := TMijnCalculator.Create;
  3.   Try
  4.     C1.ValueA := 10;
  5.     C1.ValueB := 20;
  6.     ShowMessage('A = ' + FloatToStr(C1.ValueA) + ', B = ' + FloatToStr(C1.ValueB) + #13#10 +
  7.                 'Optellen = ' + FloatToStr(C1.Optellen) + #13#10 +
  8.                 'Aftrekken = ' + FloatToStr(C1.Aftrekken) + #13#10 +
  9.                 'Vermenigvuldigen = ' + FloatToStr(C1.Vermenigvuldigen) + #13#10 +
  10.                 'Delen = ' + FloatToStr(C1.Delen));
  11.   Finally
  12.     C1.Free;
  13.   End;
  14. End;
Regel 2 in onze code zorgt ervoor dat instantie C1 van onze klasse gecreërd wordt;
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:
  1. // In onze TMijnCalculator-klasse
  2.     Function GetOptellenAsString : String;
  3.     Function GetAftrekkenAsString : String;
  4.     Function GetVermenigvuldigenAsString : String;
  5.     Function GetDelenAsString : String;
  6. //
  7.  
  8. Implementation
  9.  
  10. Function TMijnCalculator.GetOptellenAsString : String;
  11. Begin
  12.   Result := FloatToStr(GetOptellen);
  13. End;
  14.  
  15. Function TMijnCalculator.GetAftrekkenAsString : String;
  16. Begin
  17.   Result := FloatToStr(GetAftrekken);
  18. End;
  19.  
  20. Function TMijnCalculator.GetVermenigvuldigenAsString : String;
  21. Begin
  22.   Result := FloatToStr(GetVermenigvuldigen);
  23. End;
  24.  
  25. Function TMijnCalculator.GetDelenAsString : String;
  26. Begin
  27.   Result := FloatToStr(GetDelen);
  28. End;
Deze extra functies kunnen we gewoon toevoegen aan het Public-gedeelte van
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:
  1. type
  2.   TMijnCalculator = Class(TObject)
  3.   Private
  4.     fValueA : Single;
  5.     fValueB : Single;
  6.   Protected
  7.     Function GetOptellen : Single;
  8.     Function GetOptellenAsString : String;
  9.     Function GetAftrekken : Single;
  10.     Function GetAftrekkenAsString : String;
  11.     Function GetVermenigvuldigen : Single;
  12.     Function GetVermenigvuldigenAsString : String;
  13.     Function GetDelen : Single;
  14.     Function GetDelenAsString : String;
  15.   Public
  16.     Constructor Create;
  17.     Property ValueA                   : Single Read fValueA Write fValueA;
  18.     Property ValueB                   : Single Read fValueB Write fValueB;
  19.     Property Optellen                 : Single Read GetOptellen;
  20.     Property OptellenAsString         : String Read GetOptellenAsString;
  21.     Property Aftrekken                : Single Read GetAftrekken;
  22.     Property AftrekkenAsString        : String Read GetAftrekkenAsString;
  23.     Property Vermenigvuldigen         : Single Read GetVermenigvuldigen;
  24.     Property VermenigvuldigenAsString : String Read GetVermenigvuldigenAsString;
  25.     Property Delen                    : Single Read GetDelen;
  26.     Property DelenAsString            : String Read GetDelenAsString;
  27.   End;
Met deze versie van onze klasse kunnen we direct de berekende waarde als
string opvragen zoals hieronder:
Delphi Code:
  1. //
  2.     ShowMessage('A = ' + FloatToStr(C1.ValueA) + ', B = ' + FloatToStr(C1.ValueB) + #13#10 +
  3.                 'Optellen = ' + C1.OptellenAsString + #13#10 +
  4.                 'Aftrekken = ' + C1.AftrekkenAsString + #13#10 +
  5.                 'Vermenigvuldigen = ' + C1.VermenigvuldigenAsString + #13#10 +
  6.                 'Delen = ' + C1.DelenAsString);
  7. //
Dan nog een ding over over-erving (inheritence)...

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:
  1. Procedure Test;
  2. Var
  3.   C : TMijnCalculator;
  4. begin
  5.   C := TMijnCalculator.Create;
  6.   Try
  7.     ShowMessage(C.ClassName);
  8.   Finally
  9.     Free;
  10.   End;
  11. End;
Als je deze procedure uitvoert, krijg je een message te zien die zegt: "TMijnCalculator"
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
VideoRipper is offline   Met citaat antwoorden
Oud 12-Jul-09, 10:08   #2
GolezTrol
Moderateur
 
GolezTrol's Avatar
 
Geregistreerd op: Oct 2002
Berichten: 12.760
Mooie uitleg! Even losgeweekt uit "Classes, nog te ingewikkeld of?".
__________________
Klik hier!
GolezTrol is nu online   Met citaat antwoorden
Antwoord

Bookmarks


Momenteel bekijken: 1 (0 leden en 1 gasten en/of zoekmachine bots) actieve gebruikers dit onderwerp
 
Onderwerpopties Zoek in onderwerp
Zoek in onderwerp:

Geavanceerd zoeken
Weergavemodus Stem op dit onderwerp:
Stem op dit onderwerp::

Berichting Regels
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is Aan
Smilies zijn Aan
[IMG] code is Aan
HTML code is Uit

Forumnavigatie


Alle tijden zijn GMT +1. De tijd is nu 01:57.


Forum software: vBulletin, versie 3.8.4
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd.
Copyright ©2008, NLDelphi.com (Dutch Delphi programming)