UML-Zustandsmaschine - UML state machine

Die UML-Zustandsmaschine , auch als UML-Zustandsdiagramm bekannt , ist eine Erweiterung des mathematischen Konzepts eines endlichen Automaten in Informatikanwendungen, wie es in der UML-Notation ( Unified Modeling Language ) ausgedrückt wird .

Bei den dahinter stehenden Konzepten geht es darum, die Funktionsweise eines Geräts, eines Computerprogramms oder eines anderen (häufig technischen) Prozesses so zu organisieren, dass sich eine Entität oder jede ihrer Unterentitäten immer genau in einem von mehreren möglichen Zuständen befindet und wo es gute gibt -definierte bedingte Übergänge zwischen diesen Zuständen.

Die UML-Zustandsmaschine ist eine objektbasierte Variante des Harel-Zustandsdiagramms , die von UML angepasst und erweitert wird. Das Ziel von UML - Zustandsmaschinen, ist es, die wichtigsten Einschränkungen der traditionellen zu überwinden Finite-State - Maschinen , während ihre Hauptvorteile erhalten bleiben. UML-Zustandsdiagramme führen die neuen Konzepte hierarchisch verschachtelter Zustände und orthogonaler Regionen ein und erweitern gleichzeitig den Begriff der Aktionen . UML-Zustandsmaschinen haben die Eigenschaften von Mealy-Maschinen und Moore-Maschinen . Sie unterstützen Aktionen , die sowohl vom Status des Systems als auch vom auslösenden Ereignis abhängen , wie bei Mealy-Maschinen, sowie Eintritts- und Austrittsaktionen , die eher Zuständen als Übergängen zugeordnet sind, wie bei Moore-Maschinen.

Der Begriff "UML-Zustandsmaschine" kann sich auf zwei Arten von Zustandsmaschinen beziehen: Verhaltenszustandsmaschinen und Protokollzustandsmaschinen . Verhaltenszustandsmaschinen können verwendet werden, um das Verhalten einzelner Entitäten (z. B. Klasseninstanzen), eines Subsystems, eines Pakets oder sogar eines gesamten Systems zu modellieren. Protokollstatusautomaten werden zum Ausdrücken von Verwendungsprotokollen verwendet und können zum Festlegen der zulässigen Verwendungsszenarien von Klassifizierern, Schnittstellen und Ports verwendet werden.

Grundlegende Zustandsmaschinenkonzepte

Viele Softwaresysteme sind ereignisgesteuert , was bedeutet, dass sie kontinuierlich auf das Auftreten eines externen oder internen Ereignisses warten, z. B. einen Mausklick, einen Tastendruck, einen Zeitstrich oder das Eintreffen eines Datenpakets. Nach dem Erkennen des Ereignisses reagieren solche Systeme, indem sie die entsprechende Berechnung durchführen, die das Manipulieren der Hardware oder das Erzeugen von "weichen" Ereignissen umfassen kann, die andere interne Softwarekomponenten auslösen. (Aus diesem Grund werden ereignisgesteuerte Systeme alternativ als reaktive Systeme bezeichnet .) Sobald die Ereignisbehandlung abgeschlossen ist, wartet das System wieder auf das nächste Ereignis.

Die Reaktion auf ein Ereignis hängt im Allgemeinen sowohl vom Typ des Ereignisses als auch vom internen Zustand des Systems ab und kann eine Zustandsänderung umfassen, die zu einem Zustandsübergang führt . Das Muster von Ereignissen, Zuständen und Zustandsübergängen zwischen diesen Zuständen kann abstrahiert und als Finite-State-Machine (FSM) dargestellt werden.

Das Konzept eines FSM ist bei der ereignisgesteuerten Programmierung wichtig, da die Ereignisbehandlung explizit sowohl vom Ereignistyp als auch vom Status des Systems abhängt. Bei korrekter Verwendung kann eine Zustandsmaschine die Anzahl der Ausführungspfade durch den Code drastisch reduzieren, die an jedem Verzweigungspunkt getesteten Bedingungen vereinfachen und das Umschalten zwischen verschiedenen Ausführungsmodi vereinfachen. Umgekehrt kann die Verwendung einer ereignisgesteuerten Programmierung ohne ein zugrunde liegendes FSM-Modell dazu führen, dass Programmierer fehleranfälligen, schwer zu erweiternden und übermäßig komplexen Anwendungscode erstellen.

Grundlegende UML-Zustandsdiagramme

UML behält die allgemeine Form der traditionellen Zustandsdiagramme bei . Die UML-Zustandsdiagramme sind gerichtete Graphen, in denen Knoten Zustände und Konnektoren Zustandsübergänge bezeichnen. Zum Beispiel zeigt 1 ein UML-Zustandsdiagramm, das der Computertastatur-Zustandsmaschine entspricht. In UML werden Zustände als abgerundete Rechtecke dargestellt, die mit Zustandsnamen gekennzeichnet sind. Die als Pfeile dargestellten Übergänge sind mit den auslösenden Ereignissen gekennzeichnet, gefolgt von der Liste der ausgeführten Aktionen. Der anfängliche Übergang stammt aus dem durchgezogenen Kreis und gibt den Standardstatus an, wenn das System zum ersten Mal gestartet wird. Jedes Zustandsdiagramm sollte einen solchen Übergang haben, der nicht beschriftet werden sollte, da er nicht durch ein Ereignis ausgelöst wird. Dem anfänglichen Übergang können Aktionen zugeordnet sein.

Abbildung 1: UML-Zustandsdiagramm, das die Zustandsmaschine der Computertastatur darstellt

Veranstaltungen

Ein Ereignis ist etwas, das sich auf das System auswirkt. Genau genommen bezieht sich der Begriff Ereignis in der UML-Spezifikation eher auf die Art des Auftretens als auf eine konkrete Instanz dieses Auftretens. Zum Beispiel ist Tastendruck ein Ereignis für die Tastatur, aber jeder Tastendruck ist kein Ereignis, sondern eine konkrete Instanz des Tastenanschlagereignisses. Ein weiteres Ereignis, das für die Tastatur von Interesse ist, ist möglicherweise das Einschalten, aber das Einschalten morgen um 10:05:36 Uhr ist nur eine Instanz des Einschaltereignisses.

Einem Ereignis können zugeordnete Parameter zugeordnet sein , sodass die Ereignisinstanz nicht nur das Auftreten eines interessanten Vorfalls, sondern auch quantitative Informationen zu diesem Ereignis übermitteln kann. Beispielsweise verfügt das durch Drücken einer Taste auf einer Computertastatur erzeugte Tastenanschlagereignis über zugeordnete Parameter, die den Zeichenscancode sowie den Status der Umschalt-, Strg- und Alt-Tasten übermitteln.

Eine Ereignisinstanz überlebt das augenblickliche Auftreten, das sie generiert hat, und überträgt dieses Ereignis möglicherweise an eine oder mehrere Zustandsautomaten. Nach der Generierung durchläuft die Ereignisinstanz einen Verarbeitungslebenszyklus, der aus bis zu drei Phasen bestehen kann. Zunächst wird die Ereignisinstanz empfangen, wenn sie akzeptiert wird und auf die Verarbeitung wartet (z. B. wird sie in die Ereigniswarteschlange gestellt ). Später wird die Ereignisinstanz geschickt an die Zustandsmaschine, an welcher Stelle er das aktuelle Ereignis wird. Schließlich wird es verbraucht, wenn die Zustandsmaschine die Verarbeitung der Ereignisinstanz beendet hat. Eine konsumierte Ereignisinstanz steht nicht mehr zur Verarbeitung zur Verfügung.

Zustände

Jede Zustandsmaschine hat einen Zustand , der die Reaktion der Zustandsmaschine auf Ereignisse regelt. Wenn Sie beispielsweise eine Taste auf einer Tastatur drücken, wird als Zeichencode entweder ein Groß- oder ein Kleinbuchstabe generiert, je nachdem, ob die Feststelltaste aktiv ist. Daher kann das Verhalten der Tastatur in zwei Zustände unterteilt werden: den "Standard" -Zustand und den "caps_locked" -Zustand. (Die meisten Tastaturen verfügen über eine LED, die anzeigt, dass sich die Tastatur im Status "caps_locked" befindet.) Das Verhalten einer Tastatur hängt nur von bestimmten Aspekten ihres Verlaufs ab, z. B. davon, ob die Feststelltaste gedrückt wurde, nicht jedoch. auf wie viele und genau welche anderen Tasten zuvor gedrückt wurden. Ein Zustand kann alle möglichen (aber irrelevanten) Ereignissequenzen abstrahieren und nur die relevanten erfassen.

Im Zusammenhang mit Software-Zustandsautomaten (und insbesondere klassischen FSMs) wird der Begriff Zustand häufig als eine einzelne Zustandsvariable verstanden , die nur eine begrenzte Anzahl von a priori bestimmten Werten annehmen kann (z. B. zwei Werte bei der Tastatur oder mehr) allgemein - eine Art Variable mit einem Aufzählungstyp in vielen Programmiersprachen). Die Idee der Zustandsvariablen (und des klassischen FSM-Modells) ist, dass der Wert der Zustandsvariablen den aktuellen Zustand des Systems zu einem bestimmten Zeitpunkt vollständig definiert. Das Konzept des Zustands reduziert das Problem der Identifizierung des Ausführungskontexts im Code auf das Testen nur der Zustandsvariablen anstelle vieler Variablen, wodurch viel bedingte Logik eliminiert wird.

Erweiterte Zustände

In der Praxis wird es jedoch für alle Zustandsmaschinen, die über sehr einfache hinausgehen, schnell unpraktisch , den gesamten Zustand der Zustandsmaschine als eine einzige Zustandsvariable zu interpretieren . Selbst wenn wir eine einzelne 32-Bit-Ganzzahl in unserem Maschinenzustand haben, könnte dies zu über 4 Milliarden verschiedenen Zuständen führen - und zu einer vorzeitigen Zustandsexplosion führen . Diese Interpretation ist nicht praktikabel, daher wird in UML-Zustandsmaschinen der gesamte Zustand der Zustandsmaschine üblicherweise in (a) aufzählbare Zustandsvariable und (b) alle anderen Variablen aufgeteilt, die als erweiterter Zustand bezeichnet werden . Eine andere Möglichkeit, dies zu sehen, besteht darin, die aufzählbare Zustandsvariable als qualitativen Aspekt und den erweiterten Zustand als quantitative Aspekte des gesamten Zustands zu interpretieren . In dieser Interpretation impliziert eine Änderung der Variablen nicht immer eine Änderung der qualitativen Aspekte des Systemverhaltens und führt daher nicht zu einer Änderung des Zustands.

Zustandsmaschinen, die durch erweiterte Zustandsvariablen ergänzt werden, werden als erweiterte Zustandsmaschinen bezeichnet, und UML-Zustandsmaschinen gehören zu dieser Kategorie. Erweiterte Zustandsautomaten können den zugrunde liegenden Formalismus auf viel komplexere Probleme anwenden, als dies ohne die Einbeziehung erweiterter Zustandsvariablen praktikabel ist. Wenn wir beispielsweise eine Art Limit in unserem FSM implementieren müssen (z. B. die Anzahl der Tastenanschläge auf der Tastatur auf 1000 begrenzen), müssten wir ohne erweiterten Status 1000 Status erstellen und verarbeiten - was nicht praktikabel ist. Mit einer erweiterten Zustandsmaschine können wir jedoch eine key_count Variable einführen , die auf 1000 initialisiert und bei jedem Tastendruck dekrementiert wird, ohne die Zustandsvariable zu ändern .

Abbildung 2: Erweiterte Zustandsmaschine der "billigen Tastatur" mit erweiterter Zustandsvariable key_count und verschiedenen Schutzbedingungen

Das Zustandsdiagramm aus Abbildung 2 ist ein Beispiel für eine erweiterte Zustandsmaschine, bei der der vollständige Zustand des Systems (als erweiterter Zustand bezeichnet ) die Kombination eines qualitativen Aspekts - der Zustandsvariablen - und der quantitativen Aspekte - der erweiterten Zustandsvariablen ist .

Der offensichtliche Vorteil erweiterter Zustandsmaschinen ist die Flexibilität. Zum Beispiel würde das Ändern des Grenzwerts key_count von 1000 auf 10000 Tastenanschläge die erweiterte Zustandsmaschine überhaupt nicht komplizieren. Die einzige erforderliche Änderung wäre das Ändern des Initialisierungswerts der key_count erweiterten Statusvariablen während der Initialisierung.

Diese Flexibilität erweiterter Zustandsmaschinen ist jedoch aufgrund der komplexen Kopplung zwischen den "qualitativen" und den "quantitativen" Aspekten des erweiterten Zustands mit einem Preis verbunden. Die Kopplung erfolgt durch die mit Übergängen verbundenen Schutzbedingungen, wie in Abbildung 2 dargestellt.

Schutzbedingungen

Schutzbedingungen (oder einfach Schutzbedingungen) sind boolesche Ausdrücke, die dynamisch basierend auf dem Wert erweiterter Statusvariablen und Ereignisparameter ausgewertet werden . Schutzbedingungen wirken sich auf das Verhalten einer Zustandsmaschine aus, indem sie Aktionen oder Übergänge nur aktivieren, wenn sie als WAHR ausgewertet werden, und sie deaktivieren, wenn sie als FALSCH ausgewertet werden. In der UML-Notation werden die Schutzbedingungen in eckigen Klammern angezeigt (z. B. [key_count == 0] in Abbildung 2).

Die Notwendigkeit von Wachen ist die unmittelbare Folge des Hinzufügens von speichererweiterten Zustandsvariablen zum Formalismus der Zustandsmaschine. Erweiterte Zustandsvariablen und Schutzvorrichtungen werden sparsam eingesetzt und bilden einen leistungsstarken Mechanismus, der das Design vereinfachen kann. Andererseits ist es leicht möglich, erweiterte Staaten und Wachen zu missbrauchen.

Aktionen und Übergänge

Wenn eine Ereignisinstanz ausgelöst wird, reagiert die Statusmaschine mit Aktionen wie dem Ändern einer Variablen, dem Ausführen von E / A, dem Aufrufen einer Funktion, dem Generieren einer anderen Ereignisinstanz oder dem Wechseln in einen anderen Status. Alle mit dem aktuellen Ereignis verknüpften Parameterwerte stehen allen Aktionen zur Verfügung, die direkt durch dieses Ereignis verursacht werden.

Das Umschalten von einem Zustand in einen anderen wird als Zustandsübergang bezeichnet , und das Ereignis, das ihn verursacht, wird als auslösendes Ereignis oder einfach als Auslöser bezeichnet . Befindet sich die Tastatur im Tastaturbeispiel beim Drücken der CapsLock-Taste im Status "Standard", wechselt die Tastatur in den Status "caps_locked". Befindet sich die Tastatur jedoch bereits im Status "caps_locked", führt das Drücken von CapsLock zu einem anderen Übergang - vom Status "caps_locked" zum Status "default". In beiden Fällen ist das Drücken von CapsLock das auslösende Ereignis.

In erweiterten Zustandsmaschinen kann ein Übergang einen Schutz haben , was bedeutet, dass der Übergang nur "ausgelöst" werden kann, wenn der Schutz TRUE ergibt. Ein Zustand kann als Reaktion auf denselben Auslöser viele Übergänge haben, solange er nicht überlappende Schutzvorrichtungen hat. Diese Situation kann jedoch zu Problemen bei der Auswertung der Schutzvorrichtungen führen, wenn der gemeinsame Auslöser auftritt. Die UML-Spezifikation schreibt absichtlich keine bestimmte Reihenfolge vor. Vielmehr belastet UML den Designer, Wachen so zu gestalten, dass die Reihenfolge ihrer Bewertung keine Rolle spielt. Praktisch bedeutet dies, dass Wachausdrücke keine Nebenwirkungen haben sollten, zumindest keine, die die Bewertung anderer Wachen mit demselben Auslöser verändern würden.

Run-to-Completion-Ausführungsmodell

Alle Zustandsmaschinenformalismen, einschließlich UML-Zustandsmaschinen, setzen allgemein voraus, dass eine Zustandsmaschine die Verarbeitung jedes Ereignisses abschließt, bevor sie mit der Verarbeitung des nächsten Ereignisses beginnen kann. Dieses Ausführungsmodell wird als " Run to Completion" oder "RTC" bezeichnet.

Im RTC-Modell verarbeitet das System Ereignisse in diskreten, unteilbaren RTC-Schritten. Neue eingehende Ereignisse können die Verarbeitung des aktuellen Ereignisses nicht unterbrechen und müssen gespeichert werden (normalerweise in einer Ereigniswarteschlange ), bis die Zustandsmaschine wieder inaktiv wird. Diese Semantik vermeidet vollständig alle internen Parallelitätsprobleme innerhalb einer einzelnen Zustandsmaschine. Das RTC-Modell umgeht auch das konzeptionelle Problem der Verarbeitung von Aktionen, die mit Übergängen verbunden sind, bei denen sich die Zustandsmaschine für die Dauer der Aktion nicht in einem genau definierten Zustand befindet (zwischen zwei Zuständen liegt). Während der Ereignisverarbeitung reagiert das System nicht (nicht beobachtbar), so dass der schlecht definierte Zustand während dieser Zeit keine praktische Bedeutung hat.

Beachten Sie jedoch, dass RTC nicht bedeutet, dass eine Zustandsmaschine die CPU monopolisieren muss, bis der RTC-Schritt abgeschlossen ist. Die Voreinstellungsbeschränkung gilt nur für den Aufgabenkontext der Zustandsmaschine, die bereits mit der Verarbeitung von Ereignissen beschäftigt ist. In einer Multitasking-Umgebung können andere Aufgaben (die nicht mit dem Aufgabenkontext der besetzten Zustandsmaschine zusammenhängen) ausgeführt werden, wodurch möglicherweise die aktuell ausgeführte Zustandsmaschine verhindert wird. Solange andere Zustandsautomaten keine Variablen oder andere Ressourcen miteinander teilen, bestehen keine Gefahren für die Parallelität .

Der Hauptvorteil der RTC-Verarbeitung ist die Einfachheit. Der größte Nachteil besteht darin, dass die Reaktionsfähigkeit einer Zustandsmaschine durch ihren längsten RTC-Schritt bestimmt wird. Das Erreichen kurzer RTC-Schritte kann Echtzeitdesigns häufig erheblich erschweren.

UML-Erweiterungen des traditionellen FSM-Formalismus

Obwohl die traditionellen FSMs ein hervorragendes Werkzeug zur Lösung kleinerer Probleme sind, ist allgemein bekannt, dass sie selbst bei mäßig involvierten Systemen nicht mehr zu handhaben sind. Aufgrund des als Zustands- und Übergangsexplosion bekannten Phänomens wächst die Komplexität eines herkömmlichen FSM tendenziell viel schneller als die Komplexität des von ihm beschriebenen Systems. Dies geschieht, weil der traditionelle Staatsmaschinenformalismus Wiederholungen verursacht. Wenn Sie beispielsweise versuchen, das Verhalten eines einfachen Taschenrechners mit einem herkömmlichen FSM darzustellen, werden Sie sofort feststellen, dass viele Ereignisse (z. B. das Drücken der Taste Löschen oder Aus) in vielen Zuständen identisch behandelt werden. Ein herkömmlicher FSM, der in der folgenden Abbildung gezeigt wird, hat keine Möglichkeit, eine solche Gemeinsamkeit zu erfassen, und erfordert in vielen Zuständen das Wiederholen derselben Aktionen und Übergänge. Was in den traditionellen Zustandsautomaten fehlt, ist der Mechanismus zum Ausklammern des gemeinsamen Verhaltens, um es über viele Zustände hinweg zu teilen.

Ein Taschenrechner (links) und die traditionelle Zustandsmaschine mit mehreren Übergängen Löschen und Aus (rechts)

UML-Zustandsautomaten beheben genau diesen Mangel der herkömmlichen FSMs. Sie bieten eine Reihe von Funktionen zum Eliminieren der Wiederholungen, sodass die Komplexität einer UML-Zustandsmaschine nicht mehr explodiert, sondern die Komplexität des von ihr beschriebenen reaktiven Systems getreu wiedergibt. Offensichtlich sind diese Funktionen für Softwareentwickler sehr interessant, da nur sie den gesamten State-Machine-Ansatz wirklich auf reale Probleme anwendbar machen.

Hierarchisch verschachtelte Zustände

Die wichtigste Neuerung von UML-Zustandsautomaten gegenüber den herkömmlichen FSMs ist die Einführung hierarchisch verschachtelter Zustände (daher werden Zustandsdiagramme auch als hierarchische Zustandsautomaten oder HSMs bezeichnet ). Die mit der Verschachtelung von Zuständen verbundene Semantik lautet wie folgt (siehe Abbildung 3): Befindet sich ein System im verschachtelten Zustand, z. B. "Ergebnis" (als Unterzustand bezeichnet ), befindet es sich auch (implizit) im umgebenden Zustand "an" (aufgerufen) der Superstaat ). Diese Zustandsmaschine versucht, jedes Ereignis im Kontext des Unterzustands zu behandeln, der sich konzeptionell auf der unteren Ebene der Hierarchie befindet. Wenn das "Ergebnis" des Unterzustands jedoch nicht vorschreibt, wie mit dem Ereignis umgegangen werden soll, wird das Ereignis nicht wie bei einer herkömmlichen "flachen" Zustandsmaschine stillschweigend verworfen. Vielmehr wird es automatisch im übergeordneten Kontext des Superstaats "Ein" behandelt. Dies ist gemeint, wenn sich das System im Zustand "Ergebnis" sowie "Ein" befindet. Natürlich ist die Verschachtelung von Zuständen nicht nur auf eine Ebene beschränkt, und die einfache Regel der Ereignisverarbeitung gilt rekursiv für jede Verschachtelungsebene.

Abbildung 3: Ein Taschenrechner (links) und die UML-Zustandsmaschine mit Zustandsverschachtelung (rechts)

Zustände, die andere Zustände enthalten, werden zusammengesetzte Zustände genannt . Umgekehrt werden Zustände ohne interne Struktur einfache Zustände genannt . Ein verschachtelter Zustand wird als direkter Unterzustand bezeichnet, wenn er in keinem anderen Zustand enthalten ist. Andernfalls wird es als transitiv verschachtelter Unterzustand bezeichnet .

Da die interne Struktur eines zusammengesetzten Zustands beliebig komplex sein kann, kann jede hierarchische Zustandsmaschine als interne Struktur eines (übergeordneten) zusammengesetzten Zustands angesehen werden. Es ist konzeptionell zweckmäßig, einen zusammengesetzten Zustand als die ultimative Wurzel der Zustandsmaschinenhierarchie zu definieren. In der UML-Spezifikation hat jede Zustandsmaschine einen Spitzenzustand (die abstrakte Wurzel jeder Zustandsmaschinenhierarchie), der alle anderen Elemente der gesamten Zustandsmaschine enthält. Das grafische Rendern dieses allumfassenden Top-Status ist optional.

Wie Sie sehen können, soll die Semantik der hierarchischen Zustandszerlegung die Wiederverwendung von Verhalten erleichtern. Die Unterzustände (verschachtelte Zustände) müssen nur die Unterschiede zu den Superzuständen (enthaltenden Zuständen) definieren. Ein Unterzustand kann das allgemeine Verhalten leicht von seinen Superzuständen erben, indem er häufig behandelte Ereignisse einfach ignoriert, die dann automatisch von übergeordneten Zuständen behandelt werden. Mit anderen Worten, die hierarchische Zustandsverschachtelung ermöglicht die Programmierung nach Unterschieden .

Der am häufigsten hervorgehobene Aspekt der staatlichen Hierarchie ist die Abstraktion - eine alte und leistungsfähige Technik zur Bewältigung der Komplexität. Anstatt alle Aspekte eines komplexen Systems gleichzeitig anzusprechen, ist es häufig möglich, einige Teile des Systems zu ignorieren (zu abstrahieren). Hierarchische Zustände sind ein idealer Mechanismus zum Ausblenden interner Details, da der Designer sie leicht verkleinern oder vergrößern kann, um verschachtelte Zustände auszublenden oder anzuzeigen.

Zusammengesetzte Zustände verbergen jedoch nicht einfach die Komplexität. Sie reduzieren es auch aktiv durch den leistungsstarken Mechanismus der hierarchischen Ereignisverarbeitung. Ohne eine solche Wiederverwendung könnte bereits eine moderate Zunahme der Systemkomplexität zu einer explosionsartigen Zunahme der Anzahl von Zuständen und Übergängen führen. Beispielsweise vermeidet die hierarchische Zustandsmaschine, die den Taschenrechner darstellt (Abbildung 3), das Wiederholen der Übergänge Löschen und Aus in praktisch jedem Zustand. Durch die Vermeidung von Wiederholungen kann das Wachstum von HSMs proportional zum Wachstum der Systemkomplexität bleiben. Mit dem Wachstum des modellierten Systems steigt auch die Möglichkeit zur Wiederverwendung und wirkt somit möglicherweise der überproportionalen Zunahme der Anzahl von Zuständen und Übergängen entgegen, die für herkömmliche FSMs typisch sind.

Orthogonale Regionen

Die Analyse durch hierarchische Zustandszerlegung kann die Anwendung der Operation 'Exklusiv-ODER' auf einen bestimmten Zustand umfassen. Befindet sich ein System beispielsweise im Superzustand "Ein" (Abbildung 3), befindet es sich möglicherweise auch entweder im Unterzustand "Operand1" ODER im Unterzustand "Operand2" ODER im Unterzustand "Operiert" ODER im Unterzustand "Ergebnis". Unterzustand. Dies würde zur Beschreibung des Superzustands "Ein" als "ODER-Zustand" führen.

UML-Zustandsdiagramme führen auch die komplementäre UND-Zerlegung ein. Eine solche Zerlegung bedeutet, dass ein zusammengesetzter Zustand zwei oder mehr orthogonale Bereiche enthalten kann (orthogonale Mittel, die in diesem Zusammenhang kompatibel und unabhängig sind), und dass ein solcher zusammengesetzter Zustand bedeutet, dass er sich gleichzeitig in allen seinen orthogonalen Bereichen befindet.

Orthogonale Regionen befassen sich mit dem häufigen Problem einer kombinatorischen Zunahme der Anzahl von Zuständen, wenn das Verhalten eines Systems in unabhängige, gleichzeitig aktive Teile fragmentiert wird. Beispielsweise verfügt eine Computertastatur neben der Haupttastatur über eine unabhängige Zehnertastatur. Erinnern Sie sich aus der vorherigen Diskussion an die beiden bereits identifizierten Zustände der Haupttastatur: "default" und "caps_locked" (siehe Abbildung 1). Das numerische Tastenfeld kann sich auch in zwei Zuständen befinden - "Zahlen" und "Pfeile" - abhängig davon, ob die Num-Sperre aktiv ist. Der vollständige Zustandsraum der Tastatur in der Standardzerlegung ist daher das kartesische Produkt der beiden Komponenten (Haupttastatur und Ziffernblock) und besteht aus vier Zuständen: "Standardnummern", "Standardpfeile", "caps_locked-Nummern", "und" caps_locked - Pfeile. " Dies wäre jedoch eine unnatürliche Darstellung, da das Verhalten der Zehnertastatur nicht vom Zustand der Haupttastatur abhängt und umgekehrt. Durch die Verwendung orthogonaler Bereiche kann vermieden werden, dass sich unabhängige Verhaltensweisen als kartesisches Produkt vermischen und stattdessen getrennt bleiben, wie in Abbildung 4 dargestellt.

Abbildung 4: Zwei orthogonale Bereiche (Haupttastatur und Ziffernblock) einer Computertastatur

Es ist zu beachten, dass, wenn die orthogonalen Bereiche vollständig unabhängig voneinander sind, ihre kombinierte Komplexität einfach additiv ist, was bedeutet, dass die Anzahl der zur Modellierung des Systems erforderlichen unabhängigen Zustände einfach die Summe k + l + m + ... ist , wobei k, l, m, ... bezeichnen die Anzahl der ODER-Zustände in jedem orthogonalen Bereich. Der allgemeine Fall der gegenseitigen Abhängigkeit führt jedoch andererseits zu einer multiplikativen Komplexität, so dass im Allgemeinen die Anzahl der benötigten Zustände das Produkt k × l × m × ... ist .

In den meisten realen Situationen wären orthogonale Regionen nur annähernd orthogonal (dh nicht wirklich unabhängig). Daher bieten UML-Zustandsdiagramme eine Reihe von Möglichkeiten für orthogonale Regionen, um ihr Verhalten zu kommunizieren und zu synchronisieren. Unter diesen umfangreichen (manchmal komplexen) Mechanismen ist das vielleicht wichtigste Merkmal, dass orthogonale Regionen ihr Verhalten koordinieren können, indem sie Ereignisinstanzen aneinander senden.

Obwohl orthogonale Bereiche eine Unabhängigkeit der Ausführung implizieren (was mehr oder weniger Parallelität ermöglicht), erfordert die UML-Spezifikation nicht, dass jedem orthogonalen Bereich ein separater Ausführungsthread zugewiesen wird (obwohl dies auf Wunsch erfolgen kann). Tatsächlich werden orthogonale Bereiche am häufigsten innerhalb desselben Threads ausgeführt. Die UML-Spezifikation erfordert nur, dass sich der Designer nicht auf eine bestimmte Reihenfolge verlässt, damit Ereignisinstanzen an die relevanten orthogonalen Bereiche gesendet werden.

Ein- und Ausstiegsaktionen

Jeder Status in einem UML-Statusdiagramm kann optionale Eingabeaktionen enthalten , die beim Eintritt in einen Status ausgeführt werden, sowie optionale Beendigungsaktionen , die beim Verlassen eines Status ausgeführt werden. Eintritts- und Austrittsaktionen sind Zuständen zugeordnet, nicht Übergängen. Unabhängig davon, wie ein Status eingegeben oder verlassen wird, werden alle seine Ein- und Ausstiegsaktionen ausgeführt. Aufgrund dieser Eigenschaft verhalten sich Zustandsdiagramme wie Moore-Maschinen . Die UML-Notation für Statuseintritts- und -austrittsaktionen besteht darin, das reservierte Wort "Eintrag" (oder "Ausstieg") im Status direkt unter dem Namensfach zu platzieren, gefolgt vom Schrägstrich und der Liste beliebiger Aktionen (siehe Abbildung 5).

Abbildung 5: Toaster-Zustandsmaschine mit Ein- und Ausstiegsaktionen

Der Wert von Ein- und Ausstiegsaktionen besteht darin, dass sie Mittel für eine garantierte Initialisierung und Bereinigung bieten , ähnlich wie Klassenkonstruktoren und Destruktoren in der objektorientierten Programmierung . Betrachten Sie beispielsweise den Status "door_open" aus Abbildung 5, der dem Verhalten des Toasters bei geöffneter Tür entspricht. Dieser Zustand hat eine sehr wichtige sicherheitskritische Anforderung: Deaktivieren Sie die Heizung immer bei geöffneter Tür. Außerdem sollte bei geöffneter Tür die interne Lampe, die den Ofen beleuchtet, aufleuchten.

Natürlich könnte ein solches Verhalten modelliert werden, indem jedem Übergangspfad, der zum Zustand "door_open" führt, geeignete Aktionen (Deaktivieren der Heizung und Einschalten des Lichts) hinzugefügt werden (der Benutzer kann die Tür jederzeit während des "Backens" oder "Toastens" öffnen "oder wenn der Ofen überhaupt nicht benutzt wird). Es sollte nicht vergessen werden, die interne Lampe bei jedem Übergang zu löschen, der den Zustand "door_open" verlässt. Eine solche Lösung würde jedoch die Wiederholung von Aktionen in vielen Übergängen verursachen. Noch wichtiger ist, dass ein solcher Ansatz das Design bei nachfolgenden Verhaltensänderungen fehleranfällig macht (z. B. vergisst der nächste Programmierer, der an einer neuen Funktion wie der Top-Browning arbeitet, möglicherweise einfach, die Heizung beim Übergang zu "door_open" zu deaktivieren).

Ein- und Ausstiegsaktionen ermöglichen eine sicherere, einfachere und intuitivere Implementierung des gewünschten Verhaltens. Wie in 5 gezeigt, könnte spezifiziert werden, dass die Austrittsaktion von "Heizen" die Heizung deaktiviert, die Eingangsaktion zu "Türöffner" die Ofenlampe aufleuchtet und die Austrittsaktion von "Türöffnen" die Lampe löscht. Die Verwendung von Ein- und Ausstiegsaktionen ist der Platzierung einer Aktion auf einem Übergang vorzuziehen, da sie wiederholte Codierungen vermeidet und die Funktion verbessert, indem ein Sicherheitsrisiko beseitigt wird. (Heizung an, während die Tür geöffnet ist). Die Semantik der Austrittsaktionen garantiert, dass die Heizung unabhängig vom Übergangspfad deaktiviert wird, wenn sich der Toaster nicht im Zustand "Heizen" befindet.

Da Eingabeaktionen automatisch ausgeführt werden, wenn ein zugeordneter Status eingegeben wird, bestimmen sie häufig die Betriebsbedingungen oder die Identität des Status, ähnlich wie ein Klassenkonstruktor die Identität des zu erstellenden Objekts bestimmt. Zum Beispiel wird die Identität des "Heiz" -Zustands durch die Tatsache bestimmt, dass die Heizung eingeschaltet ist. Diese Bedingung muss vor dem Eintritt in einen Unterzustand von "Heizen" hergestellt werden, da Eintrittsaktionen in einen Unterzustand von "Heizen", wie "Toasten", von einer ordnungsgemäßen Initialisierung des Superzustands "Heizen" abhängen und nur die Unterschiede zu dieser Initialisierung ausführen. Folglich muss die Reihenfolge der Ausführung von Eingabeaktionen immer vom äußersten Zustand in den innersten Zustand (von oben nach unten) übergehen.

Es überrascht nicht, dass diese Reihenfolge der Reihenfolge entspricht, in der Klassenkonstruktoren aufgerufen werden. Die Konstruktion einer Klasse beginnt immer an der Wurzel der Klassenhierarchie und folgt allen Vererbungsebenen bis hinunter zur instanziierten Klasse. Die Ausführung von Exit-Aktionen, die dem Destruktoraufruf entsprechen, erfolgt in genau umgekehrter Reihenfolge (Bottom-Up).

Interne Übergänge

Sehr häufig führt ein Ereignis dazu, dass nur einige interne Aktionen ausgeführt werden, führt jedoch nicht zu einer Zustandsänderung (Zustandsübergang). In diesem Fall umfassen alle ausgeführten Aktionen den internen Übergang . Wenn Sie beispielsweise auf einer Tastatur tippen, werden verschiedene Zeichencodes generiert. Sofern nicht die Feststelltaste gedrückt wird, ändert sich der Status der Tastatur nicht (es tritt kein Statusübergang auf). In UML sollte diese Situation mit internen Übergängen modelliert werden, wie in Abbildung 6 dargestellt. Die UML-Notation für interne Übergänge folgt der allgemeinen Syntax, die für Exit- (oder Entry-) Aktionen verwendet wird, mit Ausnahme des Wortes Entry (oder Exit) für den internen Übergang ist mit dem auslösenden Ereignis gekennzeichnet (siehe z. B. den internen Übergang, der durch das ANY_KEY-Ereignis in Abbildung 6 ausgelöst wird).

Abbildung 6: UML-Zustandsdiagramm der Tastaturzustandsmaschine mit internen Übergängen

Ohne Ein- und Ausstiegsaktionen wären interne Übergänge identisch mit Selbstübergängen (Übergänge, bei denen der Zielzustand mit dem Quellzustand identisch ist). Tatsächlich sind in einer klassischen Mealy-Maschine Aktionen ausschließlich mit Zustandsübergängen verbunden. Die einzige Möglichkeit, Aktionen auszuführen, ohne den Zustand zu ändern, besteht in einem Selbstübergang (in Abbildung 1 oben in diesem Artikel als gerichtete Schleife dargestellt). Bei Vorhandensein von Eintritts- und Austrittsaktionen wie in UML-Zustandsdiagrammen umfasst ein Selbstübergang jedoch die Ausführung von Exit- und Eintrittsaktionen und unterscheidet sich daher deutlich von einem internen Übergang.

Im Gegensatz zu einem Selbstübergang werden infolge eines internen Übergangs niemals Eingangs- oder Ausgangsaktionen ausgeführt, selbst wenn der interne Übergang von einer höheren Hierarchieebene als dem derzeit aktiven Zustand geerbt wird. Interne Übergänge, die von Superzuständen auf jeder Verschachtelungsebene geerbt werden, verhalten sich so, als wären sie direkt im aktuell aktiven Zustand definiert.

Übergangsausführungssequenz

Die Verschachtelung von Zuständen in Kombination mit Eintritts- und Austrittsaktionen verkompliziert die Zustandsübergangssemantik in HSMs im Vergleich zu herkömmlichen FSMs erheblich. Wenn es sich um hierarchisch verschachtelte Zustände und orthogonale Bereiche handelt , kann der einfache Begriff aktueller Zustand ziemlich verwirrend sein. In einem HSM können mehrere Zustände gleichzeitig aktiv sein. Befindet sich die Zustandsmaschine in einem Blattzustand, der in einem zusammengesetzten Zustand enthalten ist (der möglicherweise in einem übergeordneten zusammengesetzten Zustand usw. enthalten ist), sind auch alle zusammengesetzten Zustände aktiv, die den Blattzustand entweder direkt oder transitiv enthalten . Da einige der zusammengesetzten Zustände in dieser Hierarchie orthogonale Bereiche haben könnten, wird der aktuelle aktive Zustand tatsächlich durch einen Baum von Zuständen dargestellt, beginnend mit dem einzelnen oberen Zustand an der Wurzel bis hin zu einzelnen einfachen Zuständen an den Blättern. Die UML-Spezifikation bezieht sich auf einen solchen Statusbaum als Statuskonfiguration.

Abbildung 7: Zustandsrollen in einem Zustandsübergang

In UML kann ein Zustandsübergang zwei beliebige Zustände direkt verbinden. Diese beiden Zustände, die zusammengesetzt sein können, werden als Hauptquelle und Hauptziel eines Übergangs bezeichnet. Abbildung 7 zeigt ein einfaches Übergangsbeispiel und erläutert die Statusrollen in diesem Übergang. Die UML-Spezifikation schreibt vor, dass für einen Statusübergang die folgenden Aktionen in der folgenden Reihenfolge ausgeführt werden müssen (siehe Abschnitt 15.3.14 in OMG Unified Modeling Language (OMG UML), Infrastrukturversion 2.2 ):

  1. Bewerten Sie den mit dem Übergang verbundenen Schutzzustand und führen Sie die folgenden Schritte nur aus, wenn der Schutz TRUE ergibt.
  2. Beenden Sie die Quellstatuskonfiguration.
  3. Führen Sie die mit dem Übergang verbundenen Aktionen aus.
  4. Geben Sie die Zielzustandskonfiguration ein.

Die Übergangssequenz ist im einfachen Fall leicht zu interpretieren, wenn sowohl die Hauptquelle als auch das Hauptziel auf derselben Ebene verschachtelt sind. Zum Beispiel bewirkt der in Fig. 7 gezeigte Übergang T1 die Auswertung des Schutzes g (); gefolgt von der Abfolge der Aktionen: a(); b(); t(); c(); d(); und e() ; unter der Annahme, dass der Wächter g() TRUE bewertet.

Im allgemeinen Fall von Quell- und Zielzuständen, die auf verschiedenen Ebenen der Statushierarchie verschachtelt sind, ist es möglicherweise nicht sofort ersichtlich, wie viele Verschachtelungsebenen beendet werden müssen. Die UML - Spezifikation schreibt vor, dass ein Übergang umfasst alle verschachtelten Zustände aus dem aktuellen aktiven Zustand austritt (das ein direkter oder transitiver Unterzustand des Hauptquellenzustandes sein kann) bis zu, aber nicht einschließlich, der kleinste gemeinsame Vorfahre (LCA) Zustand der Haupt Quell- und Hauptzielzustände. Wie der Name schon sagt, ist die Ökobilanz der niedrigste zusammengesetzte Zustand, der gleichzeitig ein Superstaat (Vorfahr) sowohl des Quell- als auch des Zielzustands ist. Wie zuvor beschrieben, ist die Reihenfolge der Ausführung von Exit-Aktionen immer vom am tiefsten verschachtelten Status (dem aktuell aktiven Status) in der Hierarchie bis zur LCA, jedoch ohne die LCA zu verlassen. Beispielsweise ist die Ökobilanz (s1, s2) der in Fig. 7 gezeigten Zustände "s1" und "s2" der Zustand "s".

Die Eingabe der Zielzustandskonfiguration beginnt auf der Ebene, auf der die Exit-Aktionen aufgehört haben (dh innerhalb der Ökobilanz). Wie zuvor beschrieben, müssen Eingabeaktionen vom Status der höchsten Ebene über die Statushierarchie bis zum Hauptzielstatus ausgeführt werden. Wenn der Hauptzielzustand zusammengesetzt ist, schreibt die UML-Semantik vor, dass die Submaschine unter Verwendung der lokalen Anfangsübergänge rekursiv "gebohrt" werden soll. Die Zielzustandskonfiguration wird erst vollständig eingegeben, nachdem ein Blattzustand ohne anfängliche Übergänge festgestellt wurde.

Lokale versus externe Übergänge

Vor UML 2 war die einzige verwendete Übergangssemantik der externe Übergang , bei dem die Hauptquelle des Übergangs immer verlassen und das Hauptziel des Übergangs immer eingegeben wird. UML 2 behielt die Semantik des "externen Übergangs" aus Gründen der Abwärtskompatibilität bei, führte jedoch auch eine neue Art von Übergang ein, den lokalen Übergang, der als lokaler Übergang bezeichnet wird (siehe Abschnitt 15.3.15 in Unified Modeling Language (UML), Infrastructure Version 2.2 ). Für viele Übergangstopologien sind externe und lokale Übergänge tatsächlich identisch. Ein lokaler Übergang führt jedoch nicht zum Verlassen des Hauptquellstatus und zum erneuten Eintritt in den Hauptquellstatus, wenn der Hauptzielstatus ein Unterzustand der Hauptquelle ist. Darüber hinaus führt ein lokaler Zustandsübergang nicht zum Verlassen des Hauptzielzustands und zum erneuten Eintritt in den Hauptzielzustand, wenn das Hauptziel ein Superzustand des Hauptquellzustands ist.

Abbildung 8: Lokale (a) versus externe Übergänge (b).

Fig. 8 stellt lokale (a) und externe (b) Übergänge gegenüber. In der oberen Reihe sehen Sie den Fall der Hauptquelle, die das Hauptziel enthält. Der lokale Übergang bewirkt keinen Austritt aus der Quelle, während der externe Übergang einen Austritt und einen erneuten Eintritt in die Quelle bewirkt. In der unteren Reihe von Abbildung 8 sehen Sie den Fall des Hauptziels, das die Hauptquelle enthält. Der lokale Übergang bewirkt keinen Eintritt in das Ziel, während der externe Übergang einen Austritt und einen Wiedereintritt in das Ziel bewirkt.

Ereignisverschiebung

Manchmal kommt ein Ereignis zu einem besonders ungünstigen Zeitpunkt an, wenn sich eine Zustandsmaschine in einem Zustand befindet, der das Ereignis nicht verarbeiten kann. In vielen Fällen ist das Ereignis so beschaffen, dass es (innerhalb bestimmter Grenzen) verschoben werden kann, bis das System in einen anderen Zustand übergeht, in dem es besser auf die Behandlung des ursprünglichen Ereignisses vorbereitet ist.

UML-Zustandsautomaten bieten einen speziellen Mechanismus zum Verschieben von Ereignissen in Zuständen. In jedem Zustand können Sie eine Klausel einfügen [event list]/defer . Wenn ein Ereignis in der Liste der zurückgestellten Ereignisse des aktuellen Status auftritt, wird das Ereignis für die zukünftige Verarbeitung gespeichert (zurückgestellt), bis ein Status eingegeben wird, in dem das Ereignis nicht in der Liste der zurückgestellten Ereignisse aufgeführt ist. Beim Eintritt in einen solchen Zustand ruft die UML-Zustandsmaschine automatisch alle gespeicherten Ereignisse ab, die nicht mehr zurückgestellt werden, und verbraucht diese Ereignisse dann entweder oder verwirft sie. Es ist möglich, dass für einen Superstaat ein Übergang für ein Ereignis definiert wird, das von einem Unterzustand verschoben wird. In Übereinstimmung mit anderen Bereichen in der Spezifikation von UML-Zustandsmaschinen hat der Unterzustand Vorrang vor dem Superzustand, das Ereignis wird zurückgestellt und der Übergang für den Superzustand wird nicht ausgeführt. Im Fall von orthogonalen Regionen, in denen eine orthogonale Region ein Ereignis verzögert und eine andere das Ereignis konsumiert, hat der Konsument Vorrang und das Ereignis wird konsumiert und nicht zurückgestellt.

Die Einschränkungen von UML-Zustandsautomaten

Harel-Zustandsdiagramme, die die Vorläufer von UML-Zustandsmaschinen sind, wurden als "visueller Formalismus für komplexe Systeme" erfunden, sodass sie von Anfang an untrennbar mit der grafischen Darstellung in Form von Zustandsdiagrammen verbunden waren. Es ist jedoch wichtig zu verstehen, dass das Konzept der UML-Zustandsmaschine eine bestimmte grafische oder textuelle Notation überschreitet. Die UML-Spezifikation macht diese Unterscheidung deutlich, indem sie die Semantik der Zustandsmaschine klar von der Notation trennt.

Die Notation von UML-Zustandsdiagrammen ist jedoch nicht rein visuell. Jede nichttriviale Zustandsmaschine erfordert eine große Menge an Textinformationen (z. B. die Spezifikation von Aktionen und Wachen). Die genaue Syntax von Aktions- und Schutzausdrücken ist in der UML-Spezifikation nicht definiert. Daher verwenden viele Benutzer entweder strukturiertes Englisch oder formell Ausdrücke in einer Implementierungssprache wie C , C ++ oder Java . In der Praxis bedeutet dies, dass die Notation des UML-Zustandsdiagramms stark von der spezifischen Programmiersprache abhängt .

Trotzdem ist die Semantik der meisten Zustandsdiagramme stark auf grafische Notation ausgerichtet. Beispielsweise stellen Zustandsdiagramme die Abfolge der Verarbeitung schlecht dar, sei es die Reihenfolge der Auswertung von Schutzvorrichtungen oder die Reihenfolge des Versendens von Ereignissen an orthogonale Bereiche . Die UML-Spezifikation umgeht diese Probleme, indem sie den Designer belastet, sich nicht auf eine bestimmte Sequenzierung zu verlassen. Es ist jedoch der Fall, dass bei der tatsächlichen Implementierung von UML-Zustandsmaschinen zwangsläufig die vollständige Kontrolle über die Ausführungsreihenfolge besteht, was zu der Kritik führt, dass die UML-Semantik möglicherweise unnötig einschränkend ist. In ähnlicher Weise erfordern Zustandsdiagramme viele Installationsgeräte (Pseudostate wie Verbindungen, Gabeln, Verbindungsstellen, Auswahlpunkte usw.), um den Steuerungsfluss grafisch darzustellen. Mit anderen Worten, diese Elemente der grafischen Notation bieten im Vergleich zu einfach strukturiertem Code keinen großen Mehrwert für die Darstellung des Kontrollflusses .

Die UML-Notation und -Semantik sind wirklich auf computergestützte UML-Tools ausgerichtet . Eine UML-Zustandsmaschine, wie sie in einem Tool dargestellt wird, ist nicht nur das Zustandsdiagramm, sondern eine Mischung aus grafischer und textueller Darstellung, die sowohl die Zustandstopologie als auch die Aktionen genau erfasst. Die Benutzer des Tools können mehrere komplementäre Ansichten derselben Zustandsmaschine erhalten, sowohl visuell als auch textuell, während der generierte Code nur eine der vielen verfügbaren Ansichten ist.

Siehe auch

Liste der Softwareanwendungen, die dedizierte Unterstützung für hierarchische Finite-State-Maschinen bieten

Verweise

Externe Links