2009-05-03 4 views
4

OCaml의 객체 지향 구조 및 유형 시스템을 사용하여 방문자 디자인 패턴을 구현하려고 시도 중이고 요소의 인스턴스 생성시 문제가 발생했습니다.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 : (< 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;; 

"Visiting Salesman"이 출력됩니다. 종업원에 대한 강제는 현실 세계 시나리오에 더 가깝도록 만드는 것입니다.

지표화 된 솔루션

다시 문제를 보면,이 순간에, 모든 다른 종류가 알려져 있기 때문에하는 매개 변수화 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;; 

이 반환 :

방문 판매원 S 밥

방문 회계사 메리

방문 판매원 S 고소

단지 직원 클래스는 매개 변수화함으로써,이 수 이 솔루션의 장점은 직원이 방문자에 대해 알 필요가 없으므로 직원이 자신의 편집 단위로 정의 할 수 있다는 것입니다. 새로운 유형의 직원을 추가 할 때 코드를보다 깨끗하게 처리하고 재 컴파일을 줄이는 것이 중요합니다.

+0

그 컴파일 문제를 해결 한 것,하지만 필자는 세일즈맨 개체의 함수 호출로 컴파일하려고하면 비슷한 문제가 발생합니다. 함수를 호출하려면 어떻게해야합니까? 다시 한 번 감사드립니다! –

+0

이 문제를 이해하고 있는지 확실하지 않습니다. 코드를 게시 할 수 있습니까? 또한 재귀 적 정의를 사용하여보다 단순하지만 (제한적 임에도 불구하고) 솔루션을 추가했습니다. 희망이 도움이됩니다! –

+0

너의 것은 똑같은 일을 성취하기위한 훨씬 더 우아한 해결책이다. 개정 본에서 요청한 내용을 수행 할 수있는 방법이 있다고 생각되면 원래 질문의 코드를 업데이트했습니다. –