/* Parametric type version In CSC123 there are several kinds of students: undergraduate graduate nilstudent weirdstudent Each kind of student has different attributtes, and needs to be treated differently. I want to write code that: A. abstracts away the differences between types of students, and B. abstracts away the different algorithms that operates on a list of students. The student is the visitee, and each visitor implements some operation on a polymorphic list of students. */ using System; public interface student { T accept(svisitor v); } public interface svisitor { T visit(undergrad u); T visit(grad g); T visit(nilstudent n); } public class stubase // base class for student linked list { public string name; public double gpa; public student next; // note type is 'student' not stubase. } // concrete visitee classes: no method other than *accept* implemented: public class undergrad : stubase, student { public string major; public undergrad(string n, string m, double g, student s) { name=n; major=m; gpa=g; next = s;} public T accept(svisitor v) { return v.visit(this);} } public class grad : stubase, student { public string thesistopic; public grad(string n, string t, double g, student s) { name=n; gpa=g; thesistopic=t; next=s;} public T accept(svisitor v) { return v.visit(this);} } public class nilstudent : student { public T accept(svisitor v) { return v.visit(this);} } ////////// Concrete visitee classes: /////////////// public class printer : svisitor // prints info for each student { public object visit(undergrad x) { Console.WriteLine("undergraduate "+x.name+" majors in "+x.major); return x.next.accept(this); } public object visit(grad g) { Console.WriteLine("graduate student "+g.name+" has thesis topic "+g.thesistopic); return g.next.accept(this); } public object visit(nilstudent n) { return null; } }//printer // visitor that looks for students on academic probation public class probationchecker : svisitor { public object visit(undergrad x) { if (x.gpa<2.0) Console.WriteLine(x.name+" is on probation"); return x.next.accept(this); } public object visit(grad x) { if (x.gpa<3.0) Console.WriteLine(x.name+" is on probation"); return x.next.accept(this); } public object visit(nilstudent n) { return null; } } //probationchecker visitor // visitor with internal state: finds average gpa of grads and undergrads, // prints info when done, returns average of all students. public class gpaaverager : svisitor { double usum = 0; // undergraduate gpa sum double gsum = 0; // graduate gpa sum int ucount = 0; // number of undergrads int gcount = 0; // number of grads public double visit(undergrad u) { ucount++; usum += u.gpa; return u.next.accept(this); } public double visit(grad u) { gcount++; gsum += u.gpa; return u.next.accept(this); } public double visit(nilstudent n) { double uave = usum/ucount; // hope it doesn't divide by zero! double gave = gsum/gcount; Console.WriteLine("undergrad gpa average is "+uave); Console.WriteLine("graduage gpa average is "+gave); return (usum+gsum)/(ucount+gcount); } }// gpaaverager ////////////////// weirdstudents public class csc123b { public static nilstudent NIL = new nilstudent(); public static void Main() { student d = new undergrad("john","cs",1.8,NIL); student c = new grad("jane","lambda calculus",3.7,d); student b = new grad("bernie","machine learning",2.8,c); student a = new undergrad("herman","math",4.0,b); // a now points to first cell of a linked list. a.accept(new printer()); a.accept(new probationchecker()); double x = a.accept(new gpaaverager()); Console.WriteLine("overall gpa average is "+x); // weirdstudents student e = new weirdstudent("sam",100,0.2,a); // no object adaptor needed, but ... } } public interface estudent : student { T accept(evisitor v); } public interface evisitor : svisitor { T visit(weirdstudent x); } public class weirdstudent : stubase, student { public int weirdness; // weirdness rating public weirdstudent(string n, int t, double g, student s) { name=n; gpa=g; weirdness=t; next=s;} public T accept(svisitor v) { if (v is evisitor) return ((evisitor)v).visit(this); else throw new Exception("only evisitors can visit weirdstudents"); } } // Objects can nolonger decide for themselves!!! I need ifelse again!