6                  Framework-Partitionierung

Der wohl wichtigste Einfluß auf die Evolution eines Frameworks ist unserer Ansicht nach die scharfe Abgrenzung des unterstützten Modells. Es muß von Anfang an explizit gemacht werden, auf welchen Annahmen das Framework basiert und was der Kern seines fachlichen Modells ist. Nur so ist eine Diskussion mit allen Beteiligten möglich, um soviel Erfahrung auf dem Anwendungsgebiet einzubringen wie möglich.

Die Abgrenzung des Aufgabenbereiches findet zum einen für das Framework als Ganzes statt. Zum anderen haben wir es aber auch als sehr hilfreich erfahren, das Framework in Teilbereiche aufzuteilen, für die ebenfalls genaue Aussagen getroffen werden, welche Abstraktionen sie repräsentieren sollen und auf welche Konzepte sie sich stützen.

Mit der Aufteilung einher geht die Strukturierung des Frameworks. Bei der Entwicklung des Micrologica-Telefonie-Frameworks haben wir eine Strukturierungs-Technik entwickelt, die wir als Partitionierung bezeichnen. Dadurch, daß einzelne fachliche Konzepte voneinander getrennt werden, werden Design-Entscheidungen über ihre Realisierung und ihre Beziehungen zueinander explizit gemacht und stehen der konstruktiven Kritik offen. Zusätzlich bringt die Strukturierung von Frameworks ähnliche Vorteile für die objektorientierte Programmierung wie die Modulbildung für die imperative Programmierung: Sie ermöglicht eine Reduzierung der Komplexität, unterstützt das Information Hiding und erlaubt es, Abstraktionsstufen auszudrücken.

Ich möchte daher zunächst noch einmal die Partitionen des Telefonie-Frameworks vorstellen und wie wir zu ihnen gekommen sind. Daraus abgeleitet stelle ich dann das Konzept eines Design-Zentrums vor, das bei der Abgrenzung des Aufgabenbereichs des Frameworks hilft.

Die Technik der Partitionierung wird dann allgemein diskutiert, und es werden Konstruktions­techniken erörtert, die helfen, unabhängige Partitionen zu bilden.

6.1          Die Partitionen des Telefonie-Frameworks

In Kapitel 4.3 habe ich bereits erwähnt, daß das Telefonie-Framework in einzelne "Bereiche" unterteilt ist. An dieser Stelle möchte ich nun auf die Gründe eingehen, die zu dieser Partitionierung geführt haben und anhand welcher Kriterien sie vorgenommen wurde.

Um zu Strukturierungskriterien zu kommen, haben wir uns an den in der Analyse erkannten Problemen der Telefonie orientiert (siehe Kapitel 3.5). Bei der Modellierung haben wir uns von den Gegenständen einer realen Telefonie-Konfiguration leiten lassen. Wir haben uns bemüht, das im Framework realisierte Modell den bisher verwendeten Begriffen und Konzepten so ähnlich wie möglich zu gestalten. Das zentrale Element ist daher eine "virtuelle" Tk-Anlage. Die verwendeten Protokolle, deren ständiger Wechsel erwartet wird, sind in eigenen Partitionen gekapselt. Die Funktion dieser Partitionen ist eindeutig festgelegt, und auch die Interaktion mit anderen Partitionen kann abstrakt beschrieben werden. Die konkrete Realisierung variiert. Diese Modellierung vergegenständlicht die Dinge, mit denen die Telefonie-Spezialisten bereits seit langem gearbeitet haben. Es war daher auch leicht möglich, über sie zu diskutieren und Mißverständnisse zu erkennen, bevor das vollständige Modell implementiert und lauffähig war. Auch bei der Wahl der Bezeichnungen haben wir uns (von wenigen in der bisherigen Praxis existierenden Zweideutigkeiten abgesehen) an der bestehenden Fachsprache orientiert.

Obwohl bei Micrologica eine ganze Reihe von Telefonie-Anwendungen erstellt werden, haben wir das Einsatzgebiet des Frameworks auf die Erstellung von Tk-Anlagen-Treibern einge­schränkt. Die zunächst beabsichtigte Erstellung von generischen Komponenten, die in beliebigen Telefonie-Anwendungen verwendbar sind, wurde verworfen, da es sich zeigte, daß es nur bei einer klaren Festlegung des Einsatzkontextes möglich war, konkrete Aufgaben­verteilungen und Kommunikationsbeziehungen festzulegen.

Bei der Entwicklung weiterer Frameworks werden möglicherweise Komponenten dieses Frame­works wiederverwendet oder verallgemeinert. Dies setzt aber auch wieder eine konkrete Abgrenzung des Einsatzkontextes voraus.

Über die Festlegung auf Tk-Anlagen-Treiber hinaus haben wir ausdrücklich einige Grund­annahmen gemacht, die den Einsatzkontext weiter einschränken:

·      es wird erwartet, daß in Zukunft immer mehr Tk-Anlagen mit CSTA-konformen oder zumindest CSTA-ähnlichen Telefonie-Konzepten arbeiten werden; die Kommunikation mit solchen Anlagen sollte daher einfach zu bewältigen sein, für anders geartete Anlagen wird eine aufwendigere Protokolladaption akzeptiert

·      die Interprozeßkommunikation wird höchstwahrscheinlich auch in Zukunft über die PSI-Middleware stattfinden, so daß es hier nicht zwingend notwendig, ist eine völlig abstrakte Schnittstelle zu definieren

·      die wichtigste Zielplattform ist Novell Netware, um aber eine möglichst weitgehende Plattformunabhängigkeit zu erreichen, werden nur portable C++ Funktionen verwendet. Alle weiteren Plattformabhängigkeiten sollen von der PSI-Middleware gekapselt werden.

Die Partitionierung erlaubt es, diese Designentscheidungen jeweils in einer eigenen Partition zu kapseln. Die Beziehungen der Partitionen drücken die Hierarchie der fachlichen Abstraktionen aus.

Abbildung 41 zeigt die vollständige Partitionierung des Telefonie-Frameworks sowie die Abhängigkeiten der Partitionen untereinander.

Abbildung 41: Die Partitionen des Telefonie-Frameworks

Zum einen sind durch diese Partitionierung die fachlichen Bereiche voneinander getrennt. Zum anderen wurde darüber hinaus jedoch ein besonderes Augenmerk auf die Abhängigkeiten der Partitionen voneinander gelegt. Sie sind stufenartig strukturiert und sind nur von Partitionen auf tieferen Stufen abhängig. Gegenseitige Beziehungen der Partitionen konnten dadurch beseitigt werden, daß die Kommunikation über Abstraktionen stattfindet, die auf tieferen Stufen angesiedelt sind.

Die Partitionen gruppieren die Klassen des Frameworks. Die logische Schnittstelle einer Partition ist jedoch reduziert und stellt nicht einfach alle Klassen der Partition zur Verfügung. Sie ist aufgeteilt in einen öffentlichen und einen privaten Teil. Die verwendete Programmier­sprache C++ stellt jedoch nur unzureichende Mittel zur Verfügung, um diese Konstruktion aus­zudrücken. Daher wird die Trennung zwischen dem öffentlichen und dem privaten Teil der Partition bisher nur implizit per Konvention bzw. in der Dokumentation ausgedrückt. Für spätere Versionen wird angestrebt, die Klassendeklarationen der privaten Klassen soweit wie möglich den Benutzern der Partition vorzuenthalten.

Die Partition der virtuellen Tk-Anlage besteht beispielsweise aus ca. 15 Klassen. Davon ist jedoch nur eine Klasse öffentlich. Die interne Realisierung wird entsprechend dem Fassade-Muster verborgen. Bei anderen Partitionen besteht die öffentliche Schnittstelle aus mehreren Klassen, aber fast nie sind alle Klassen öffentlich.

Abbildung 42: Unterstrukturierung der softwaretechnischen Basis

Die Partitionen sind z.T. weiter unterstrukturiert. Die softwaretechnische Basis (siehe Abbildung 42) besteht u.a. aus Klassen-Teams, die Behälter oder ein Meta-Object-Protocol realisieren. Ebenso bestehen die Telefonie-Basis-Objekte (siehe Abbildung 43) aus einer Reihe von Basisabstraktionen aus dem Telefoniebereich: den Objekten des Half-Call-Modells (siehe Kapitel 3.4.2), den Events der Tk-Anlage und den Requests an die Tk-Anlage (siehe Kapitel 3.4.1).

Abbildung 43: Unterstrukturierung der Telefonie-Basis-Objekte

Analog zur angestrebten Unabhängigkeit der Partitionen wird auch eine Trennung der unter­schiedlichen Abstraktionen unterhalb der Partitionen angestrebt. Im nachhinein wird deutlich: Die Kriterien zur Partitionsabgrenzung sind keineswegs starr. Sie hängen von der Größe des zu strukturierenden Frameworks ab und von der Komplexität der einzelnen fachlichen Bereiche. Innerhalb eines größeren, umfassenderen Frameworks könnten die jetzigen Partitionen die Unter­struktur einer größeren Partition bilden.

Die von uns gewählte Partitionierung ist nicht zufällig robust gegenüber Änderungs­anforderungen: Da sie sich an den realen Telefonie-Komponenten orientiert, konnte die lang­jährige Erfahrung der Micrologica-Mitarbeiter darauf übertragen werden. Die Erfahrungen über die Interaktion dieser Komponenten und die potentiellen Reibungspunkte waren zumindest leichter auf unser Modell übertragbar als auf ein völlig neues Modell, das stärker von den realen Gegenständen abweicht.

Diese Aufteilung hat sich als äußerst förderlich für die Evolutionsfähigkeit des Frameworks erwiesen. Da die Partitionen weitgehend voneinander entkoppelt sind, waren die meisten bisherigen Evolutionsschritte lokal innerhalb einer Partition.

6.2          Abgrenzung des Anwendungsbereichs für ein Framework

Um ein Framework auf seine Evolution vorzubereiten, muß man sich festlegen, mit welchen Arten von Änderungen man bereit wäre zu leben und was die Kern-Abstraktionen sind, auf die sich das Framework verläßt.

Dies mag zunächst selbstverständlich erscheinen. In der Praxis handelt es sich dabei um eine schwierige Gratwanderung: Bei einer exakten Festlegung des zu unterstützenden Bereichs kann die Unterstützung sehr spezifisch ausfallen. Je weiter der Bereich gewählt wird, desto generischer und allgemeiner wird die Unterstützung.

Andererseits sind Frameworks dazu gedacht, eine Reihe von Anwendungen zu unterstützen, so daß der abzudeckende Bereich nicht zu eng gewählt werden darf, denn schließlich muß der ökonomische Aufwand einer Framework-Entwicklung gerechtfertigt werden. Je mehr Anwendungen mit dem Framework realisiert werden können, desto leichter ist dies zu erreichen.

Im Telefonie-Framework wurde beispielsweise zunächst bewußt auf die Unterstützung anderer Bereiche außerhalb der konkreten Tk-Anlagen-Steuerung verzichtet, um diesen Bereich mit sehr konkreten Abstraktionen zu unterstützen.

Eine Entscheidung über den Fokus des Frameworks kann nur im konkreten Fall gefällt werden. Es hat sich jedoch als hilfreich erwiesen, die getroffenen Annahmen über die Art der zu unterstützenden Anwendungen explizit zu machen. Einem Außenstehenden (auch in der gleichen Firma) ist es oft nicht leicht ersichtlich, ob das Framework sein konkretes Problem unterstützen kann. Daher sollte dokumentiert sein, welche Grundannahmen vorliegen und was anpaßbar ist.

In [MätBisch 96] wird der Begriff Design-Zentrum verwendet, um die einem Framework zugrundeliegende Kern-Abstraktion zu beschreiben.

Definition 25: Design-Zentrum

Die zentrale Design-Abstraktion eines Frameworks wird als Design-Zentrum (engl. design center) bezeichnet. Es verkörpert die wichtigsten Entwurfsentscheidungen des Frameworks.

Bei der Evolution eines Frameworks muß das Design-Zentrum immer intakt bleiben. Sobald das Design-Zentrum eines Frameworks verändert werden muß, besteht die Gefahr von massiven Änderungen in allen auf dem Framework basierenden Anwendungen, da sich diese auf die vom Design-Zentrum verkörperte Grundannahme verlassen haben.

Bei der Konstruktion eines Frameworks kann Vorsorge getroffen werden, um das Design-Zentrum möglichst frei zu halten von den Änderungen in seiner Umgebung. Durch eine lose Kopplung mit seiner Umgebung werden "Sollbruchstellen" angelegt. Es bietet sich an, das Design-Zentrum in einer eigenen Partition zu realisieren.

Für die Evolution eines Frameworks bedeutet lose Kopplung eine Begrenzung der Aus­wirkungen von Änderungen. Zum einen ist die Schnittstelle zwischen den Partitionen schmaler, so daß bei vielen Änderungen keine anderen Partitionen tangiert werden. Zum anderen erlaubt eine lose Kopplung den Austausch von Partitionen, z.B. weil sie aufgrund der veränderten Anforderungen völlig neu geschrieben werden mußten.

6.2.1      Das Design-Zentrum des Telefonie-Frameworks

Im Telefonie-Framework repräsentiert die Partition "virtuelle Tk-Anlage" das Design-Zentrum. Die Annahme, daß alle Tk-Anlagen von Requests gesteuert werden und die Anlagen über Events ihren Zustand melden, ist für unser Modell zentral. Auch die Kommunikation der virtuellen Tk-Anlage mit ihrer Umwelt ist Teil des Design-Zentrums, denn sie entspricht unseren Annahmen über die Zusammenarbeit der virtuellen Tk-Anlage mit "Nachrichten­empfängern", über die mit den Anwendungen und der realen Tk-Anlage kommuniziert wird.

Um eine lose Kopplung des Design-Zentrums zu erreichen, wird die gesamte Partition von einer Fassaden-Klasse ocVirtualPbx repräsentiert (vergl. [GHJV 95], S. 185ff).

Abbildung 44: Funktion des Fassade

Über den Einsatz einer Fassaden-Klasse hinaus ist aber auch die Gestaltung der Schnittstelle dieser Fassaden-Klasse wichtig: In einer ersten Modellierung hatte sie etwa folgende Schnitt­stelle:

 

class ocVirtualPbx {

public:

      void vHandleMake(ocMakeCall * poMakeCall);

      void vHandleAnswerCall(ocAnswerCall * poMakeCall);

      void vHandleTransferCall(ocTransferCall * poMakeCall);

      // ...

};

Abbildung 45: Zu feste Kopplung der virtuellen Tk-Anlage

Diese Art der Modellierung hat zur Folge, daß für jeden neuen Request-Typ die Schnittstelle der virtuellen Tk-Anlage angepaßt werden müßte. Im Hinblick auf die schnell fortschreitende Entwicklung auf dem Markt für Tk-Anlagen wäre diese Art der Evolution sicherlich sehr unerfreulich.

Daher wurden die Requests in einer mehrstufigen Vererbungshierarchie implementiert (siehe Abschnitt 4.3.2). Diese Hierarchie macht der virtuellen Tk-Anlage in einer Oberklasse ocRequest genau diejenigen Eigenschaften zugänglich, die sie benötigt (hauptsächlich: Starten der Abarbeitung).

 

 

class ocVirtualPbx {

public:

void vHandleRequest(ocRequest * poReq);

 

// ...

};

Abbildung 46: Lose Kopplung der virtuellen Tk-Anlage

Auf diese Weise können weitere Request-Typen hinzukommen, ohne daß die virtuelle Tk-Anlage geändert werden muß. Andererseits wäre die virtuelle Tk-Anlage auch leicht auswechselbar, wenn aus unerwarteten Gründen die bisherige Implementation nicht mehr den Anforderungen genügt. Die neue Implementation müßte lediglich diese minimale Schnittstelle weiter unterstützen.

Die lose Kopplung hat aber auch noch eine weitere, indirekte Folge: Man stelle sich vor, daß die virtuelle Tk-Anlage das Request nach der Abarbeitung zurückgibt. Sie könnte dies nur unter der ihr bekannten (Ober-)Klasse tun. Es wäre das Problem des Empfängers, den dynamischen Typ des ihm übergebenen Objekts herauszufinden, wenn er auf speziellere Eigen­schaften zugreifen möchte. Auf die Implikationen des hierfür notwendigen Meta-Object-Protocol wurde bereits in Kapitel 5.2 ausführlich eingegangen.

6.3          Notwendigkeit der Framework-Strukturierung

Nachdem in diesem Kapitel die Partitionierung bisher nur auf das Telefonie-Framework bezogen wurde, möchte ich nun allgemeiner die grundsätzliche Notwendigkeit diskutieren, Frame­works zu strukturieren. Danach werde ich den bisher informell verwendeten Begriff der Framework-Partition definieren und Entwurfskriterien zur Partitionierung erörtern.

Ein wichtiger Einflußfaktor für die Framework-Evolution resultiert aus der internen Aufteilung des Frameworks. Denn obwohl ein Framework zunächst als geschlossene Einheit erscheint, gibt es gewichtige Gründe, Frameworks weiter zu untergliedern:

·      Begrenzung der Auswirkung von Änderungen

Um mit ungerichteter Evolution umgehen zu können, ist es sehr hilfreich, "Sollbruchstellen" zu haben, die die Auswirkungen von Änderungen begrenzen. Dies war im Telefonie-Framework eine der Hauptintentionen zur Partitionierung.

·      Handhabbarkeit

Wie bei allen nicht-trivialen Softwaresystemen ist es auch bei der Entwicklung und Pflege von Frameworks allgemein sehr hilfreich, wenn sie hinreichend fein strukturiert sind. Große Frameworks werden hierdurch überhaupt erst handhabbar.

·      Kombinierbarkeit von verschiedenen Frameworks

Oft haben Framework-Anwender das Bedürfnis, die Funktionalität von mehreren unabhängig voneinander entwickelten Frameworks zu benutzen. Neben anderen Kriterien ist eine klare Abgrenzung der einzelnen Funktionsbereiche innerhalb des Frame­works eine Grundvoraussetzung hierfür. Dies wird für das Telefonie-Framework erst dann relevant, wenn es z.B. zusammen mit einem GUI-Framework eingesetzt werden soll.

·      Reduzierung des Overhead

Je mächtiger ein Framework ist, desto größer ist auch die Wahrscheinlichkeit, daß es Funktionalität enthält, die von vielen Anwendungen nicht benötigt wird. Es wäre wünschenswert, diese Anwendungen nicht unnötig aufzublähen. Daher sollten einzelne Bereiche des Frameworks einzeln verwendbar sein, ohne daß ihre Referenzen zu anderen Teilen des Frameworks dazu führen, daß das gesamte Framework eingebunden werden muß.

Die Ähnlichkeit dieser Forderungen zur Modularisierung bei der imperativen Programmierung ist offensichtlich. Die meisten objektorientierten Sprachen bieten jedoch kein Ausdrucksmittel, um eine Modularisierung oberhalb der Ebene von Klassen auszudrücken.

Hierzu möchte ich im folgenden zwei Aspekte erörtern:

1.    die Aufteilung in einzelne Bereiche: die Partitionierung

2.    die Beziehungen der Bereiche untereinander: die Strukturierung

6.3.1      Framework-Partitionierung

Bei der Beschreibung des Micrologica-Telefonie-Frameworks habe ich allgemein von "Bereichen" des Frameworks gesprochen. Als Bezeichnung für die "Bereiche" eines Frame­works möchte ich den Begriff "Partitionen" etablieren, wie zu Beginn dieses Kapitels angedeutet.

Der Begriff "Partition" ist weniger mißverständlich als der Begriff "Layer", der in der Literatur des öfteren in ähnlicher Bedeutung verwendet wird (z.B. [BGKLRZ 97], [GHJV 95, S. 28]). Seit Dijkstra bezeichnen Layer eine strenge Schichtenarchitektur, bei der Kommunikation aus­schließlich mit angrenzenden Schichten möglich ist (vgl. [Dijkstra 68]). Auch das bekannte ISO 7-Schichten-Modell beinhaltet beispielsweise diese Annahme.

Diese strenge Trennung ist bei Frame­works meist nicht zu erreichen und auch nicht notwendig. Die höhere Flexibilität gegenüber einer reinen Schichten-Architektur ist gerade eine der Stärken von Frameworks. Eine reine Schichten-Architektur hat kein Ausdrucksmittel, um schichten­übergreifende Gemeinsamkeiten auszudrücken (vgl. [Campbell 92]). Um eine Austauschbarkeit zu gewährleisten, sollten die Partitionen untereinander jedoch lose gekoppelt sein.

Definition 26: Framework-Partition

Eine Framework-Partition enthält das fachliche und technische Design eines Teil­bereiches des Framework-Designs. Sie dient der Modularisierung des Frameworks. Ähnlich wie ein Framework wird sie von Klienten als Ganzes verwendet, bezieht sich aber auf andere Partitionen und ist nicht allein einsetzbar.

Es stellt sich jedoch die Frage, worin sich eine Partition von einem Framework unterscheidet. Tatsächlich wird in der Literatur oft von Frameworks gesprochen, die wieder aus diversen Frameworks bestehen. Diese Begriffsbildung führt jedoch zu einer inflationären Verwendung des Begriffs Framework, so daß kaum noch ein Unterschied zum Klassen-Team besteht. Ganz wesentlich für ein Framework ist aber das fachlich abgeschlossene Modell. Während Partitionen nur ein Teilmodell enthalten und nur in Kombination mit anderen Partitionen einsetzbar werden, sind Frameworks eigenständig verwendbar! Gelegentlich enthalten Partitionen so wenig vom fachlichen Modell, daß sie einzeln verwendbar sind. Dann haben sie allerdings nicht die Funktion eines Frameworks, sondern eher die Funktion einer Bibliothek. Im Telefonie-Frame­work betrifft dies beispielsweise die softwaretechnische Partition.

Die ursprüngliche Definition von Frameworks muß zur Abgrenzung daher wie folgt erweitert werden:

Definition 27: Framework (endgültige Fassung)

Ein Framework stellt ein generisches Design zu einer Reihe verwandter Probleme zur Verfügung. Es enthält bereits einen Teil der Implementation, muß für konkrete Anwendungen aber noch spezialisiert bzw. parametrisiert werden. Die Anwendung verwendet das Framework als Ganzes, so daß auch der Kontrollfluß der Anwendung vom Framework vorgegeben wird.

Das Framework enthält ein fachlich abgeschlossenes Modell und ist eigenständig verwendbar. Es benötigt zusätzlich höchstens eine softwaretechnische Unterstützung.

Die Framework-Partitionierung weist viele Parallelen zur Modularisierung bei imperativer Programmierung auf. Den meisten objektorientierten Sprachen fehlt es jedoch an Ausdrucks­mitteln für eine Modularisierung in größeren Einheiten als Klassen. Dies ist besonders deshalb schmerzlich, weil es daher kaum Möglichkeiten gibt, technisch die Schnittstelle einzu­schränken, die eine Partition nach außen anbietet. Wünschenswert wäre es, zwischen öffentlichen Schnitt­stellen-Klassen und privaten Implementierungs-Klassen unterscheiden zu können.

Das Cluster-Konzept von Eiffel unterstützt die Modularisierung hauptsächlich auf konzeptioneller Ebene. Die Cluster werden auf Verzeichnisse im Dateisystem abgebildet. Eine Möglichkeit, um die Schnittstelle eines Clusters zu definieren, gibt es nicht.

C++ hat überhaupt keine Mittel, um die Partitionierung auszudrücken. Sie muß daher mit Programmierkonventionen ausgedrückt werden. Die Einschränkung der Partitionsschnittstelle kann nur durch Kommentare und sonstige Dokumentation erfolgen.

Eine Ausnahme bildet Java: Hier gibt es das Package-Konzept, das es ermöglicht, jede Klasse einem Package zuzuweisen und zu definieren, ob die Klasse nur innerhalb dieses Packages sichtbar sein soll oder ob sie auch von außen zugreifbar ist. Auf diese Weise ist es möglich, die Schnittstelle von Packages durch die Sprachsyntax auszudrücken und abzusichern.

In Programmiersprachen, die diese Möglichkeit nicht bieten, sollte trotzdem die Unterteilung in öffentliche und private Klassen beibehalten werden. Diese muß dann durch Programmier­konventionen unterstützt werden.

6.3.2      Framework-Strukturierung

Ein besonderes Augenmerk sollte den Beziehungen der Framework-Partitionen untereinander gewidmet werden. Dabei sind sowohl Benutzungs- als auch Vererbungsbeziehungen zu beachten.

Diese Beziehungen möchte ich als Framework-Strukturierung bezeichnen.

Definition 28: Framework-Struktur

Die Beziehungen der Framework-Partitionen untereinander bezeichnet man als Frame­work-Struktur.

Um die Evolutionsfähigkeit eines Frameworks zu gewährleisten, sollte eine Framework-Struktur angestrebt werden, bei der die Partitionen stufenartig aufeinander aufbauen. Durch diese Stufenbildung (engl. levelization) wird erreicht, daß keine zyklischen Beziehungen zwischen den Partitionen entstehen, die die getrennte Weiterentwicklung behindern würden.

Abbildung 47: Bildung der Framework-Struktur durch vertikale Partitionierung

Um eine Stufenbildung zu ermöglichen, müssen die Partitionen entsprechend gebildet werden. Abbildung 47 zeigt die vertikale Partitionierung des Micrologica-Telefonie-Frame­works. Zur Partitionierung wurden Konzepte unterschiedlicher Abstraktionsniveaus verschiedenen Stufen zugeordnet. Fachlich unabhängige Abstraktionen wurden immer getrennt, auch wenn sie sich auf ein und derselben Stufe befinden.

So wurden das Telefonie-Protokoll und das Tk-Anlagen-Protokoll auf eine höhere Stufe gestellt als die Telefonie-Abstraktionen ("Telefonie-Basis-Objekte" genannt), auf denen beide basieren (vertikale Partitionierung).

Abbildung 48: Horizontale Partitionierung der Telefonie-Basis-Objekte

Auch innerhalb von Partitionen lassen sich weitere Partitionen finden. Abbildung 48 zeigt die horizontale Partitionierung der Telefonie-Basis-Objekte. Diese Konzepte stehen auf gleichem Abstraktionsniveau nebeneinander und sind nicht voneinander abhängig.

Definition 29: Horizontale Partitionierung

Die Aufteilung in Partitionen ähnlichen Abstraktionsgrads, die nicht aufeinander aufbauen, wird als horizontale Partitionierung bezeichnet.

Definition 30: Vertikale Partitionierung

Die Aufteilung in Partitionen, die aufeinander aufbauen, wird als vertikale Partitionierung bezeichnet.

Zur Darstellung der Abstraktionen einer Partition dürfen nur Konzepte aus der Partition selbst und aus darunter liegenden Stufen verwendet werden, nicht aber aus anderen Partitionen auf derselben Stufe oder aus höheren Stufen.

Definition 31: Stufenbildung

Unter Stufenbildung verstehen wir die Zuordnung der Framework-Partitionen zu auf­einander aufbauenden Gruppen. Abhängigkeiten dürfen jeweils nur zu Partitionen aus darunter befindlichen Stufen existieren.

Auf fachlicher Ebene sind die vertikale Partitionierung und Stufenbildung als Bildung von Abstraktionsebenen zu interpretieren, während auf technischer Ebene die Kopplung des Systems im Vordergrund steht. Es ist wichtig, diese Stufenbildung nicht nur im Design, sondern auch in der Implementation konsequent durchzuhalten. Auf die bei der technischen Realisierung auftretenden Probleme und den Umgang mit ungewollten Beziehungen zwischen Partitionen geht der folgende Abschnitt ein.

6.4          Konstruktionstechniken für Partitionen

Die Framework-Partitionierung entsteht, wie das ganze Framework, in einem evolutionären Prozeß. Dabei kommt es im Laufe des Entwicklungszyklus, z.B. durch Erweiterungen der Funktionalität oder Aufsplittung von Partitionen, oft dazu, daß Partitionen mit gegenseitigen Abhängigkeiten entstehen. Dadurch werden die aufgestellten Kriterien zur Framework-Strukturierung verletzt.

Die gegenseitigen Abhängigkeiten behindern die Stufenbildung und führen zu einer starken Kopplung innerhalb des Frameworks. In diesem Abschnitt möchte ich daher einige Konstruktions­techniken vorstellen, um diese gegenseitigen Abhängigkeiten aufzulösen.

Die Bildung von Partitionen stellt ein enges Zusammenspiel von fachlichen und strukturell begründeten Gesichtspunkten dar. Die Abgrenzung der Partitionen muß (siehe oben) nach fachlichen Gesichtspunkten durchgeführt werden. Andererseits gibt es strenge strukturelle Anforderungen an die Abhängigkeit der Partitionen untereinander.

Man muß sich, insbesondere wenn es wie hier um die Auflösung von Abhängigkeiten geht, vergegenwärtigen, daß Software-Design ein gestaltgebender Vorgang ist. Es gibt nicht das fachliche Modell, sondern es gibt viele mögliche Modelle. Welches Modell gewählt wird, hängt von vielen Faktoren ab - unter anderem auch von strukturellen Gesichtspunkten des aus dem fachlichen Modell folgenden technischen Modells.

Wenn man vor dem Problem steht, Abhängigkeiten zwischen Partitionen aufzulösen, kann es daher hilfreich sein, sich zu vergegenwärtigen, welche strukturellen Umformungs­möglichkeiten bestehen, um die Abhängigkeiten aufzuheben und welche Folgen diese für das fachliche Modell haben. Erst dann kann man sich entscheiden, welche Modellierung zu wählen ist.

Ich möchte nun einige Techniken vorstellen, die von Lakos für die Modulbildung vorgeschlagen wurden (vgl. [Lakos 96, Kapitel 5]), und diese auf die Entkopplung von Partitionen übertragen. Um die Diskussion anschaulich zu halten, werde ich im folgenden nur die Abhängigkeiten zwischen Klassen betrachten - die Abhängigkeit von Partitionen folgt aus den Abhängigkeiten der enthaltenen Klassen.

Definition 32: Eskalierung

Das Anheben gegenseitig verwendeter Funktionalität bei Klassen mit zyklischen Beziehungen wird als Eskalierung (engl. escalation) bezeichnet. Der von der jeweils anderen Klasse benötigte Teil wird extrahiert und auf einer höheren Stufe angesiedelt.

Abbildung 49 zeigt zwei voneinander abhängige Klassen (die Pfeile bedeuten hier eine allgemeine "ist angewiesen auf"-Beziehung) und wie sie mittels Eskalierung voneinander getrennt werden könnten.

Um eine Eskalierung durchzuführen, werden die Klassen in einen unabhängigen (xfrei und yfrei) und einen von der anderen Klasse abhängigen Teil (xabh und yabh) aufgespalten. Falls nach der Aufteilung xabh und yabh weiterhin voneinander abhängen, so können sie zu einer neuen Klasse z zusammengefaßt werden, wenn sich dies fachlich anbietet. Anderenfalls müssen sie analog zum ersten Schritt erneut aufgeteilt werden. Falls man bei diesem Vorgehen zwei gegenseitig abhängige Klassen behält, ist die Eskalation gescheitert bzw. nicht möglich.

 

 

zyklische Ausgangssituation

Aufteilung, Stufenbildung

endgültige Struktur

Abbildung 49: Eskalierung

Die Technik der Eskalierung bietet sich an, wenn nur ein kleiner Teil der Funktionalität der einen Klasse auf die andere Klasse angewiesen ist. Wenn x und y beispielsweise Klassen sind, die jeweils einen Konstruktor oder Konvertierungs-Operator füreinander haben, aber sonst voneinander unabhängig sind, bietet es sich an, die Konvertierung aus den Klassen zu entfernen und als neue Klasse z zu implementieren.

Definition 33: Degradierung

Das Absenken gegenseitig benutzter Funktionalität bei Klassen mit zyklischen Beziehungen wird als Degradierung (engl. demotion) bezeichnet. Der von der jeweils anderen Klasse benötigte Teil wird extrahiert und auf einer niedrigeren Stufe ange­siedelt.

 

 

zyklische Ausgangssituation

Aufteilung, Stufenbildung

endgültige Struktur

Abbildung 50: Degradierung

Zur Degradierung werden die Klassen analog zur Eskalierung aufgespalten. Sollten weiterhin Abhängigkeiten zwischen xabh und yabh bestehen, könnten beide zusammengefaßt oder erneut aufgespalten werden.

Die Technik der Degradierung bietet sich beispielsweise an, um gemeinsame Kommunika­tions­protokolle oder Basisfunktionalität aus Klassen zu extrahieren.

Definition 34: Redundanz

Die Mehrfach-Implementierung eines (möglichst kleinen) Teils der Funktionalität wird als Redundanz bezeichnet. Sie dient dazu, die gegenseitige Abhängigkeit zwischen Klassen aufzubrechen.

 

 

 

zyklische Ausgangssituation

endgültige Struktur

Abbildung 51: Redundanz

Eine redundante Implementierung trennt die Klassen völlig, indem der von der jeweils anderen Klasse benötigte Teil doppelt vorhanden ist. Hier wird zwar eine optimale Trennung erreicht, jedoch zum Preis eines verdoppelten Wartungsaufwandes, falls am redundanten Teil Änderungen notwendig werden. Diese Technik sollte daher mit Vorsicht und nur dann eingesetzt werden, wenn sehr kleine Teile redundant implementiert werden müssen.

Während man zyklische Abhängigkeiten zwischen Klassen-Teams im Ausnahmefall noch akzeptieren kann, sind sie zwischen Partitionen auf jeden Fall zu vermeiden.

Bei dieser Betrachtung der Framework-Strukturierung darf man die fachliche Motivation nicht aus den Augen verlieren und Klassen nur danach in Partitionen einteilen, wie ihre strukturellen Abhängigkeiten beschaffen sind. Wenn sich in einem Framework keine azyklische Partitionierung finden läßt, die den fachlichen Gegebenheiten entspricht, so ist dies ein starkes Indiz, daß die fachliche Modellierung überarbeitet werden sollte.

Die Wahl, welche Technik zum Aufbrechen von zyklischen Beziehungen verwendet wird (Eskalierung, Degradierung oder Redundanz), muß durch die fachliche Modellierung entschieden werden. Diese Techniken dürfen nicht Selbstzweck sein um zyklen- und sinn-freie Strukturen zu erzeugen, sondern müssen die fachliche Modellierung unterstützen.

Die Unabhängigkeit der Partitionen ist, über die hier dargestellten Gründe der Evolutions­fähigkeit hinaus, auch für andere Eigenschaften des Frameworks wichtig. Sie ermöglicht beispielsweise das getrennte Testen einzelner Partitionen oder erleichtert die arbeitsteilige Implementation des Frameworks. Für den Framework-Anwender wird durch die Dekomposition in kleinere Einheiten die Erlernbarkeit gefördert.

6.5          Partitionierungskriterien

Es gibt einige Ansätze für die Aufteilung von objektorientierten Systemen. Beispielsweise bietet die 3-Ebenen-Architektur (engl. three-tier architecture) Kriterien, um Anwendungen in drei Komponenten zu zergliedern: Front-End (Darstellung und Interaktion), fachliches Modell und Datenspeicherung (siehe [Hirschfeld 97]). Diese Aufteilung ist jedoch rein technisch motiviert, also unabhängig von der Komplexität der jeweiligen Bereiche in einer konkreten Anwendung. Sie skaliert nicht mit dem Umfang der Anwendung, sondern bietet immer nur drei Ebenen. Für die Strukturierung werden keine Kriterien angegeben.

Meiner Ansicht nach hängen die Kriterien für die Partitionsbildung sehr stark vom zu unterstützenden Anwendungsbereich ab und sollten aus dem fachlichen Modell abgeleitet werden. Ein weiteres Beispiel soll dies verdeutlichen:

Im Telefonie-Framework spielte bisher die Hardware-Konfiguration eine entscheidende Rolle und mit ihr technische Abwägungen über die zu unterstützenden Protokolle und Hardware. Dies spiegelt sich auch in der von uns gewählten Partitionierung wider. Sobald direkt Aufgabenbereiche von Benutzern unterstützt werden, böte sich eine Strukturierung anhand dieser Aufgabenbereiche an.

Wenn beispielsweise das Telefonie-Framework um weitere Funktionsbereiche erweitert wird, böte es sich an, die Framework-Partitionen an den Haupteinsatzbereichen von Telefonie-Systemen (Inbound- und Outbound-Geschäft) zu orientieren. Abbildung 52 zeigt eine mögliche Partitionierung eines umfassenderen Frameworks. Die bisher realisierte Funktionalität des Tk-Anlagen-Treibers wäre dann eine Partition unter vielen.

 

Abbildung 52: Mögliche Partitionierung eines umfangreicheren Telefonie-Frameworks

Ein Beispiel für ein Framework (das sog. GEBOS-Framework), dessen Aufteilung sich an den zu unterstützenden Aufgabenbereichen orientiert, findet sich in [BGKLRZ 97]. Dort wird das Framework anhand der Aufgabenbereiche im Bankgeschäft strukturiert.

Eine andere mögliche Art der Partitionsbildung könnte sich an der Organisationsstruktur des einsetzenden Unternehmens orientieren. Die in dieser Arbeit besprochenen Telefonie-Systeme sind Standardsoftware und können daher nur nach Funktionsbereichen strukturiert werden, denn der konkrete Einsatzkontext ist unbekannt. Eine auf einen speziellen Kunden zuge­schnittene Lösung könnte aber durchaus dessen Organisationsstruktur berücksichtigen. Beispiels­weise könnte die Partitionierung auch die Art der zu unter­stützenden Arbeitsplätze wider­spiegeln. Die Unterstützung für einen Kapitalanlageberater würde sicher anders aussehen als für den Zeitschriftenvertrieb per Telefon. Eine an der Organisationsstruktur orientierte Partitionierung könnte sich von der in Abbildung 52 vorgestellten Partitionierung beispiels­weise durch die fehlende Trennung in Inbound- und Outbound-Geschäft auszeichnen, wenn diese in der betrieblichen Praxis nicht vorhanden ist.

6.6          Zusammenfassung

Die Framework-Partitionierung basiert auf vielen in der Softwaretechnik bekannten Ansätzen und Methoden. Sie kombiniert unter der Bezeichnung "Layering" bekannte Techniken zur Schichtenbildung mit Konstruktionskriterien und -techniken, die u.a. aus der modularen Programmierung übertragen wurden.

Entscheidend ist jedoch die Folge der Partitionierung für die Evolution von Frameworks: Durch die explizite Modellierung einzelner Bereiche werden die Designentscheidungen verdeutlicht und können früh überprüft werden. Die Orientierung an den bereits bestehenden fachlichen Konzepten macht den Entwurf kommunizierbar - auch für Nicht-Software-Entwickler. Die Stufenbildung und lose Kopplung der Partitionen macht einzelne Design­entscheidungen auch im nachhinein leichter revidierbar.


 


 


Last updated: 24. Aug 2005
Page maintained by Jan Willamowius
Impressum · Datenschutz
 
English: Home | Linux | Perl | Java | Eiffel | Books | Music | Jan Willamowius | Updates | Site Map
Deutsch: Home | Badminton | ISBN-Suche | Musik-Suche | Rezepte | Jan Willamowius