2009-05-03 16 views
4

Ich versuche, das Visitor Design Pattern mit OCamls OO-Konstrukten und dem Typsystem zu implementieren und stoße bei der Instantiierung eines Elements auf Probleme.Visitor Design Pattern in OCaml

class virtual ['hrRep] employee = object 
method virtual receiveEvaluation : 'hrRep -> unit 
method virtual getName : string 
end;; 

class ['hrRep] accountant myName = object (self : 'a) 
inherit ['hrRep]employee 
val name = myName 
method receiveEvaluation rep = rep#visitAccountant self 
method getName = name 
end;; 

class ['hrRep] salesman myName = object (self : 'a) 
inherit ['hrRep]employee 
val name = myName 
method receiveEvaluation rep = rep#visitSalesman self 
method getName = name 
end;; 

class virtual ['accountant, 'salesman] hrRep = object (self) 
method virtual visitSalesman : 'salesman -> unit 
method virtual visitAccountant : 'accountant -> unit 
end;; 

class ['employee, 'salesman] lowerLevelHRRep = 
     object (self) inherit ['employee, 'salesman]hrRep 
method visitSalesman s = print_endline ("Visiting salesman "^s#getName) 
method visitAccountant a = 
     print_endline ("Visiting accountant "^a#getName) 
end;; 

let s1 : (<visitSalesman : 'a -> unit>) salesman = new salesman "Bob";; 
let a1 : (<visitAccountant : 'a -> unit>) accountant = new accountant "Mary";; 
let s2 : (<visitSalesman : 'a -> unit>) salesman = new salesman "Sue";; 
let h1 : (<getName : string>, <getName : string>) lowerLevelHRRep = new lowerLevelHRRep;; 

s1#receiveEvaluation h1;; 

Der Fehler, den ich bei der Zusammenstellung erhalten ist:

The type of this expression, <visitSalesman : 'a -> unit; _.. > salesman as 'a, 
contains type variables that cannot be generalized. 

jedoch kompiliert den Code minus der Linie, die die salesman Instanziierung.

Wie gehe ich bei der Instanziierung der salesman unter Beibehaltung der Funktionalität der Klassen?

bearbeiten Fehler empfangen mit Aufruf receiveEvaluation:

This expression has type (<getName:string>, < getName:string>) lowerLevelHRRep 
but is here used with type <visitSalesman : 'a salesman -> unit > as 'a. 

Der zweite Objekttyp hat keine Methode visitAccountant.

Antwort

4

EDIT - Getrennt die Antwort in drei Hauptpunkte: die Auflösung des anfänglichen Übersetzungsfehler, eine rekursive Lösung, und eine parametrisierte Lösung

Auflösung des Kompilierungsfehler

Beachten Sie, dass Ihr Code funktioniert fein in der obersten Ebene:

# let s = new salesman();; 
val s : < visitSalesman : 'a -> unit; _.. > salesman as 'a = <obj> 

Diese Art von Compiler-Fehler wird durch Hinzufügen einer Typanmerkung zu helfen, den Compiler herauszufinden, die Art, die allgemein gelöst. Als die oberste Ebene uns freundlicherweise mitgeteilt hat, was es war, können wir die Instanziierung ändern:

let s : (< visitSalesman : 'a -> unit>) salesman = new salesman();; 

Und das kompiliert!

Eine rekursive Lösung

Es ist möglich, die Komplexität zu reduzieren, indem rekursiven Klassen. Damit entfällt die Notwendigkeit für parametrisierte Klassen, aber alle Objekte müssen in derselben Quelldatei definiert werden.

Dies druckt "Besuchsverkäufer". Der Zwang zum Mitarbeiter ist nur, um dies einem realen Szenario näher zu bringen.

Eine parametrisierte Lösung

wieder auf das Problem der Suche, ich glaube es nicht notwendig ist, eine parametrisierte hrRep zu haben, denn in diesem Moment alle anderen Arten bekannt sind.Mit nur die Mitarbeiter-Klasse machen parametrisiert, bekomme ich diese:

class virtual ['a] employee = 
object 
    method virtual receiveEvaluation : 'a -> unit 
    method virtual getName : string 
end 

class ['a] accountant name = 
object(self) 
    inherit ['a] employee 
    val name = name 
    method receiveEvaluation rep = rep#visitAccountant self 
    method getName = "A "^name 
end 

class ['a] salesman name = 
object(self) 
    inherit ['a] employee 
    val name = name 
    method receiveEvaluation rep = rep#visitSalesman self 
    method getName = "S "^name 
end 

class virtual hrRep = 
object 
    method virtual visitAccountant : hrRep accountant -> unit 
    method virtual visitSalesman : hrRep salesman -> unit 
end 

class lowerLevelHRRep = 
object 
    inherit hrRep 
    method visitAccountant a = print_endline ("Visiting accountant "^a#getName) 
    method visitSalesman s = print_endline ("Visiting salesman "^s#getName) 
end;; 

let bob = new salesman "Bob";; 
let mary = new accountant "Mary";; 
let sue = new salesman "Sue";; 
let h = new lowerLevelHRRep;; 
bob#receiveEvaluation h;; 
mary#receiveEvaluation h;; 
sue#receiveEvaluation h;; 

Das gibt:

Besuch Verkäufer S Bob

Besuch accountant A Mary

Besuch Verkäufer S Sue

Der Vorteil dieser Lösung besteht darin, dass die Mitarbeiter den Besucher nicht kennen müssen und daher in ihren eigenen Kompilierungseinheiten definiert werden können, l Einführen zu sauberer Code und weniger Neukompilierung beim Hinzufügen neuer Arten von Mitarbeitern.

+0

Das scheint Kompilierung Problem gelöst haben, aber ich bekomme eine ähnliche, wenn ich versuche, mit einem Aufruf einer Funktion in einem Verkäufer-Objekt zu kompilieren. Wie gehe ich vor, um die Funktion aufzurufen? Danke noch einmal! –

+0

Nicht sicher, dass ich dieses Problem verstehe, können Sie etwas Code posten? Außerdem fügte ich eine hoffentlich einfachere (wenn auch begrenzte) Lösung mit rekursiven Definitionen hinzu. Ich hoffe es hilft! –

+0

Ihre ist eine viel elegantere Lösung, um das Gleiche zu erreichen. Ich habe den Code in der ursprünglichen Frage aktualisiert, wenn Sie glauben, dass es ein Mittel gibt, um das zu erreichen, was ich in der Überarbeitung gefordert habe. –