代码之家  ›  专栏  ›  技术社区  ›  Mat Kelly

OCaml中的访问者设计模式

  •  4
  • Mat Kelly  · 技术社区  · 17 年前

    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 .

    如何实例化 推销员 同时保持类的功能?

    编辑

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

    第二个对象类型没有方法 visitAccountant .

    1 回复  |  直到 10 年前
        1
  •  4
  •   small_duck    17 年前

    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

    拜访会计师A Mary

    拜访推销员S Sue

    这种解决方案的优点是,员工不需要知道访问者,因此可以在自己的编译单元中定义,从而在添加新类型的员工时可以更清晰地编写代码,减少重新编译的工作量。

    推荐文章