简体   繁体   English

F# 命令模式

[英]F# Command Pattern

I am trying to implement the command pattern to control a robot.我正在尝试实现命令模式来控制机器人。 I'm using this to explore how to implement the command pattern in F#.我正在使用它来探索如何在 F# 中实现命令模式。 Below is my implementation:下面是我的实现:

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

In an effort to come up with a functional solution I chose to make the robot's actions return a new instance of the robot at its new position after each invocation (avoid mutation).为了提出一个功能解决方案,我选择让机器人的动作在每次调用后在其新的 position 处返回一个机器人的新实例(避免突变)。 I also made the command functions close over the state required to execute the actions.我还关闭了执行操作所需的 state 的命令功能。

I was curious whether people feel these were good design decisions when implementing the pattern?我很好奇人们在实现该模式时是否认为这些是好的设计决策? Or, if there was any other advice people could give on implementing the pattern?或者,如果人们可以就实施该模式提供任何其他建议?

To avoid going the OO way of combining data with operations in a "type" and representing this combination as "Object", a more functional approach in my POV would be to define data and operations separately in a module as shown below:为了避免采用 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}

Now you can create a new Walle and use |> function to pass that to a series of functions that transform the Walle "data".现在您可以创建一个新的 Walle 并使用 |> function 将其传递给一系列转换 Walle“数据”的函数。 It is all about data and transformation on that data, no objects:).这完全是关于数据和该数据的转换,没有对象:)。 It may not feel like a command pattern as that is more suited for OO style.它可能不像命令模式,因为它更适合 OO 风格。 You really don't need patterns in FP or do we?您真的不需要 FP 中的模式,还是我们?

For the robot example, I would rather just use the imperative style, ie changing the states of the robot object.对于机器人示例,我宁愿只使用命令式样式,即更改机器人 object 的状态。 Because a robot object usually has the notion of states and actions to change the states.因为机器人 object 通常有状态的概念和改变状态的动作。 From the OO design perspective, some kinds of objects are better to be immutable, eg, String, DateTime in .NET, but a lot of them are not.从OO设计的角度来看,有些对象最好是不可变的,例如.NET中的String、DateTime,但很多都不是。

Immutable objects of course have advantages.不可变对象当然有优势。 In the persistent version in your question, you can save all the past states of the robot and can easily UnDo commands on the robot.在您问题的持久版本中,您可以保存机器人的所有过去状态,并且可以轻松地UnDo机器人上的命令。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM