oop-trainer.de
Ralf Schneeweiß

Komponentenbasierte Entwicklung von Embedded Software


Komponentenbasierte Architektur verspricht höhere Flexibilität, vereinfachte Wartung und in reifem Zustand auch höhere Qualität von Software in eingebetteten Systemen. Sie bringt aber auch grundlegende Änderungen für den Einsatz der Software und die Arbeit der Entwicklungsteams mit sich. Dieser Artikel diskutiert die Chancen und Anforderungen komponenten­basierter Entwicklung für die Architektur und den Entwicklungsprozess.

Einleitung

Im Gegensatz zur Softwareentwicklung in der Unternehmens-IT existiert für die Embedded Software Entwicklung kein breit gefächertes Angebot praxisgetesteter Komponententechnologien. Standardisierte Methoden und Architekturen nehmen für die Entwicklung von Geschäftsanwendungen Entscheidungen vorweg, die in der Entwicklung von eingebetteten Systemen getroffen werden müssen und ganze Entwicklungsphasen wesentlich bestimmen. Das liegt zum einen daran, dass die Hardware- und Betriebssystemplattformen der Unternehmens-IT viel stärker standardisiert und vergleichbar sind als bei eingebetteten System und zum anderen, dass die Unternehmens-Software eine längere Tradition als Massenprodukt hat als das eingebettete System.

<<Abbildung: 4 plus 1 Sichten Architekturmodell von P. Kruchten>>
 
Abbildung 1: Das 4 plus 1 Sichten Architekturmodell von P. Kruchten.

Aus der Einbettung des Software­systems in einen größeren Kontext folgt eigentlich nur, dass die Qualität des Gesamt­kontextes von der Software abhängig ist. Die Software kann auch nicht ständig neu konfiguriert oder erneuert werden. Die Software muss also ein stabiles Fehler- und Ausnahme­konzept besitzen, was auf die Bestand­teile der Software übertragen werden muss. Diese Rahmen­bedingung stellt sich also auch an eine Komponenten­technologie, die im eingebetteten System zur Anwendung gebracht werden soll.

Darüber hinaus gibt es praktisch keine Beschränkung, was den Entscheidungs­prozess zur Auswahl einer adäquaten Technik erheblich erschwert. Denn genau diese Freiheit ist es, die anfängliche Misserfolge verursacht und die Gefahr in sich birgt zuviel Technik für die angestrebte Software aufzuwenden. Die schon in Unternehmens-IT eingesetzte Technik bietet sich gewissermaßen an, auch in einem eingebetteten System eingesetzt zu werden. Das kann dazu führen, dass man sich mit dieser Technik auch Komplexität, Laufzeit- und Footprintprobleme erkauft.
Es muss also eine Abwägung der einzelnen Technikaspekte stattfinden. Mit anderen Worten: eine echter Software­architektur­prozess. Die große Varianz in der Konfektion der eingebetteten Systeme zwingt dazu. Ein solcher Architekturprozess soll in diesem Artikel durchgesprochen werden und in seinen verschiedenen Aspekten nachvollziehbar gemacht werden. Dazu orientiert sich der Artikel am 4 plus 1 Sichten Architekturmodell von P. Kruchten (siehe Abbildung 1).



Motivationen für die Komponentisierung

Es gibt verschiedene Motivationen für die Komponentisierung von Software. Einige wesentliche sollen hier vorgestellt werden:


  • Verständlichkeit der Architektur
  • Unabhängige Entwicklung von Systemteilen
  • Rekombinierbarkeit der Komponenten
  • Wiederverwendbarkeit
  • Separate Testbarkeit von Teilen des Systems
  • Wartbarkeit durch bessere Strukturierung

Daneben gibt es aber auch technische Anforderungen, die eine Komponentisierung erzwingen:


  • Verteilung über Plattformgrenzen
  • Updatefähigkeit von Systembestandteilen


<<Abbildung: Logische Sicht>>

Die logische Sicht in der Architektur

Die logische Sicht der Architektur zeigt prinzipielle abstrakte Strukturen in der Software. Dazu muss zunächst identifiziert werden, aus welchen grundsätzlichen Bestandteilen sie besteht. Mit einer solchen Systemzerteilung kommt man zu einer ersten Komponentensicht, die sehr stark von Fachwissen und architektonischem Erfahrungs­wissen geprägt ist. Dieser Erfahrungs­horizont wird nicht nur individuell von einem Softwarearchitekt erarbeitet, um in eine Architektur eingebracht zu werden. Viele Architekturideen existieren als publizierte Muster oder als Allgemeinwissen. Die aus diesen Zutaten entstandene Systemskizze muss zunächst relativ abstrakt sein. Komponenten in dieser Architekturphase spiegeln nicht unbedingt die Komponenten wieder die später im fertigen System erkennbar sind. Diese prinzipiellen Komponenten sind abstrakt und müssen nun in ihren Beziehungen zueinander verfeinert werden.

<<Abbildung: Komponentendiagramm einer Stereoanlage>>
 
Abbildung 2: Komponentendiagramm einer Stereoanlage.

Damit ist man an der Schnitt­stellen­frage angelangt. Welche Schnitt­stellen­technologie soll verwendet werden? Gibt es eine existierende Komponenten­technologie, die den Anforderungen gerecht werden kann? Entscheidungen aufgrund dieser Frage­stellungen führen zu einer ersten Konkretisierung der Architektur in dem Sinne, dass man sich prinzipiell auf Techniken festlegt.

Der nächste Schritt könnte bereits in der Definition der Schnittstellen liegen. Man stellt sich die Frage, welche Interaktion soll zwischen den identifizierten Komponenten stattfinden und welche Methoden dazu nötig sind. Das führt zu kategorisierten und letztendlich zu typisierten Schnittstellen. Abbildung 2 zeigt ein UML Komponenten­diagramm mit Schnittstellen zwischen den Komponenten einer einfachen Stereoanlage. Insbesondere die Schnittstellen­beziehung zwischen dem Verstärker und den Lautsprecherboxen ist interessant, da sie eine Multiplizität besitzt. Es können entweder ein Paar Lautsprecher oder eben zwei Paare angeschlossen werden. Der Schnittstellentyp könnte jetzt in dem Komponenten­diagramm noch genannt werden. Die Aussage des Diagramms bezieht sich bisher auf Komponenten­typen und nicht auf Instanzen oder Rollen, denn es ist in dem Diagramm nicht gezeigt, dass es einen rechten und einen linken Lautsprecher geben muss.

Natürlich ist das gewählte Beispiel sehr einfach und stammt zudem nicht aus der Welt der Software. Prinzipiell soll Softwarearchitektur aber System­bestand­teile und deren Beziehungen sichtbar machen. In diesem Sinne kann man Software­komponenten ohne weiteres mit Hardware­komponenten vergleichen.



<<Abbildung: Physische Sicht>>

Die physische Sicht in der Architektur

Die physische Sicht wird im Rahmen von Komponenten­architektur wird auch oft die Rollensicht genannt. Es werden in dieser Sicht keine prinzipiellen Bestand­teile, sondern konkrete gezeigt. Dabei tritt die Schnittstellen­darstellung in den Hintergrund und wird durch die Port-Darstellung ersetzt. Diese muss nicht ohne die Darstellung der Schnittstellen­typisierung auskommen. Im Vordergrund steht aber die Sichtbarkeit der real existierenden Verbindungen zwischen den Komponenten in einem Softwaresystem. Solche Verbindungen lassen sich nur darstellen, wenn auch alle Instanzen von Komponenten sichtbar sind. In einem Komponenten­diagramm das eine physische Systemsicht zeigt, stellen die Komponenten also keine abstrakten Typen dar, sondern real existierende Komponenten­instanzen in ihren jeweiligen Umweltbeziehungen. Man nennt ein solches Diagramm unter anderem auch Rollendiagramm, da die Instanzen für das Gesamtsystem bestimmte Rollen einnehmen.

<<Abbildung: Rollendiagramm einer Stereoanlage>>
 
Abbildung 3: Rollendiagramm einer Stereoanlage.

Die Beziehungen der Komponenten­instanzen in einer solchen System­sicht werden durch Ports realisiert. Ports sind eins-zu-eins-Sichten von Beziehungen. Keine typi­sierten Schnitt­stellen die eine Verbin­dungs­mög­lichkeit dar­stellen und eine Multiplizität beinhalten können, sondern exakt die Beziehung die im realen System zwischen den Komponenten aufgebaut wird. Dabei spielt die Schnitt­stellen­typisierung besten­falls eine unter­geordnete Rolle.

In der UML werden die Ports als kleine Rech­tecke an der Komponenten­grenze darge­stellt, die mit einer Verbindungs­linie zu einem ebensolchen Port an einer anderen Komponente verbunden sind . Ein solches Verteilungsdiagramm wie in Abbildung 3 gezeigt kann auf den ersten Blick einem herkömmlichen Komponentendiagramm sehr ähnlich sein. Es enthält aber Komponenten­instanzen mit ihren Rollenbezeichnungen (zwei Lautsprecher­instanzen mit den Rollen rechts und links). Außerdem werden die Instanzen in ihrer Umgebung dargestellt. Die Systemkonfiguration muss in einem solchen Verteilungs- oder Rollendiagramm sichtbar werden.

In vielen Softwareprojekten wird eine adäquate physische Sicht auf die Komponentenarchitektur vergessen. Man ist der Meinung, dass Komponenten­diagramme ausreichen die Typenaussagen liefern. Dadurch fehlt häufig eine adäquate Systemsicht. Man kennt zwar grundsätzlich die Art der eingesetzten Komponenten, nicht aber die Zahl ihrer Instanzen und ihre Laufzeit­interaktion mit anderen Komponenteninstanzen. Aus Architektursicht kann das schon einer Art Blindflug gleichen, da man keine wirkliche Vorstellung vom laufenden System entwickeln kann.



<<Abbildung: Entwicklungssicht>>

Die Entwicklungssicht

In der Entwicklungssicht stellt man sich auf die durch die Komponententechnologie veränderten Rahmen­bedingungen ein. Man ergreift Chancen, die sich ohne Komponentisierung nicht geboten hätten.

Eine der naheliegenden Vorteile komponentenorientierter Architektur ist natürlich die Möglichkeit unterschiedliche Systembestandteile separat und unabhängig voneinander zu entwickeln. Zwischen verschiedenen Versionen und Varianten der einzelnen Systembestandteile können unterschiedliche Kombinationen gebildet werden. Was zunächst als Chance erscheint verlangt aber sehr schnell nach einem sehr konsequenten Konfigurations­management­prozess, um die Kombinations­möglichkeiten überhaupt noch beherrschbar zu machen. Man reduziert die möglichen Kombinationen auf die nötigen, um den Testaufwand in Grenzen zu halten.

Eine weitere Chance bietet sich beim Test. Ist die Komponente funktional schön abgegrenzt, kann sie in einer eigens für den Test geschaffenen Umgebung getestet werden. Ein solcher "Sandkasten" (engl.: sand box) verspricht die einfache Ortung von Fehlern in den Komponenten ohne störende Einflüsse von Fremdcode. Ausserdem beschleunigt sich die turn-around-Zeit des Entwicklers (Codierung-Compilierung-Test), denn es muss nicht jedesmal das Gesamtsystem gestartet werden, um den Code zu testen der gerade in Entwicklung ist.
Auch zu diesem Aspekt muss gesagt werden, dass die Theorie besser klingt als es in der realen Softwareentwicklung aussieht. Da die meisten Komponenten zu ihrem Funktionieren andere Komponenten zur Interaktion brauchen, lassen sich selten reine sand boxes definieren. Man muss die benötigten anderen Komponenten entweder mit in den Test integrieren oder man muss sie durch eine Testimplemetierung vorspiegeln. Solche vorgespiegelten und nur für die Testdurchführung geschriebenen Komponenten heißen Mock-Komponenten. Ihre Entwicklung und Pflege kostet viel Entwicklungszeit.
Darüber hinaus ersetzt der Test der Einzelkomponente nicht den Test des Komponentenaggregats. Also entweder einer Gruppe von Komponenten die im System zusammenwirken müssen, oder eben des Gesamtsystems. Es kann sich also auch im Testbereich schnell Ernüchterung breit machen mit der Einsicht, dass nicht alle gedachten Vorteile auch tatsächlich erreichbar sind. Auch in diesem Bereich liegt die Kunst in der Reduktion aufs Wesentliche.

Nicht zuletzt ist die Architektur gefragt sich über die Testbarkeit der Systemkomponenten Gedanken zu machen und bei deren Testtiefe und Testintensität Gewichtungen vorzunehmen. Überlässt man diese Entscheidungen dem Entwickler, läuft man Gefahr nicht zielgerichtete Testaufwände zu generieren.



<<Abbildung: Prozesssicht>>

Die Prozesssicht

Wie im Einführungskapitel bereits angedeutet, muss für den Einsatz von Komponenten­technologie im Bereich der Embedded Software eine ausgeprägter Architektur­prozess existieren, da die Varianz der Systeme dort nur selten den Einsatz einer Standard­technologie erlaubt. Der Architekturprozess ist jedoch nicht der einzige wichtige Prozess, der von der Umstellung auf Komponententechnologie berührt ist.

In der Prozesssicht möchte ich deshalb das Augenmerk auf die Bereiche legen, die alle von großer Bedeutung für die Komponentisierung von Software sind:

  1. Projektmanagement
  2. Architekturprozess
  3. Konfigurationsmanagement
  4. Systemtest.

Architektur und Projektmanagement

Man kann leicht akzeptieren, dass die Komponentisierung eines Softwaresystems viel Architekturarbeit mit sich bringt. Dennoch sollte man durch den nun sichtbaren technischen Aufwand nicht vergessen, dass die Architektur letzendlich den Anforderungen an das System zu dienen hat. Die Aufstockung des Architekturteams zur Lösung der mit der Komponentisierung aufgeworfenen Fragestellungen kann dazu führen, dass die Architektur selbstreferentiell zu arbeiten beginnt. Sie löst Probleme, die sich aus dem Einsatz von Technik ergeben. Damit erfüllt sie technische Anforderungen mit Technik aus denen sich neue Anforderungen ergeben.

Die Umstellung von nicht komponentenorientierter Entwicklung auf Komponententechnik muss aus den genannten Gründen vom Projektmanagement eng und über einen langen Zeitraum begleitet werden. Es muss sichergestellt werden, dass die Systemanforderungen von der Architektur adressiert werden und dass die Dokumentation einfach, verständlich und verfügbar ist.

Häufig erweisen sich schwergewichtige Modellierungstools gerade im letztgenannten Punkt als sehr hinderlich, wenn die verfügbaren Lizenzen den Leserkreis einschränken oder die Dokumentation nur an speziell vorbereiteten Arbeitsstationen abgerufen werden kann. Architektur verlangt nicht nach teuren oder komplizierten Werkzeugen. Es ist möglich auch mit verbreiteter Standardsoftware eine Architekturdokumentation zu erstellen, die lesbar und vollständig ist. Die Forderung an das Architekturteam nach Verständlichkeit kann helfen. All diese Forderungen dem Architekturteam gegenüber durchzusetzen erfordert Problembewusstsein und Ausdauer. Eine echte Managementaufgabe.

<<Abbildung: Entwicklungsbranches eines monolithischen Systems.>>
 
Abbildung 4: Entwicklungsbranches eines monolithischen Systems.

Konfigurationsmanagement und Systemtest

Kommen wir nun zum Konfigurationsmanagement und zur Software- bzw. Systemintegration: ein Vorteil aus komponenten­basierter Entwicklung ist natürlich die Möglichkeit der Rekombinierbarkeit der Teile und damit auch die Varianten­vielfalt des Systems. Diese Vielfalt muss aber auch beherrscht werden. Viele Varianten bedeuten einen erhöhten Testaufwand. Es muss die Frage geklärt werden, welche Varianten überhaupt einem Test unterzogen werden sollen, um den Testaufwand nicht übermäßig wachsen zu lassen. Die getesteten Varianten müssen dann von den nicht getesteten unterschieden und mit Versions­informationen versehen werden, um sie von den nicht getesteten abzugrenzen. Damit eröffnet sich ein Problemfeld das beherrscht werden will.

In einem nicht komponentenorientierten Umfeld macht man schnell die Erfahrung, dass die parallele Entwicklung unterschiedlicher Features sich gegenseitig stören kann. Die Abhängigkeiten der Quellcodes untereinander sind nicht immer auf das mögliche Mindestmaß reduziert, so dass eine Änderung an der Funktionalität A zu einem Versagen der Funktionalität B auch dann führen kann, wenn beide inhaltlich nichts miteinander zu tun haben. Programmiersprachen wie C oder C++ verführen zu einer solchen unbeabsichtigten Abhängigkeit von Systembestandteilen untereinander. Das ist ein Aspekt, der neben anderen durch die Komponentenorientierung gelöst werden soll.

<<Abbildung: Entwicklungsbranches von Komponenten.>>
 
Abbildung 5: Entwicklungsbranches von Komponenten.

Zunächst einmal lassen sich durch Komponenten­orientierung verschie­dene funktionale Aspekte in ihrer Entwicklung parallelisieren. Diese Parallelisierung führt zu einer ruhigeren und damit effektiveren Vorwärt­sentwicklung unterschied­licher Teile des Gesamtsystems, denn sie stören sich nicht gegenseitig, wie es bei monolithischen Systemen der Fall ist. Die Abgrenzung der Komponenten hat auf architek­tonischer Ebene bereits eine inhaltliche Abgrenzungs­leistung gefordert. Schließlich mussten die zu entwickelnden Komponenten identifiziert werden und mit funktionalen Eigenschaften ausgestatten werden. Die Abgrenzung mündet in der Definition von Schnittstellen über die die verschiedenen Systemteile kommunizieren. Dieser Formalisierungs­schritt in den Abhängigkeiten der Teilsysteme reduziert unbeabsichtigte Abhängigkeit. Es bleibt die formale Abhängigkeit der Implementierung von den Schnittstellen die im Idealfall - bei maximaler innerer Kohäsion der Komponenten und maximaler Entkopplung der Einzel­komponenten voneinander - die rein sachlich inhärente ist. Diese Reduktion der unbeabsichtigten Abhängigkeiten der Systemteile führt also zu einer ruhigeren Entwicklung. Umso bewusster findet nun der Integrationsschritt zum Gesamtsystem statt.

<<Abbildung: Integration von Komponenten in ein Gesamtsystem.>>
 
Abbildung 6: Integration von Komponenten in ein Gesamtsystem.

In den Abbildungen 4 bis 6 sind prinzipielle Release- oder Versions­stränge gezeigt, wie sie durch ein Versionierungs­werkzeug (CVS, Subversion, GIT, usw.) verwaltet werden können. Abbildung 4 zeigt ein monolithisches System mit vielen Teil­integrationen, während Abbildung 5 ein System zeigt, das in Komponenten zerteilt ist. Jede Komponente hat dabei einen eigenen Releasestrang, der nur wenig von Feature- oder Bugfix­integrationen berührt wird. Wenn etwas integriert wird, dann hat es auch inhaltlich mit der Komponente zu tun.

Dafür wird ein zusätzlicher Integrations­schritt nötig, der die Einzel­komponenten zum Gesamtsystem integriert - Abbildung 6. Dieser Integrationschritt ist ein zeitlicher Punkt auf dem Releasestrang des Gesamtsystems. Dieser Realesastrang kann auch zu einem releasebaum führen, wenn man Varianten beabsichtigt. Um solche Systemreleases zu bauen müssen die Schnittstellen stabil sein, damit die Komponenten zueinander passen. Die Vorwärtsentwicklung der Schnittstellen muss also geplant ablaufen, da sie für das Gesamtsystem wichtig sind. Es deshalb ratsam, die Schnittstellen unabhängig von den Komponenten auf einem separaten Versionsstrang zu verwalten und so stabil als irgend möglich zu halten. Jede Änderung an den Schnittstellen zieht Änderungen an den betroffenen Komponenten nach sich. Die Implementierungen müssen also nachgezogen werden. Daher muss Stabilität als eines der obersten Prinzipien in der Schittstellen­entwicklung angesehen werden.











09.09.2012

Ralf Schneeweiß



Am 22.09.2011 hat an der Technischen Akademie Esslingen ein Symposium zur Softwarearchitektur stattgefunden. Im Rahmen dieses Symposiums wurde der Inhalt des Artikels in einem Workishop bearbeitet. Die Präsentationen zu diesem Workshop finden Sie als PDF-Dateien über die folgenden Links:

                    PDF-Datei mit der Präsentation zur Komponententechnologie im Embedded System
                    PDF-Datei mit der Präsentation zur Umstellung auf Komponententechnologie



Zum Anfang des Dokuments.