Al sinds jaar en dag zijn we bekend met het roemruchte Client-Server model. Hierbij kan onder andere onderscheid worden gemaakt tussen een two-tier en een three-tier architectuur. Menigmaal is ons verteld dat we een strikte scheiding moeten aanbrengen tussen de interface, de middle-tier (applicatielogica) en de database. De voordelen zijn evident: we zijn niet gebonden aan de database keuze of de gekozen client interface (bijv. browser/windows).
Ook zijn we bekend met OOP (Object Oriented Programming) en vertalen we deze concepten in meer of mindere mate in een framework met als voornaamste doelen: herbruikbaarheid van componenten en onderhoudsgemak door ‘single point of inheritance’.
Het doel van dit artikel is om duidelijk te maken dat we daar maar ten dele in slagen en stelt een aantal benaderingen voor die daar verandering in kunnen brengen.
Fig. 1: three-tier model
Afhankelijkheden
Als je aan een groep VFP ontwikkelaars vraagt waar ze mee zouden beginnen bij de ontwikkeling van een nieuw framework, dan zeggen negen van de tien: ‘Subclassen van de VFP base classes’. Dat is immers de makkelijkste manier om een ‘single point of inheritance’ te verkrijgen. Dit is zonder meer het geval, maar deze benadering kent een zeer nadelige bijwerking, namelijk: het creëren van afhankelijkheden.
Slim gebruik van OOP betekent het zoveel mogelijk vermijden van afhankelijkheden
Als we alle componenten van ons nieuwe framework op deze framework classes baseren, dan is een zeer belangrijke vorm van hergebruik verloren gegaan. Immers, wanneer we vanuit een andere, reeds bestaande applicatie gebruik willen maken van een slimme component uit het framework, dan kan dit niet zonder alle classes mee te nemen waar de betreffende component van afhankelijk is. Dit zorgt er in veel gevallen voor dat vrijwel het hele framework in het andere project getrokken wordt.
Horizontale herbruikbaarheid
Je zou dus onderscheid kunnen maken tussen wat ik noem ‘verticale herbruikbaarheid’ (hergebruik van classes binnen dezelfde applicatie hierarchie) en ‘horizontale herbruikbaarheid’ (hergebruik over applicaties heen die niet allemaal op hetzelfde framework gebaseerd zijn).
Het is van belang om bij het maken van een component na te denken over de mogelijkheden tot hergebruik, de noodzaak tot het hebben van een ‘single point of inheritance’ en de consequenties met betrekking tot onderhoud.
Zo is het bijvoorbeeld veel minder noodzakelijk om een custom class op een framework class te baseren dan dat voor een een interface element zoals een textbox te doen.
Ik geef er persoonlijk de voorkeur aan om bepaalde componenten rechtstreeks op de VFP base classes te baseren en in een afzonderlijke class library onder te brengen om zodoende afhankelijkheden te vermijden.
Je zou kunnen zeggen dat op deze wijze enerzijds gebruik gemaakt wordt van het OOP concept en anderzijds van Component Based Development (CBD).
Wanneer OOP en wanneer CBD?
Om zoveel mogelijk te beantwoorden aan de gestelde doelen van hergebruik en onderhoudsgemak moeten we bepalen wanneer we welke methode toepassen. Als je er over nadenkt, kom je tot de conclusie dat je vooral behoefte hebt aan een ‘single point of inheritance’ in de interface-laag en ben je in de middle-tier en de data-laag juist meer gebaat bij horizontale herbruikbaarheid.
In de interface-laag heb je meer behoefte aan een ‘single point of inheritance’ en en in de middle-tier en de data-laag juist meer aan horizontale herbruikbaarheid
Ik kan me voorstellen dat het je inmiddels begint te duizelen en het wordt dan ook tijd voor een paar concrete voorbeelden.
Het geven van een voorbeeld van objecten die we bijna altijd willen baseren op de framework superclasses is eenvoudig. Interface controls zoals de textbox, combo, listbox, grid, lookup controls e.d. worden dermate veel gebruikt in een applicatie dat we daar gebaat zijn bij een ‘single point of inheritance’. Als zich dan de situatie voordoet dat we bijvoorbeeld een ander font willen gebruiken, dan is dit eenvoudig in de betreffende controls aan te passen zonder dat dit in ieder form opnieuw hoeft te gebeuren.
Voorbeeld – een onafhankelijke errorhandler
Een goed voorbeeld van standalone componenten zijn classes die gebaseerd zijn op een custom class. Als je bijvoorbeeld een custom class gemaakt hebt voor de koppeling met externe software en je hebt deze gebaseerd op de VFP custom base class, dan is deze component zo te maken dat hij hergebruikt kan worden in elke willekeurig andere applicatie.
We kunnen echter nog een stap verder gaan. Stel, je wilt een volledig onafhankelijke plug & play errorhandler maken die in iedere willekeurige andere VFP applicatie te gebruiken is, simpelweg door hem te instantiëren, dan ga je als volgt te werk:
1. Maak een cusError class in een afzonderlijke class library (bijvoorbeeld _error.vcx).
2. Geef de class een duidelijke object interface met bijvoorbeeld de volgende properties en methods: Activate(), Catch(), Deactivate(), Finally(), lIgnoreErrors, nState, Stop(), Terminate().
3. Activeer de errorhandler automatisch in de Init door een aanroep naar this.Activate().
4. Voeg een form toe aan de classlibrary voor het tonen van foutmeldingen vanuit het Catch() event waarbij in dit geval óók de interface elementen rechtstreeks gebaseerd zijn op VFP base classes.
5. Initialiseer de errorhandler in een willekeurige applicatie (kan zelfs een standalone prg zijn) door de eenvoudige code: this.NewObject('cusError', '_Error') .
6. Wil je in een andere applicatie een andere user-interface, dan kun je frmError subclassen en naar wens aanpassen.
Een uitgewerkte versie van dit voorbeeld is te downloaden vanaf de SDN site: zoek even naar dit artikel in magazine 82. Instantiëren in je applicatie is voldoende!
BINDEVENT()
We hebben al geconstateerd dat het zoveel mogelijk vermijden van afhankelijkheden een goede zaak is. De in VFP8 geïntroduceerde BindEvent() functie is hierin van onschatbare waarde.
We weten inmiddels allemaal wel hoe deze functie werkt, maar simpel gezegd kun je een event van een ander object koppelen aan een event van je component, bijvoorbeeld in een resizer-control:
*- BINDEVENT(oEventSource, cEvent, oEventHandler,
*- cDelegate [, nFlags])
BINDEVENT(this.parent, ‘Resize’, this, ‘myResizeEvent’)
Listing 1: Voorbeeld code BindEvent
Vroeger moest je om dit te bereiken een aanroep doen vanuit het Resize()-event van de parent-control (bijv. een form):
*- Old way
This.cusResizer.myResize()
Listing 2: Voorbeeld code ‘oude’ methode
Het grote nadeel van deze code is echter dat de parent-control kennis moet hebben van de interface van de resizer-control waardoor deze qua interface afhankelijk is geworden.
In het eerste geval echter kan de resizer-control zonder problemen vervangen worden door een andere, ook al heeft die een heel andere interface.
Wanneer je hier bij het ontwerpen van je componenten/controls rekening mee houdt, zal het in de toekomst veel gemakkelijker zijn om onderhoud te plegen en/of bepaalde componenten te vervangen door betere.
CursorAdapter
De CursorAdapter vormt een belangrijke toevoeging aan de reeds uitgebreide datahandling-mogelijkheden in VFP. Dit heeft meer consequenties dan je op het eerste gezicht zou vermoeden.
Met de komst van de CursorAdapter hebben we feitelijk de beschikking gekregen over object-georiënteerde data componenten!
Je kunt bijvoorbeeld uitstekend je business rules in de CursorAdapter onderbrengen, waarna deze indien gewenst gesubclassed kan worden. Dit is niet mogelijk bij het onderbrengen van business rules in de database. Doordat de CursorAdapter over events beschikt, verloopt ook de communicatie naar de interface veel eenvoudiger (bijvoorbeeld het bijwerken van de toolbar) en hebben we meer mogelijkheden om controle uit te oefenen. De CursorAdapter is met recht een object-geörienteerde middle-tier component die met behulp van BindEvents() een veel striktere scheiding tussen de interface, de middle-tier en de data-laag kan bewerkstelligen.
Conclusie
Samenvattend kunnen we concluderen dat de vertrouwde three-tier client-server architectuur nog steeds voldoet, maar dat de keuzes die we maken bepalend zijn voor het rendement van de herbruikbaarheid van onze programmatuur.
auteur: Omar van Galen
tijdschrift: SDGN magazine no. 83