// visitor pattern example using System; // visitees : subclasses of student: undergrad, grad, nostudent, linked list // svisitors : gpa averager, printer, etc ... public interface svisitor { object visitu(undergrad u); object visitg(grad g); object visitn(nostudent n); } public interface visitee { object accept(svisitor v); } //// svisitor classes: // superclass student public class stubase { public string name; public double gpa; public int tuition; public stubase(string n, double g) {name=n; gpa=g;} public visitee next; // next student in linked list. } // only subclasses are visitees public class undergrad : stubase, visitee { public string major; public int year; // class year public undergrad(string n, double g, string m, int y) : base(n,g) { major = m; year = y; tuition = 20000; } // now for the all-important accept: public object accept(svisitor v) { return v.visitu(this); } }//undergrad public class grad : stubase, visitee { public string topic; public string advisor; public grad(string n, double g, string t, string a) : base(n,g) { topic = t; advisor = a; tuition = 25000; } public object accept(svisitor v) { return v.visitg(this); } }// grad public class nostudent : visitee { public object accept(svisitor v) { return v.visitn(this); } } // Question, why isn't accept in the stubase superclass? // Because "this" will refer to a stubase, not a subclass, stubase // cannot be visited. ////////// svisitors // print visitor prints info public class printer : svisitor { public object visitu(undergrad u) { Console.WriteLine("undergrad "+u.name+" graduates in "+u.year); return u.next.accept(this); // visit next student } public object visitg(grad g) { Console.WriteLine("grad student "+g.name+" has advisor "+g.advisor); return g.next.accept(this); } public object visitn(nostudent n) { Console.WriteLine("--- that's all the students ---"); return null; } }//printer visitor ///// gpa averager visitor : prints average gpa's of all grads and undergrads public class gpatracker : svisitor { double ggpa = 0; // gpa of grads int grads = 0; // number of grads double ugpa = 0; // gpa of undergrads int ugrads = 0; // number of undergrads public object visitu(undergrad u) { ugpa += u.gpa; ugrads++; return u.next.accept(this); } public object visitg(grad g) { ggpa += g.gpa; grads++; return g.next.accept(this); } public object visitn(nostudent n) { Console.WriteLine("Average undergrad gpa is "+ugpa/ugrads); Console.WriteLine("Average grad gpa is "+ggpa/grads); return null; } }//gpatracker ///// add and return tuition: CS NIRVANA! //// Now for something weird: public interface evisitor : svisitor // extended visitor { object visitw(weirdstudent w); } public class weirdstudent : stubase // does not implement visitee { public int weirdness; // weirdness ranking public weirdstudent(string n, double g, int w) : base(n,g) {weirdness=w;} public object accept(evisitor ev) {return ev.visitw(this);} // cannot accept svisitor, only evisitor } // visitor to return name of weirdest student public class weirdestvisitor : evisitor { int w = 0; string name = "nobody"; public object visitu(undergrad u) { return u.next.accept(this); } public object visitg(grad g) { return g.next.accept(this); } public object visitw(weirdstudent ws) { if (ws.weirdness>w) { w = ws.weirdness; name = ws.name; } return ws.next.accept(this); } public object visitn(nostudent n) { return name; } } // problem: weirdstudent does not implement visitee, can't integrate into // regular linked list // Need: object adapter that adapts a weirdstudent to a visitee: public class adapter : visitee { weirdstudent w; public adapter(weirdstudent a) {w=a;} public object accept(svisitor v) {return ((evisitor)v).visitw(w);} } /////////// main public class students08 { public static void Main() { undergrad a = new undergrad("john",2.1,"math",2010); undergrad b = new undergrad("jane",3.1,"cs",2009); grad c = new grad("fred",3.5,"lambda calc","liang"); grad d = new grad("mary",3.2,"algorithms","krish"); a.next = d; d.next = b; b.next = c; c.next = new nostudent(); a.accept(new printer()); a.accept(new gpatracker()); weirdstudent w1 = new weirdstudent("larz",5.6,80); weirdstudent w2 = new weirdstudent("narx",1.1,10000); w1.next = new adapter(w2); w2.next = a; string wname = (string)w1.accept(new weirdestvisitor()); Console.WriteLine("the weirdest student is "+wname); } //main }