Framework für Zustandsorientierte Programmierung


Diplomarbeit, 2007

106 Seiten, Note: 1


Leseprobe


Inhaltsverzeichnis

1. Einleitung

2. Grundlegende Begriffsdefinitionen
2.1. Zustand
2.2. Framework
2.3. Design Patterns

3. Das SDS-Projekt
3.1. SDS Container
3.2. Zustände von SDS Components
3.3. Zustände von Services
3.4. Abhängigkeiten zu verschiedenen Komponentenzuständen

4. Motivation

5. Konzeption
5.1. Modell eines Zustandsobjekts
5.2. Transitionsalgorithmus
5.3. Elementare Arten von Zustandsobjekten
5.3.1. Einfache Zustandsobjekte
5.3.2. Aggregierte Zustandsobjekte
5.3.3. Maskierte Zustandsobjekte
5.3.4. Geschaltete Zustandsobjekte
5.3.5. Strebsame Zustandsobjekte
5.4. Zusammengesetzte Arten von Zustandsobjekten
5.4.1. Kombinierte Zustandsobjekte
5.4.2. Integrierte Strebsame Zustandsobjekte
5.4.3. Gemeinsame IS-Zustandsobjekte

6. Entwurf
6.1. Klasse StateBase (Grundversion)
6.2. KlasseState
6.3. Klasse AggregateState
6.4. KlasseMaskedState
6.5. KlasseSwitchedState
6.6. Klasse StrivingState
6.7. Klassen ComboState, ISState, SharedlSState und CompositeState
6.8. Klasse MaskPool für ISState
6.9. TransitionMode und ActivityHandler für StateBase

7. Anwendung im SDS Container
7.1. ComponentManager
7.2. ServiceManager
7.3. Beispielszenarien für den LifecylceState

8. Mögliche Erweiterungen
8.1. Ein weiteres Zustandsobjekt: Das Funktions-ZO
8.2. Konfiguration der Zustandsobjekte mittels Annotations und XML

9. Die Zustandsorientierte Programmierung

Stichwortverzeichnis

Literaturverzeichnis

Vorwort

Object-oriented software development requires that individual developers have unscheduled critical masses of time in which they can think, innovate, and develop, and meet informally with other team members as necessary to discuss detailed technical issues. The management team must plan for this unstructured time

Grady Booch, Object-Oriented Analysis and Design

1. Einleitung

Die Zustandsorientierunq und das Zustandsframework

Diese Ausarbeitung beschreibt ein Framework zur Unterstützung der Zustandsorientierten Programmierung. Mit Zustandsorientierter Programmierung ist hier ein Programmierparadigma gemeint, das Variablen und deren Zustände in den Mittelpunkt stellt. Die Variablen werden funktional in Beziehung gesetzt. Ändert sich der Zustand einer Variablen, so werden die davon abhängenden Variablen angepasst.

Die Zustandsorientierung kann auf der Objektorientierung basieren. Genau dies ist beim hier beschriebenen Framework (im Folgenden auch Zustandsframework genannt) der Fall. Das Zustandsframework ist in der objektorientierten Programmiersprache C# entwickelt. Es umfasst hauptsächlich sog. Zustandsk/assen und erlaubt eine Zustandsorientierte Programmierung, indem Attribute von Klassen durch jene Zustandsklassen gekapselt sind. Die Zustandsklassen fungieren gewissermaßen als Container für Attribute und kontrollieren deren Zugriff, insbesondere deren Manipulationen. Hierzu ein einfaches Beispiel:

Abbildung in dieser Leseprobe nicht enthalten

Mitarbeiter (Employee) haben eine Arbeitsleistung (performance). Deren Chef (Boss) wacht über ihre durchschnittliche Arbeitsleistung (staffPerformance). Sinkt diese unter 98%, so werden ein paar Mitarbeiter entlassen[1]. Die beiden Performance-Attribute sind eigentlich vom Typ double, werden aber durch eine sog. Einfache Zustandsk/asse (State) bzw. eine Aggregierte Zustandsk/asse (AggregateState) gekapselt. Im Code-Listing nicht abgebildet ist die Konfiguration der entsprechenden Zustandsobjekte. Insbesondere die Aggregierten Zustandsobjekte müssen durch eine Aggregatsfunktion zur Durchschnittbildung parametrisiert werden und die zugehörigen Einfachen Zustandsobjekte müssen als Kind-Zustände definiert werden. Darüber hinaus ist die Methode PerformanceChangedO als sog. Trans/f/ons-L/sfenerfür staffPerformance zu definieren. Ändert sich also die durchschnittliche Arbeitsleistung der Belegschaft bzw. findet diesbezüglich ein Zusfandsübergang bzw. eine Trans/f/on statt, so wird PerformanceChangedO aufgerufen.

Unter Verwendung des Zustandsframeworks lassen sich Attribute durch deklarative Programmierung funktional miteinander in Verbindung bringen, Zustandsübergänge lassen sich verfolgen und, wie später noch ersichtlich wird, lassen sich vom Zustandsobjekt vorgesehene Zustandsübergänge sogar blockieren. Letzteres bedeutet, dass der Client bei Transitionen die Möglichkeit hat, entsprechende Aktivitäten durchzuführen. Sind diese nicht erfolgreich, so kann der Client bestimmen, dass das Zustandsobjekt in seinem alten Zustand verharrt.

Alles in Allem ist das Zustandsframework ein hilfreiches Werkzeug, um Zusammenhänge zwischen Attributen einfach herzustellen und damit eine zustandorientierte Sichtweise in der Programmierung zu fördern.

Der Kontext

Entstanden ist das Framework in Folge einer Beschäftigung des Autors bei Siemens Corporate Research in Princeton in den USA. Dort wird ein Verteiltes System entwickelt. Der Service-Container des Systems hat ein komplexes Lifecycle- Management für seine Komponenten zu realisieren. Entsprechend ist die Ausrichtung des Zustandsframeworks. Seine Zustandsklassen sind nach den Anforderungen des Service-Containers von Siemens entworfen und sind daher keine allgemeine Sammlung von Zustandsklassen für Zustandsorientierte Programmierung. Dennoch sind vor allem die primitiveren Zustandsklassen recht universell einsetzbar. Außerdem ist eine Erweiterung des Frameworks um weitere Zustandsklassen sehr einfach möglich.

Das Flaggschiff: Integrierte Strebsame Zustandsobiekte

Der Schwerpunkt dieser Ausarbeitung bei der Definition von Zustandsklassen liegt in der Herleitung von Strebsamen Zustandsobjekten und den darauf basierenden Integrierten Strebsamen Zustandsobjekten. Letztere werden verwendet, um die Zustände der Komponenten in ihrem Lebenszyklus zu steuern. Die Bezeichnung strebsam rührt daher, dass durch einen sog. angestrebten Zustand festgelegt werden kann, welcher Zustand für das Zustandsobjekt wünschenswert wäre. Strebsame Zustandsobjekte haben aber Abhängigkeiten zu anderen Zustandsobjekten - sog. Restriktive Zustandsobjekte -, die entsprechende Zustandsübergänge verhindern können. Ob der angestrebte Zustand zum tatsächlichen Zustand wird, hängt also davon ab, ob die restriktiven Zustandsobjekte dies zulassen. Die Bezeichnung integriert meint, dass es sich um ein Netz von gleichartigen Zustandsobjekten handelt, die sich gegenseitig beeinflussen.

Zum Beispiel beim Service-Container von Siemens haben Komponenten nacheinander die Zustände Deployed, Created, Initialized und Active, dann ist die Komponente hochgefahren und bereit für ihre Verwendung. Im Übrigen wird aus technischer Sicht das entsprechende Attribut als Enumeration abgebildet. Mit jedem Übergang zwischen zweier dieser Zustände sind notwendige Aktivitäten verbunden, die jeweils erfolglos durchgeführt werden können und daraufhin das Hochfahren der Komponente ggf. abgebrochen werden muss. Zudem gilt, dass Komponenten von anderen Komponenten abhängig sein können und beim Hochfahren sichergestellt sein muss, dass eine Komponente erst Active wird, bevor ihre abhängige Komponente Active werden kann. Indes können andere Transitionen unabhängig von den Komponenten-Abhängigkeiten passieren. Und dieser Service-Container hat eine weitere Anforderung: Zwar muss eine Komponente beim Hochfahren u.U. darauf warten Active zu werden, bis ihre Abhängigkeiten Active sind, hingegen darf eine Komponente beim Herunterfahren nicht blockiert werden. Das bedeutet, dass Komponenten ihre abhängigen Komponenten ggf. zwingen müssen auf Initialized herunterzufahren, sodass sie selbst zum Herunterfahren im Stande sind.

Insgesamt also eine komplexe Anforderung an eine Zustandsklasse. Hier ist besonders ersichtlich, dass eine Auslagerung solcher Funktionalitäten in Zustandsklassen sinnvoll ist, statt diese direkt in jene Klassen zu implementieren, in denen sie gebraucht werden. Denn diese haben i.d.R. andere Dinge zu erledigen und würden sonst unnötig überladen werden. Die Integrierte Strebsame Zustandsklasse ist modular implementiert. Des Weiteren kann sie durch eine überschaubare Schnittstelle einfach verwendet werden.

Der Aufbau der Diplomarbeit

In der vorliegenden Ausarbeitung werden zunächst in Kapitel 2 Grundbegriffe wie Zustand und Framework diskutiert, bevor im Kapitel 3 das Verteilte System von Siemens vorgestellt wird. Insbesondere wird dabei auf den Service-Container und dessen Lifecycle-Management eingegangen, denn dies soll durch das Zustandsframework letztlich realisiert werden. Kapitel 4 schafft dann aus Sicht des Service-Containers die Motivation für die Entwicklung des Zustandsframeworks. Im folgenden Kapitel wird dieses auf konzeptioneller Ebene vorgestellt. Erst wird auf das grundlegende Modell eines Zustandsobjektes eingegangen, dann werden die einzelnen Arten von Zustandsobjekten vorgestellt. Kapitel 6 behandelt die Implementierung des Frameworks. Für ein gutes Verständnis wird Bezug zu angewendeten Design Patterns genommen, sowie zu Programmierprinzipien, vor allem objektorientierter Natur. Anschließend wird die Programmierung des Service­Containers unter Anwendung der Zustandsklassen gezeigt. Und zum Abschluss beschreibt Kapitel 8 mögliche Erweiterungen des Frameworks, die insbesondere auf ein allgemeineres Einsatzgebiet abzielen, bis dann Kapitel 9 mit der Einordnung des beschriebenen Frameworks in das Paradigma der Zustandsorientierung die Arbeit abrundet.

2. Grundlegende Begriffsdefinitionen

2.1. Zustand

Nach Grady Booch ist ein Zustand eines Objekts wie folgt definiert[2]:

The state of an object represents the cumulative results of its behavior.

Helmut Balzert hat eine technischere, mehr auf die Objektorientierte Programmierung bezogene Definition[3]:

Der Zustand (state) eines Objekts wird durch seine Attributwerte bzw. Daten und die jeweiligen Verbindungen zu anderen Objekten bestimmt.

Darüber hinaus definiert er den Begriff Verhalten folgendermaßen:

Das Verhalten (behavior) eines Objekts wird durch seine Menge von Operationen beschrieben.

In der OOP werden Programme mithilfe von Klassen strukturiert. Klassen bestehen aus Attributen und aus Methoden. In Rahmen der UML spricht man meist von Operationen, wobei eine Operation genau genommen die Spezifikation einer Methode darstellt[4]. Die Methoden einer Klasse dienen dem Zugriff auf deren Attribute. Im Idealfall greifen nur die Methoden einer Klasse auf deren Attribute zu.

Dadurch werden die Attribute durch die zugehörigen Methoden gekapselt. Sie stellen die Integrität ihrer Attribute sicher. Konkrete Ausprägungen von Klassen heißen Objekte. Sie wiederum besitzen Attributwerte und weisen ein Verhalten auf. Das Verhalten wird durch die Methoden beschrieben. Die Attributwerte eines Objekts definieren dessen Zustand.

Werden durch einen Methodenaufruf Attributwerte eines Objekts verändert, so ändert sich also der Zustand eines Objekts. Ein Zustandsübergang bzw. eine sog.

Transition findet statt.

Zustände und Zustandsübergänge können durch sog. Zustandsautomaten beschrieben werden. In der UML erfolgt dessen grafische Darstellung durch UML Zustandsdiagramme. Sie können sich auf unterschiedlichen Abstraktionsniveaus befinden. Das denkbar feingranularste Zustandsdiagramm würde für jede Konstellation von Attributwerten einer Klasse einen eigenen Zustand benennen. So ein Diagramm wäre in den allermeisten Fällen völlig unübersichtlich und unsinnig.

Die in einem Zustandsdiagramm dargestellten Zustände vertreten i.d.R. Mengen von Attributwerten und nur wenn zwischen verschiedenen Mengen ein Übergang stattfindet, ist tatsächlich die Rede von einem Zustandsübergang.

(Abstrakte) Zustände werden manchmal in Anwendungen überwacht. Dabei übernehmen sog. Manager-Ob/ekte die Überwachung bzw. das Management anderer Objekte. Beispielsweise bei Containern in Verteilten System ist es üblich, dass ein Komponenten-Manager die Überwachung einer Komponente übernimmt. Er verfügt dazu über Attribute, die den Zustand der jeweiligen Komponente auf einem bestimmten Abstraktionsniveau ausdrücken. Entsprechende Attributwerte werden hier als Explizite Zustände bezeichnet. Sie befinden sich in eigens definierten Attributen, um abstrakte Zustände zu kennzeichnen. Im Rahmen der Komponentenverwaltung wäre dies z.B. ein Fehlerzustand oder ein Lifecycle- Zustand. Typischerweise werden explizite Zustände durch Enumerationen abgebildet.

2.2. Framework

Diese Ausarbeitung befasst sich mit einem Framework für eine zustandsorientierte Programmierung. Bei einem Framework handelt es sich um eine wieder verwendbare Teilanwendung, die spezialisiert werden kann, um maßgeschneiderte Anwendungen zu erstellen5. Es handelt sich also um ein Gerüst von Klassen, bei dem die Aspekte Wiederverwendbarkeit und Erweiterbarkeit betont sind.

Im Gegensatz zu Klassenbibliotheken sind Frameworks jeweils auf eine Familie verwandter Anwendungen bezogen. Klassenbibliotheken sind da weniger bereichsspezifisch. Des Weiteren sind Frameworks aktiv und Klassenbibliotheken passiv, was heißt, dass Frameworks den Kontrollfluss in der Anwendung steuern und[5]

Klassenbibliotheken das nicht tun[6]. Die Klassen eines Frameworks sind auch viel stärker miteinander gekoppelt, als dies bei Klassenbibliotheken der Fall ist. Die meisten Klassen einer Klassenbibliothek besitzen überhaupt keine Bindung untereinander.

2.3. Design Patterns

Design Patterns (Entwurfsmuster) in der Objektorientierten Programmierung wurden erstmals 1995 von der sog. Gang of Four (GoF) katalogisiert und in einem Buch veröffentlicht. Sie definieren darin Design Patterns wie folgt[7]:

[...] design patterns [...] are descriptions of communicating ob/ects and classes that are customized to solve a general design problem in a particular context.

Design Patterns besitzen demnach eine bestimmte Granularität. Sie beschreiben weder ganze Subsysteme, noch die Interna einer Klasse. Ihr Abstraktionsniveau liegt dazwischen.

Generell die Idee, Muster zu verwenden, geht auf den Architekten Christopher Alexander zurück. Er beschreibt in seinen Büchern eine Vielzahl von Mustern, die in der Architektur ihre Anwendung finden. Den Begriff des Musters definiert er folgendermaßen[8]:

Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice.

Die inzwischen gängige[9] Definition lautet:

Ein Muster ist eine Lösung eines Problems in einem bestimmten Kontext.

Die GoF beschreibt Design Patterns auf einheitliche Art und Weise. Jedes ihrer Musterbeschreibungen ist folgendermaßen untergliedert:

Abbildung in dieser Leseprobe nicht enthalten

Ohne hier auf Details eingehen zu wollen, lässt es allein die Aufstellung der Überschriften innerhalb einer einzelnen Entwurfsmusterbeschreibung erahnen, wie ausführlich die Darstellung ist.

In dieser Ausarbeitung werden speziell in Kapitel 6, in dem der Entwurf des Frameworks beschrieben ist, die verwendeten Patterns erwähnt und deren Absicht (Intent) nach der GoF wiedergegeben. Weiterführende Erläuterungen, warum gerade dieses Design Pattern und kein anderer Entwurf im Framework gewählt wurde, werden unterlassen. Stattdessen sei auf die GoF verwiesen, die in ihren umfangreichen Ausarbeitungen über Design Patterns die Begründungen liefern.

Mittlerweile gibt es eine ganze Reihe von Pattern-Büchern im OO-Umfeld, welche sich mitunter nicht nur mit Entwurfsmustern, sondern auch mit den grobgranulareren Architekturmustern auseinandersetzen. Falls es für den Leser als hilfreich erachtet wird, wird auch auf Patterns verwiesen, die über die 23 Ür-Musfer der GoF hinausgehen.

Der Grund, warum bei den Beschreibungen der Klassen des Frameworks überhaupt auf die jeweils angewendeten Design Patterns verwiesen wird, ist folgender: Vorausgesetzt der Leser besitzt ausreichend Kenntnisse über die entsprechenden Muster, so kann der Entwurf des Framework erheblich leichter vermittelt werden. Die Kommunikation über die Konzepte des Entwurfs wird einfacher.

3. Das SDS-Projekt

Das Siemens Distributed System (SDS) ist ein Siemens Softwareentwicklungsprojekt, um eine einheitliche Kommunikationsplattform anzubieten. Es ist ein Verteiltes System mit zwei[10] [11] Kernkomponenten, nämlich dem sog. SDS Communication Framework (CF) und dem SDS Service Container (Container).

Das SDS Communication Framework bietet eine nachrichtenbasierte Kommunikationsinfrastruktur sowohl für plattformunabhängige als auch größtenteils sprachunabhängige Interprozesskommunikation. Um mit beliebigen Programmiersprachen Dienste für SDS entwickeln zu können, muss jeweils ein sprachspezifischer Adapter angeboten werden. Derzeit existieren Adapter für C++, Java und C#.

Der SDS Service Container verwaltet SDS Services und SDS Shared Components. Hauptsächlich realisiert er deren Lifecycle-Management und publiziert ihre Zustände.

3.1. SDS Container

Aus technischer Sicht verwaltet ein Container SDS Services und SDS Components. Üblicherweise besteht ein Service aus mehreren Components. Der Service dient also zur Gruppierung von Komponenten. Allerdings kann eine Komponente auch stand­alone sein. Damit ist sie nicht Teil eines Services. In einem solchen Fall spricht man von einer sog. Shared Component.

Komponenten können voneinander abhängig sein. Service-interne Komponenten können zum einen von Komponenten abhängig sein, die sich im selben Service befinden, zum anderen können sie von Shared Components - also von alleinstehenden Komponenten - abhängig sein. Demgegenüber können Shared Components nur abhängig von anderen Shared Components sein.

Wie bereits erwähnt verwaltet der Container Services und Komponenten. Für diesen Zweck verwendet er ein Objekt vom Typ ComponentManager für jede einzelne Komponente und entsprechend wird ein Service von einem sog. ServiceManager überwacht.

3.2. Zustände von SDS Components

Ein ComponentManager hat im Allgemeinen drei (explizite) Komponentenzustände, nämlich Lifecycle State, Error State und Availability Indicator. Nachfolgend werden diese Zustandstypen vorgestellt.[12]

Lifecycle State

Abbildung in dieser Leseprobe nicht enthalten

Der ComponentManager verwaltet den Lifecycle State einer Komponente. Mögliche Zustände sind Deployed, Created, Initialized und Active.

Jede Komponente ist durch eine XML-Konfiguration beschrieben. Wenn entsprechende Konfigurationsdaten vorhanden sind, ist eine Komponente Deployed. Jede Komponente, die Dep/oyed ist, kann gestartet werden.

Der erste Schritt des Start-Prozesses einer Komponente ist es, ihre Assembly (z.B. eine dll) zu laden und das Komponentenobjekt anschließend zu instanziieren. Falls das einwandfrei funktioniert, erhält die Komponente den Zustand Created.

Anschließend sind typischerweise zwei Aktionen notwendig. Erstens kann die XML- Konfiguration Konfigurationsparameter für eine Komponente beinhalten (einfache Key-Value-Paare). Diese müssen in der Komponenteninstanz gesetzt werden. Und zweitens ruft der ComponentManager über ein Komponenten-Interface die Methode initializeO auf. Dies erlaubt der Komponente initialisierende Arbeiten durchzuführen. Beide Aktionen müssen erfolgreich bewältigt werden, um die Komponente in den Zustand Initialized zu überführen.

Nun der finale Schritt. Eine Komponente kann Abhängigkeiten zu anderen Komponenten haben, um deren Dienste in Anspruch zu nehmen. Zunächst verwendet der ComponentManager SetDependencyO, um alle referenzierten Komponenten zu installieren. Die Komponenten werden mittels Dependency Injection[13] untereinander bekannt gemacht - genau genommen durch Interface Injection. Danach wird die ActivateO-Methode der Komponente aufgerufen. Es können nur Abhängigkeiten zu Komponenten gesetzt werden, die selbst bereits aktiviert sind. Es ist also wichtig, die einzelnen Komponenten in der richtigen Reihenfolge zu aktivieren. Der letzte Zustand einer Komponente wird als Active bezeichnet.

Um eine aktive Komponente herunterzufahren ruft der ComponentManager, analog zum Start-Prozess, die DeactivateO-Methode der Komponente auf. Das Deaktivieren einer Komponente ist nur möglich, wenn keine weiteren aktiven Komponenten von dieser abhängig sind. Das bedeutet wiederum, die Reihenfolge in welcher Komponenten z.B. innerhalb eines Services deaktiviert werden, muss sinnvoll definiert sein. Eine erfolgreich deaktivierte Komponente befindet sich sodann im Zustand Initia/ized.

Während des Stopp-Prozesses einer initialisierten Komponente ruft der ComponentManager von seiner Komponente die Methode UninitializeO auf. Dieser Schritt ist stets unabhängig von anderen Komponenten. Anschließend ist die Komponente wieder Created.

Dann geht die Komponente zurück in den Zustand Dep/oyed, wo der Stopp-Prozess schließlich endet.

Error State

Abbildung in dieser Leseprobe nicht enthalten

Es gibt lediglich zwei verschiedene Ausprägungen von Error State: NoError und Fai/ed. NoError bedeutet eine Komponente ist funktionsfähig und Failed kennzeichnet eine Komponente als fehlerhaft.

Eine Komponente kann in jedem einzelnen Lifecycle State Fai/ed sein. Z.B. bedeutet beim Hochfahren die Kombination aus dem Lifecycle State Dep/oyed und dem Error State Fai/ed, dass der Erzeugungsprozess fehlgeschlagen ist, weil beispielsweise die DLL der Komponente nicht verfügbar war. Die Kombination aus dem Lifecycle State Created und dem Error State Fai/ed bedeutet, der Initialisierungsprozess schlug fehl usw. usf.

Availability Indicator

Eine Komponente kann ihren Grad von Verfügbarkeit selbst spezifizieren. Der sog. Avai/abi/ity Indicator ist durch eine Gleitkommazahl zwischen 0 und 1 angegeben.

Der Wert 0 definiert eine nicht-verfügbare Komponente, wohingegen der Wert 1 eine vollständige Verfügbarkeit angibt.

3.3. Zustände von Services

Obwohl ein Service lediglich einen Rahmen für konkrete Komponenten darstellt, hat er auch eigene explizite Zustände. Dies sind aggregierte Zustände seiner Komponenten. Ein Service besitzt einen Error State und einen Availability Indicator.

Error State

Der Error State von einem Service ist Failed, sobald mindestens ein Error State seiner Komponenten Failed ist; ansonsten ist er NoError.

Availability Indicator

Der Availability Indicator eines Services wird aus dem Mittelwert aus den Availability Indicators seiner Komponenten gebildet.

3.4. Abhängigkeiten zu verschiedenen Komponentenzuständen

Bis jetzt wurde nur auf Abhängigkeiten zwischen gleichartigen Komponentenzuständen eingegangen. Im Folgenden werden Abhängigkeiten zwischen verschiedenen Typen von Komponentenzuständen beschrieben:

Error State - Lifecycle State

Während dem Starten einer Komponente kann der Lifecycle State nur zum jeweils nächst höheren Level ansteigen (also Deployed -> Created, Created -> Initialized etc.) solange der Error State NoError bleibt. Ist der Error State einmal Failed, so wird der Startprozess gestoppt.

Availability Indicator - Lifecycle State

Eine Komponente kann ihren Availability Indicator nur eigenmächtig setzen, wenn ihr Lifecycle State Active ist. Ansonsten erzwingt der ComponentManager einen Availability Indicator von 0.

4. Motivation

Das Zustandshandling im SDS Container ist komplex. Unterschiedliche Ausprägungen einer Zustandsart sind voneinander abhängig. So aggregiert ein Service Zustände seiner Komponenten. Überdies setzen Komponenten für ihre Aktivierung voraus, dass assoziierte Komponenten zuvor aktiviert worden sind. Analoges gilt beim Herunterfahren. Außerdem sind Instanzen unterschiedlicher Zustandsarten voneinander abhängig. Beispielsweise ist ein Fortschreiten beim Hochfahren einer Komponente nur im Error State NoError möglich.

Das Zustandshandling im SDS Container ist veränderlich. Derartige Verhaltensweise ist nicht in Stein gemeißelt. Die Erfahrung bei Siemens zeigt, dass schon in vergangenen Versionen in diesem Zusammenhang mehrere Änderungen vollzogen worden sind. Auch der aktuelle Entwurf lässt unter Architekten Diskussionsbedarf zu. Das bedeutet nicht, dass er schlecht sein muss. Dennoch ist und bleibt hier viel Konzeptionelles schlichtweg Auslegungssache. Kritisch betrachtet werden kann z.B. ob Anzahl und Interpretation der einzelnen Zustände im Lebenszyklus optimal sind. Beim Availability Indicator kann man selbst über seine Existenz an sich streiten. Oder auch über die Details, wie: Ist ein Gleitkommawert zwischen 0 und 1 nicht zu allgemein? Sollte man hier keine feste Abstufung wählen?

Das Zustandshandling ist komplex und gegenüber Veränderungen anfällig. Diese Symptome drängen zu einer flexiblen, gut durchdachten Lösung. Natürlich kann man die Abhängigkeiten beim Lifecycle State auch direkt in der Klasse ComponentManager ausprogrammieren. Das ist die Klasse, die eine einzelne Komponente verwaltet und überwacht. Die Gefahr dabei ist, dass sie dadurch überladen, undurchsichtig und letztendlich schwer wartbar wird; Keine guten Voraussetzungen für etwas, das sich in Zukunft wahrscheinlich sowieso wieder bald ändern wird. Oft helfen bei notwendigen Änderungen an einer komplexen, unflexiblen Implementierung nur ein Löschen der entsprechenden Klassen und der radikale Neuanfang.

Aus diesen Gründen wird zur Organisation der expliziten Zustände im SDS Container ein flexibles Zustandsframework entworfen.

5. Konzeption

ln diesem Kapitel wird das Zustandsframework vorgestellt, welches einen zustandorientierten Programmieransatz unterstützt. Im Wesentlichen besteht das Framework aus Klassen, deren Instanzen im Folgenden als sog. Zustandsobjekte bezeichnet werden. Es werden jene Arten von Zustandsobjekten beschrieben, mit denen das Lifecycle-Management im SDS Container umgesetzt werden kann. Insofern orientiert sich das Framework an den Einsatz in einem Service-Container. Dennoch sind zumindest die meisten elementaren Arten von Zustandsobjekten (siehe 5.3.) recht universell verwendbar. In jedem Fall allgemeingültig ist das vorab präsentierte Modell eines Zustandsobjekts mit dessen Transitionsalgorithmus.

5.1. Modell eines Zustandsobjekts

Zustandsobjekte sind Objekte im Sinne der OOP. Sie sind Behälter für Zustände. Sie exponieren Zustände. Da es im Zusammenhang mit Zustandsobjekten nur so von Zuständen wimmelt, wird der Eindeutigkeit halber der Zustand eines

Zustandsobjekts auch als exponierter Zustand bezeichnet. Die Zustände in einem Zustandsobjekt sind von einem bestimmten Zustandstyp. Zustandsobjekte sind parametrisiert durch einen Zustandstyp.

Ein Zustandstyp definiert eine Menge von zusammengehörigen Zuständen, die durch Transitionen miteinander verkettet sind. Technisch gesehen werden Zustandstypen üblicherweise durch Enumerationen, aber auch durch elementare Typen (double, int...) abgebildet.

Zustandsobjekte besitzen zu jedem Zeitpunkt genau einen Zustand. Dieser wird durch einen sog. Transitionsalgorithmus (bzw. Zustandsübergangsalgorithmus) bestimmt. Der Transitionsalgorithmus ist durch die Art des Zustandsobjekts festgelegt. Er besitzt meist verschiedene Eingabegrößen. Durch jede Änderung einer Eingabegröße wird der Transitionsalgorithmus ausgeführt und ein oder mehrere Zustandsübergänge finden statt, wenn sich der neu ermittelte Zustand vom bisherigen Zustand unterscheidet.

Folgende Abbildung zeigt das Grundmodell eines Zustandsobjekts mit seinen Ein- und Ausgabeobjekten:

Abbildung in dieser Leseprobe nicht enthalten

Die Eingaben für den Transitionsalgorithmus werden vom Client und von sog.

Eingabe-Zustandsobjekten bestimmt. Der Client ist i.d.R. der Besitzer des Zustandsobjekts und hat somit direkten Zugriff auf dessen öffentliche Methoden. Generell kann er beliebige Parameter an den Transitionsalgorithmus übertragen und damit dessen Ausführung anstoßen. Eingabe-Zustandsobjekte sind Zustandsobjekte, die den Transitionsalgorithmus zur Ausführung bringen, wenn sich ihr eigener Zustand ändert.

Ist der Transitionsalgorithmus einmal gestartet, kann dieser während seiner Ausführung einen Aktivitäten-Handler benutzen. Ein Aktivitäten-Handler ist primär dazu da, die mit einer Transition verbundene Aktivität durchzuführen. Er wird aufgerufen, wenn vorläufig ein neuer Zustand ermittelt wurde und hat daraufhin die Möglichkeit, die entsprechende Transition zu blockieren, wenn die Abarbeitung der Aktivität nicht erfolgreich war. In einem solchen Fall bleibt der bisherige Zustand unberührt (deshalb vorläufig). Andernfalls wird der Zustand angenommen, so wie ihn der Transitionsalgorithmus ermittelt hat.

Ausgaben des Transitionsalgorithmus sind Transitionen bzw. Zustandsübergänge, was sich dadurch erkenntlich macht, dass er neue Zustände im Zustandsobjekt installiert. Für die Benachrichtigung von Zustandsübergängen können sich beliebige Objekte registrieren. Diese werden Transitions-Listener genannt.

Benachrichtigungen über Transitionen werden grundsätzlich in derselben Reihenfolge ausgeliefert, in der sie ausgelöst wurden (also: First come, first served).

Für die Verkettung von Zustandsobjekten zu ihren Eingabe-Zustandsobjekten wird derselbe Mechanismus verwendet, damit sie von deren Transitionen benachrichtigt werden. Ein Zustandsobjekt registriert sich bei dessen Eingabe-Zustandsobjekte für Zustandsübergänge. Demnach ist es selbst Transitions-Listener seiner Eingabe­Zustandsobjekte.

Die Existenz von Eingabegrößen, durch Client oder Eingabe-Zustandsobjekt zur Verfügung gestellt, sowie von Transitions-Listenern als auch von Aktivitäten­Handlern ist grundsätzlich optional. Allerdings sind mindestens eine Eingabegröße und ein Transitions-Listener erforderlich, andernfalls ist die Notwendigkeit eines entsprechenden Zustandsobjekts ad absurdum geführt.

5.2. Transitionsalgorithmus

Während schon im letzten Abschnitt der Transitionsalgorithmus grob vorgestellt wurde und dabei insbesondere seine externen Schnittstellen beschrieben sind, wird in diesem Abschnitt vor allem die innere Struktur näher beleuchtet.

Die nächste Abbildung zeigt das Modell des Transitionsalgorithmus im Überblick:

Abbildung in dieser Leseprobe nicht enthalten

Der Transitionsalgorithmus besteht aus einer Zielfunktion und einem Transferalgorithmus. Die Zielfunktion erhält Eingaben von außen, also entweder vom Client oder von Eingabe-Zustandsobjekten. Er ermittelt einen Zielzustand, den er an den Transferalgorithmus übergibt. Der Transferalgorithmus strebt die Überführung des bisherigen exponierten Zustands zum Zielzustand an. Wie dies prinzipiell vonstatten geht, bestimmt der sog. Transitionsmodus.

Bislang sind zwei verschiedene Transitionsmodi definiert, wenngleich auch noch weitere denkbar wären:

1. Direkt: Dieser Modus ist wohl der nächstliegende. Es wird versucht einen unmittelbaren Zustandsübergang vom bisherigen Zustand zum Zielzustand durchzuführen.
2. Sequentiell: Dieser Modus setzt einen sequentiellen Zustandstyp voraus. Das bedeutet, die möglichen Zustände müssen in einer Reihe definiert sein. Die Zustände haben grundsätzlich einen eindeutigen Vorgängerzustand und einen eindeutigen Nachfolgezustand. Ausnahmen sind der kleinste Zustand, der lediglich einen eindeutigen Nachfolgezustand kennt und der größte Zustand mit nur einem eindeutigen Vorgängerzustand. Die Zustände sind demnach in einer nicht-zyklischen doppelt verketteten Liste angeordnet.

Ist ein sequentieller Zustandstyp gegeben, so wird beim sequentiellen Transitionsmodus der bisherige Zustand sukzessive in den Zielzustand überführt; entweder aufsteigend in der definierten Reihenfolge der Zustände oder absteigend. Je nachdem, ob der Zielzustand größer oder kleiner als der bisher exponierte Zustand ist.

Hierbei werden ggf. mehrere Transitionen durch nur einmaliges Anstoßen des Transferalgorithmus durchgeführt. Wichtig ist, dass bei jeder einzelnen Transition auch die Transitions-Listener informiert werden müssen.

Wenn der Transferalgorithmus für Transitionen mit einem Aktivitäten-Handler kommuniziert, spricht man von sog. geführten (managed) Transitionen. Bei jeder einzelnen vorgesehenen Transition erteilt der Transferalgorithmus dem Aktivitäten- Handler den Auftrag, die mit einer Transition verbundene Aktivität durchzuführen. Wenn die Aktivität erfolgreich war, liefert der Handler eine positive Rückmeldung, wenn die Aktivität fehlerbehaftet war, liefert er eine negative Rückmeldung. Letzteres führt dazu, dass die Transition blockiert wird. Die Benachrichtigung der Transitions- Listener entfällt. Auch eventuell weitere vorgesehene Zustandsübergänge, wie es etwa bei sequentiellem Transitionsmodus der Fall sein könnte, werden unterlassen.

Im Gegensatz dazu gibt es bei nicht-geführten (unmanaged) Transitionen keinen Aktivitäten-Handler. Infolgedessen wird der von der Zielfunktion ermittelte Zustand letztendlich immer umgesetzt.

Als Beispiel für Zustandsobjekte mit Aktivitäten-Handler bzw. mit geführten Transitionen sei hier das oben beschriebene Lifecycle-State-Handling aufgeführt. Beim Hochfahren einer Komponente müssen bei jedem Zustandsübergang Aktionen durchgeführt werden. Tritt dabei ein Fehler auf, so hat sie im aktuellen Zustand zu verharren. Der Lifecycle State nutzt dazu einen Aktivitäten-Handler, der bei einem Fehler eine negative Rückmeldung gibt.

Zum Abschluss gibt folgender PAP die Arbeitsweise des Transferalgorithmus wieder. Exemplarisch zeigt er einen sequentiellen Transitionsmodus mit geführten Transitionen auf:

Abbildung in dieser Leseprobe nicht enthalten

5.3. Elementare Arten von Zustandsobjekten

Es sind viele sinnvolle Arten von Zustandsobjekten (kurz: ZOs) denkbar. Dieses Framework deckt diesbezüglich nur einen kleinen Teil ab. Im Folgenden werden Grundtypen beschrieben, die, ganz im Sinne eines objektorientierten Frameworks, für Erweiterungen offen sind.

Der Unterschied zwischen ihnen liegt darin, wie die Zielfunktion des Transitionsalgorithmus arbeitet und welche Eingabegrößen sie beeinflussen. Speziell beim Client als Eingabegröße ist auch noch zu berücksichtigen auf welche Art und Weise er dies tut.

Ob bei einem konkreten Zustandsobjekt geführte oder nicht-geführte Transitionen ausgeübt werden oder welcher Transitionsmodus (direkt oder sequentiell) zum Einsatz kommt, nimmt auf die Art des Zustandsobjekts keinen Einfluss. Beide Aspekte sind eher als zusätzliche Option zu betrachten.

In folgender Tabelle sind die Arten von Zustandsobjekten im Überblick dargestellt. Ausführlicher sind sie in den jeweiligen Unterabschnitten beschrieben.

Abbildung in dieser Leseprobe nicht enthalten

5.3.1. Einfache Zustandsobjekte

Einfache Zustandsobjekte besitzen als einzige Eingabegröße einen gewünschten Folgezustand. Dieser wird vom Client an die Zielfunktion übergeben. Die Zielfunktion stellt hier quasi eine Identitätsfunktion dar. Das bedeutet, was vom Client kommt, wird direkt als Zielzustand installiert.

Abbildung in dieser Leseprobe nicht enthalten

5.3.2. Aggregierte Zustandsobjekte

Aggregierte Zustandsobjekte bilden aus einem bis beliebig vielen Kind­Zustandsobjekten einen aggregierten Zustand.

Der Client parametrisiert den Transitionsalgorithmus durch eine Aggregatsfunktion. Das Zustandsobjekt selbst übergibt an die Aggregatsfunktion die Zustände der Kind­Zustandsobjekte und aktualisiert diese bei jeder Änderung eines Kind­Zustandsobjekts. Dabei sind alle Kind-Zustandsobjekte gleichwertig, d.h. kein Kind besitzt eine Sonderrolle. Aus der Aggregatsfunktion ergibt sich dann der Zielzustand.

Typische Beispiele für Aggregatsfunktionen sind die Minimum- und die Maximum­Funktion. Auch eine Durchschnittfunktion, selbst mit Gewichtungen, ist denkbar.

Abbildung in dieser Leseprobe nicht enthalten

5.3.3. Maskierte Zustandsobjekte

Maskierte Zustandsobjekte erlauben es einen Zustand anhand eines definierten Schemas, einer sog. Maske, in einen anderen Zustand umzuwandeln.

Ein Maskiertes Zustandsobjekt besitzt ein Original-Zustandsobjekt als Eingabegröße. Der Client definiert die Maske. Ändert sich der Originalzustand, so ermittelt der Transitionssalgorithmus mit Hilfe der Maske den entsprechenden maskierten Zustand.

Die Zustandstypen von Originalzuständen und maskierten Zuständen müssen nicht identisch sein. Wichtig ist nur, dass die Maske beide Zustandstypen kennt und Regeln für die Maskierung aller möglichen Originalzustände definiert.

Die Implementierung einer Maske kann vielseitig sein. Beispielsweise kann eine Hashtabelle verwendet werden, die als Key sämtliche Originalzustände besitzt und als Value die maskierten Pendants. Auch eine direkte Berechnung der maskierten Werte ist denkbar.

Abbildung in dieser Leseprobe nicht enthalten

5.3.4. Geschaltete Zustandsobjekte

Geschaltete Zustandsobjekte haben als Eingangsgrößen ein Primär-ZO und ein Sekundär-ZO. Der Client besitzt die Möglichkeiten, das Primär-ZO zu aktivieren bzw. zu deaktivieren. Das bedeutet, der Zustand des Primär-ZOs wird zum Zielzustand bzw. der des Sekundär-ZOs wird zum Zielzustand.

[...]


[1] Dieser Chef ist zwar einfach gestrickt, dafür aber berechenbar.

[2] Grady Booch, 1993, Object-Oriented Analysis and Design, S. 200

[3] Helmut Balzert, 2000, Lehrbuch der Software-Technik, S. 156

[4] Vgl. Hitz et al., 2005, UML@Work, S. 37

[5] Vgl. Bernd Brügge, Allen Dutoit, 2004, Objektorientierte Softwaretechnik, S. 364

[6] Vgl. Bernd Brügge, Allen Dutoit, 2004, Objektorientierte Softwaretechnik, S. 366

[7] Gamma et al., 1995, Design Patterns, S. 3

[8] Alexander et al., 1977, A Pattern Language

[9] Vgl. Eric Freeman, Elisabeth Freeman, Entwurfsmuster von Kopf bis Fuß, 2006, S. 579

[10] Name geändert

[11] Offiziell gehört noch eine dritte Komponente zu den SDS-Kernkomponenten, nämlich ein Persistenzdienst. Der wird hier aber nicht weiter beschrieben.

[12] Die Zustandstypen werden nicht vollständig beschrieben; nur insoweit sie hier relevant sind.

[13] Siehe Martin Fowler, 2004, http://martinfowler.com/articles/injection.html

Ende der Leseprobe aus 106 Seiten

Details

Titel
Framework für Zustandsorientierte Programmierung
Hochschule
Fachhochschule Regensburg
Note
1
Autor
Jahr
2007
Seiten
106
Katalognummer
V186431
ISBN (eBook)
9783869437095
ISBN (Buch)
9783869431826
Dateigröße
1415 KB
Sprache
Deutsch
Schlagworte
framework, zustandsorientierte, programmierung
Arbeit zitieren
Christian Silberbauer (Autor:in), 2007, Framework für Zustandsorientierte Programmierung, München, GRIN Verlag, https://www.grin.com/document/186431

Kommentare

  • Noch keine Kommentare.
Blick ins Buch
Titel: Framework für Zustandsorientierte Programmierung



Ihre Arbeit hochladen

Ihre Hausarbeit / Abschlussarbeit:

- Publikation als eBook und Buch
- Hohes Honorar auf die Verkäufe
- Für Sie komplett kostenlos – mit ISBN
- Es dauert nur 5 Minuten
- Jede Arbeit findet Leser

Kostenlos Autor werden