【问题标题】:F# Command PatternF# 命令模式
【发布时间】:2011-05-05 14:18:58
【问题描述】:

我正在尝试实现命令模式来控制机器人。我正在使用它来探索如何在 F# 中实现命令模式。下面是我的实现:

type Walle(position, rotate) =     
    let (x:float,y:float) = position
    let rotation = rotate
    member this.Move(distance) =
        let x2 = distance * sin (System.Math.PI/180.0 * rotation)
        let y2 = distance * cos (System.Math.PI/180.0 * rotation)
        let newPosition = (x+x2, y+y2)
        Walle(newPosition, rotation)
    member this.Rotate(angle) = 
        let newRotation = 
            let nr = rotation + angle
            match nr with
            | n when n < 360.0 -> nr
            | _ -> nr - 360.0
        Walle(position, newRotation)

let Move distance = fun (w:Walle) -> w.Move(distance)
let Rotate degrees = fun (w:Walle) -> w.Rotate(degrees)

let remoteControl (commands:List<Walle->Walle>) robot = 
    commands |> List.fold(fun w c -> c w)

let testRobot() =
    let commands = [Move(10.0);Rotate(90.0);Move(16.0);Rotate(90.0);Move(5.0)]
    let init = Walle((0.0,0.0),0.0)
    remoteControl commands init

为了想出一个功能性的解决方案,我选择让机器人的动作在每次调用后在其新位置返回一个新的机器人实例(避免突变)。我还让命令函数关闭了执行操作所需的状态。

我很好奇人们在实现该模式时是否认为这些是好的设计决策?或者,如果人们可以就实施该模式提供任何其他建议?

【问题讨论】:

    标签: design-patterns f#


    【解决方案1】:

    为了避免采用“类型”中将数据与操作组合并将这种组合表示为“对象”的 OO 方式,我的 POV 中更实用的方法是在模块中分别定义数据和操作,如下所示:

    module Walle = 
     type Walle = {Position : float * float; Rotation : float}
    
     let Move distance (w:Walle) = 
        let x2 = distance * sin (System.Math.PI/180.0 * w.Rotation)        
        let y2 = distance * cos (System.Math.PI/180.0 * w.Rotation)
        {w with Position = (w.Position |> fst) + x2, (w.Position |> snd) + y2 }
    
     let Rotate angle (w:Walle) = 
        let newRotation = 
            let nr = w.Rotation + angle
            match nr with
            | n when n < 360.0 -> nr
            | _ -> nr - 360.0
        {w with Rotation = newRotation}
    

    现在您可以创建一个新的 Walle 并使用 |> 函数将其传递给一系列转换 Walle“数据”的函数。这完全是关于数据和对该数据的转换,没有对象:)。它可能不像命令模式,因为它更适合 OO 风格。您真的不需要 FP 中的模式,还是我们?

    【讨论】:

      【解决方案2】:

      对于机器人示例,我宁愿只使用命令式样式,即更改机器人对象的状态。因为机器人对象通常具有状态和改变状态的动作的概念。从 OO 设计的角度来看,某些类型的对象最好是不可变的,例如 .NET 中的 String、DateTime,但很多都不是。

      不可变对象当然有优势。在您问题的持久版本中,您可以保存机器人的所有过去状态,并且可以轻松地在机器人上使用UnDo 命令。

      【讨论】:

      • 我知道你希望 Walle 是可变的。
      猜你喜欢
      • 2012-04-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-24
      • 2013-05-18
      相关资源
      最近更新 更多