2017-06-20 4 views
3

Angenommen, ich den folgenden CodeAufrufen von Methoden mit Klasseninstanz als Argument in Python

IDLE = 0 
STARTED = 1 
STOPPED = 2 
ERRORED = 3 
# additional states as needed 

class StateMachine: 
    def __init__(self) 
     self.state = IDLE 

    def start(self): 
     self.state = STARTED 
     # do something 

    def stop(self): 
     self.state = STOPPED 
     # do something 

    def reset(self): 
     self.state = IDLE 
     # do something 

Unsere aktuellen Schnittstelle ermöglicht es einem Client zu ändern, um den Zustand einer Instanz durch Angabe der gewünschten Soll-Zustand haben, an welchem ​​Punkt wir laufen bestimmte Validierungsprüfungen und dann die entsprechende Methode. Ich würde im Idealfall eine Wörterbuchzuordnung des gewünschten Zielzustands auf die richtige Methode beibehalten, um massive und sinnlose if-Anweisungsblöcke zu vermeiden. das heißt

if target_state = STARTED: 
    instance.start() 
elif target_state = STOPPED: 
    instance.stop() 
... 

Aber ich bin unsicher, ob die folgende Lösung gute Praxis betrachtet wird oder nicht (es fühlt sich ein bisschen whacky calling Methoden aus der Klasse eine Instanz als arg verwenden).

state_mapping = { 
    IDLE: StateMachine.reset, 
    STARTED: StateMachine.start, 
    .... 
} 

Und dann mit Aufruf:

action = state_mapping[target_state] 
action(instance) 
.... 

Irgendwelche Gedanken?

+0

Haben Sie versucht, einen 'Lambda'-Ausdruck zu verwenden? –

+0

Ich denke, die Lösung ist in Ordnung. Vielleicht sollten Sie eine Bankmarkierung in Betracht ziehen, um zu sehen, was zeiteffizienter ist. Wenn Effizienz keine Rolle spielt, sollte Lesbarkeit sein. Ich persönlich finde die 'if'' elif' 'elif'' else' Lösung lesbarer - auch wenn es ein wenig langweilig ist. – ChickenFeet

+0

Es gibt einen weiteren Ansatz, bei dem es sich um eine StateMachine-Klasse und dann um eine Unterklasse für jeden Status handelt, z. B. StateMachineStopped, StateMachineStarted. Sie wechseln mit einem 'self .__ class__ = StateMachineStopped'-Ansatz hin und her. Siehe https://stackoverflow.com/questions/13280680/how-dangerous-is-setting-self-class-to-something-else/24643654#24463654 zum Beispiel. –

Antwort

2

Nicht so whacky.

Allerdings ist zu beachten, dass action eine ungebundene Methode ist, die auf den ersten Blick des Methodenaufrufs nicht sehr offensichtlich ist; außer dass ich aus erster Hand weiß, wie dieses Wörterbuch definiert ist.

Ich denke, eine besser lesbare Alternative ist, das Verfahren aus dem Beispiel zu nennen:

state_mapping = { 
    IDLE: "reset", 
    STARTED: "start", 
    .... 
} 

action = state_mapping[target_state] 
getattr(instance, action)() 

Dies wird gleichermaßen Lesbarkeit im Fall verbessern, wenn das Verfahren mehr als ein Argument nimmt.

+0

Danke für die Rückmeldung, ich hatte darüber nachgedacht, aber auch etwas zwischen den beiden Ansätzen hin und her gerissen. Nun, da Sie die Lesbarkeit erwähnt haben, ist es sinnvoll, stattdessen "getattr" zu verwenden. – Andy

1

Eine andere Alternative.

Da Ihre Klasse "StateMachine" heißt, sollte sie vielleicht eine Methode haben, die Statusänderung auszuführen?

In diesem Fall können Sie gebundene Methoden in Ihrer Karte

class StateMachine: 
    ... 

    def ChangeState(self, target): 
     state_mapping = { IDLE: self.reset, STARTED: self.start, ... } 
     state_mapping[target]() 

verwenden Sie möchten mit ungültigen Zielzustände behandeln, oder lass es einfach eine KEY_ERROR Ausnahme ausgelöst.

Verwandte Themen