4 Stimmen

F# Befehlsmuster

Ich versuche, das Befehlsmuster zu implementieren, um einen Roboter zu steuern. Ich benutze dies, um zu erkunden, wie man das Befehlsmuster in F# implementiert. Unten ist meine Implementierung:

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:ListWalle>) 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 dem Bestreben, eine funktionale Lösung zu finden, habe ich mich entschieden, dass die Aktionen des Roboters nach jedem Aufruf eine neue Instanz des Roboters an seiner neuen Position zurückgeben sollen (Mutation vermeiden). Ich habe auch die Befehlsfunktionen so erstellt, dass sie den für die Ausführung der Aktionen erforderlichen Zustand schließen.

Ich war neugierig, ob die Leute denken, dass diese guten Designentscheidungen bei der Implementierung des Musters waren? Oder ob es irgendwelche anderen Ratschläge gibt, die die Leute bei der Implementierung des Musters geben könnten?

12voto

Ankur Punkte 32819

Um den OO-Weg zu vermeiden, Daten mit Operationen in einem "Typ" zu kombinieren und diese Kombination als "Objekt" darzustellen, wäre aus meiner Sicht ein funktionalerer Ansatz, Daten und Operationen separat in einem Modul zu definieren, wie unten gezeigt:

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}

Jetzt können Sie einen neuen Walle erstellen und die |> Funktion verwenden, um diesen an eine Reihe von Funktionen zu übergeben, die die "Daten" des Walles transformieren. Es geht nur um Daten und Transformation dieser Daten, keine Objekte :). Es mag sich vielleicht nicht wie ein Befehlsmuster anfühlen, da dieses eher für den OO-Stil geeignet ist. Brauchen wir wirklich Muster in der FP oder etwa doch?

1voto

Yin Zhu Punkte 16798

Für das Roboterbeispiel würde ich eher den imperativen Stil verwenden, d.h. die Zustände des Roboterobjekts ändern. Da ein Roboterobjekt normalerweise den Begriff von Zuständen und Aktionen zum Ändern der Zustände hat. Aus der OO-Design-Perspektive sind einige Arten von Objekten besser unveränderlich, z.B. String, DateTime in .NET, aber viele von ihnen sind es nicht.

Unveränderliche Objekte haben natürlich Vorteile. In der persistenten Version in Ihrer Frage können Sie alle vergangenen Zustände des Roboters speichern und können problemlos UnDo-Befehle auf dem Roboter ausführen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X