[alg] preview visitor artikel

Pagina: 1
Acties:
  • 116 views sinds 30-01-2008
  • Reageer

Acties:
  • 0 Henk 'm!

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Naar aanleiding van 'ojee, wat is het nivo op got toch weer laag' gebeuren zouden we artikelen en tutorials schrijven om op GoT te plaatsen. Dit is een preview van een artikel wat ik in elkaar heb geplakt en wil graag jullie mening erover weten. Het is nog niet helemaal klaar en de typo`s/kromme zinnen moeten er ook nog uitgehaald worden :D Helaas is op dit moment de code in Java, dus voor mensen die de taal niet beheersen kan het wat lastiger lezen zijn.

VISITOR DESIGN PATTERN.

VOORWOORD

Het visitor design pattern is een pattern waarmee op een uitzonderlijke fraaie manier functionaliteit toegevoegd kan worden aan classes, blabla

Door Peter Veentjer alias Alarmnummer.

INLEIDING

Ik wil graag met jullie het visitor design pattern delen, omdat dit namelijk een zeer krachtig design pattern is waarmee je op een hele fraaie manier functionaliteit toe kan voegen aan objecten ipv dit bij objecten zelf te implementeren. Er kleven 3 nadelen aan de laatste aanpak:
1)als je de sourcecode niet hebt dan is het lastig om nieuwe functionaliteit toe te voegen.
2)doordat je uiteindelijk veel methodes krijgt in je classes, worden ze vaak zeer onoverzichtelijk.
3)de logica van een stuk functionaliteit ligt verspreid over een hele hierarchie van objecten en daardoor krijg je minder snel inzicht.

Met het Visitor design pattern kan dit grotendeels verholpen worden, dus het is een uitermate krachtig en handige design pattern en naar mijn mening is dit veruit de mooiste uit het Design Patterns - Elements of Reusable Object Oriented Software boek (ook wel bekend als het GoF boek)

Maar om het visitor design pattern te begrijpen zal ik eerst uitleggen wat de term 'dispatch' inhoud.

UITLEG DISPATCH

Stel dat ik de volgende interface en implementaties voor een elementair typesysteem heb:

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface Type{
    void print();
}

class IntType implements Type{
    void print(){System.out.println("int");}
}

class RealType implements Type{
    void print(){System.out.println("real");}
}

class StringType implements Type{
    void print(){System.out.println("string");}
}


En ik heb de volgende aanroepende code:
Type t = new StringType();
t.print();

Dan krijg je dus 'string' op je scherm omdat runtime uitgezocht wordt welke implementatie van print() bij t hoort, dit heet ook wel polymorphisme.

Om dispatch wat beter uit te leggen ga ik jullie eerst uitleggen hoe je een niet statische methode als statische functie kan schrijven. Als je een niet statische methode hebt, dan heeft de methode beschikking over het object waarop de methode wordt uitgevoerd. Maar hoe kan je dit als statische functie schrijven, want een statische functie heeft dus geen object waarop hij wordt uitgevoerd. Als je het object waarop die functie uitgevoerd moet worden nou eens meegeeft als argument, dan heeft die functie dus wel beschikking over dat object. Dit gebeurt onder de grond ook bij java, daar wordt namelijk dit argument impliciet meegegeven en die kan je gewoonlijk benaderen via de 'this' variable. Gelukkig hoef je niet altijd expliciet this op te geven, maar bij compilatie wordt dit er wel gewoon aan toegevoegd. Het komt er dus op neer dat er in principe geen verschil bestaat tussen een methode en een functie.

Hieronder staat een voorbeeld van de conversie van print methodes naar print functies. Ze zuhb meteen in een aparte class gezet zodat je een goed overzicht krijgt.

code:
1
2
3
4
5
class Print{
    static void print(IntType t){System.out.println("int");}
    static void print(RealType t){System.out.println("real");}
    static void print(StringType t){System.out.println("string");}
}


Zoals je ziet heeft iedere functie nu beschikking over het object waar hij eventueel allerlei data uit kan lezen of acties op uit kan voeren.

Als je nu het volgende aanroepende code zou schrijven:
Type type = new StringType();
Print.print(type)

Dan krijg je dus weer 'string' op het scherm. De reden dat er 'string' komt te staan, is dat er runtime uitgezocht wordt welke functie uitgevoerd moet worden. Dit heet ook wel dispatch: de runtime selectie van de juiste functie/methode op basis van de argument types.

In dit geval gebeurt dit op basis van 1 argument, namelijk t, dit heet dan ook wel een single dispatch. Als je 2 argumenten gebruikt om de juiste keuze te maken, dan heb je een double dispatch, en bij n argumenten is het een multidispatch. En een methode die gebruikt maakt van multidispatch heet ook wel een multimethod.

Als bij een multi/double dispatch de lexografische volgorde van argumenten belangrijk is dan spreek je van een asymetrische multidispatch en anders van een symetrische, dit gegeven is verder niet interessant voor het visitor designpattern maar volledigheidswijze vermeld ik het wel even.

Verder zou je je kunnen afvragen of de gebruikte aanroepende syntax voor deze 'externe' functies nou zo handig is. En daarnaast zal zo`n functie beschikking moeten hebben over data die je eventueel niet zou willen laten zien. Om dit probleem te verhelpen zijn 'open objects' uitgevonden. Dit zijn objecten waarop op een soortgelijke manier extern functies toegevoegd kunnen worden terwijl ze wel beschikking hebben over allerlei private informatie.

In onderstaande code geef ik een voorbeeld van symetrische double dispatched functies om te bepalen of een type een subtype is van een ander type.

code:
1
2
3
4
5
6
7
class IsSubType{
    static boolean isSubType(IntType subType, IntType superType){return true;}
    static boolean isSubType(IntType subType, RealType superType){return true;}
    static boolean isSubType(RealType subType, RealType superType){return true;}
    static boolean isSubType(StringType subType, StringType superType){return true;}
    static boolean isSubType(Type subType,Type superType){return false;}
}


Als je deze functie gaat aanroepen met isSubType(new IntType(),new RealType()), dan weet je dat de 2e functie wordt gekozen en dat je een true als antwoord krijgt. Als je aanroept met: isSubType(new StringType(),new RealType()) dan voldoet alleen de laatste functie en krijg je dus een false terug.

Ik vind persoonlijke de bovenstaande code erg mooi en dit zie je wel vaker in functionele programmeertalen onder de algemene noemer: pattern matching. (Pattern matching kan bij functionele talen vaak nog veel meer dan alleen multidispatch). En verder is het bij functionele programmeertalen gebruikelijk om een functie te maken over meerdere types en dat zie je hier ook in terug.

Er is alleen een groot probleem. Bovenstaande single en multidispatch voorbeelden zullen in talen zoals Java, c++, c#, Delphi niet werken omdat zij alleen single dispatch mbv polymorphisme bezitten en geen algemene single dispatch en al helemaal geen double of multidispatch. Daarnaast zal isSubType code ook niet gaan werken omdat java gebruik maakt van de meest ruime methode selectie en niet de meest strikte methode selectie. Aangezien de laatste methode de ruimste is (alle types kunnen daar terecht) zal deze methode altijd uitgevoerd worden en die anderen dus niet.

UITLEG VISITORS.

Wat een Visitor doet is gebruik maken van het mechanisme van polymorphistische dispatch om algemene single dispatch te verkrijgen.

Dit gebeurt mbv een callback op de volgende manier:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
interface Visitor{
    void visit(IntType t);
    void visit(RealType t);
    void visit(StringType t);
}

interface Type{
    void accepts(Visitor v);
}

class IntType implements Type{
    void accepts(Visitor v){v.visit(this)};
}

Je maakt eerst een algemene visitor interface waarin alle Types staan. Je maakt het mogelijk dat een visitor ieder type kan bezoeken mbv een accepts(Visitor v) methode. En daarbij ga je het polymorphe gedrag van die methode gebruiken om die dispatch over te brengen op de visitor interface (de callback). Als je bv een IntType hebt en je gaat met een visitor op bezoek, dan zal dus de visit(IntType t) worden uitgevoerd uit de Visitor interface. Dus op basis van het type wordt de juiste methode aangeroepen op de interface. En nu hebben we dus onze algemene single dispatch.

DE DEFAULT VISIT EN ADAPTERS.

Soms is het vrij vervelend om alle methodes van een interface te moeten implementeren. Je zou dan een Adapter kunnen maken voor die interface waarin iedere methode al een lege implementatie heeft.

code:
1
2
3
4
5
abstract class VisitorAdapter implements Visitor{
    void visit(IntType t){}
    void visit(RealType t){}
    void visit(StringType t){}
}


Als je nu alleen bij een string een actie wilt ondernemen, kan je volstaan met deze code.

code:
1
2
3
class StringTypeAction extends VisitorAdapter{
    void visit(StringType t);
}


Zoals je ziet hoef je nu alleen nog maar de interessante methodes te overriden, en bij de rest wordt een lege methode aangeroepen. Het missende aan deze aanpak is dat je nu geen actie kan ondernemen op de rest. Dit zal ik verduidelijken aan de hand van het volgende voorbeeld. Stel dat je wilt bepalen of iets wel of geen getal is. Je zou dan bij de visit(IntType t) en visit(RealType t) de waarde op true kunnen zetten, en bij visit(String t) zet je hem op false. Om dit voor elkaar te krijgen zou je een default visit kunnen maken, die iedere visit methode aanroept als het niet overriden wordt.

code:
1
2
3
4
5
6
abstract class VisitorAdapter implements Visitor{
    void visit(IntType t){visitDefault(t);}
    void visit(RealType t){visitDefault(t);}
    void visit(StringType t){visitDefault(t);}
    void visitDefault(Type t){};
}


code:
1
2
3
4
5
abstract class IsNumber extends VisitorAdapter{
    boolean _isNumber;
    void visit(StringType t){_isNumber=false;}
    void visitDefault(Type t){_isNumber=true;}
}



DOUBLE AND MULTIDISPATCH MBV VISITOR.

Zoals vaak ten onrechte wordt gezegd krijg je met een visitor dus geen double dispatch, maar een single dispatch. Alleen wordt dit dus gedaan aan de hand van double dispatch. Je zoekt namelijk eerst de juiste visit implementatie uit mbv polymorphisme (1e dispatch) en later wordt in de interface de juiste methode gekozen. Dit gebeurt compile time, en heet daarom ook wel een statische dispatch. Hiermee hebben we ook meteen onze 2e dispatch te pakken.

Maar je kan wel een double/multi dispatched versie maken aan de hand van meerdere visitor calls.

Voorbeeld van een double dispatched versie (ik heb de StringType er even uitgelaten ivm lengte code).
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
interface DoubleDispatchedTypeVisitor{
    void visit(IntType t1, IntType t2);
    void visit(IntType t1, RealType t2);
    void visit(RealType t1, IntType t2);
    void visit(RealType t1, RealType t2);
}

class DoubleDispatcher implements TypeVisitor{
    
    DoubleDispatchedTypeVisitor _guest;//set by constructor
    Type t2;//set by constructor.

    void visit(IntType t1){
        TypeVisitor v = new TypeVisitor(){
            void visit(IntType t2){_guest.visit(t1,t2);}
            void visit(RealType t2){_guest.visit(t1,t2);}
        }
        t2.accepts(v);
    }

    void visit(RealType t1){
        TypeVisitor v = new TypeVisitor(){
            void visit(IntType t2){_guest.visit(t1,t2);}
            void visit(RealType t2){_guest.visit(t1,t2);}
        }
        t2.accepts(v);
    }
}

class IsSubType implements DoubleDispatchedTypeVisitor{
    boolean _result;
    void visit(IntType subType, IntType superType){_result=true;}
    void visit(IntType subType, RealType superType){_result=true;}
    void visit(RealType subType, IntType superType){_result=false;}
    void visit(RealType subType, RealType superType){_result=true;}
}

En aanroepen met:

Type intType = new IntType();
Type realType = new RealType();

IsSubType isSubType = new IsSubType();
intType.accepts(new DoubleDispatcher(realType,isSubType));
boolean result = isSubType.getResult();

Eventueel kan dit natuurlijk nog wel wat opgeschoond worden.

En IsSubType zou je zelfs nog kunnen vereenvoudigen mbv een algemene adapter class naar:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
abstract class implements DoubleDispatchedTypeVisitorAdapter{
    void visit(IntType t1, IntType t2){visitDefault(t1,t2);}
    void visit(IntType t1, RealType t2){visitDefault(t1,t2);}
    void visit(RealType t1, IntType t2){visitDefault(t1,t2);}
    void visit(RealType t1, RealType t2){visitDefault(t1,t2);}
    void visitDefault(Type t1, Type t2){};
}

class IsSubType extends DoubleDispatchedTypeVisitorAdapter{
    boolean _result;
    void visit(RealType subType, IntType superType){_result=false;}
    void visitDefault(Type t1, Type t2){_result = false;}
}


Of zelfs tot:
code:
1
2
3
4
class IsSubType extends DoubleDispatchedTypeVisitorAdapter{
    boolean _result = true;
    void visit(RealType subType, IntType superType){_result=false;}
}


Een voor multidispatched versie kan je nu ook wel een soorgelijke oplossing bedenken. Het probleem aan deze aanpak is alleen dat het dus enorm in de methodes kan komen te lopen, tenslotte moet je voor iedere n types en m dispatches n^m visit methodes schrijven.

VOORBEELD VISITOR IMPLEMENTATIE.

Nu we dus beschikking hebben over een single dispatch hoeven we alleen nog maar een implementatie te maken voor die Visitor interface om daar functionaleit aan te koppelen. Ik zou nu de volgende PrintVisitor kunnen maken:

code:
1
2
3
4
5
class PrintVisitor implements Visitor{
    void visit(IntType type){System.out.println("int");}
    void visit(RealType type){System.out.println("real");}
    void visit(StringType type){System.out.println("string");}
}


Als je dit nu aanroept met:
Type p = new StringType();
p.accepts(new PrintVisitor());

Dan krijg je dus 'string' op je scherm te staan.

Zoals je ziet is het dus nu erg eenvoudig om nieuwe functionaliteit aan classes toe te voegen zonder dat die classes daarvoor veranderd moeten worden. Ze hoeven dus alleen een accepts methode te hebben en later kan daar nieuwe functionliteit aan worden toegevoegd.

Om het gemak van de visitor nog wat verder te verduidelijken staan hieronder nog een aantal visitor implementaties.

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class IsSubType implements Visitor{
    Type _subType;
    boolean _result;
    IsSubType(Type subType){_subType = subType);
    
    void visit(IntType type){_result = _subType instanceof IntType;}
    
    void visit(RealType type){
        _result = (_subType instanceof IntType) || 
              (_result = _subType instanceof RealType);
    }
    
    void visit(StringType type){_result = _subType instanceof StringType}
}



VOOR EN NADELEN VISITORS.

Het is verder gebruikelijk om bij design patterns de voor en nadelen ook te vermelden en dat wil ik jullie dan ook niet onthouden.

Voordelen aan visitors:
1) je objecten blijven schoner omdat je allerlei handige functies niet in de class zelf hoeft te plaatsen.
2) je krijgt een hele mooie bundeling in 1 object van een bepaald stuk functionaliteit zodat het niet verspreid ligt over je class hierarchie.
3) in een visitor kan je allerlei toestands informatie meenemen die gewoonlijk als argument in de methode aanroep meegenomen had moet worden. Dit is vooral handig bij traversal over een structuur heen en ik zal in het volgende deel op dit punt terug komen.

Nadelen aan de visitors:
1) het kan lastig zijn om nieuwe classes aan je hierarchie toe te voegen. Stel dat je geen beschikking hebt over de visitor interface source dan kan je geen nieuwe visit methode toevoegen. In A Pattern Language To Visitors geven ze een oplossing voor dit probleem: de element adder. Als je wel beschikking hebt over de source van de Visitor interface maar andere programmeurs hebben een implementatie hiervan gemaakt. Dan kan krijg je dus in de implementatie een methode te weinig en zal je een foutmelding krijgen dat een methode niet gevonden is. Dit is verder wel een zeer vervelend probleem.
2) Als je de sources niet hebt van een class (hierarchie) dan zal is het vrijwel onmogelijk om het Visitor design pattern in te bouwen.
3) Zo nu en dan moet je ecapsulation van objecten verbreken omdat je dus nu allerlei logica buiten de bezochte objecten gaat plaatsen en je toch hele specifieke informatie nodig hebt of acties op worden ondernomen.

Ik hoop dat jullie wat opgestoken hebben van mijn verhaal want ik heb er zelf wel plezier in gehad, terwijl schrijven zeker niet eens van mijn sterke punten is. En als er genoeg interesse is zal ik binnenkort nog een hele krachtige uitbreiding op het Visitor design pattern plaatsen, namelijk 'Visitor Guides'.

Literatuur opgave:

-Design Patterns - Elements of Reusable Object Oriented Software
http://www.amazon.com/exec/obidos/tg/detail/-/0201633612
-MultiJava: Design, implementation, and evaluation of a Java-compatible language supporting modular open classes and symmetric multiple dispatch
http://www.cs.iastate.edu...tijava/papers/TR01-10.pdf
-A Pattern Language To Visitors
http://jerry.cs.uiuc.edu/...mai0/PLoP2001_ymai0_1.pdf

Acties:
  • 0 Henk 'm!

  • momania
  • Registratie: Mei 2000
  • Laatst online: 02-06 19:57

momania

iPhone 30! Bam!

Erg interresant! Keep up the good work! :)

* momania weet nog niet zo veel over Design Patterns, maar dit helpt zeker...

ps. typo's in je code tags: let ff op het '}' teken

Neem je whisky mee, is het te weinig... *zucht*


Acties:
  • 0 Henk 'm!

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Ik zie het, het komt omdat dit gewoonlijk niet mijn manier van schrijven is :) Ik heb er al een aantal gefixed.

Acties:
  • 0 Henk 'm!

Anoniem: 7195

Goed artikel :) Je kent mn mening over je punt, dus daar ga ik verder niet op in. :)

Acties:
  • 0 Henk 'm!

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Ik hoop dat ik je dan met het volgende deel enthousiaster kan maken. Ik denk dat ik iets ga doen met expressies icm visitor guides, want hiermee kan je uitzonderlijke mooie voorbeelden maken.

Acties:
  • 0 Henk 'm!

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 02-06 22:44

Creepy

Tactical Espionage Splatterer

* Creepy vind het een erg duidelijk artikel

Met het design pattern gebeuren ben ik niet echt bekend. Dit scheelt een hoop. Snap ik eindelijk waar jij nu altijd precies over blaat ;)
(en het is hard nodig dat ik eens een boek ga doorspitten over dit onderwerp. Ik merk dat ik een aantal dingen (singe en multiple dispatch) al gebruik, zonder dat ik met deze techniek bekend was )

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


Acties:
  • 0 Henk 'm!

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Gelukkig :)

Het lijkt me eerlijk gezegd wel een beetje vreemd dat jij verder gebruik maakt van single of multi dispatch als je zelf niet weet dat je het gebruikt. Dit komt omdat de 'normale' oo talen zoals Delphi, c++,c# en java dus geen single of multidispatch ondersteunen afgezien van single mbv polymorphisme en als je multi dispatch wilt gebruiken dan heb je toch wel een dispatch framework/ taalextentie/wijziging vm etc nodig (Voor java gebruik ik Nice als taalextensie om te werken met multidispatch, werkt erg fijn).

Het zou verder ook kunnen dat je statische 'multidispatch' ziet als echt, terwijl dit niet zo is. Multidispatch is echt runtime uitzoeken welke methode/functie je nodig bent. Daarom ben ik dus ook niet zo blij met die veelgenoemde double dispatch van het visitor design pattern terwijl dit ook alleen maar een single dispatch is.

Ik ben trouwens niet 100% zeker van de andere talen, maar van java ben ik het echt 100% zeker.

En verder zijn design patterns niet veel anders dan een beschrijving van bouwstenen en zo nu en dan heb je er wel eens een zelf bedacht. Het is geen mysterieus iets, maar wel erg handig om te kennen omdat je veel meer oo uitdrukkingskracht krijgt :)

Acties:
  • 0 Henk 'm!

  • Juup
  • Registratie: Februari 2000
  • Niet online
Mijn OO (en Java) kennis is te beperkt om hier veel mee te kunnen Alarm maar ik vind het zeer bewonderenswaardig wat je doet.
Van mij mag het 'Jip & Janneke' gehalte wat hoger ;)

Ben ik nou zo dom of zijn jullie nou zo slim?


Acties:
  • 0 Henk 'm!

Anoniem: 62689

Leuk artikel. Ga zo door! Vooral het stukje over dispatching vond ik zeer informatief. Wanneer denk je het volgende artikel af te hebben?
Er is alleen een groot probleem. Bovenstaande single en multidispatch voorbeelden zullen in talen zoals Java, c++, c#, Delphi niet werken omdat zij alleen single dispatch mbv polymorphisme bezitten en geen algemene single dispatch en al helemaal geen double of multidispatch. Daarnaast zal isSubType code ook niet gaan werken omdat java gebruik maakt van de meest ruime methode selectie en niet de meest stricte methode selectie. Aangezien de laatste methode de ruimste is (alle types kunnen daar terecht) zal deze methode altijd uitgevoerd worden en die anderen dus niet.
Geldt dit ook voor C# dat de methode met de meest algemene baseclass wordt uitgezocht?

edit
Net getest. Blijkt idd dat de meest generieke klasse wordt uitgezocht. Waarom is dit zo besloten door taal guru's?
/edit

Acties:
  • 0 Henk 'm!

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
In principe hoef ik hier niets voor uit te zoeken omdat ik voor het volgende artikel baseer op reeds bestaande sourcecode en intussen redelijk wat ervaring op dit gebied. Het hoeft verder niet lang te duren. Maar verder hoop ik dat andere mensen nu ook over de brug komen met wat leuke stukken zodat we gemeenschappelijk wat invulling kunnen geven aan het tutorial en artikel gedeelte op GoT.

[edit]
Ik weet niet waarom hiervoor gekozen is. In 'Depth-first Polymorphism (or Customised Polyseme)' staat uitgelegd hoe ze wel de strengste selectie mbv reflectie voor elkaar krijgen. Ik ben het verder niet helemaal met de naam eens. Ik zou dit een combinatie willen noemen van polymorphisme en overloading.

Verder experimenteer ik op dit moment met Nice Dit is een extensie op de java taal, waarmee symetrische multiple dispatch wel mogelijk is. Verder is er een krachtiger type systeem waardoor je oa generics hebt en op een zeer elegante manier heb je geen null checks meer. Verder is er nog veel meer leuks te ondekken in deze fraaie extensie.

Acties:
  • 0 Henk 'm!

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 02-06 22:44

Creepy

Tactical Espionage Splatterer

Hmm... * Creepy moet dan maar ff terug naar de boeken ;)
Maar als ik jou meerdere visits zie maken m.b.v. 1 parameter (singe dispatch) dan lijkt me dit ook gewoon te kunnen in Delphi m.b.v. het overload directive. Maar dit is inderdaad wel statisch voor zover ik dit heb gebruikt.
Ik zou eens moeten experimenteren of dit ook at runtime zou kunnen. Lijkt me van wel, want tijdens runtime achterhalen van welke klasse het object een instantie is, is ook geen probleem (Tobject.class, Tobject.classname) in Delphi. En zoniet, dan is het met genoemde properties wel het 1 en ander te faken.

edit:
Ik moet beter lezen... single dispatch m.b.v. polymorphisme kan wel dus, en dat is geen algemene single dispatch.


* Creepy gaat dit eens laten bezinken, en komt hier na z'n vakantie op terug ;)

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


Acties:
  • 0 Henk 'm!

  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

(overleden)
Twee duimpjes omhoog voor u Alarmnummer :)

Ik zal trouwens nog eens kijken of ik een mooi RMI verhaaltje in elkaar kan draaien :)

Acties:
  • 0 Henk 'm!

  • Soultaker
  • Registratie: September 2000
  • Laatst online: 15:23
Misschien is een concreet voorbeeld leuk om de tekst wat toegankelijker te maken; verder lijkt het me een prima artikel. Ik zou nog wel een beetje sleutelen aan de volgorde van alinea's en het plaatsen van kopjes enzo, maar dat had je zelf ook wel bedacht. Goed werk! (Y) <-- MSN-thumbs-up-icon!

Acties:
  • 0 Henk 'm!

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Ik ben blij dat iedereen er tevreden over is. Ik zal inderdaad nog even een paar extra visitor implementaties erbij maken zodat je beter de kracht ervan kan zien. En zal het verder nog even opschonen. Maar waar komt het eigelijk te staan? Zijn er nog speciale tekst opmaak mogelijkheden? *ziet d2k er wel eens mee opscheppen* ;)

Acties:
  • 0 Henk 'm!

  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

(overleden)
Moet je even blaten bij ACM oid, die wil je post dan vast wel HTML rechten geven.

Echter je kunt ook gewoon met [ table ] [ /table ] de boel voor elkaar zien te krijgen :P

Acties:
  • 0 Henk 'm!

Anoniem: 22001

Sorry dat ik zo laat reageer, ik had nu pas tijd het te lezen :)

Ik vind het een erg leuk artikel :) Je spreekt jezelf echter wel tegen op een gegeven moment. Aan het begin meld je dit als nadeel van het normale OO ontwerpen (zonder Visitors) geef je dit:
Alarmnummer:
1)als je de sourcecode niet hebt dan is het lastig om nieuwe functionaliteit toe te voegen.
Dit zal dan door het Visitor design patter dan wel opgelost worden, denk ik dan, maar nee, als een van de nadelen van het Visitor patter geef jij:
2) Als je de sources niet hebt van een class (hierarchie) dan zal is het vrijwel onmogelijk om het Visitor design pattern in te bouwen.
Dus dat probleem los je niet op, behalve dan eventueel als de ontwerper van de code waar je de sourcecode niet van hebt het Visitor pattern heeft toegepast. Dat is dan wel direct een ander voordeel van het Visitor design pattern: Anderen kunnen makkelijker functionaliteit toevoegen aan jou classes (in binaire vorm), of zie ik dat fout?

Acties:
  • 0 Henk 'm!

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Precies. Als daar al een visitor inzit, dan kan je heel eenvoudig functionaliteit toevoegen. Als je de source niet hebt, dan kan je dus geen visitor toevoegen.

Acties:
  • 0 Henk 'm!

  • kvdveer
  • Registratie: November 2000
  • Laatst online: 07-11-2023

kvdveer

Z.O.Z.

Ik heb het idee dat dit gezien kan/moet worden als een nèt iets ander paradigma dan OO, een soort uitbreiding.
Visitors zijn klassen die puur voor eenmalig gebruik van een method zijn (ja je kan ze recyclen) . Om het maken van een visitor te vereenvoudigen zouden er stucturen moeten komen waarmee 'trusted' visitors toegang moeten krijgen tot de private members. Een trusted visitor moet dan bijvoorbeeld member zijn van dezelfde package. De het 'type' visitor zou dan net zo'n status moeten krijgen als interface of zo. Object moet de standaard method krijgen accept().
Al met al zijn dit behoorlijk wat wijzigingen, waarvan ik denk dat ze nodig zijn om het visitor concept werkbaar te maken. (letop: IMHO)

Misschien een interressant idee om uit te werken: Reverse visitor. (ik noem het nu even Host)

code:
1
2
3
4
5
6
7
8
9
10
11
12
13
class IntType extends Type {
  [...]
}

class PrintHost {
  public PrintHost(IntType x) {
    System.out.println("int");
  }  

  public PrintHost(Type x) {
    System.err.println("Unknown type");
  }
}

Localhost, sweet localhost


Acties:
  • 0 Henk 'm!

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 07-10-2022
kvdveer: Ik heb het idee dat dit gezien kan/moet worden als een nèt iets ander paradigma dan OO, een soort uitbreiding.
Dat gevoel is niet zo vreemd. Ik schreef pas dit:
Bij het Visitor pattern zijn methoden die samen een bepaalde operatie implementeren, gegroepeerd in 1 klasse en dus niet verspreid over de object-structuur. Hierdoor is het makkelijk om een nieuwe operatie toe te voegen (in de vorm van een visitor), maar het is minder makkelijk als de boom-structuur verandert.

Het Visitor pattern komt op dit punt sterk overeen met de aanpak in functioneel programmeren, waar het toevoegen van een nieuwe functie over een data-structuur ook erg eenvoudig is, maar een aanpassing van de data-structuur erg vervelend kan zijn....
Het afscheiden van een operatie is een aanpak die niet direct past in het klassieke plaatje van data gekoppeld met operaties. Zoals ik hierboven beschrijf, komt het eigenlijk nog meer overeen met de aanpak in functionele talen. Dat wil echter niet zeggen dat een klasse die een 'operatie', 'functie' of iets dergelijke vertegenwoordigd lelijk is of niet prettig werkt: het kan soms juist een hele interessante en succesvolle aanpak zijn. Ik zie een klasse daarom ook liever als iets wat een 'rol' of 'taak' vervult in een applicatie. Als een operatie een duidelijk rol is, waarbij je de operatie bijvoorbeeld ook nog graag zou willen specialiseren of op verschillende manieren wilt implementeren is een operatie als een klasse een hele goede aanpak. Dit komt bijvoorbeeld overeen met het Strategy pattern.

Zodra een klasse meedere rollen gaat vervullen ben je pas echter verkeerd bezig: als je bepaalde rollen wilt gaan specialiseren krijg je namelijk serieuze inheritance problemen: je wilt specialisaties namelijk misschien ook wel samen gebruiken.

Voorbeeldje:

Stel klasse A vervult rol R en S. Rol R wil je echter specialiseren en daarom maak je een subklasse B van A. Hierin specialiseer je rol R tot rol R'. Als je rol S wilt specialiseren tot S' maak je een nieuwe subklasse van A: C. Als je nu R' en S' samen wilt gebruiken zit je in de penarie: je moet allemaal zaken gaan kruisen en het wordt allemaal helemaal niet prettig.

Delegatie lost dit op en je ziet dit terug in het Strategy pattern. De dubbele rol van klasse A splits je in meedere klassen die allemaal maar 1 rol vervullen: A en B. Je kan nu beide klassen zelfstandig specialiseren en beide klassen kunnen via delegatie met de specialisaties van de andere klassen werken. Dit is een bekende refactoring: replace inheritance with delegation. Het signaal dat je deze refactoring moet toepassen: "a subclass uses only part of a superclass interface or does want to inherit data". Dit is precies wat ik bedoel met rollen....

Hum, die gaat een beetje off-topic *D , maar het gaat dus in het algemeen over een bepaalde stijl van programmeren die een beetje ruikt naar functioneel denken ;) .

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


Acties:
  • 0 Henk 'm!

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
kvdveer schreef op 21 augustus 2002 @ 21:44:
Ik heb het idee dat dit gezien kan/moet worden als een nèt iets ander paradigma dan OO, een soort uitbreiding.
Zoals mbravenboer al zei en in het artikel staat, lijkt dit inderdaad verdacht veel op een functionele aanpak. (dus functionele paradigma).

En wat jij verder bedoelt zijn Open Objects. Dit stukje komt dus nog in de uiteindelijke versie te staan, en staat verder uitgelegd in de 2e link onderaan het artikel. Een open object is een object waarbij niet alle functionaliteit in dat object geplaatst hoeft te worden, terwijl er extern aan dat object toch functies toegevoegd kunnen worden die beschikking hebben over private info. En daarna blijft de methode syntax gehandhaafd (terwijl dit eigelijk alleen een syntaxtisch suiker is voor statische functies).

Sommige mensen zullen misschien zeggen dat ze het vervelend vinden dat hun encapsulation verbroken gaat worden, maar je krijgt er aan de andere kant gewoon dezelfde functionaliteit op een andere manier schrijven.

Anoniem: 27915

Als opensource persoon vind ik dit een erg vreemde manier van tegen objecten aankijken - alsof je er geen toegang toe zou hebben. :P. Maar erg interessant, alleen vind ik persoonlijk de visitor methode toch niet echt handig - ik zie liever de functionaliteit van een klasse in de klasse zelf.... Maargoed, toch weer wat moois geleerd. Dit kende ik nog niet.

thumbs up!

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Het probleem zal uiteindelijk worden dat je stampvolle classes krijgt (dus veel regels), en functionaliteit verspreid over een hele lading objecten.

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 07-10-2022
beelzebubu: ik zie liever de functionaliteit van een klasse in de klasse zelf.... Maargoed, toch weer wat moois geleerd.
Het gaat natuurlijk niet om kleine lullige methoden die properties opleveren of iets dergelijks. Het visitor pattern wordt zeer intensief gebruikt in tools die met abstract syntax trees werken. Denk dan vooral aan compilers, refactoring tools, pretty-printers, beautifiers, documentatie generatoren enzovoorts. Bij dergelijke tools is het vaak echt ondenkbaar om alle functionaliteit in de boom-structuur te stoppen. De boom-structuur zou dan totaal exploderen om het vaak grote operaties zijn die moeten worden uitgevoerd. Bovendien wordt de boom-structuur bij dergelijke problemen vaak gegenereerd door een parser-generator, waardoor het sowieso vrijwel onmogelijk is om de operaties in de boom op te nemen.

Bij dergelijke tools kan je er daarom eigenlijk wil gif op innemen dat ze het visitor pattern gebruiken als ze in een taal als Java, C# of C++ geimplementeerd zijn.

Blog, Stratego/XT: Program Transformation, SDF: Syntax Definition, Nix: Software Deployment


  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
nog ff een toevoegen mbt double en multidispatch gemaakt. Moet nog ff verder uitgewerkt worden, maar is wel een aardig voorbeeld.

Acties:
  • 0 Henk 'm!

  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

(overleden)
Nou intresse genoeg iig vanaf mij al :) Doe mij ook maar de 'Visitor Guides' dan :P

Acties:
  • 0 Henk 'm!

Anoniem: 27915

Alarmnummer schreef op 22 augustus 2002 @ 01:05:
Het probleem zal uiteindelijk worden dat je stampvolle classes krijgt (dus veel regels),
Hmm, misschien stampvolle classes, maar niet stampvolle files. In C++/C kun je objecten over meerdere files uitspreiden. ;). Kan java dat ook?
en functionaliteit verspreid over een hele lading objecten.
Dat wel ja.

* Anoniem: 27915 moet hier maar eens rustig een nachtje over slapen... Misschien gebruik ik het over een jaar wel en snap ik niet hoe je ooit zonder zou kunnen. :D.

Wat het voordeel is van functionaliteit in objecten zelf stoppen, is dat je dan alle static functies die bij dat object horen kan gebruiken. In veel gevallen (in my case, that is) is dat toch zeker handig, omdat het object toch een specifieke functie heeft en heel compact is gecode. Grote objecten zie ik sowieso al als nogal lelijk design, ik gooi dan over het algemeen de code in meerdere files per object of maak er meerdere objecten van (parent/child), voor zover mogelijk.

Maar ik kan me best voorstellen dat je in sommige gevallen die object functies niet nodig hebt en dat dit pattern dan best handig is... B).

Acties:
  • 0 Henk 'm!

  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

(overleden)
Anoniem: 27915 schreef op 23 augustus 2002 @ 10:11:
Hmm, misschien stampvolle classes, maar niet stampvolle files. In C++/C kun je objecten over meerdere files uitspreiden. ;). Kan java dat ook?
Is dat relevant? Lijkt me niet eens dat je dat wilt. Je krijgt dan verspreide functionaliteit over meerdere files, wat me juist niet overzichtelijk lijkt.

Acties:
  • 0 Henk 'm!

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024
Anoniem: 27915 schreef op 23 augustus 2002 @ 10:11:
Hmm, misschien stampvolle classes, maar niet stampvolle files. In C++/C kun je objecten over meerdere files uitspreiden. ;). Kan java dat ook?
Dat kan bij Java niet, ik wist trouwens niet dat dat bij c++ wel kon. Weer iets geleerd.
Dat wel ja.
(Je bent het er dus mee eens dat het wel handig is om de functionaliteit niet te verspreiden)

Ik vind het zelf ook erg handig omdat je nu weer heel modulair kan ontwerpen. Mijn 'data houdende' objecten die worden steeds dommer en krijgen eigelijk alleen nog maar getters/setters en basis methodes. En verder krijg je dus 'functionaliteit' modules. Ik vind dit een erg handige manier van werken. En verder vind ik betere dispatch functionaliteit ook handiger werken omdat ik mooiere constructies kan maken terwijl ik enorm veel code kan hergebruiken.
* Anoniem: 27915 moet hier maar eens rustig een nachtje over slapen... Misschien gebruik ik het over een jaar wel en snap ik niet hoe je ooit zonder zou kunnen. :D
Het gaat vooral erg interessant worden met visitor guide`s. Dan zul je zien dat de visitor een erg krachtigedesign pattern is.
Maar ik kan me best voorstellen dat je in sommige gevallen die object functies niet nodig hebt en dat dit pattern dan best handig is... B).
In principe is er geen verschil in wat je doet. Het gaat er alleen om wat handiger is. Ik vind persoonlijk een functionele aanpak handiger omdat je beter overzicht houd en je objecten minder vervuild raken. Het probleem aan java is, dat dus geen 'open objects' ondersteund. Bij een open object kan je in een andere module je functionaliteit plaatsen, maar toch beschikking hebben over private informatie + je kan het mbv een methode aanroepen ipv een functie.

Acties:
  • 0 Henk 'm!

  • TheOneLLama
  • Registratie: Oktober 2000
  • Laatst online: 20-01-2022

TheOneLLama

A llama like no llama before

Anoniem: 27915 schreef op 23 augustus 2002 @ 10:11:
[...]


Hmm, misschien stampvolle classes, maar niet stampvolle files. In C++/C kun je objecten over meerdere files uitspreiden. ;). Kan java dat ook?
Je zou het kunnen doen in Java door steeds je class weer te extenden..

Opera OpenOffice.org Jabber Psi jabber://llama@mordax.com


Acties:
  • 0 Henk 'm!

Anoniem: 25556

Mijn excuses voor het kicken van deze thread, maar imo heb ik een nuttige toevoeging..

Naar aanleiding van Alarmnummers's propaganda hier voor visitor design patterns, heb ik besloten bij een nieuw project wat zich hier erg goed voor leent, deze te implementeren.

Probleem was echter dat ik een Delphi programmeur ben, en niet zo gruwelijk thuis in Java. Ik vond de bovenstaande tekst weliswaar leerzaam, maar moeilijk te plaatsen aangezien de voorbeelden vnl. in Java gegeven zijn.

Daarom ben ik op zoek gegaan naar Delphi voorbeelden, en kwam uiteindelijk uit op deze .pdf:

http://www.modelmakertool...oads/mmdesignpatterns.zip

Dit is een vrij complete beschrijving van een aantal design patterns, waaronder het visitor design pattern. Dit inclusief een aantal heldere voorbeelden (in pascal) en een uitleg hoe dit in Delphi te realiseren.

Leek me de moeite van het toevoegen waard..
Pagina: 1