Welkom bij de Design Patterns



Vergelijkbare documenten
IMP Uitwerking week 13

Objectgeorïenteerd werken is gebaseerd op de objecten die door het systeem gemanipuleerd worden.

Datatypes Een datatype is de sort van van een waarde van een variabele, veel gebruikte datatypes zijn: String, int, Bool, char en double.

Abstracte klassen & Interfaces

Ontwerp van Informatiesystemen

2 Ik en autisme VOORBEELDPAGINA S

Zelftest Programmeren in Java

DEEL 1. WERKBOEK 5 Eigen keuze Monique van Dam YOU: De keuze is aan jou!

Les 3 - maandag 3 januari De Wilgenstam kleutergroep van meester Jasper

Tentamen Object Georiënteerd Programmeren TI oktober 2014, Afdeling SCT, Faculteit EWI, TU Delft

Theorieboek. leeftijd, dezelfde hobby, of ze houden van hetzelfde. Een vriend heeft iets voor je over,

Kleine cursus PHP5. Auteur: Raymond Moesker

Dit boekje is van... Mijn naam is: Mijn gezinsvoogd heet: Het telefoonnummer van de gezinsvoogd is:

Als je nog steeds hoopt dat oplossingen buiten jezelf liggen dan kun je dit boekje nu beter weg leggen.

Vrienden kun je leren

Object Oriented Programming

Deel het leven Johannes 4:1-30 & december 2014 Thema 4: Gebroken relaties

Reflectiegesprekken met kinderen

Tentamen Object Georiënteerd Programmeren TI januari 2013, Afdeling SCT, Faculteit EWI, TU Delft

Object Oriented Ontwerp. Yannick Reekmans

ALGORITME objectgeoriënteerd programmeren

Omschrijf bij ieder onderdeel van de methode de betekenis ervan. Java kent twee groepen van klassen die een GUI kunnen maken: awt en swing.

Uitwerking Aanvullend tentamen Imperatief programmeren Woensdag 24 december 2014, uur

WORD GROTER DAN DAT WAT JOU KLEIN HOUDT. Ann Weiser Cornell en Egbert Monsuur

Inspirerend Presenteren

Herman gaat met zijn dochter Lies naar de dierentuin. Joppie de hond gaat ook mee. Ze gaan gelijk naar de apen, die dicht bij de ingang zijn.

Tentamen Imperatief en Object-georiënteerd programmeren in Java voor CKI

Klassen & objecten, overerving, abstracte klassen, debuggen, interfaces, formulieren, polymorfie, statische methoden, event-handlers

TEST 1: Eerst denken of eerst doen? Kruis steeds het antwoord aan dat het best bij jou past. Probeer zo eerlijk mogelijk te antwoorden.

Om mee te beginnen: boekfragment en opdrachten

INFITT01 - Internettechnologie WEEK 8

Aan het eind van deze lesbrief wordt uitgelegd wat het nut van OOP is en vind je een aantal oefenopdrachten.

Teksten bewerkt uit het gezinsboek Ons Dagelijks Brood veertigdagentijd van pastoor M. Hagen door EBP voor

MEE Nederland. Raad en daad voor iedereen met een beperking. Moeilijk lerend. Uitleg over het leven van een moeilijk lerend kind

Die nacht draait Cees zich naar me toe. In het donker voel ik heel zachtjes zijn lippen op mijn wang.

Timemanagement Kerngebieden onderscheiden

Hoofdstuk 1: Inleiding. Hoofdstuk 2: Klassen en objecten Datahiding: afschermen van implementatiedetails. Naar de buitenwereld toe enkel interfaces.

Communicatie op de werkvloer

Lucas 10: Mag Jezus jouw naaste zijn?

Meten van mediawijsheid. Bijlage 6. Interview. terug naar meten van mediawijsheid

3. Wat betekent dat voor de manier waarop lesgegeven zou moeten worden in de - voor jou - moeilijke vakken?

Researchverslag: rituelen Joanna Siccama GAR1-B leraar: Harald Warmelink

Vraag 1. Vraag 1a TERUGKOPPELING PROEFTENTAMEN. Software architecture

Wij zijn Kai & Charis van de Super Student en wij geven studenten zin in de toekomst.

Inhoud Inhoud. Over dit boek 7. 1 Eclipse IDE (Integrated Development Environment) 9. 2 Functionele specificatie 13

150 Tips om kinderen te laten zien dat je om ze geeft!

Lekker ding. Maar Anita kijkt boos. Hersendoden zijn het!, zegt ze. Die Jeroen is de ergste. Ik kijk weer om en zie hem meteen zitten.

Handleiding voor het maken van je eigen webpagina op de schoolsite

Verder zijn er de nodige websites waarbij voorbeelden van objectgeoriënteerd PHP (of Objec Oriented PHP, OO PHP) te vinden zijn.

Preek Psalm 78: september 2015 In het spoor van Opening winterwerk Spiegelbeeld I

Wat is PDD-nos? VOORBEELDPAGINA S. Wat heb je dan? PDD-nos is net als Tourette een neurologische stoornis. Een stoornis in je hersenen.

B a s S m e e t s w w w. b s m e e t s. c o m p a g e 1

WORD GROTER DAN DAT WAT JOU KLEIN HOUDT. Ann Weiser Cornell en Egbert Monsuur

Lesbrief 14. Naar personeelszaken.

Overerving & Polymorfisme

ADHD: je kunt t niet zien

Koningspaard Polle en de magische kamers van paleis Kasagrande

VI. Klassen en objecten

Connect Social Business

Relaties. HDYO heeft meer informatie beschikbaar over de Ziekte van Huntington voor jongeren, ouders en professionals op onze website:

HANDIG ALS EEN HOND DREIGT

[PILOT] Aan de slag met de Hoofdzaken Ster

13 Acquisitietips. AngelCoaching. Coaching en training voor de creatieve sector

TITEL ACTIVITEIT + beschrijving: filosofisch gesprek over geloven.

Luisteren en samenvatten

4 Denken. in het park een keer gebeten door een hond. Als Kim een hond ziet wil ze hem graag aaien. Als

Oplossingsgerichte vragen (Het Spel van Oplossingen IKB & TS)

Beertje Anders. Lief zijn voor elkaar. Afspraak 2

Weer naar school. De directeur stapt het toneel op. Goedemorgen allemaal, zegt hij. * In België heet een mentor klastitularis.

OBJECT SPAGHETTI : PATTERNS BIEDEN UITKOMST? Wat is het probleem nou eigenlijk? public class CoffeeDrinker { private CoffeeProducer mycoffeeproducer;

Opdracht 7a. Applicatiebouw 2014/2015

Inhoud. 1 Wil je wel leren? 2 Kun je wel leren? 3 Gebruik je hersenen! 4 Maak een plan! 5 Gebruik trucjes! 6 Maak fouten en stel vragen!

Online Titel Competentie Groepsfase Lesdoel Kwink van de Week

Software-Ontwikkeling I Academiejaar

Grenzeloze vrijheid? Discussiebijeenkomst tienerclub

Hoofdstuk 2. Contact maken, inlichtingen verstrekken en onderhandelen

De begeleider als instrument bij gedragsproblemen

1.Inleiding: De Plug & Play Business Formule

Lou en Lena: NEE tegen geweld!

5 manieren om je eigen pad te bewandelen

Connect Social Business

Hele fijne feestdagen en een gezond en vrolijk 2017! Raymond Gruijs. BM Groep ARBO West Baanzinnig

Het Netwerk Boek. Zakelijk Netwerken. Wie Wat Waar Waarom Hoe. Social Media Personal Branding Tips

Bijeenkomst over geloofsopvoeding Communiceren met je puber Deze bijeenkomst sluit aan bij Moments, magazine voor ouders van jongeren van jaar

De gelijkenis van de onbarmhartige dienstknecht

11 dingen die je nu kunt doen om meer te gaan verkopen

Ik-Wijzer Ik ben wie ik ben

De Vergeten Abstracties

Les 1 Kikker en de Vreemdeling

Wees duidelijk tegen je klanten

Programmeren in Java 3

tientallen miljoenen euro s per jaar. Ook een vrijwilliger heeft zo een economische waarde.

Knabbel en Babbeltijd.

Dialogen website Motiveren tot rookstop

De bij die niet kon vliegen

Vakgroep CW KAHO Sint-Lieven

Werkboek Het is mijn leven

Het houden van een spreekbeurt

De gelijkenis van de barmhartige Samaritaan.

Transcriptie:

1 Inleiding tot Design Patterns g h Welkom bij de Design Patterns g Nu we in Objectville wonen, moeten we ook met de Design Patterns kennismaken... dat doet hier iedereen. We zullen gauw de ster zijn van de woensdagavond patternsgroep van Jim en Betty. Iemand heeft je problemen al eens opgelost. In dit hoofdstuk leer je waarom (en hoe) je de kennis en ervaring kunt inzetten van andere ontwikkelaars die dezelfde weg bij het ontwerpprobleem hebben afgelegd en deze trip hebben overleefd. Eerst bekijken we het gebruik en de voordelen van Design Patterns, bespreken we enkele belangrijke OO-ontwerpprincipes, en wandelen we door een voorbeeld om te zien hoe een pattern werkt. De beste manier om patterns te gebruiken is ze uit het hoofd te leren, om vervolgens de plaatsen waar je ze kunt toepassen te herkennen in jouw ontwerpen en in bestaande applicaties. In plaats van hergebruik van code krijg je hergebruik van ervaring. Dit is een nieuw hoofdstuk 1 421_01_nl.indd 1 30-04-2007 22:58:11

SimUDuck Het begon met een eenvoudige SimUDuck-toepassing Joe werkt voor een bedrijf dat veel succes heeft met met een simulatiespel van een eendenvijver, SimUDuck. Het spel kan een groot aantal verschillende eendensoorten tonen die rondzwemmen en kwaken. De oorspronkelijke ontwerpers van het systeem gebruikten standaard- OO-technieken en creëerden een superklasse Duck waar alle andere eendentypen van afgeleid zijn. Alle eenden kwaken en zwemmen, de superklasse zorgt voor de implementatiecode. Duck quack() swim() display() // ANDERE eend-achtige methoden... De methode display() is abstract, aangezien alle subtypen van eend er verschillend uitzien. Ieder subtype van duck is verantwoordelijk voor de implementatie van het gedrag van zijn eigen display() om te laten zien hoe deze op het scherm verschijnt. MallardDuck display() { // lijkt op een wilde // eend RedheadDuck display() { // lijkt op een // roodkuifeend Veel andere soorten eenden erven van de klasse Duck. Het afgelopen jaar heeft het bedrijf steeds meer last van de concurrentie. Na een week lang brainstormen tijdens een heisessie, denkt de bedrijfsleiding dat het tijd wordt voor een grote innovatie. Ze moeten echt iets geweldigs laten zien op de komende aandeelhoudersvergadering volgende week in Maui. 2 Hoofdstuk 1 421_01_nl.indd 2 30-04-2007 22:58:22

Inleiding tot Design Patterns Nu moeten we de eenden leren vliegen De bedrijfsleiding besloot dat vliegende eenden in de simulator precies datgene was dat concurrerende eendensimulators zou wegvagen. En natuurlijk vertelde de manager van Joe dat het geen probleem zou zijn om dit in een weekje voor elkaar te krijgen. Tenslotte, zei de baas van Joe, is hij een OO-programmeur... hoe moeilijk kan dat nu zijn. Ik hoef alleen een methode fly() aan de klasse Duck toe te voegen en alle eenden zullen haar erven. Nu is het de tijd om te laten zien hoe goed ik in OO ben. Dit willen we. Joe Alle subklassen erven fly(). Duck quack() swim() display() fly() // ANDERE eend-achtige methoden... Dit heeft Joe toegevoegd. MallardDuck display() { // lijkt op een // wilde eend RedheadDuck display() { // lijkt op een // roodkuifeend Andere eendensoorten Je bent hier 4 3 421_01_nl.indd 3 30-04-2007 22:58:23

Er ging iets mis Maar er liep iets helemaal mis Joe, ik ben hier op de aandeelhoudersvergadering. Ze hebben net een demo getoond en er vlogen badeenden over het scherm. Is dat een grap of zo? Misschien moet je eens op monster.com gaan rondkijken! Door fly() in de superklasse te plaatsen, zorgde hij ervoor dat ALLE eenden konden vliegen, inclusief diegenen die dat niet mogen. Wat is er gebeurd? Joe had er niet aan gedacht dat niet alle subklassen van Duck mogen vliegen. Toen Joe nieuw gedrag aan de superklasse Duck toevoegde, voegde hij tevens gedrag toe aan een aantal subklassen van Duck waarvoor dat niet van toepassing was. Het gevolg was dat er nu niet-vliegende objecten in het programma SimUDuck gingen vliegen. Een lokale update zorgde voor niet-lokale neveneffecten (vliegende badeenden)! Duck quack() swim() display() fly() // ANDERE eend-achtige methoden... OK, er zit een klein foutje in mijn ontwerp. Ik begrijp overigens niet waarom ze dat niet gewoon een feature noemen. Op zich is dat toch heel leuk... Hij had in verband met hergebruik aan overerving gedacht, hetgeen met het oog op onderhoud niet zo goed uitpakte. MallardDuck display() { // lijkt op een // wilde eend ReadheadDuck display() { // lijkt op een // roodkuifeend RubberDuck quack() { // overridden door Squeak display() { // lijkt op een badeend Badeenden kwaken niet, dus wordt quack() overridden door Squeak. 4 Hoofdstuk 1 421_01_nl.indd 4 30-04-2007 22:58:25

Inleiding tot Design Patterns Joe denkt over overerving na... Via override zou ik natuurlijk de methode fly() voor de badeend kunnen vervangen, op dezelfde manier als bij de methode quack()... Maar wat gebeurt er dan wanneer we houten lokeenden aan het programma toevoegen? Die vliegen en kwaken ook niet... RubberDuck quack() { // squeak display() { // badeend fly() { // override, zodat ze niets // doen DecoyDuck Dit is een andere klasse in de hiërarchie; merk op dat ze net als de badeend niet vliegt en bovendien kwaakt ze niet. quack() { // override, zodat ze niets doen display() { // lokeend fly() { // override, zodat ze niets doen Slijp je potlood Welke van de volgende alternatieven zijn nadelig voor het gebruik van overerving om voor het gedrag van een eend te zorgen? (Meerdere alternatieven zijn mogelijk.) A. Code wordt in de subklassen gedupliceerd. B. Runtime gedragsveranderingen zijn zéér moeilijk/onmogelijk. C. We kunnen eenden niet laten dansen. D. Het is moeilijk om het gedrag van alle eenden te kennen. E. Eenden kunnen niet tegelijk vliegen en kwaken. F. Veranderingen kunnen onbedoeld andere eenden beïnvloeden. Je bent hier 4 5 421_01_nl.indd 5 30-04-2007 22:58:27

Overerving is niet het antwoord Hoe zit dat met een interface? Joe realiseert zich dat overerving vermoedelijk niet het antwoord is, omdat hij zojuist een memo heeft ontvangen waarin staat dat de leiding nu wil dat het product voortaan ieder zes maanden wordt geüpdatet (op welke manier is nog niet besloten). Joe weet dat de specificaties blijven veranderen en dat hij gedwongen zal zijn naar de methoden fly() en quack() te kijken en deze via een override zal moeten vervangen, iedere keer dat er een nieuwe subklasse aan Duck wordt gehangen... en wel voor eeuwig. Hij heeft dus een betere manier nodig waarbij slechts een aantal (maar niet alle) eendensoorten vliegen en kwaken. Ik zou fly() uit de superklasse Duck kunnen halen en een interface Flyable kunnen maken met een fly()-methode. Op die manier zullen alleen eenden die kunnen vliegen deze interface implementeren en over een methode fly() beschikken... en ik zou ook een interface Quackable kunnen maken omdat niet alle eenden kunnen kwaken. Flyable fly() Quackable quack() Duck swim() display() // ANDERE eend-achtige methoden... MallardDuck ReadheadDuck RubberDuck DecoyDuck display() fly() quack() display() fly() quack() display() quack() display() Wat denk jij van dit ontwerp? 6 Hoofdstuk 1 421_01_nl.indd 6 30-04-2007 22:58:30

Inleiding tot Design Patterns Dat is wel ongeveer het domste dat je kunt doen. Heb je al eens van codeduplicatie gehoord? Als je al dacht dat het vervangen van een paar methoden via override een slecht idee was, hoe denk je dan over het aanbrengen van een paar kleine veranderingen in het vlieggedrag... in alle 48 vliegende subklassen van Duck?! Wat zou jij doen als je Joe was? We weten dat niet alle subklassen vlieg- of kwaakgedrag moeten vertonen, dus is overerving niet het juiste antwoord. Weliswaar wordt via de implementatie van Flyable en/of Quackable in de subklassen een deel van het probleem opgelost (zodanig dat badeenden niet meer kunnen vliegen), maar het concept van hergebruik van code voor een dergelijk gedrag wordt onmogelijk en zo ontstaat een andere nachtmerrie voor het onderhoud. Bovendien kunnen er meerdere soorten vlieggedrag voorkomen bij eenden die wel kunnen vliegen... Op dit punt gestrand, zou je kunnen wachten tot er een Design Pattern op een wit paard aan komt galopperen en je dag weer goed maakt. Maar wat is daar voor lol aan? Nee, we gaan maar eens op de ouderwetse manier een oplossing verzinnen... door de juiste softwareontwerpprincipes volgens OO toe te passen. Zou het niet mooi zijn als er een manier bestond om software zo te bouwen dat wanneer we deze moeten veranderen, we dit met zo weinig mogelijk gevolgen voor de bestaande code kunnen doen? We zouden dan minder tijd besteden aan het opnieuw bewerken van de code en meer tijd hebben om het programma nog cooler te maken... Je bent hier 4 7 421_01_nl.indd 7 30-04-2007 22:58:31

Veranderen is de constante De enige constante bij softwareontwikkeling Oké, wat is het enige ding waar je bij softwareontwikkeling altijd op kunt rekenen? Los van wat je maakt of in welke taal je programmeert, wat is de enige constante die je altijd tegenkomt? VERANDERING (Gebruik een spiegel om het antwoord te lezen). Het maakt niet uit hoe goed je een applicatie ontwerpt, na verloop van tijd moet zij groeien of veranderen, want anders zal de applicatie afsterven. Slijp je potlood Er zijn een hoop oorzaken die tot veranderingen kunnen leiden. Maak een lijst met redenen waarom je de code van applicaties moest veranderen (we hebben er maar vast een paar genoemd om je op weg te helpen). Mijn klanten of gebruikers vonden dat ze iets anders wilden, of ze wilden nieuwe functionaliteit. Mijn bedrijf besloot op een andere databaseleverancier over te stappen en koopt ook zijn gegevens van een andere leverancier die een verschillend formaat gebruikt. Oei! 8 Hoofdstuk 1 421_01_nl.indd 8 30-04-2007 22:58:32

Inleiding tot Design Patterns Terug naar het probleem We weten nu dat overerving niet goed heeft uitgepakt, aangezien het gedrag van de eenden in de diverse subklassen verandert en we het gedrag niet in alle subklassen kunnen toepassen. Het gebruik van een Flyable- en Quackable-interface klonk in eerste instantie veelbelovend alleen eenden die echt kunnen vliegen kunnen Flyable zijn, enzovoort maar Java-interfaces kennen geen implementatiecode en is er zo geen hergebruik van code. En dat betekent weer dat wanneer je een bepaald gedrag moet aanpassen, je gedwongen bent om dit op te sporen en en te veranderen in alle subklassen waarin dit gedrag gedefinieerd wordt; op deze manier worden waarschijnlijk nieuwe fouten geïntroduceerd! Gelukkig bestaat er ook voor deze situatie een ontwerpprincipe. Ontwerpprincipe Bepaal de aspecten van je applicatie die variëren en scheid deze van de aspecten die hetzelfde blijven. Dit is de eerste van vele ontwerpprincipes. We besteden daar het hele boek door meer tijd aan. Neem datgene dat varieert en isoleer dit, zodat de rest van je code niet wordt beïnvloed. Het gevolg? Minder onbedoelde effecten door veranderingen in de code en een grotere flexibiliteit in jouw systemen! Anders gezegd, bevat je code een aspect dat verandert, bijvoorbeeld met iedere nieuwe systeemeis, dan weet je dat je een gedrag hebt dat eruit gelicht moet worden en moet worden afgezonderd van alle code die niet verandert. We kunnen dit principe ook op een andere manier benaderen: verzamel de delen die variëren en isoleer deze. Op deze wijze kun je de delen die variëren, veranderen of uitbreiden zonder dat de delen die niet veranderen worden beïnvloed. Hoe eenvoudig dit concept ook is, het vormt de basis voor bijna ieder design pattern. Alle patterns voorzien in een manier om bepaalde systeemdelen onafhankelijk van de andere delen te veranderen. Mooi, het wordt tijd om het eendengedrag uit de subklassen van Duck te lichten! Je bent hier 4 9 421_01_nl.indd 9 30-04-2007 22:58:32

Het eruit lichten van veranderlijke delen Scheid wat verandert van wat hetzelfde blijft Waar zullen we beginnen? Zover we weten werkt de klasse Duck goed, afgezien van de problemen met fly() en quack(), en zijn er geen andere delen die zouden moeten variëren of regelmatig veranderen. Dus afgezien van enkele kleine veranderingen laten we het grootste deel van de klasse Duck met rust. Nu gaan we dus de delen die veranderen scheiden van de delen die hetzelfde blijven. We maken nu, volledig gescheiden van de klasse Duck, twee sets met klassen, een voor fl y en een voor quack. Iedere set klassen gaat de volledige implementatie van hun respectievelijke gedrag bevatten. Zo kunnen we een klasse hebben voor de implementatie van quacking, een andere klasse voor de implementatie van squeaking en weer een andere klasse voor de implementatie van silence. We weten dat fly() en quack() de delen van de klasse Duck zijn die voor eenden verschillen. Om dit gedrag van de klasse Duck te scheiden, halen we beide methoden uit de klasse Duck en maken we een nieuwe set klassen die het betreffende gedrag vertegenwoordigen. De klasse Duck is nog steeds de superklasse van alle eenden, maar we lichten het gedrag fly en het gedrag quack eruit en plaatsen deze in een andere klassestructuur. Nu krijgen het vliegen en het kwaken ieder een eigen set klassen. Hier staat de implementatie van de diverse soorten gedrag. Eruit lichten wat kan veranderen. Klasse Duck Vlieggedrag Kwaakgedrag Eendengedrag 10 Hoofdstuk 1 421_01_nl.indd 10 30-04-2007 22:58:32

Inleiding tot Design Patterns Het eendengedrag ontwerpen Hoe moeten we nu de set klassen voor de implementatie van het gedrag fly en quack ontwerpen? We willen flexibel blijven; het was uiteindelijk juist de inflexibiliteit van het eendengedrag die ons in moeilijkheden bracht. En we weten dat we gedrag aan de instanties van Duck willen toekennen. We willen wellicht een nieuwe instantie van MallardDuck concretiseren en deze initialiseren met een bepaald soort vlieggedrag. En als we dan toch bezig zijn, waarom zorgen we er dan niet voor dat we het gedrag van een eend dynamisch kunnen veranderen? Anders gezegd, we moeten methoden voor het instellen van het gedrag in de klassen van Duck opnemen, zodat we bijvoorbeeld het vlieggedrag van de klasse MallardDuck at runtime kunnen veranderen. Met dit doel voor ogen bekijken we het tweede ontwerpprincipe: Ontwerpprincipe Programmeer naar een interface, niet naar een implementatie. We gaan voor ieder gedrag een interface gebruiken bijvoorbeeld Fly- Behavior en QuackBehavior en iedere implementatie van een gedrag implementeert een van deze interfaces. Dus deze keer zijn het niet de klassen van Duck die de interfaces voor het vliegen en kwaken implementeren. In plaats daarvan maken we een set klassen waarvan de enige bestaansreden is dat ze een gedrag voorstellen (bijvoorbeeld squeaking ). De klasse met het gedrag en niet de klasse Duck implementeert de gedragsinterface. Dit staat in contrast tot de manier waarop we de zaken tot dusver regelden, waarbij we het gedrag realiseerden of via een concrete implementatie in de superklasse Duck, of voor een specifieke implementatie in de subklassen zelf. In beide gevallen vertrouwden we op een implementatie. We maakten ons afhankelijk van een specifieke implementatie en er was geen ruimte voor het veranderen van het gedrag (anders dan door meer code te schrijven). In ons nieuwe ontwerp gebruiken we voor de subklassen van Duck het gedrag dat door een interface wordt voorgesteld (FlyBehavior en Quack- Behavior); de actuele implementatie van het gedrag (dus de specifieke code voor het gedrag in de klasse die FlyBehavior of QuackBehavior implementeert) zit niet langer opgesloten in de subklassen van Duck. Van nu af aan staat het gedrag van Duck in een afzonderlijke klasse een klasse die een bepaalde gedragsinterface implementeert. Op deze manier hoeven de klassen van Duck niets te weten van de implementatiedetails van hun eigen gedrag. fly() FlyWithWings fly() { // implementeert het vliegen // van eenden <<Interface>> Flybehavior FlyNoWay fly() { // doe niets - kan niet // vliegen // Je bent hier 4 11 421_01_nl.indd 11 30-04-2007 22:58:33

Naar een interface programmeren Ik begrijp niet waarom je een interface voor FlyBehavior moet gebruiken. Je kunt hetzelfde bereiken via een abstracte superklasse. Draait het daar bij polymorfisme niet om? Programmeren naar een interface betekent feitelijk Programmeren naar een supertype. Het woordje interface heeft een dubbele betekenis. Maar dit is geen toeval. We kennen het concept interface, maar er bestaat ook een constructie met de naam interface in Java. Je kunt naar een interface programmeren zonder de interface van Java te gebruiken. Het gaat er om zodanig van polymorfisme gebruik te maken door naar een supertype te programmeren zodat het actuele runtimeobject niet is opgesloten in de code. We kunnen de uitdrukking programmeren naar een supertype vervangen door het gedeclareerde type van de variabele dient een supertype te zijn, doorgaans een abstracte klasse of een interface, zodat de objecten die aan dergelijke variabelen worden toegekend, iedere concrete implementatie van het supertype kunnen zijn; dat betekent dat de klasse die ze declareert, niets hoeft te weten over de actuele objecttypen! Misschien is dit oud nieuws, maar om er zeker van te zijn dat we over hetzelfde praten, volgt hier een eenvoudig voorbeeld waarin een polymorf type wordt gebruikt denk aan een abstracte klasse Animal, met twee concrete implementaties, Dog en Cat. Programmeren naar een implementatie zou opleveren: abstract supertype (kan een abstracte klasse OF een interface zijn) Dog h = new Dog(); h.bark(); Declaratie van de variabele h als type Dog (een concrete implementatie van Animal) dwingt ons er toe om een concrete implementatie te coderen. concrete implementaties Dog MakeSound() { bark(); bark() { // blafgeluid makesound() 12 Hoofdstuk 1 Animal Cat makesound() { meow(); meow() { // miauwgeluid Maar programmeren naar een interface/supertype zou geven: Animal animal = new Dog(); animal.makesound(); Nog beter dan de instantiatie van het subtype hard in de code te coderen (zoals new Dog()), is het om de concrete implementatie van het object at runtime toe te kennen: a = getanimal(); a.makesound(); We weten dat het een Dog is, maar we kunnen nu de referentie naar animal polymorf gebruiken. We weten niet WAT het actuele subtype van animal is... we zorgen er alleen voor dat het weet hoe het op makesound() moet reageren. 421_01_nl.indd 12 30-04-2007 22:58:34

Inleiding tot Design Patterns Implementatie van het eendengedrag We hebben nu twee interfaces FlyBehavior en QuackBehavior samen met hun bijbehorende klassen voor de implementatie van hun concreet gedrag: fly() <<interface>> FlyBehavior FlyBehavior is een interface die alle vliegende klassen implementeert. Alle nieuwe vliegende klassen hoeven alleen de methode fly() te implementeren. <<interface>> QuackBehavior quack() En idem voor het kwaakgedrag; we hebben een interface die alleen een methode quack() bevat die we moeten implementeren. FlyWithWings fly() { // implementeert het // vliegen van eenden FlyNoWay fly() { // doe niets - kan niet // vliegen! Quack quack() { // implementeert // eendengekwaak Squeak quack() { // implementeert piepen // van een badeend MuteQuack quack() { // doe niets - kan niet // kwaken! Dit is de implementatie van het vliegen van alle eenden met vleugels. En dit is de implementatie voor alle eenden die niet kunnen vliegen. Wanneer er echt gekwaakt wordt. Wanneer er alleen gepiept wordt. Gekwaak zonder geluid. In dit ontwerp kunnen andere objecttypen ons gedrag voor vliegen en kwaken hergebruiken omdat dit gedrag niet langer binnen onze klassen van Duck is weggeborgen. En we kunnen nieuw gedrag toevoegen zonder dat we de bestaande gedragsklassen hoeven te veranderen of de klassen van Duck die het vlieggedrag gebruiken hoeven te benaderen. Op deze wijze krijgen we het voordeel van HERGEBRUIK zonder de troep die hoort bij het gebruik van overerving. Je bent hier 4 13 421_01_nl.indd 13 30-04-2007 22:58:35

Gedrag in een klasse Er bestaan geen domme vragen V: Moet ik altijd eerst mijn applicatie implementeren, daarna bekijken wat er verandert, en tot slot teruggaan en deze zaken scheiden en inkapselen? A: Niet altijd; vaak anticipeer je al tijdens het ontwerpen van een applicatie op de aandachtsgebieden die kunnen variëren waarna je vervolgt met het inbouwen van de flexibiliteit om hier in de code rekening mee te houden. Je ontdekt dat de principes en patterns in iedere fase van de ontwikkelingslifecycle kunnen worden toegepast. V: Moeten we van Duck ook een interface maken? A: In dit geval niet. Je zult nog zien dat wanneer we de hele boel bij elkaar hebben gevoegd, we baat hebben bij het feit dat Duck geen interface is en dat specifieke eenden, zoals de MallardDuck, gemeenschappellijke eigenschappen en methoden erven. Nu we de veranderlijke zaken uit de overerving van Duck hebben verwijderd, kunnen we zonder problemen de vruchten van deze structuur plukken. V: Het is wel een beetje vreemd een klasse te hebben die alleen maar een gedrag vormt. Zijn klassen niet bedoeld om dingen voor te stellen? Moeten klassen niet zowel toestanden EN gedrag bezitten? A: In een OO-systeem, ja. Daarin vertegenwoordigen klassen gewoonlijk dingen met toestanden (instantievariabelen) en methoden. In dit geval zijn die dingen gedrag. Maar zelfs een gedrag kan nog steeds een toestand en methoden hebben; het vlieggedrag kan bijvoorbeeld instantievariabelen kennen die de attributen voor het vlieggedrag voorstellen (vleugel, slagen per minuut, maximale hoogte en snelheid enzovoort). Slijp je potlood 1 Wat zou jij doen als in je in het nieuwe ontwerp vliegen met raketaandrijving aan de SimUDuck-applicatie moest toevoegen? 2 Kun je een klasse verzinnen die het gedrag van Quack kan gebruiken en toch geen eend is? 2) Een voorbeeld: een lokfluit voor eenden (een apparaat dat eendengeluiden maakt). 1) Maak een klasse FlyRocketPowered die de interface FlyBehavior implementeert. Antwoorden: 14 Hoofdstuk 1 421_01_nl.indd 14 30-04-2007 22:58:36

Inleiding tot Design Patterns Integratie van het eendengedrag De sleutel is dat Duck het vlieg- en kwaakgedrag delegeert, in plaats van gebruik te maken van methoden voor het vliegen en kwaken, die gedefinieerd worden in de klasse Duck (of haar subklassen). Dat gaat als volgt: 1 Eerst voegen we twee instantievariabelen toe aan de klasse Duck met de namen fl ybehavior en quackbehavior. Deze worden gedeclareerd als het type van de interface (dus geen concreet implementatietype van een klasse). Ieder object eend zal deze variabelen polymorf invullen om naar het specifieke gedragstype at runtime te verwijzen (FlyWithWings, Squeak enzovoort). Bovendien verwijderen we de methoden fly() en quack() uit de klasse Duck (en ook uit iedere subklasse), omdat we dit gedrag willen verplaatsen naar de klassen FlyBehavior en QuackBehavior. We vervangen fly() en quack() in de klasse Duck door twee vergelijkbare methoden, performfly() en performquack(). Je zult nog zien hoe deze werken. De gedraqsvariabelen worden gedeclareerd als het interfacetype voor dat gedrag. Deze methoden vervangen fly() en quack(). Duck FlyBehavior flybehavior QuackBehavior quackbehavior performquack() swim() display() performfly() // ANDERE eend-achtige methoden... Instantievariabelen bevatten de referentie naar bepaald gedrag at runtime. Vlieggedrag Eendengedrag Kwaakgedrag 2 Nu implementeren we performquack(): public class Duck { QuackBehavior quackbehavior; // meer public void performquack() { quackbehavior.quack(); Iedere Duck refereert aan iets dat de interface QuackBehavior implementeert. In plaats van zelf het kwaakgedrag af te handelen, delegeert het object Duck dit gedrag aan het object dat door quackbehavior wordt aangewezen. Tamelijk eenvoudig, nietwaar? Om te kwaken staat Duck het object dat wordt aangewezen door quackbehavior, toe om voor hem te kwaken. In dit deel van de code kan het ons niet schelen welk soort object het is, ons interesseert alleen maar dat het weet hoe het quack() uitvoert! Je bent hier 4 15 421_01_nl.indd 15 30-04-2007 22:58:36

Het eendengedrag integreren Nog meer integratie... 3 Nu gaan we ons druk maken over hoe de instantievariabelen flybehavior en quackbehavior ingesteld worden. We werpen een blik op de klasse MallardDuck: public class MallardDuck extends Duck { public MallardDuck() { quackbehavior = new Quack(); flybehavior = new FlyWithWings(); Denk er aan dat MallardDuck de instantievariabelen quackbehavior en flybehavior van de klasse Duck erft. Een MallardDuck gebruikt de klasse Quack om het kwaken af te handelen. Wanneer performquack wordt aangeroepen, wordt de verantwoordelijkheid voor het kwaken gedelegeerd aan het object Quack en wordt er echt gekwaakt. En het gebruikt FlyWithWings als type voor FlyBehavior. public void display() { System.out.println("Ik ben een echte wilde eend"); Het kwaken van een MallardDuck is dus het echte levende gekwaak van een eend, en dus geen gepiep of geluidloos gekwaak.wat is er aan de hand? Wordt er een instantie gemaakt van MallardDuck, dan initialiseert de constructor de instantievariabele quackbehavior die MallardDuck heeft geërfd, naar een nieuwe instantie van het type Quack (een concrete implementatie van de klasse QuackBehavior). En hetzelfde geldt voor het vlieggedrag van de eend: de constructor van MallardDuck initialiseert de instantievariabele flybehavior met een instantie van het type FlyWithWings (een concrete implementatie van de klasse FlyBehavior). 16 Hoofdstuk 1 421_01_nl.indd 16 30-04-2007 22:58:37

Inleiding tot Design Patterns Wacht eens even, zei je niet dat we NIET naar een implementatie mochten programmeren? Maar wat doe je dan in die constructor? We maken daar een nieuwe instantie van een concrete implementatie van de klasse Quack! Goed punt, dat doen we inderdaad... voor het moment. Later in dit boek beschikken we over meer patterns in onze toolbox waarmee we dit kunnen verhelpen. Let er overigens wel op dat we het gedrag via concrete klassen instellen (door de instantiatie van een gedragsklasse als Quack of FlyWithWings en er vervolgens onze referentievariabelen voor het gedrag aan toe te kennen), maar we kunnen dit in runtime eenvoudig wijzigen. We beschikken hier over veel flexibiliteit, maar we leveren slecht werk door de instantievariabelen op een flexibele wijze te initialiseren. Maar denk eens na, aangezien quackbehavior een instantievariabele van het type interface is, kunnen we (met de magie van polymorfisme), dynamisch at runtime een andere QuackBehavior-implementatie toekennen. Neem eens even de tijd om te bekijken hoe jij een eend zou implementerem zodat zijn gedrag at runtime kan veranderen. (Enkele pagina s verder vind je daar de code voor.) Je bent hier 4 17 421_01_nl.indd 17 30-04-2007 22:58:38

Het gedrag van de eend testen De code voor Duck testen 1 Typ en compileer de volgende klasse Duck (Duck.java), en tevens de klasse MallardDuck die twee pagina s terug staat (MallardDuck.java). public abstract class Duck { FlyBehavior flybehavior; QuackBehavior quackbehavior; public Duck() { public void performfly() { flybehavior.fly(); public void performquack() { quackbehavior.quack(); Declareer twee referentievariabelen van het interfacetype gedrag. Alle subklassen van Duck (in dezelfde package) erven deze. Delegeren aan de gedragsklasse. 2 public void swim() { System.out.println("Alle eenden drijven, ook lokeenden!"); Typ en compileer de interface FlyBehavior (FlyBehavior. java) evenals de twee implementatiegedragsklassen (FlyWithWings.java en FlyNoWay.java). public interface FlyBehavior { public void fly(); De interface die alle vliegende gedragsklassen implementeert. public class FlyWithWings implements FlyBehavior { public void fly() { System.out.println("Ik vlieg!!"); De implementatie van vlieggedrag voor eenden die KUNNEN vliegen... public class FlyNoWay implements FlyBehavior { public void fly() { System.out.println("Ik kan niet vliegen"); De implementatie van vlieggedrag voor eenden die NIET vliegen (zoals badeenden en lokeenden). 18 Hoofdstuk 1 421_01_nl.indd 18 30-04-2007 22:58:38

Inleiding tot Design Patterns De code voor Duck testen, vervolg 3 Typ en compileer de interface QuackBehavior (QuackBehavior.java) evenals de implementatie voor de drie gedragsklassen (Quack.java, MuteQuack.java en Squeak.java). public interface QuackBehavior { public void quack(); public class Quack implements QuackBehavior { public void quack() { System.out.println("Kwaak"); public class MuteQuack implements QuackBehavior { public void quack() { System.out.println("<<Stilte>>"); 4 public class Squeak implements QuackBehavior { public void quack() { System.out.println("Piep"); Type en compileer de testklasse (MiniDuckSimulator. java). public class MiniDuckSimulator { public static void main(string[] args) { Duck mallard = new MallardDuck(); mallard.performquack(); mallard.performfly(); 5 Draai de code File Edit Window Help Yabadabadoo %java MiniDuckSimulator Kwaak Ik vlieg!! Aanroep van de van MallardDuck geërfde methode performquack, die dit vervolgens delegeert aan het object QuackBehavior (d.w.z. roept quack() aan via de van Duck geërfde referentie quackbehavior). Vervolgens doen we hetzelfde met de van MallardDuck geërfde methode performfly(). Je bent hier 4 19 421_01_nl.indd 19 30-04-2007 22:58:38

Eenden met dynamisch gedrag Gedrag dynamisch instellen Wat jammer dat we geen gebruikmaken van al dat dynamische talent dat we in onze eenden hebben aangebracht. Stel je voor dat we het eendengedrag via een setmethode in de subklassen van Duck konden instellen in plaats van dit te initaliseren in de constructor van Duck. 1 Voeg twee nieuwe methoden toe aan de klasse Duck: public void set FlyBehavior(FlyBehavior fb) { flybehavior = fb; public void setquackbehavior(quackbehavior qb) { quackbehavior = qb; We kunnen deze methoden iedere keer aanroepen wanneer we het gedrag van de eenden vliegensvlug willen veranderen. Duck FlyBehavior flybehavior; QuackBehavior quackbehavior; swim() display() performquack() performfly() setflybehavior() setquackbehavior() // ANDERE eend-achtige methoden... Opmerking redacteur: weg met dat flauwe woordgrapje 2 Maak een nieuw type Duck (ModelDuck.java). public class ModelDuck extends Duck { public ModelDuck() { flybehavior = new FlyNoWay(); quackbehavior = new Quack(); Onze modeleend begint zijn leven op de grond... zonder een manier om weg te vliegen. public void display() { System.out.println("Ik ben een modeleend."); 3 Maak een nieuw type FlyBehavior (FlyRocket- Powered.java). Dit klopt, we creëren vlieggedrag voor raketaandrijving. public class FlyRocketPowered implements FlyBehavior { public void fly() { System.out.println("Ik vlieg met raketaandrijving!"); 20 Hoofdstuk 1 421_01_nl.indd 20 30-04-2007 22:58:39

Inleiding tot Design Patterns 4 Verander de testklasse (MiniDuckSimulator. java), voeg de ModelDuck toe en maak Model- Duck geschikt voor raketaandrijving. voor public class MiniDuckSimulator { public static void main(string[] args) { Duck mallard = new MallardDuck(); mallard.performquack(); mallard.performfly(); Duck model = new ModelDuck(); model.performfly(); model.setflybehavior(new FlyRocketPowered()); model.performfly(); Als het werkt, heeft de modeleend zijn vlieggedrag dynamisch veranderd! Je kunt dat NIET DOEN als de implementatie binnen de klasse Duck was opgesloten. De eerste aanroep van performfly() delegeert naar het flybehavior-object dat in de constructor van ModelDuck is ingesteld, in dit geval een instantie van FlyNoWay. Hier wordt de setmethode voor het gedrag dat het model geërfd heeft aangeroepen, en... voilà! Het model kan ineens met raketaandrijving vliegen! 5 Start de simulator! File Edit Window Help Yabadabadoo %java MiniDuckSimulator Kwaak Ik vlieg!! Ik kan niet vliegen. na Ik vlieg met raketaandrijving. Om het gedrag van een eend at runtime te veranderen, hoef je alleen maar de setmethode voor dat gedrag van de eend aan te roepen. Je bent hier 4 21 421_01_nl.indd 21 30-04-2007 22:58:39

Resumé Resumé over ingekapseld gedrag Nu we in het diepe zijn gedoken bij het ontwerpen van de eendensimulator, wordt het tijd om wat adem te halen en het geheel te overzien. Hierna staat de volledige opnieuw bewerkte klassenstructuur. Hierin staat alles dat je zou verwachten: eenden die erven van Duck, vlieggedrag geïmplementeerd door FlyBehavior en kwaakgedrag geïmplementeerd door QuackBehavior. Merk op dat we in het begin de dingen een beetje anders hebben beschreven. In plaats van het eendengedrag te beschouwen als een verzameling gedragingen, bekijken we dit nu als een familie algoritmen. Bedenk dat in het ontwerp van SimUDuck de algoritmen de dingen voorstellen die een eend kan doen (kwaken en vliegen), maar we hadden dezelfde techniek net zo goed kunnen gebruiken voor een verzameling klassen voor de berekening van de inkomstenbelasting voor ieder land van de EU. Besteed veel zorg aan de relaties tussen de klassen. Je kunt het beste een potlood pakken en de juiste relatie noteren (IS-EEN, HEEFT EEN en IMPLEMENTEERT) bij iedere pijl in het klassendiagram. Client De client gebruikt een ingekapselde familie algoritmen voor zowel het vliegen als het kwaken. Duck FlyBehavior flybehavior QuackBehavior quackbehavior swim() display() performquack() performfly() setflybehavior() setquackbehavior() // ANDERE eend-achtige methoden... Ingekapseld vlieggedrag fly() FlyWithWings fly() { // implementeert vliegende // eenden <<Interface>> FlyBehavior <<Interface>> QuackBehavior quack FlyNoWay fly() { // niets doen - kan niet // vliegen Ingekapseld kwaakgedrag Denk aan iedere verzameling gedragingen als aan een familie algoritmen. MallardDuck display() { // lijkt op een wilde // eend RedheadDuck display() { // lijkt op een // roodkuifeend RubberDuck display() { // lijkt op een badeend DecoyDuck display() { // lijkt op een lokeend Quack quack() { // implementiert // eendengekwaak Squeak quack() { // implementeert // gepiep badeend MuteQuack quack() { // niets doen kan niet // kwaken! 22 Hoofdstuk 1 Deze gedragingen algoritmen zijn uitwisselbaar. 421_01_nl.indd 22 30-04-2007 22:58:41

Inleiding tot Design Patterns HEEFT-EEN is soms beter dan IS-EEN De relatie HEEFT-EEN is een interessante: iedere eend heeft een FlyBehavior en een QuackBehavior waaraan de eend het vliegen en kwaken delegeert. Wanneer je twee klassen als deze samenvoegt, dan gebruik je compositie. In plaats van het gedrag te erven, verkrijgt de eend zijn gedrag uit het (juiste) gedragsobject waarmee hij is samengesteld. Dit is een belangrijke techniek; we gebruiken feitelijk ons derde ontwerpprincipe: Ontwerpprincipe Geef aan compositie de voorkeur boven overerving. Je hebt gezien dat het creëren van systemen via compositie meer flexibiliteit geeft. Niet alleen kun je zo een familie algoritmen inkapselen, maar je kunt zo tevens het gedrag at runtime veranderen, zolang het object dat je samenstelt de correcte interface voor het gedrag implementeert. Compositie wordt in veel design patterns toegepast en je zult nog veel meer over de voordelen en nadelen ervan in dit boek ontmoeten. Meester: Sprinkhaan, vertel eens wat je over de objectgeoriënteerde aanpak hebt geleerd. Leerling: Ik heb geleerd dat de belofte van objectoriëntatie hergebruik is. Meester: Ga verder Sprinkhaan... Leerling: Meester, via overerving kunnen alle goede dingen opnieuw gebruikt worden, waardoor de ontwikkeltijd drastisch omlaag gaat alsof we bamboe maaien in de bossen. Meester: Sprinkhaan, wordt er meer tijd aan de code besteed voor of nadat de ontwikkeling gereed is? Leerling: Het antwoord is nadat Meester. We besteden altijd meer tijd aan onderhoud en aanpassingen van de software dan tijdens de oorspronkelijke ontwikkeling. Meester: Dus Sprinkhaan, moeten we dan meer tijd besteden aan hergebruik dan aan onderhoudbaarheid en uitbreidbaarheid? Leerling: Meester, ik denk het wel. Meester: Ik zie dat je nog veel moet leren. Ik zou graag willen dat je verder over overerving gaat mediteren. Je hebt gezien dat overerving haar problemen kent en dat er andere manieren voor hergebruik bestaan. Hersenkraker Een lokfluit is een apparaat dat jagers gebruiken om eendengekwaak te imiteren. Hoe zou jij je eigen lokfluit implementeren die niet van de klasse Duck erft? Meester en leerling Je bent hier 4 23 421_01_nl.indd 23 30-04-2007 22:58:42

Het strategy pattern Over Design Patterns gesproken... 1. Gefeliciteerd met je eerste pattern! Je hebt zojuist je eerste design pattern toegepast het STRATEGY pattern. Inderdaad, je hebt het Strategy Pattern gebruikt om de SimUDuck-applicatie om te bouwen. Dankzij dit pattern is de simulator geschikt voor alle veranderingen die tijdens een volgende businesstrip kunnen worden uitgebroed. Nu je de lange weg naar het toepassen ervan hebt afgelegd, volgt hier de formele definitie van dit pattern: Het Strategy Pattern definieert een familie algoritmen, isoleert ze en maakt ze uitwisselbaar. Strategy maakt het mogelijk om het algoritme los van de client die deze gebruikt, te veranderen. Gebruik DEZE definitie wanneer je je vrienden moet imponeren en beleidsmedewerkers moet beïnvloeden. 24 Hoofdstuk 1 421_01_nl.indd 24 30-04-2007 22:58:42

Inleiding tot Design Patterns Design Puzzel Hierna staan een aantal klassen en interfaces voor een Action-Adventure. Er zijn klassen voor spelfiguren en klassen voor het gedrag van wapens. Iedere spelfiguur kan slechts een enkel wapen tegelijk gebruiken, maar kan wel op ieder moment tijdens het spel van wapen wisselen. Aan jou de taak om de boel op orde brengen... (De antwoorden staan aan het einde van dit hoofdstuk.) Jouw taak: 1. 1 Arrangeer de klassen. 2. Identificeer een abstracte klasse, een interface en acht klassen. 3. Teken pijlen tussen de klassen. a. Teken een dergelijke pijl voor overerving ( extends ). b. Teken een dergelijke pijl voor een interface ( implements ). c. Teken deze pijl voor een HEEFT-EEN -relatie. 4. 4 Voeg in de juiste klasse de methode setweapon() in. Character WeaponBehavior weapon; fight(); Queen fight() {... King fight() {... fight() {... Knight fight() {... Troll KnifeBehavior useweapon() { // implementeert steken met een mes <<Interface>> WeaponBehavior useweapon(); AxeBehavior useweapon() { // implementeert met een bijl hakken SwordBehavior useweapon() { // implementeert met een zwaard zwaaien BowAndArrowBehavior useweapon() { // implementeert met pijl en boog schieten setweapon(weaponbehavior w) { this.weapon = w; Je bent hier 4 25 421_01_nl.indd 25 30-04-2007 22:58:43

Dinergesprek Tijdens een etentje opgevangen... Alice Ik wil graag frites met ketchup en mayonaise, chocoladeen vanille-ijs, een gegrilde kaas-baconsandwich, een tonijnsalade op toast, een bananensplit met ijs en klein gesneden bananen en een kop koffie met melk en twee suikerklontjes,... oh, en leg een hamburger op de grill! Flo Eenmaal frites rood/ wit, eenmaal bruin/wit, een Jack Benny, een radio, een huisboot, een kinderkoffie en verschroei er een! Wat is het verschil tussen deze twee bestellingen? Geen! Ze bestellen beiden hetzelfde. Maar Alice gebruikt tweemaal zoveel woorden en stelt daarbij het geduld van de morrige kok op de proef. Wat heeft Flo dat Alice niet heeft? Een gemeenschappelijk vocabulaire met de kok. Het is zo niet alleen makkelijker om met de kok te communiceren, maar zo hoeft de kok ook minder te onthouden omdat hij de diner patterns al in zijn hoofd heeft zitten. Design Patterns zorgen voor een gemeenschappelijke vocabulaire met andere ontwikkelaars. Ken je het vocabulaire, dan kun je makkelijker met andere ontwikkelaars communiceren en andere ontwikkelaars die de patterns nog niet kennen, inspireren om ze te leren. Het verhoogt ook je conceptueel niveau over architecturen omdat je op patternniveau denkt en niet op het platvloerse objectniveau. 26 Hoofdstuk 1 421_01_nl.indd 26 30-04-2007 22:58:44

Inleiding tot Design Patterns Op kantoor opgevangen... Ik heb een broadcast klasse gemaakt. Die houdt alle luisterende objecten bij en stuurt een boodschap naar iedere luisteraar wanneer er nieuwe gegevens zijn. Heel cool is dat de luisteraars zich op ieder moment bij de broadcast kunnen aansluiten of zichzelf weer kunnen verwijderen. Het is werkelijk heel dynamisch en los gekoppeld! Rick Hersenkraker Kun je nog een ander gemeenschappelijk vocabulaire verzinnen behalve het voor OO-ontwerpen en het bestellen van eten? (Aanwijzing: denk eens aan automonteurs, timmerlui, chefkoks en luchtverkeersleiders) Welke kwaliteiten worden samen met de taal gecommuniceerd? Kun je aspecten van het OO-ontwerp bedenken die samen met de namen van patterns gecommuniceerd worden? Welke kwaliteiten worden samen met de naam Strategy Pattern gecommuniceerd? Rick, waarom zeg je niet gewoon dat je een Observer Pattern gebruikt? Precies. Als je met patterns communiceert, dan weten andere ontwikkelaars precies welk ontwerp je beschrijft. Maar voorkom patternkoorts... Dat heb je als je patterns gaat gebruiken voor Hallo Wereld... Je bent hier 4 27 421_01_nl.indd 27 30-04-2007 22:58:45

Gemeenschappelijk vocabulaire De kracht van een gemeenschappelijk patternvocabulaire Wanneer je communiceert via patterns, dan doe je meer dan het delen van vaktaal. Gemeenschappelijke patternvocabulaires zijn KRACHTIG. Als je via patterns met andere ontwikkelaars of je team communiceert, dan praat je niet alleen over een patternnaam, maar ook over een hele verzameling kwaliteiten, karakteristieken en beperkingen die het pattern vertegenwoordigt. Met patterns kun je meer zeggen met minder. Wanneer je een pattern in een beschrijving gebruikt, dan weten andere ontwikkelaars snel en precies wat voor een ontwerp je in gedachten hebt. Door op patternniveau te praten blijf je langer bij het ontwerp. Door met behulp van patterns over softwaresystemen te praten, blijft de discussie langer op het ontwerpniveau, zonder dat je moet afdalen naar de technische details van de implementatie van objecten en klassen. We gebruiken het strategy pattern om het diverse gedrag van onze eenden te implementeren. Dat betekent dat het eendengedrag is ingekapseld in zijn eigen verzameling klassen die makkelijk veranderd en uitgebreid kunnen worden, zo nodig in runtime. Hoeveel ontwerpvergaderingen heb je bijgewoond die al snel degradeerden tot een bespreking van implementatiedetails? Gemeenschappelijke vocabulaires brengen vaart in je ontwikkelteam. Een team dat vertrouwd is met design patterns, schiet harder op en er is minder ruimte voor misverstanden. Gemeenschappelijke vocabulaires moedigen junior ontwikkelaars aan om zich sneller te ontwikkelen. Junior ontwikkelaars kijken tegen ervaren ontwikkelaars op. Wanneer senior ontwikkelaars design patterns gebruiken, zullen de junior ontwikkelaars deze ook willen leren. Zet dus een gemeenschap van patterngebruikers op in je organisatie. Wanneer je team begint met het delen van ontwerpideeën en ervaringen in termen van patterns, zet je een gemeenschap op van pattern gebruikers. Overweeg het opzetten van een studiegroep voor patterns binnen je organisatie, misschien word je tijdens het leerproces al betaald... ;) 28 Hoofdstuk 1 421_01_nl.indd 28 30-04-2007 22:58:45

int fly Inleiding tot Design Patterns Hoe gebruik ik Design Patterns? We hebben allemaal wel eens met bibliotheken en frameworks van de plank gewerkt. We pakten ze op, schreven wat code rond hun API s, compileerden deze in onze programma s en hadden baat bij een hoop code die door anderen was geschreven. Denk eens aan de API van Java en alle functionaliteit die deze je geeft: netwerk, GUI, IO enzovoort. Bibliotheken en frameworks brengen je zo al een heel eind op weg naar een ontwikkelingsmodel waar je gewoon de componenten selecteert om ze in te pluggen. Maar... ze helpen ons niet om onze applicatie te structureren op een wijze die eenvoudig te begrijpen en te onderhouden is en genoeg flexibiliteit biedt. Hier komen Design Patterns om de hoek kijken. Design Patterns komen niet direct in de code terecht, ze komen eerst in je HERSENEN terecht. Wanneer je eenmaal een goed inzetbare kennis van patterns hebt verworven, kun je deze in je nieuwe ontwerpen toepassen en je oude code herbewerken wanneer je vindt dat deze is gedegradeerd tot een onwerkbare bak spaghetticode. Een stapel Patterns Client MallardDuck display() { // lijkt op een wilde // eend Duck Object dat de toestand bevatt. FlyBehavior flybehavior QuackBehavior quackbehavior swim() display() performquack() performfly() setquackbehavior() setflybehavior() // ANDERE eendachtige methoden display() { display() { RedheradDuck Rubber Duck Decoy Duck // lijkt opeen // roodkuifeend // lijkt op een badeend 8 8 8 8 Subjekt-Objekt automatische update/ berichtgeving 8 display() { quack() { // lijkt op een lokeend // implementeert Duck object Inkapseld vlieggedrag <<Interface>> FlyBehavior FlyWithWings FlyNoWay fly() { fly() { // implementeert vliegen // niets doen - kan niet // eenden // vliegen Ingekapseld kwaakgedrag // eendengekwaak <<Interface>> quaken() QuakVerhalten quack() { // implementeert // gepiep badeend quack() { // niets doenn kan niet Quack Squeak MuteQuack OBSERVER Dog objectt Mouse object Cat object Controller Beobachter View afhankelijke objecten Request // kwkaen! MVC Model Je HERSENEN Je met design patterns vernieuwde en verbeterde code! Er bestaan geen domme vragen V: Als design patterns zo belangrijk zijn, waarom bouwt iemand dan geen bibliotheek van ze, zodat ik dat niet hoef te doen? A: Design patterns zijn van een hoger niveau dan bibliotheken. Design patterns zeggen ons hoe we klassen en objecten kunnen structureren om bepaalde problemen op te lossen en het is ons werk om deze ontwerpen aan een bepaalde applicatie aan te passen. V: Vormen bibliotheken en frameworks ook geen design patterns? A: Frameworks en bibliotheken zijn geen design patterns; ze zorgen voor bepaalde implementaties die we aan onze code koppelen. Bibliotheken en frameworks maken soms echter wel gebruik van design patterns in hun implementatie. Dat is prachtig omdat, wanneer je design patterns eenmaal begrijpt, je ook sneller de API s begrijpt die rond design patterns gestructureerd zijn. V: Bestaan er geen bibliotheken met design patterns? A: Nee, maar je zult later pattern catalogi tegenkomen waarin patterns worden vermeld die je in je applicaties kunt gebruiken. Je bent hier 4 29 421_01_nl.indd 29 30-04-2007 22:58:46

Waarom design patterns? Patterns zijn niets anders dan het gebruik van de ontwerpprincipes van OO. Sprinkhaan, dit is een wijdverbreid misverstand, maar het is wel wat subtieler. Je zult nog veel moeten leren... Sceptische ontwikkelaar Ontwikkelaar: Oké, maar is dit niet gewoon allemaal een goed objectgeoriënteerd ontwerp? Ik bedoel, zolang ik de regels voor inkapseling volg en kennis heb van abstractie, polymorfisme en overerving, moet ik dan werkelijk over Design Patterns nadenken? Het is eigenlijk toch tamelijk eenvoudig? Heb ik daarom niet al die OOcursussen gevolgd? Ik denk dat Design Patterns heel nuttig zijn voor mensen die niets weten over een goed OO-ontwerp. Goeroe: Aha, dit is het werkelijke misverstand over objectgeoriënteerde ontwikkeling: dat we denken dat wanneer we de basis van OO kennen, we ook automatisch goede, herbruikbare en onderhoudbare systemen bouwen. Ontwikkelaar: Niet dan? Goeroe: Nee, de praktijk leert dat het bouwen van OO-systemen met deze eigenschappen niet altijd voor de hand ligt en alleen gebeurt door noeste arbeid. Ontwikkelaar: Ik denk dat ik het begrijp. Deze minder voor de hand liggende manieren om objectgeoriënteerde systemen te bouwen zijn verzameld... Goeroe:... juist, in een verzameling patterns die we Design Patterns noemen. Ontwikkelaar: Dus wanneer ik de patterns ken, kan ik het vervelende werk dan overslaan en meteen ontwerpen maken die het altijd doen? Goeroe: Ja tot op zekere hoogte wel, maar ontwerpen is een kunst. Er zullen altijd compromissen zijn. Maar volg je weloverwogen en langdurig geteste design patterns, dan ben je goed bezig. Pattern vriendelijke goeroe 30 Hoofdstuk 1 421_01_nl.indd 30 30-04-2007 22:58:49

Inleiding tot Design Patterns Denk eraan, kennis van concepten als abstractie, overerving en polymorfisme maken je nog niet tot een goede objectgeoriënteerde ontwerper. Een ontwerpgoeroe bekijkt hoe je flexibele ontwerpen kunt maken die onderhoudbaar zijn en met verandering kunnen omgaan. Ontwikkelaar: En wat moet ik doen als ik geen pattern kan vinden? Goeroe: Er bestaan enkele objectgeoriënteerde principes die de basis vormen voor de patterns en wanneer je deze kent, kunnen ze je helpen een probleem aan te pakken waarvoor je geen passend pattern kunt vinden. Ontwikkelaar: Principes? U bedoelt andere dan abstractie, inkapseling, en... Goeroe: Ja. Een van de geheimen voor het maken van onderhoudbare OO-systemen is te bedenken hoe deze in de toekomst kunnen veranderen en deze principes gaan over dat soort onderwerp. Je bent hier 4 31 421_01_nl.indd 31 30-04-2007 22:58:49

Jouw design toolbox 32 Hoofdstuk 1 Gereedschappen voor je ontwerp-toolbox Je hebt het eerste hoofdstuk nu bijna gehad! Je hebt al enkele gereedschappen in je OO-toolbox gelegd; voor we naar hoofdstuk 2 gaan, sommen we ze nog even op. OO-Basics Abstractie Inkapseling Polymorfisme Overerving OO-Principes Isoleer wat verandert. Verkies compositie boven overerving. Programmeer naar een interface en niet naar een implementatie. OO-Patterns Strategy definieert een familie algoritmen, kapsel ieder algoritme in en maak deze uitwisselbaar. Strategy laat toe algoritmen onafhankelijk van de clients die deze gebruiken, te veranderen. We gaan ervan uit dat je de basisbeginselen van OO kent en weet hoe je polymorfisme bij klassen gebruikt, hoe overerving lijkt op design by contract en hoe inkapseling werkt.voel je jezelf onzeker over deze zaken, pak dan eerst je Javaboek uit de kast, neem dit door en ga dan pas naar het volgende hoofdstuk. We zullen dit onderweg nader bekijken en ook nog het een en ander aan de lijst toevoegen. Denk er bij het lezen van dit boek steeds aan hoe patterns op de basisbeginselen en principes van OO berusten. Eén afgehandeld, maar nog vele te gaan! Punt voor punt ß Je bent geen goede OO-ontwerper als je alleen de basis van OO kent. ß Goede OO-ontwerpen zijn herbruikbaar, uitbreidbaar en onderhoudbaar. ß Patterns tonen hoe je systemen met goede OO-ontwerpkwaliteiten kunt bouwen. ß Patterns zijn bewezen objectgeoriënteerde ervaring. ß Patterns leveren geen code op, ze geven algemene oplossingen voor ontwerpproblemen. Deze kun je voor een bepaalde applicatie toepassen. ß Patterns worden niet uitgevonden, ze worden ontdekt. ß De meeste patterns en principes gaan over het aanbrengen van veranderingen in software. ß De meeste patterns maken het mogelijk om systeemdelen onafhankelijk van andere delen te veranderen. ß We proberen vaak het veranderlijke systeemdeel te bepalen en dit in te kapselen. ß Patterns zorgen voor een gemeenschappelijk taalgebruik dat de communicatie met andere ontwikkelaars kan optimaliseren. 421_01_nl.indd 32 30-04-2007 22:58:50