[java] Generieke html-forms en sql-code dmv Visitors

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

  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Topicstarter
Voor een opdracht op de TUD hier (jaaa, huiswerk! :P) ben ik bezig met een Servlet.

Het is een vrij simpele webapplicatie (geen zoekfunctionaliteit, geen toeganscontrole) die een bibliotheek voorstelt met als voornaamste doel een aantal OO-paradigma toe te passen. De opdracht is dan ook iets als: "maak een applicatie/voorbeeld die een (aantal) OO-paradigma toepast".

Nou vind ik in principe dat de boel "af genoeg" is voor de opdracht, maar ben toch nog niet helemaal tevreden over een paar punten.

Ik heb gekozen om dmv Visitors de bibliotheek-objecten uit de database te vissen, in de database bij te werken en op te slaan of te verwijderen (de LibraryVisitors).
Daarnaast zijn er een stel Action-objecten die dmv een Factory geinstantieerd worden aan de hand van de action= request-parameter. Voor het verder afhandelen van deze Actions heb ik een andere Visitortree in het leven geroepen (de ActionVisitors). Er zijn drie belangrijke LibraryVisitors en twee belangrijke Visitors die zowel een ActionVisitor als een LibraryVisitor zijn.

Te weten, resp:
DestroyLibraryVisitor (voor het verwijderen van objecten uit de 'database', er is nog een DatabaseDestroyLibraryVisitor, maar dat boeit verder niet zo).
StoreLibraryVisitor (voor zowel de inserts als de updates in de database).
ReadLibraryVisitor (voor het inlezen van de objecten)
en
ActionHandleVisitor (de initiele object-creatie a.d.h.v. de gekozen Action en verdere afhandeling van de action, zoals het verwijderen van het object (als in, de verwijder-visitor aanmaken en zijn werk laten doen op dat object))
OutputVisitor (verantwoordelijk voor het tonen van de output, er is dan nog een HTML en een VRML versie van, maar die erven grote delen van de basis, voor zover iets uberhaupt al in VRML te doen is)

Daarnaast is er eenl LibraryObject met een stel specificaties (Library, BookShelf, Book, etc), voor de meeste operaties daarop is er weer een Action object (met BaseAction als generalisatie) ala EditBookShelfAction, DeleteBookAction en ShowSubjectAction.

Een ding waar ik tegen aan liep was iets dat ik ook wel al wist, als je zoiets hebt:
Java:
1
2
3
4
5
6
// Pak de BookShelf variant
void Visit(BookShelf b);
// Pak de Book variant
void Visit(Book b);
// Pak overigen variant
void Visit(LibraryObject);

Bij dit voorbeeldje zal altijd de laatste methode gebruikt worden, is hier een generieke oplossing voor bedacht waarbij er wel een default-methode op te geven * is zonder dat er aanpassingen in de Visitable-Objecten nodig zijn?
Uiteraard kan je wel alle losse Visit methoden aanmaken en diegenen die niks hoeven te doen allemaal dezelfde methode aan laten roepen, maar echt super is dat niet...

Verder vond ik het erg vervelend om een generalisatie te vinden voor het creeren van Objecten uit de DB, aangezien een Person-object afgebeeld in de DB er zo uit ziet: person = personid, personname, description, ... en een Book-object zo: book = bookid, bookname, description, subjectid, ownerid, bookshelfid heb je al een stel verschillen waar ik niet zomaar een oplossing voor ken.

Een Book heeft namelijk als variabelen resp. een int, string, string, Subject-object, Person-object en BookShelf-object...

Toch is het grootste deel van de code voor het invullen van de velden van de objecten vrijwel hetzelfde. * Is hier een of andere generalisatie aan te brengen die niet een hele serie taken open laat voor de specialisaties?

En iets vergelijkbaars geldt voor de formulieren die gegenereerd kunnen worden, hoewel er wel het een en ander in losse methoden te stouwen is, heb ik niet het idee dat er significant gegeneraliseerd kan worden.
Een Subject-object heeft alleen een Name en een Description die te editten zijn, terwijl het Book-object een keuze-lijst voor Subjects, Owners (Persons), Authors (geen Persons) en BookShelfs nodig heeft...
* Is dit nog enigszins efficient te generaliseren zonder dat er een hoop werk over blijft bij de specialistische methoden?

Een kleine motivatie voor de vragen is ook wel op zijn plaats, van de 5602 regels code in totaal (inc commentaar en witregels) is er 3776 te vinden in de Visitors en daarvan het grootste deel in de 5 belangrijkste:
code:
1
2
3
4
5
6
7
8
9
    641 action/ActionHandleVisitor.java
    129 action/BaseActionVisitor.java
    212 action/HTMLOutputVisitor.java
    973 action/OutputVisitor.java
    595 action/VRMLOutputVisitor.java
    109 library/DatabaseDestroyLibraryVisitor.java
    594 library/DatabaseReadLibraryVisitor.java
    239 library/DatabaseStoreLibraryVisitor.java
   3492 total

En het is daar uit ook wel duidelijk dat het de boel flink beter beheersbaar (mocht er een nieuw data-type nodig zijn, ik heb bijvoorbeeld nog geen zoekfunctionaliteit ingebouwd) wordt als er een groot deel weg te generaliseren is...

Er is vast een hoop nog onduidelijk, maar ik kan zo gauw niet meer bedenken wat ik aan generieke uitleg moet tikken zonder gelijk maar de hele code te posten, dus vraag er op los :)

  • jelmervos
  • Registratie: Oktober 2000
  • Niet online

jelmervos

Simple user

Huiswerk? :?

"The shell stopped unexpectedly and Explorer.exe was restarted."


  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Topicstarter

Nou, dit is eigenlijk het doorgeschoten deel dat puur uit interesse enzo voortkomt :)

Het huiswerk deel is opzich allang af (wat er nu dus al gewoon werkend geimplementeerd is).

[ Voor 10% gewijzigd door ACM op 17-01-2003 01:27 ]


  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

(overleden)
ACM schreef op 17 januari 2003 @ 01:15:
(...)Een ding waar ik tegen aan liep was iets dat ik ook wel al wist, als je zoiets hebt:
Java:
1
2
3
4
5
6
// Pak de BookShelf variant
void Visit(BookShelf b);
// Pak de Book variant
void Visit(Book b);
// Pak overigen variant
void Visit(LibraryObject);

Bij dit voorbeeldje zal altijd de laatste methode gebruikt worden, is hier een generieke oplossing voor bedacht waarbij er wel een default-methode op te geven * is zonder dat er aanpassingen in de Visitable-Objecten nodig zijn?
Uiteraard kan je wel alle losse Visit methoden aanmaken en diegenen die niks hoeven te doen allemaal dezelfde methode aan laten roepen, maar echt super is dat niet...
Altijd de laatste? Het Visitor pattroon voorziet juist in double dispatch :? Maw. dit gaat gewoon werken en hij pakt gewoon het de methode gespecificeerd bij Book, en doet dus een toString() op het argument en roept niet de default methode aan.
Java:
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
37
38
39
40
41
42
43
44
45
46
47
48
49
public interface LibraryObjectVisitor {

        // Foei! de methode visit moet met een kleine letter
        // beginnen ACM! :P
        void visit(BookShelf shelf);
        void visit(Book book );
        void visit(LibraryObject library);
}

public class LibraryObjectVisitorAdapter implements LibraryObjectVisitor{ 

        public void visit( BookShelf shelf ) {
                
                defaultVisit( shelf );
        }
        
        public void visit( Book book ) {
        
                defaultVisit( book );
        }

        public void visit( LibraryObject library ) {

                defaultVisit( library );
        }
        
        // Adapterclass so a empty method
        public void defaultVisit( LibraryObject library ) { }
        
}

// Define een visitor die alleen wat nuttigs doet voor een boek
//
public class BookFunctionVisitor {

        public void visit( Book book ) {
        
                System.out.println( book );
        }
}

public class TestClass {
        
        public static void main( String argv[] ) {

                LibraryObject aBook = new Book( );
                aBook.accept( new BookFunctionVisitor ); 
         }
}


Als dat bij jou wel zo is, moet je even de implementatie van accept() van de Visitable class posten. Normaal is ie zo

Java:
1
2
3
public void visit( LibraryObjectVisitor visitor ) {
   visitor.visit( this );
}
Verder vond ik het erg vervelend om een generalisatie te vinden voor het creeren van Objecten uit de DB, aangezien een Person-object afgebeeld in de DB er zo uit ziet: person = personid, personname, description, ... en een Book-object zo: book = bookid, bookname, description, subjectid, ownerid, bookshelfid heb je al een stel verschillen waar ik niet zomaar een oplossing voor ken.

Een Book heeft namelijk als variabelen resp. een int, string, string, Subject-object, Person-object en BookShelf-object...

Toch is het grootste deel van de code voor het invullen van de velden van de objecten vrijwel hetzelfde. * Is hier een of andere generalisatie aan te brengen die niet een hele serie taken open laat voor de specialisaties?
Als je ze allemaal dezelfde 'vulinterface' kan geven, dan zou het een eitje zijn. Dit is echter niet het geval. Ik neem nu aan dat je voor elke 'vulzooi' een object hebt die dmv een parameter weet welk persoon/object ie uit de db moet halen? Kun je dat niet generaliseren in een interface?
Verder wil je dit in aparte objecten hebben en hier een neit te strakke binding tussen je objecten leggen door zo'n sterke generalisatie. Je gaat dan problemen krijgen als Book een double, string, string nodig heeft en een persoon een int, string, string :)
En iets vergelijkbaars geldt voor de formulieren die gegenereerd kunnen worden, hoewel er wel het een en ander in losse methoden te stouwen is, heb ik niet het idee dat er significant gegeneraliseerd kan worden.
Een Subject-object heeft alleen een Name en een Description die te editten zijn, terwijl het Book-object een keuze-lijst voor Subjects, Owners (Persons), Authors (geen Persons) en BookShelfs nodig heeft...
* Is dit nog enigszins efficient te generaliseren zonder dat er een hoop werk over blijft bij de specialistische methoden?
Met een Visitor en een InputField classtree misschien?
Laat je object door een visitor behandeld worden, waarna je Vistor een InputFormParameter object maakt.
Binnen dit object kun je dan een List opslaan van InputFields welke elk van een Type zijn die je nodig hebt in je HTML formulieren. Wat code verheldert het :)
Java:
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// Verdorie :/ wat een gedrocht van een naam
// This class generates a inputform parameter object for
// for each possible LibraryObject
//
public class InputFormGeneratorLibraryObjectVisitor {

        private InputFormParameter _inputFormParameter = new InputFormParameter();

        public void visit( BookShelf shelf ) {
                 
                getInputFormParameter().add( new HTMLTextField( shelf.getName(), 
                                                                "" + shelf.getNumber() );                  

        public void visit( Book book ) {
        
                // voor elk ding van boek doen :)
                getInputFormParameter().add( new HTMLTextField( "Auteur", 
                                                                book.getAuteur() ) );
                
        }
        
        public void getInputFormParameter() {
                return _inputFormParameter;
        }
        
        // Baaaaad name, but need that method to
        // reuse this object
        public void newInputForm( ) {
        
                _inputFormParameter = new InputFormParameter();
        }
}

// Need to polish the code below, it's a bit rough
//
public interface InputField {

        
        public String getCode();
        
}

public abstract class AbstractInputField {

        private String  _name;
        private _String _value;
        
        public AbstractInputField( String name, String value ) {
        
                setName( name );
                setValue( value );
       }
       
       // Imagine getters and setters here
       // 
}

public HTMLTextField extends AbstractInputField( ) {

        // Override getCode()
        //
        public String getCode() {
                
         return "<INPUT TYPE=\"TEXT\" NAME=\"" +getName()+ "\" VALUE=\"" +getValue()+ "\"";
        }
}
InputFormParameter is dan een object (daar had ik ff geen zin meer in :P ) die een lading InputFields bij elkaar bundelt en een iterator aanbied zodat je form object makkelijk er over heen kan lopen :)

Dit is allemaal even snel bedacht 's ochtends vroeg, dus er kunnen kolossale fouten in zitten :) Sterker nog, ik ben ervan overtuigt. Maar dat zien we vanzelf als meer mensen dit lezen :P
Een kleine motivatie voor de vragen is ook wel op zijn plaats, van de 5602 regels code in totaal (inc commentaar en witregels) is er 3776 te vinden in de Visitors en daarvan het grootste deel in de 5 belangrijkste:
code:
1
2
3
4
5
6
7
8
9
    641 action/ActionHandleVisitor.java
    129 action/BaseActionVisitor.java
    212 action/HTMLOutputVisitor.java
    973 action/OutputVisitor.java
    595 action/VRMLOutputVisitor.java
    109 library/DatabaseDestroyLibraryVisitor.java
    594 library/DatabaseReadLibraryVisitor.java
    239 library/DatabaseStoreLibraryVisitor.java
   3492 total

En het is daar uit ook wel duidelijk dat het de boel flink beter beheersbaar (mocht er een nieuw data-type nodig zijn, ik heb bijvoorbeeld nog geen zoekfunctionaliteit ingebouwd) wordt als er een groot deel weg te generaliseren is...

Er is vast een hoop nog onduidelijk, maar ik kan zo gauw niet meer bedenken wat ik aan generieke uitleg moet tikken zonder gelijk maar de hele code te posten, dus vraag er op los :)
Mjah, dat heb je al snel met die uit de hand gelopen school opdrachten he :)
* Glimi is bezig met een Swing Chart Object, wat ook iets meer is dan gevraagd :)

edit:
FF iets minder layout verneukt

[ Voor 3% gewijzigd door Glimi op 17-01-2003 09:21 ]


  • whoami
  • Registratie: December 2000
  • Laatst online: 10:17
ACM schreef op 17 January 2003 @ 01:15:

Een ding waar ik tegen aan liep was iets dat ik ook wel al wist, als je zoiets hebt:
Java:
1
2
3
4
5
6
// Pak de BookShelf variant 
void Visit(BookShelf b); 
// Pak de Book variant 
void Visit(Book b); 
// Pak overigen variant 
void Visit(LibraryObject);


Bij dit voorbeeldje zal altijd de laatste methode gebruikt worden, is hier een generieke oplossing voor bedacht waarbij er wel een default-methode op te geven * is zonder dat er aanpassingen in de Visitable-Objecten nodig zijn?
Bedoel je dat, als je hier bv:

Java:
1
element.Visit (aBookShel);


Hij de functie die een LibraryObject als argument neemt, gaat uitvoeren?
Vreemd.
Verder vond ik het erg vervelend om een generalisatie te vinden voor het creeren van Objecten uit de DB, aangezien een Person-object afgebeeld in de DB er zo uit ziet: person = personid, personname, description, ... en een Book-object zo: book = bookid, bookname, description, subjectid, ownerid, bookshelfid heb je al een stel verschillen waar ik niet zomaar een oplossing voor ken.

Een Book heeft namelijk als variabelen resp. een int, string, string, Subject-object, Person-object en BookShelf-object...
Aangezien het hier over objecten gaat die niet met elkaar te vergelijken zijn, zou ik hier geen inheritace toepassen. Person en book staan nl. los van elkaar. Ik zie niet direct een gemeenschappelijke parent, en functioneel gezien is die er ook niet.
En iets vergelijkbaars geldt voor de formulieren die gegenereerd kunnen worden, hoewel er wel het een en ander in losse methoden te stouwen is, heb ik niet het idee dat er significant gegeneraliseerd kan worden.
Een Subject-object heeft alleen een Name en een Description die te editten zijn, terwijl het Book-object een keuze-lijst voor Subjects, Owners (Persons), Authors (geen Persons) en BookShelfs nodig heeft...
* Is dit nog enigszins efficient te generaliseren zonder dat er een hoop werk over blijft bij de specialistische methoden?
Kun je hier geen abstract object ofzo maken voor een formulier met alle standaard functionaliteit reeds geimplementeerd en een aantal virtuele of abstracte functies.
Als je een nieuw formulier moet maken, inherit je van dat standaard formulier en moet je enkel nog de nodige methods overriden.
Een kleine motivatie voor de vragen is ook wel op zijn plaats, van de 5602 regels code in totaal (inc commentaar en witregels)
Commentaar en witregels zijn geen code.


edit:
Shiiiit, zit ik whoami's bericht te editten ipv te quoten :X

[ Voor 161% gewijzigd door Glimi op 17-01-2003 09:34 ]

https://fgheysels.github.io/


  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

(overleden)
whoami schreef op 17 January 2003 @ 09:20:
Bedoel je dat, als je hier bv:
code:
1
element.Visit (aBookShel);

Hij de functie die een LibraryObject als argument neemt, gaat uitvoeren?
Vreemd.
Nee, da's normaal voor een taal die geen multiple dispatch kent :P

Java:
1
2
3
LibraryObject element = new Book();
Visitor visitor = new BlaVisitor( );
visitor.Visit( element );

Zal niet werken :) De compiler ziet element gewoon als een LibraryObject en mapt hem dan ook naar de overeenkomstige methode van de visitor

Doe je echter dit:
Java:
1
2
3
LibraryObject element = new Book();
Visitor visitor = new BlaVisitor( );
element.accept( visitor );


Met in de accept methode
Java:
1
2
3
4
5
6
//LibraryObjectVisitor == visitor for this classtree
//
public void accept( LibraryObjectVisitor visitor ) {

   visitor.visit( this );
}

Dan werkt het wel, omdat de compiler dan ook weet dat je een Book stuurt ipv een LibraryObject

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

Alarmnummer

-= Tja =-

*heeft niet alles gelezen want het is ook zo`n enorme lading tekst*

Je moet alleen eind classes opnemen in je visitor.

code:
1
2
3
4
5
6
7
8
interface LibraryObjectVIsitor{

   void visit(BookShelf b);

   void visit(Book b);

   void visit(Library l);
}

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

Alarmnummer

-= Tja =-

Glimi schreef op 17 January 2003 @ 09:16:
[nohtml]
[...]
Altijd de laatste? Het Visitor pattroon voorziet juist in double dispatch :?
Dat zie ik ook vaak in boeken staan en ik ben het niet eens met die uitspraak. Wat de visitor doet is de 'polymorfistische' dispatch (runtime uitzoeken welke methode bij een object hoort) ombouwen naar een 'algemene' dispatch (die nog steeds single is!) Je kan eventueel wel met een chaining van meerdere visitor calls achter elkaar een multidispatch voor elkaar krijgen. Maar een standaard visitor is nog steeds een single dispatch. Ik snap verder wel hoe ze aan deze naam komen. Eerst maak je namelijk gebruik van een statische dispatch (compile time) en later van een dynamische (polymorphisme mechanisme). Maar nu loop je echt appels bij peren op te tellen als je dit samen een double dispatch wilt noemen => onzin dus.


dit is een voorbeeld van een double dispatched visitor.
code:
1
2
3
4
5
6
interface DoubleDispatchedVisitor{
     void visit(Book b1, Book b2);
     void visit(Shelf s, Book b);
     void visit(Bool b, Shelf s);
     etc
}


Hierin kies je op basis van 2 argumenten de juiste methode definitie. Dit is dus een echt double dispatch. (hij is zelfs nog symetrisch)

[ Voor 52% gewijzigd door Alarmnummer op 17-01-2003 10:05 ]


  • whoami
  • Registratie: December 2000
  • Laatst online: 10:17
[nohtml]
Glimi schreef op 17 January 2003 @ 09:35:
[nohtml]


Nee, da's normaal voor een taal die geen multiple dispatch kent :P

Java:
1
2
3
LibraryObject element = new Book();
Visitor visitor = new BlaVisitor( );
visitor.Visit( element );

Zal niet werken :) De compiler ziet element gewoon als een LibraryObject en mapt hem dan ook naar de overeenkomstige methode van de visitor
Owja, als je het zo doet, idd.

* whoami moet uiteindelijk nog eens dat Visitor pattern bekijken in dat GoF boek.
(en eens toepassen ook :P )

https://fgheysels.github.io/


  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

(overleden)
Alarmnummer schreef op 17 januari 2003 @ 09:49:
Dat zie ik ook vaak in boeken staan en ik ben het niet eens met die uitspraak. Wat de visitor doet is de 'polymorfistische' dispatch (runtime uitzoeken welke methode bij een object hoort) ombouwen naar een 'algemene' dispatch (die nog steeds single is!) Je kan eventueel wel met een chaining van meerdere visitor calls achter elkaar een multidispatch voor elkaar krijgen. Maar een standaard visitor is nog steeds een single dispatch. Ik snap verder wel hoe ze aan deze naam komen. Eerst maak je namelijk gebruik van een statische dispatch (compile time) en later van een dynamische (polymorphisme mechanisme). Maar nu loop je echt appels bij peren op te tellen als je dit samen een double dispatch wilt noemen => onzin dus.


dit is een voorbeeld van een double dispatched visitor.
code:
1
2
3
4
5
6
interface DoubleDispatchedVisitor{
     void visit(Book b1, Book b2);
     void visit(Shelf s, Book b);
     void visit(Bool b, Shelf s);
     etc
}


Hierin kies je op basis van 2 argumenten de juiste methode definitie. Dit is dus een echt double dispatch.

Mjah, heb je wel gelijk in. Laat ik het anders formuleren dan: "Het visitor design pattern voorziet een manier om Double Dispatch te kunnen benaderen."
Dat mensen die dat subtiele verschil niet zien/weten (like me) het gewoon double dispatch noemen vind ik ook best :P

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

Alarmnummer

-= Tja =-

Glimi schreef op 17 januari 2003 @ 10:06:

[...]

Mjah, heb je wel gelijk in. Laat ik het anders formuleren dan: "Het visitor design pattern voorziet een manier om Double Dispatch te kunnen benaderen."
Dat mensen die dat subtiele verschil niet zien/weten (like me) het gewoon double dispatch noemen vind ik ook best :P
Ik ben het nog niet met je formulatie eens. Het visitor design pattern biedt de mogelijkheid om een single, double, triple etc dispatch voor elkaar te krijgen. Ik zou het dan nog geen double willen noemen omdat het te beperkt is. Dan zou ik het multidispatch willen noemen.

Maar aangezien bij normaal gebruik op basis van maar 1 argument wordt uitgezocht zou ik de visitor toch echt een 'manier om een algemene single dispatch voor elkaar te krijgen' willen noemen.

  • whoami
  • Registratie: December 2000
  • Laatst online: 10:17
* whoami heeft er het GoF boek eens bijgenomen.
* whoami gaat een beetje offtopic gaan. :P

Ik zit hier nog dat Visitor pattern eens te bestuderen, en het ziet er wel allemaal mooi uit enzo, en ik zie het nut er wel van in, maar ik vraag me nu gewoon af: wanneer gebruik je best zo'n visitor. Ik bedoel, ik heb dat nog nooit (moeten?) gebruiken. (Kan aan mij liggen hoor. :+). Misschien zie ik de situatie gewoon niet waar ik een visitor kan gebruiken.
Misschien kan iemand mij hier ff een concrete situatie schetsen waarin het gebruik van het visitor pattern de moeite loont? :?

https://fgheysels.github.io/


  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

(overleden)
whoami schreef op 17 January 2003 @ 17:37:
* whoami heeft er het GoF boek eens bijgenomen.
* whoami gaat een beetje offtopic gaan. :P

Ik zit hier nog dat Visitor pattern eens te bestuderen, en het ziet er wel allemaal mooi uit enzo, en ik zie het nut er wel van in, maar ik vraag me nu gewoon af: wanneer gebruik je best zo'n visitor. Ik bedoel, ik heb dat nog nooit (moeten?) gebruiken. (Kan aan mij liggen hoor. :+). Misschien zie ik de situatie gewoon niet waar ik een visitor kan gebruiken.
Misschien kan iemand mij hier ff een concrete situatie schetsen waarin het gebruik van het visitor pattern de moeite loont? :?


Lees je GoF boek er nog eens op na. Er staat per pattroon ook nog een stukje met 'waarneer te gebruiken' :P
* Glimi heeft even geen tijd om een situatie te geven, edit dit bericht morgen wel even :)

  • whoami
  • Registratie: December 2000
  • Laatst online: 10:17
Nouja, als je verschillende objecten hebt waar je veel verschillende operaties moet kunnen op uitvoeren. Maar, ik had graag eens een real-life situatie schets gezien. :P

https://fgheysels.github.io/


  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Topicstarter
[nohtml]
Glimi schreef op 17 januari 2003 @ 09:16:
Altijd de laatste? Het Visitor pattroon voorziet juist in double dispatch :? Maw. dit gaat gewoon werken en hij pakt gewoon het de methode gespecificeerd bij Book, en doet dus een toString() op het argument en roept niet de default methode aan.
Humm, ik had het gelezen dat dat zou gebeuren en het gebeurde op een gegeven moment ook zo... Dus ging ik er van uit dat het altijd zo was :)
Anyway, bij het construeren van een testvoorbeeld liep ik wel tegen een andere beperking, dat je als je een 'default' hebt die in een subclass zit maar in de superclass wel een specificatie hebt ie die specifieke methode uit de superclass pakt, das wel vervelend als je een aantal methoden wilt overerven maar niet alles. Magoed dan maar wat viezer :P
Als dat bij jou wel zo is, moet je even de implementatie van accept() van de Visitable class posten. Normaal is ie zo
Nee, bij mij is ie anders... Met Accept ipv accept :X
Als je ze allemaal dezelfde 'vulinterface' kan geven, dan zou het een eitje zijn. Dit is echter niet het geval. Ik neem nu aan dat je voor elke 'vulzooi' een object hebt die dmv een parameter weet welk persoon/object ie uit de db moet halen? Kun je dat niet generaliseren in een interface?
Ik heb een vulvisitor die domweg een query naar de DB lanceert en daar de parameters uit trekt ala:
Java:
1
2
3
4
5
6
void Visit(Book book)
{
   // Doe select query die ResultSet rs oplevert.
   book.setName(rs.getString("bookname"));
   // etc
}
Verder wil je dit in aparte objecten hebben en hier een neit te strakke binding tussen je objecten leggen door zo'n sterke generalisatie. Je gaat dan problemen krijgen als Book een double, string, string nodig heeft en een persoon een int, string, string :)
Das waar :)
Met een Visitor en een InputField classtree misschien?
Laat je object door een visitor behandeld worden, waarna je Vistor een InputFormParameter object maakt.
[knip]
InputFormParameter is dan een object (daar had ik ff geen zin meer in :P ) die een lading InputFields bij elkaar bundelt en een iterator aanbied zodat je form object makkelijk er over heen kan lopen :)
Humm, zo zou ik er ook nog een Composite pattern in kunnen verwerken :+



whoami schreef op 17 januari 2003 @ 09:20:
Aangezien het hier over objecten gaat die niet met elkaar te vergelijken zijn, zou ik hier geen inheritace toepassen. Person en book staan nl. los van elkaar. Ik zie niet direct een gemeenschappelijke parent, en functioneel gezien is die er ook niet.
Ze hebben allemaal een naam, allemaal een laatste-bijgewerkt-datum, allemaal een laatste-toegang-datum, allemaal een beschrijving en daar getters/setters voor.
Dat ga ik echt niet allemaal bij allemaal zitten implementeren hoor :)

Ze hebben de functionele overeenkomst dat ze allemaal datarepresentaties zijn voor stukken data binnen de bibliotheek.
Kun je hier geen abstract object ofzo maken voor een formulier met alle standaard functionaliteit reeds geimplementeerd en een aantal virtuele of abstracte functies.
Als je een nieuw formulier moet maken, inherit je van dat standaard formulier en moet je enkel nog de nodige methods overriden.
Dat is ook nog een manier.
Commentaar en witregels zijn geen code.
Ja, maar ik weet niet zo snel een commando dat mijn regels code telt, terwijl `wc -l *Visitor*.java` lekker simpel werkt :+


Alarmnummer schreef op 17 januari 2003 @ 09:46:
*heeft niet alles gelezen want het is ook zo`n enorme lading tekst*

Je moet alleen eind classes opnemen in je visitor.
Dat heb ik ook, maar het is niet echt de beste manier om een default-variant van je visit op te geven, imho.


whoami schreef op 17 januari 2003 @ 17:37:
Misschien kan iemand mij hier ff een concrete situatie schetsen waarin het gebruik van het visitor pattern de moeite loont? :?
Tsk, vind je mijn uitwerking niet mooi genoeg?
Het vullen, bijwerken en verwijderen van de dataobjecten (cq, de synchronisatie met je database) en het verwerken van actie's die nodig zijn voor het verwerken van je pagina's.

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

Alarmnummer

-= Tja =-

ACM schreef op 17 January 2003 @ 20:44:
Dat heb ik ook, maar het is niet echt de beste manier om een default-variant van je visit op te geven, imho.
Dit kan je voor elkaar krijgen door een adapter voor de visitor erbij te plaatsen. Hiermee kan je ook eigen filters gaan aanmaken.

Java:
1
2
3
4
5
6
7
8
9
10
11
12
class LibAdapter implements LibVisitor{
     void visit(Boek b){
           visitDefault(b);
     }

     void visit(Plank p){
         visitDefault(p);
    }

    void visitDefault(LibOnderdeel l){
    }
}


Als ik nu naar alleen planken wil luisteren dan kan ik het volgende doen:

Java:
1
2
3
4
5
6
7
8
9
class PlankVisitor extends LibAdapter{
       void visit(Plank p){
            System.out.printnl("plank");
       }

       void visitDefault(LibOnderdeel l){
            System.out.println("geen plank");
       }
}


En ik denk verder dat het voor jou ook reuze interessant is om even te kijken naar de visitor guide:

Java:
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
class LibVisitorGuide implements LibVisitor{
     LibVisitor _guest;

    LibVisitorGuide(LibVIsitor guest){_guest = guest;}

    void visit(Plank plank){
         _guest.visit(plank);
         for(Iterator itt = plank.getBoeken();itt.hasNext();){
              ((Boek)itt.next()).visit(this);
         }
    }

    void visit(Kast kast){
         _guest.visit(kast);
         for(Iterator itt = kast.getPlanken();itt.hasNext();){
              ((Plank)itt.next()).visit(this);
         }
    }

    void visit(Lib lib){
         _guest.visit(lib);         
         for(Iterator itt = lib.getKasten();itt.hasNext();){
              ((Kast)itt.next()).visit(this);
         }
     }

    void visit(Boek boek){
        _guest.visit(boek);
    }
}

Hiermee hou je traversal en logica van elkaar gescheiden en je kan je traversels hergebruken. Je kunt het vergelijken met de fold functies van haskell,clean. En ook met de Iterator functie van java.

[ Voor 44% gewijzigd door Alarmnummer op 17-01-2003 20:58 ]


  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Topicstarter
[nohtml]
Alarmnummer schreef op 17 januari 2003 @ 20:50:
Dit kan je voor elkaar krijgen door een adapter voor de visitor erbij te plaatsen. Hiermee kan je ook eigen filters gaan aanmaken.
Ah, ok.
Ik had toch al een generieke Visitor superclass voor beide Tree's, dus die kan ik wel ombouwen tot Adapter (ik zie dat Glimi het ook al als voorbeeld gebruikte maar iets minder expliciet uitlegde).
En ik denk verder dat het voor jou ook reuze interessant is om even te kijken naar de visitor guide:
Humm :)
Ook idd wel een leuke constructie

offtopic:
Waar blijft jouw deel2 van je Visitor-guide? :P

  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Topicstarter

* ACM heeft er een foutje in een voorbeeld in ontdekt :P

Java:
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
class BreadthFirstTreeGuide implements TreeGuide
{
LinkedList<Tree.Component> queue;
void guide(TreeVisitor visitor, Tree.Node node)
{
Iterator<Tree.Component> children
= node.getChildren().iterator();
while(children.hasNext())
{
Tree.Component component = children.next();
queue.addLast(visitor);  //// <- datte...
}
toNext(visitor);
}
void toNext(TreeVisitor visitor)
{
if(!queque.isEmpty()) // das ook fout, magoed spelfouten zijn onder voorbehoud natuurlijk :)
{
Tree.Component component
= queue.removeFirst();
component.acceptVisit(visitor);
}
}
void guide(TreeVisitor visitor, Tree.Leaf leaf)
{
toNext(visitor);
}
}

Ik vind dat niet kunnen hoor :+

offtopic:
Bedankt voor het linkje :)
(of was dit nou het ontopic deel :? :P )

[ Voor 6% gewijzigd door ACM op 18-01-2003 13:32 ]


  • whoami
  • Registratie: December 2000
  • Laatst online: 10:17
[nohtml]
ACM schreef op 17 januari 2003 @ 01:15:

Ik heb gekozen om dmv Visitors de bibliotheek-objecten uit de database te vissen, in de database bij te werken en op te slaan of te verwijderen (de LibraryVisitors).
Hoe doe je dit dan precies?
Ik ben in het boek 'Patterns for Enterprise Application Architecture' het patroon 'Unit of Work' tegengekomen.
Dit patroon zorgt ervoor dat db-bewerkingen op alle objecten die binnen eenzelfde transactie thuishoren binnen 1 method gedaan worden.

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
class  UnitOfWork
{
   private  ArrayList  _newObjects;
   private  ArrayList  _deletedObjects;
   private ArrayList  _updatedObjects;

   public void AddNew ( LibraryObject obj)
   {
   }
   public void AddDeleted (LibraryObject obj)
   {
   }
   public void AddUpdated (LibraryObject obj)
   { 
   }

   public void Commit()
   {
       // Hier ga je een transactie starten en alle 
       // objecten binnen de UnitOfWork gaan 
       // afhandelen.
   }

}

https://fgheysels.github.io/


  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Topicstarter
Dat kan nog steeds met een visitor :)

Zoals je zelf al aangehaald had, de visitor is vooral bedoeld voor het weghalen van je logica uit je dataobjecten.
De libraryobjecten zijn bij mij niets anders dan geheugenrepresentaties van stukjes bibliotheek, de C programmeur zou het in structs opslaan. En de enterprise java programmeur zou ze Bean kunnen noemen denk ik :P

Maar de belangrijkste reden dat ik de visitor gebruikt heb is dat ik geen zin heb in al die objecten stukken logica te verspreiden.
Ergens moet er een query uitgevoerd worden en hoe je van het resultaat een specifiek object kan bouwen.

Soms is het zelfs zo (bij delete queries bijvoorbeeld, die enkel 'delete from $table where $tableID = $idvalue' doen) dat je vrij eenvoudig de boel generiek kunt houden. Die generieke code kan je niet domweg toepassen als je al die logica in je dataobjecten had gestopt. Met een visitor kan dat veel eenvoudiger.

Het kan heel goed dat er andere patterns veel beter geschikt zijn hiervoor, ik ken lang niet alle patterns (sterker nog ik ken er vrij weinig :) ) en kan dus ook zeker niet bepalen of de visitor het meest geschikt is, ik vind de visitor wel geschikt ervoor. Zeker tov de 'standaard manier' :)

edit:

Owja, glimi de methodes heten nu allemaal visit ipv Visit en accept ipv Accept :)

[ Voor 5% gewijzigd door ACM op 18-01-2003 15:49 ]


  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

(overleden)
[nohtml]
ACM schreef op 17 januari 2003 @ 20:44:
Humm, ik had het gelezen dat dat zou gebeuren en het gebeurde op een gegeven moment ook zo... Dus ging ik er van uit dat het altijd zo was :)
Anyway, bij het construeren van een testvoorbeeld liep ik wel tegen een andere beperking, dat je als je een 'default' hebt die in een subclass zit maar in de superclass wel een specificatie hebt ie die specifieke methode uit de superclass pakt, das wel vervelend als je een aantal methoden wilt overerven maar niet alles. Magoed dan maar wat viezer :P
Mjah, dan zou ik eerder de interface implementeren die de superclass implementeert en bepaalde dingen delegeren naar de superclass en overerven wat je _wel_ wilt
Kan dat niet omdat je interface te ruim is, dan moet je toch echt terug naar de tekentafel :P
Nee, bij mij is ie anders... Met Accept ipv accept :X
Jij boefje! http://java.sun.com/docs/...html/names.doc.html#73307 en specefiek http://java.sun.com/docs/.../html/names.doc.html#9322
zo zou ik er ook nog een Composite pattern in kunnen verwerken :+
Idd ;) en daar doen we het toch voor :P

whoami schreef op 17 januari 2003 @ 19:06:
Nouja, als je verschillende objecten hebt waar je veel verschillende operaties moet kunnen op uitvoeren. Maar, ik had graag eens een real-life situatie schets gezien. :P
* Glimi spit in code van dit jaar
Ah, wat gevonden, wat voldoet :)

Ik was bezig met het QueensProblem, maw zet een n koninginnen op een nxn schaakbord zo neer dat ze elkaar niet kunnen slaan. Dit heb ik proberen op te lossen met een genetic algoritm ( soort evolutie ). Punt is dat je in elke populatie gaat kijken welke van je data 'the fittest' is.

Normaal gesproken zou je nu natuurlijk 1/2 classes zien. Een schaakbord en een koningin (mjah ik heb ze geïntegegreerd eigenlijk, was een beetje lui). Echter omdat het er om ging dat de data fit was, heb ik besloten om de evaluatiefunctie _uit_ die class te trekken en in een visitor te zetten.
Even ter disclaimer. Dit is privé gemaakt en ik heb er niet genoeg aandacht aan besteed. Hell, ik zag net nog dat ik een variabele kon wegoptimaliseren :o

Hier de visitable
Java:
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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
public class QueensChessboard {

    private static final int DEFAULT_SIZE       =  8;
    public static final int ROW_EMPTY           = -1;

    private int _board[];

    public QueensChessboard( ) {

        this( DEFAULT_SIZE );
    }

    public QueensChessboard( int size ) {

        setSize( size );
        initializeBoard( );
    }

    public boolean setQueen( int row, int collumn ) {

        if( row > getSize( ) - 1 )
            throw new IndexOutOfBoundsException( "The row is > the size of the board" );

        if( collumn > getSize( ) -1 )
            throw new IndexOutOfBoundsException( "The collumn is > the size of the board" );

        for( int i = 0; i < _board.length; i++ ) {

            if( _board[i] == collumn && i != row && _board[i] != ROW_EMPTY ){

                return false;
            }
        }
        _board[row] = collumn;
        return true;
    }

    public int getQueen( int row ) {

        return _board[row];
    }

    public int getSize() {

        return _board.length;
    }

    /*
     * Possible loss of data, if size < _board.length
     */
    private void setSize( int size ) {

        if( size < 1 )
            throw new IllegalArgumentException( "size cannot be < 1 ");

        int oldSize     = _board.length;
        int[] newBoard  = new int[ size ]; 
        
        for( int = 0; i < size; i++ ) {
                
                if( i <= oldSize ) {
                        
                        newBoard[i] = _board[i];       
                } else {
                        newBoard[i] = ROW_EMPTY;
                }
                
        }
        
        _board = newBoard[i]
    }

    private void initializeBoard( ){

        _board = new int[ getSize() ];

        for( int i = 0; i < getSize(); i++ ) {

            _board[i] = ROW_EMPTY;
        }
    }


    public void accept( QueensChessboardVisitor visitor ) {

        visitor.visit( this );
    }

}


En hier de Visitor
Java:
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
37
38
39
40
41
42
43
44
45
46
47
48
public class FitnessQueensChessboardVisitor implements QueensChessboardVisitor {

    private static final int DEFAULT_FITNESS = 0;

    private int _fitness;

    public FitnessQueensChessboardVisitor( ) {

        this( DEFAULT_FITNESS );
    }

    public FitnessQueensChessboardVisitor( int fitness ) {

        setFitness( fitness );
    }

    public void visit( QueensChessboard board ) {

        int size = board.getSize();

        for( int i = 0; i < size; i++ ){

            for( int j = i + 1; j < size; j++) {

                if( board.getQueen( i ) + (j-i) == board.getQueen( j ) || board.getQueen( i ) - (j-i) == board.getQueen( j )) {

                    setFitness( getFitness() + 1);
                }

            }
        }
    }

    private void setFitness( int i ) {

        _fitness = i;
    }

    public int getFitness( ) {

        return _fitness;
    }

    public void reset( ) {

        setFitness( DEFAULT_FITNESS );
    }
}


Hierdoor had ik verschillende voordelen

1) Ik heb een algoritme geïsoleerd. Stel er komt een torenprobleem, dan kan dat ook in deze visitor opgenomen worden. Dan ga ik ook feitelijk gebruik maken van dispatch op het impliciete argument.
2) Het dataobject blijft 'clean' :)
3) De functie is te swappen/pluggen @ runtime

Visitor is dus niet echt een pattroon om iets reuze handig op te lossen, maar eerder een pattroon om scheiding te bregen ( like open objects ) tussen data en logica :) Maar zonder Visitors werken kan ook heel goed. Is geen 'moetje' dus :)

ACM schreef op 17 January 2003 @ 21:16:
Ah, ok.
Ik had toch al een generieke Visitor superclass voor beide Tree's, dus die kan ik wel ombouwen tot Adapter (ik zie dat Glimi het ook al als voorbeeld gebruikte maar iets minder expliciet uitlegde).
Gekke Glimi heeft een goede leermeester :* O+

Ik zal hem binnekort eens lezen. Aangezien ik nu onderwezen wordt door dhr Visser (OOMP) maakt het denk ik wel een goede indruk om daaruit te citeren :P

ACM schreef op 18 January 2003 @ 15:44:
(...)
De libraryobjecten zijn bij mij niets anders dan geheugenrepresentaties van stukjes bibliotheek, de C programmeur zou het in structs opslaan. En de enterprise java programmeur zou ze Bean kunnen noemen denk ik :P
Sterker nog, het zijn gewoon beans dan. Als jij een getter/setter hebt en aan nog wat afspraakjes voldoet en een interface implementeert dan is het officieel een Bean (en daarmee bedoel ik geen EJB)
Maar de belangrijkste reden dat ik de visitor gebruikt heb is dat ik geen zin heb in al die objecten stukken logica te verspreiden.
Ergens moet er een query uitgevoerd worden en hoe je van het resultaat een specifiek object kan bouwen.
(...)

Het kan heel goed dat er andere patterns veel beter geschikt zijn hiervoor, ik ken lang niet alle patterns (sterker nog ik ken er vrij weinig :) ) en kan dus ook zeker niet bepalen of de visitor het meest geschikt is, ik vind de visitor wel geschikt ervoor. Zeker tov de 'standaard manier' :)
Da's eigenlijk het doel van het Visitor pattroon wat je hier omschrijft. Dus je hebt wel kans dat het één van de meest geschikte pattronen zijn hiervoor ;)
edit:

Owja, glimi de methodes heten nu allemaal visit ipv Visit en accept ipv Accept :)
Vergeet dan die URL maar die ik hierboven heb vernoemd ;)

  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
ACM heeft er een foutje in een voorbeeld in ontdekt :P
Ik ga me schamen ;) . Ik snap niet dat dit heeft kunnen gebeuren: de voorbeelden zijn afkomstig uit werkende code :o .
Glimi: Ik zal hem binnekort eens lezen. Aangezien ik nu onderwezen wordt door dhr Visser (OOMP) maakt het denk ik wel een goede indruk om daaruit te citeren :P
Haha :D . Jammer dat je nog niet aan Program Transformation toe bent ;) .

Het kan zijn dat iemand het al opgemerkt heeft, maar er is een sterke link tussen de Visitor aanpak en functioneel programmeren. Bij functioneel programmeren is het heel eenvoudig om een nieuwe operatie over een data structuur toe te voegen: je schrijft gewoon een nieuwe functie die op de data structuur werkt. Bij functioneel programmeren is het echter niet zo makkelijk een je data structuur uit te breiden: je moet dan alle functies, die erg verspreid kunnen zijn, gaan aanpassen. Je zit bij functioneel programmeren dus dat de afscheiding van de operatie zowel voor als nadelen heeft. Het Visitor pattern heeft precies deze voor- en nadelen: je kan makkelijk een nieuwe operatie implementeren in de vorm van een Visitor. Als er echter een nieuwe klasse in de data structuur komt, moet je in principe alle Visitor implementaties gaan aanpassen.

Het Visitor-Guide verhaal heeft een zeer sterke link met term-rewriting: bij term rewriting definieer je een set van regels om een structuur te herschrijven en definieer (of kies) je een strategie voor de toepassing van deze regels. Dit is te vergelijken met het Visitor-Guide verhaal: de Visitor is hierbij (indien mogelijk uiteraard) voor een groot deel onafhankelijk van de toegepaste Guide en kan daardoor in verschillende situaties worden toegepast.

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


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Alarmnummer: [double dispatch] Dat zie ik ook vaak in boeken staan en ik ben het niet eens met die uitspraak.

Maar een standaard visitor is nog steeds een single dispatch.
Je moet goed onderscheid maken tussen wat je nu eigenlijk bereikt met een visitor en hoe een visitor geimplementeerd is. Je bereikt met een visitor wel degelijk double dispatch, maar de implementatie gebruikt uiteraard alleen maar single dispatch. Dat laatste is logisch: het kan immers niet anders als de taal niet meer biedt.

[quote]dit is een voorbeeld van een double dispatched visitor. [/quote
Je vergeet er 1 bij op te tellen. Java biedt single-dispatch. Java dispatched echter niet op het eerste argument wat zichtbaar is in de Java code, maar op het impliciete 'this' argument. Er wordt dus helemaal niet gedispatched op argumenten die je zelf in Java code op neemt. Een double dispatch is dus een dispatch op het impliciete 'this' argument en slechts 1 'echt' argument. Wat jij dus laat zien vereist in feite meer dan double dispatch in een taal.

Een Visitor biedt de mogelijkheid tot double dispatch om er 2x gedispatched wordt: er wordt op de Visitor gedispatched in de visit aanroep van de data structuur. De Visitor is op dat moment het impliciete eerste argument van de visit methode aanroep. Er wordt op het eerste 'echte' argument gedispatched in de aanroep van de accept methode in de data structuur. Hierdoor bereik je dus via 2x single dispatch toepassing in een taal die niet meer biedt, double dispatching.
Hierin kies je op basis van 2 argumenten de juiste methode definitie. Dit is dus een echt double dispatch. (hij is zelfs nog symetrisch)
Je kiest hier dus op basis van drie argumenten een methode definitie.

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


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
whoami: ik zie het nut er wel van in, maar ik vraag me nu gewoon af: wanneer gebruik je best zo'n visitor.
De Visitor wordt over het algemeen ook vooral gebruikt in systemen die op een soort talen werken. Het maakt niet uit of er nu een echte syntax voor die taal is: en klasse structuur zou je ook kunnen zien als een taal. Visitor worden dus met name toegepast om operaties op abstract syntax trees te implementeren. Je kan dan uiteraard denken aan compilers en andere taal -> taal transformaties, maar ook aan pretty-printers, evaluators, optimizers, refactoring tools enz. Zodra je dus op een soort taal een operatie wilt uitdrukken kom je snel bij een Visitor terecht. Je ziet het daardoor ook denk ik niet zo vaak terug in 'normale' code.

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


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

Alarmnummer

-= Tja =-

mbravenboer schreef op 19 januari 2003 @ 00:50:
[...]

Je moet goed onderscheid maken tussen wat je nu eigenlijk bereikt met een visitor en hoe een visitor geimplementeerd is. Je bereikt met een visitor wel degelijk double dispatch, maar de implementatie gebruikt uiteraard alleen maar single dispatch. Dat laatste is logisch: het kan immers niet anders als de taal niet meer biedt.

Je vergeet er 1 bij op te tellen. Java biedt single-dispatch. Java dispatched echter niet op het eerste argument wat zichtbaar is in de Java code, maar op het impliciete 'this' argument. Er wordt dus helemaal niet gedispatched op argumenten die je zelf in Java code op neemt. Een double dispatch is dus een dispatch op het impliciete 'this' argument en slechts 1 'echt' argument. Wat jij dus laat zien vereist in feite meer dan double dispatch in een taal.
In java zit alleen single dispatch (runtime uitzoeken dus) op het impliciete 1e argument: this. Maar wat die visitor doet is op dat polymorphe mechanisme een adapter plaatsen zodat je die werking om kan zetten naar een 'normale' single dispatch.

polymorfe dispatch...
Java:
1
2
3
4
5
6
7
8
9
10
11
interface Fruit{
    void print();
}

class Appel: Fruit{
    void print(){System.out.println("appel");}
}

class Banaan: Fruit{
    void print(){System.out.println("banaan");}
}


een 'normale' dispatch

Java:
1
2
3
4
5
6
7
8
9
10
interface Fruit{}

class Appel: Fruit{}

class Banaan: Fruit()

class Printer{
    void print(Appel a){System.out.println("appel");
    void print(Banaan b){System.out.println("banaan");
}


Nu heb je dus een 'normale' single dispatch. Het probleem in java en de meeste andere oo talen is dat dit niet gaat werken. Wat die visitor doet is het mechanisme van de polymorfe dispatch omzetten naar een 'normale' dispatch en verder niets.

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
interface Fruit{
    void accepts(FruitVisitor v);
}

class Appel: Fruit{
    void accepts(FruitVisitor v){v.visit(this);)
}

class Banaan: Fruit{
    void accepts(FruitVisitor v)(v.visit(this);}
}

interface FruitVisitor{
    void visit(Appel a);
    void visit(Banaan b);
}

class Printer implements FruitVisitor{
    void visit(Appel a){System.out.println("appel");}
    void visit(Banaan b){System.out.println("banaan");}
}


In de printer class is op dit moment nog steeds sprake van een single dispatch en niet van een double.
Een Visitor biedt de mogelijkheid tot double dispatch om er 2x gedispatched wordt: er wordt op de Visitor gedispatched in de visit aanroep van de data structuur. De Visitor is op dat moment het impliciete eerste argument van de visit methode aanroep. Er wordt op het eerste 'echte' argument gedispatched in de aanroep van de accept methode in de data structuur. Hierdoor bereik je dus via 2x single dispatch toepassing in een taal die niet meer biedt, double dispatching.
Ons probleem zit hem denk ik op het feit dat jij een Visitor ook onderdeel vind van het dispatchen en ik niet. De visitor is voor mij een hulpmiddel om een aanroep voor elkaar te krijgen zoals je dat in functionele talen ook ziet (patterns) en het mechanisme zelf vind ik niet erg interessant.

[ Voor 4% gewijzigd door Alarmnummer op 19-01-2003 11:37 ]


  • mbravenboer
  • Registratie: Januari 2000
  • Laatst online: 06-11-2025
Alarmnummer: In de printer class is op dit moment nog steeds sprake van een single dispatch en niet van een double.
Uiteraard, Java support immers geen double dispatch. Het hele punt is vooral een woordenspel. Over het algemeen wordt het visitor pattern verbonden met double dispatching vanwege het 2x dispatchen op verschillende objecten die een rol spelen (de Visitor en het object in de data structuur). Hiermee wordt er een met double dispatching vergelijkbaar mechanisme bereikt (vergelijkbaar met een externe operatie implementatie (Visitor) in een taal met multi dispatch).
Ons probleem zit hem denk ik op het feit dat jij een Visitor ook onderdeel vind van het dispatchen en ik niet. De visitor is voor mij een hulpmiddel om een aanroep voor elkaar te krijgen zoals je dat in functionele talen ook ziet (patterns) en het mechanisme zelf vind ik niet erg interessant.
Wat jij wel en niet interessant vindt is natuurlijk volledig jouw zaak ;) . De Visitor is echter wel degelijk een belangrijk onderdeel van het dispatching mechanisme. Als je namelijk alleen op het object (Appel, Banaan) zelf wilt dispatchen heb je juist het probleem dat je de operatie in de data structuur op moet nemen. Dat wil je niet en daarom is er een Visitor toepassing nodig.

Dit introduceert echter een probleem omdat je niet simpelweg een methode op de Visitor kan aanroepen: er wordt niet gedispatched op het eerste 'echte' argument (Appel, Banaan). Om te dispatchen op dit eerst argument moet je wel via de data structuur werken en daarvoor zijn de accept methoden.

Je moet dus zelf weten hoe jij het wilt zien, maar het is een feit dat er in de hele opzet 2x een single dispatch plaats vindt en dat beide dispatches een cruciale rol spelen in de toepassing van het Visitor pattern.

[ Voor 1% gewijzigd door ACM op 19-01-2003 13:23 ]

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


  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Topicstarter
Nog een kleine status update :)
Java:
1
2
3
4
5
6
7
      HTMLForm htmlForm = new HTMLForm("", "", "post", "CreateAuthorAction");
      htmlForm.addChild(new HTMLInputElement("Name:", "text", "name", author.getName(), 50));
      htmlForm.addChild(new HTMLTextArea("Description:", "description", author.getDescription(), 4, 40));
      htmlForm.addChild(new HTMLSelectBox("Real person:", "realpersonid", null, a.getPersonList()));
      htmlForm.addChild(new HTMLInputElement("submit", "submit", "Create It"));

      out.append(htmlForm.getHTML());

Ter vervanging van:
Java:
1
2
3
4
5
6
7
8
9
      out.append("<form action='' method='post'>\n<table>\n");
      out.append("<tr><td>Name:</td><td><input type='text' name='name' value=\"\" size='50' class='text' /></td></tr>\n");
      out.append("<tr><td>Description:</td><td><textarea name='description' rows='4' cols='40'></textarea></td></tr>\n");
      out.append("<tr><td>Real person:</td><td>" + getSelectBox("realpersonid", a.getPersonList(), null) + "</td></tr>");
      out.append("<tr><td></td><td><input type='submit' name='submit' value='Create It' class='submit' /></td></tr>\n");
      out.append("</table>\n");
      out.append("<input type='hidden' name='action' value='CreateAuthorAction' />\n");
      out.append("<input type='hidden' name='submit' value='createit' />\n");
      out.append("</form>\n");

Toch wel iets netter, ietsepietsje minder flexibel, maar dat geeft verder niet zo :)
Dat is met strategisch overerven wel voor elkaar te krijgen, de HTMLForm is een subclass van HTMLComposite en de pattern-gebruikers kunnen dan wel raden wat voor Pattern daarvoor gebruikt is :P

Aantal regels tekst voor de OutputVisitor is hierdoor ook van 973 naar 904 gegaan, mede door het verplaatsen van stukjes functionaliteit.

En ik heb ook nog de VisitorAdapter toegepast, de VisitorGuide vind ik net iets te veel werk om nog te integreren, hoewel het zeker nuttig toegepast kan worden. De VRMLOutputVisitor kon daardoor van 594 naar 543 regels, waarbij alle verwijderde regels vrijwel duplicate methoden waren :o

* ACM heeft weeer een pattern en nog wat slimme pattern-trucjes kunnen verwerken, vindt de OO docent vast leuk ;)
En nu hou ik er echt mee op (denk ik), nou es documentatie schrijven (gelukkig is de Doclet heel lief, dat scheelt weer een stukje ;) )

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

Alarmnummer

-= Tja =-

Heb je trouwens hier al naar gekeken?

  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Topicstarter
Aargh... dat had me wel wat tijd bespaard, ik had wel het org.w3c.dom geheel gezien, maar dat van jou niet :)

Ik vind mijn variant wel mooier/prettiger werken denk ik :)

[ Voor 23% gewijzigd door ACM op 20-01-2003 12:31 ]


  • Glimi
  • Registratie: Augustus 2000
  • Niet online

Glimi

Designer Drugs

(overleden)
ACM schreef op 20 januari 2003 @ 12:27:
Aargh... dat had me wel wat tijd bespaard, ik had wel het org.w3c.dom geheel gezien, maar dat van jou niet :)

Zelfdoen is leerzamer voor school, ben je hopelijk toch wel met me eens :) Tevens had je dan wel je hele opdracht van de libs aan elkaar kunnen hangen :)

  • ACM
  • Registratie: Januari 2000
  • Niet online

ACM

Software Architect

Werkt hier

Topicstarter
Glimi schreef op 20 January 2003 @ 12:30:
Zelfdoen is leerzamer voor school, ben je hopelijk toch wel met me eens :) Tevens had je dan wel je hele opdracht van de libs aan elkaar kunnen hangen :)

Het devies wat die docent ons vrij snel leerde: Waarom het wiel opnieuw uitvinden. En: Programmeurs zijn lui.

:P

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

Alarmnummer

-= Tja =-

Een databinding framework voor HTML maken behoort niet echt tot de categorie ingewikkeld :)
Pagina: 1