2 Stimmen

Wie implementiert man einen Decorator mit nicht-lokaler Gleichheit?

Guten Tag, ich bin gerade dabei, eines meiner Programme zu überarbeiten und bin dabei auf ein interessantes Problem gestoßen.

Ich habe Übergänge in einem Automaten. Übergänge haben immer einen Startzustand und einen Endzustand. Einige Übergänge haben ein Label, das eine bestimmte Aktion kodiert, die bei der Durchquerung ausgeführt werden muss. Kein Label bedeutet keine Aktion. Einige Transitionen haben eine Bedingung, die erfüllt sein muss, um diese Bedingung zu durchlaufen. Wenn es keine Bedingung gibt, ist die Transition im Grunde eine Epsilon-Transition in einem NFA und wird durchlaufen, ohne ein Eingangssymbol zu verbrauchen.

Ich benötige die folgenden Operationen:

  • prüfen, ob der Übergang ein Etikett hat
  • dieses Etikett erhalten
  • eine Beschriftung zu einem Übergang hinzufügen
  • prüfen, ob der Übergang eine Bedingung hat
  • diese Bedingung erhalten
  • Prüfung auf Gleichheit

Nach den ersten fünf Punkten zu urteilen, klingt dies wie ein klarer Dekorator, mit einem Basisübergang und zwei Dekoratoren: Labeled und Condition. Dieser Ansatz hat jedoch ein Problem: zwei Übergänge werden als gleich angesehen, wenn ihr Start- und Endzustand gleich sind, die Beschriftungen an beiden Übergängen gleich sind (oder nicht existieren) und beide Bedingungen gleich sind (oder nicht existieren). Mit einem Dekorator könnte ich zwei Übergänge Labeled("foo", Conditional("bar", Transition("baz", "qux"))) und Conditional("bar", Labeled("foo", Transition("baz", "qux"))) haben, die eine nicht-lokale Gleichheit benötigen, d.h. die Dekoratoren müssten alle Daten sammeln und die Transition muss diese gesammelten Daten auf einer Set-Basis vergleichen:

class Transition(object):
    def __init__(self, start, end):
        self.start = start
        self.end = end
    def get_label(self):
        return None
    def has_label(self):
        return False
    def collect_decorations(self, decorations):
        return decorations
    def internal_equality(self, my_decorations, other):
        try:
            return (self.start == other.start
                    and self.end == other.end
                    and my_decorations = other.collect_decorations())
    def __eq__(self, other):
        return self.internal_equality(self.collect_decorations({}), other)

class Labeled(object):
    def __init__(self, label, base):
        self.base = base
        self.label = label
    def has_label(self):
        return True
    def get_label(self):
        return self.label
    def collect_decorations(self, decorations):
        assert 'label' not in decorations
        decorations['label'] = self.label
        return self.base.collect_decorations(decorations)
    def __getattr__(self, attribute):
        return self.base.__getattr(attribute)

Ist dies ein sauberer Ansatz? Übersehe ich etwas?

Ich bin vor allem verwirrt, weil ich dies - mit längeren Klassennamen - durch kooperative Mehrfachvererbung lösen kann:

class Transition(object):
    def __init__(self, **kwargs):
        # init is pythons MI-madness ;-)
        super(Transition, self).__init__(**kwargs)
        self.start = kwargs['start']
        self.end = kwargs['end']
    def get_label(self):
        return None
    def get_condition(self):
        return None
    def __eq__(self, other):
        try:
            return self.start == other.start and self.end == other.end
        except AttributeError:
            return False

class LabeledTransition(Transition):
    def __init__(self, **kwargs):
        super(LabeledTransition).__init__(**kwargs)
        self.label = kwargs['label']
    def get_label(self):
        return self.label
    def __eq__(self):
        super_result = super(LabeledTransition, self).__eq__(other)
        try:
            return super_result and self.label == other.label
        except AttributeError:
            return False

class ConditionalTransition(Transition):
    def __init__(self, **kwargs):
        super(ConditionalTransition, self).__init__(**kwargs)
        self.condition = kwargs['condition']

    def get_condition(self):
        return self.condition

    def __eq__(self, other):
        super_result = super(ConditionalTransition, self).__eq__(other)
        try:
            return super_result and self.condition = other.condition
        except AttributeError:
            return False

# ConditionalTransition about the same, with get_condition
class LabeledConditionalTransition(LabeledTransition, ConditionalTransition):
    pass

Die Klasse LabledConditionalTransition verhält sich genau wie erwartet - und dass sie keinen Code enthält, ist ansprechend, und ich glaube nicht, dass MI bei dieser Größe verwirrend ist.

Natürlich wäre die dritte Möglichkeit, einfach alles in eine einzige Übergangsklasse mit einer Reihe von in has_label/has_transition zu hämmern.

Also... ich bin verwirrt. Übersehe ich etwas? Welche Implementierung sieht besser aus? Wie gehen Sie mit ähnlichen Fällen um, d.h. mit Objekten, die so aussehen, als ob ein Decorator sie behandeln könnte, aber dann kommt so eine nicht-lokale Methode daher?

EDIT : Hinzufügen der ConditionalTransition-Klasse. Im Grunde verhält sich diese Klasse wie der Dekorator, abzüglich der Reihenfolge, die durch die Reihenfolge der Erstellung der Dekoratoren entsteht. Der Übergang prüft, ob Start und Ende korrekt sind, die LabeledTransition-Klasse prüft, ob das Label korrekt ist und ConditionalTransition prüft, ob die Bedingung korrekt ist.

2voto

Florian Bösch Punkte 26702

Ich denke, es ist klar, dass niemand Ihre Frage wirklich versteht. Ich würde vorschlagen, sie in den Kontext zu stellen und kürzer zu formulieren. Als Beispiel, hier ist eine mögliche Implementierung des Zustandsmusters in Python, bitte studieren Sie es, um eine Idee zu bekommen.

class State(object):
    def __init__(self, name):
        self.name = name

    def __repr__(self):
        return self.name

class Automaton(object):
    def __init__(self, instance, start):
        self._state = start
        self.transitions = instance.transitions()

    def get_state(self):
        return self._state

    def set_state(self, target):
        transition = self.transitions.get((self.state, target))
        if transition:
            action, condition = transition
            if condition:
                if condition():
                    if action:
                        action()
                    self._state = target
            else:
                self._state = target
        else:
            self._state = target

    state = property(get_state, set_state)

class Door(object):
    open = State('open')
    closed = State('closed')

    def __init__(self, blocked=False):
        self.blocked = blocked

    def close(self):
        print 'closing door'

    def do_open(self):
        print 'opening door'

    def not_blocked(self):
        return not self.blocked

    def transitions(self):
        return {
            (self.open, self.closed):(self.close, self.not_blocked),
            (self.closed, self.open):(self.do_open, self.not_blocked),
        }

if __name__ == '__main__':
    door = Door()
    automaton = Automaton(door, door.open)

    print 'door is', automaton.state
    automaton.state = door.closed
    print 'door is', automaton.state
    automaton.state = door.open
    print 'door is', automaton.state
    door.blocked = True
    automaton.state = door.closed
    print 'door is', automaton.state

wäre die Ausgabe dieses Programms:

door is open
closing door
door is closed
opening door
door is open
door is open

0voto

Mark Roddy Punkte 25266

Aus dem Code, der gepostet wurde, ist der einzige Unterschied zwischen Transition und Labeled Transition die Rückgabe von get_lable() und has_label(). In diesem Fall können Sie diese beiden in einer einzigen Klasse zusammenfassen, die ein Label-Attribut auf None setzt und

return self.label is not None

in der Funktion has_label().

Können Sie den Code für die ConditionalTransition Klasse? Ich denke, das würde die Sache klarer machen.

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