2009-05-03 1 views
4

Я пытаюсь внедрить шаблон дизайна посетителя с использованием OCO OCO и создавать систему типов, и я сталкиваюсь с проблемами при создании элемента.Шаблон проектирования посетителей в 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

EDIT - раздельное ответ в 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;; 

Отпечатано "Посещение продавца". Принуждение к сотруднику - это просто приблизиться к сценарию реального мира.

параметризованным раствор

Глядя на эту проблему еще раз, я думаю, что не нужно иметь параметризованным Контролер, потому что в этот момент все другие типы известны.По только что делает класс сотрудника параметризованных, я получаю это:

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

Это возвращает:

Посещение коммивояжер S Bob

Посещение бухгалтер Мэри

Посещение коммивояжер S Сью

Преимущество этого решения заключается в том, что сотрудники не должны знать о посетителе и поэтому могут быть определены в своих собственных единицах компиляции, l eading для более чистого кода и меньше перекомпиляции, чтобы делать это при добавлении новых типов сотрудников.

+0

Это, похоже, разрешило проблему с компиляцией, но я получаю подобный, когда пытаюсь скомпилировать с вызовом функции в объекте продавца. Как мне обратиться к функции? Еще раз спасибо! –

+0

Не уверен, что я понимаю эту проблему, можете ли вы опубликовать код? Кроме того, я добавил, надеюсь, более простое (хотя и ограниченное) решение с использованием рекурсивных определений. Надеюсь, поможет! –

+0

Это гораздо более элегантное решение для достижения того же. Я обновил код в исходном вопросе, если вы считаете, что есть способ выполнить то, что я спросил в ревизии. –