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.