2009-05-03 146 views
4

我尝试使用OCaml的OO构造和类型系统实现访问者设计模式,并且在实例化元素时遇到问题。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;; 

我得到在编译时的错误是:

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

但是,代码编译减去行实例化salesman

如何在维护类的功能的同时实例化salesman?与呼叫接收到receiveEvaluation

编辑错误:

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

第二个目的类型没有方法visitAccountant

回答

4

编辑 - 分离在3个要点答案:初始编译错误的分辨率,递归解决方案和参数化解决方案的编译错误

需要注意的是你的代码的作品

分辨率在顶级罚款:

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

这种编译错误通常通过添加类型注释来帮助编译器确定类型来解决。由于顶级好心告诉我们它是什么,我们可以修改实例:

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

这个编译!

递归溶液

有可能通过使用递归类来降低复杂性。这完全消除了对参数化类的需求,但意味着所有对象都需要在同一个源文件中定义。

class virtual employee = 
object 
    method virtual receiveEvaluation:(hrrep -> unit) 
end 

and accountant = 
object(self) 
    inherit employee 
    method receiveEvaluation:(hrrep -> unit) = fun rep -> rep#visitAccountant (self :> accountant) 
end 

and salesman = 
object (self) 
    inherit employee 
    method receiveEvaluation:(hrrep -> unit) = fun rep -> rep#visitSalesman (self :> salesman) 
end 

and hrrep = 
object 
    method visitSalesman:(salesman -> unit) = fun s -> print_endline ("Visiting salesman") 
    method visitAccountant:(accountant -> unit) = fun s -> print_endline ("Visiting accountant") 
end 

let s = new salesman;; 
let e = (s :> employee);; 
let v = new hrrep;; 

e#receiveEvaluation v;; 

这打印“拜访推销员”。对员工的胁迫只是为了让这更接近现实世界的情景。

一个参数化的解决方案

再次在这个问题来看,我认为这是没有必要有一个参数化hrRep,因为在这一刻,所有其他类型是已知的。通过只是让员工类参数化的,我得到这个:

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;; 

这将返回:

参观推销员鲍勃

参观会计师一个玛丽

参观业务员苏。

这种解决方案的优点是员工不需要了解访问者,因此可以在他们自己的编译单元中定义l在添加新类型的员工时,转而使用更简洁的代码和更少的重新编译工作。

+0

似乎已经解决了编译问题,但是当我尝试使用对销售员对象中的函数的调用进行编译时,我得到了类似的问题。我如何去调用这个函数?再次感谢! – 2009-05-03 21:47:44