Komponentenbasierte Entwicklung von Embedded SoftwareKomponentenbasierte 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 komponentenbasierter Entwicklung für die Architektur und den Entwicklungsprozess. EinleitungIm 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.
Aus der Einbettung des Softwaresystems in einen größeren Kontext folgt eigentlich nur, dass die Qualität des Gesamtkontextes 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 Ausnahmekonzept besitzen, was auf die Bestandteile der Software übertragen werden muss. Diese Rahmenbedingung stellt sich also auch an eine Komponententechnologie, die im eingebetteten System zur Anwendung gebracht werden soll.
Darüber hinaus gibt es praktisch keine Beschränkung, was den Entscheidungsprozess 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. Motivationen für die KomponentisierungEs gibt verschiedene Motivationen für die Komponentisierung von Software. Einige wesentliche sollen hier vorgestellt werden:
Daneben gibt es aber auch technische Anforderungen, die eine Komponentisierung erzwingen:
Die logische Sicht in der ArchitekturDie 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 Erfahrungswissen geprägt ist. Dieser Erfahrungshorizont 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.
Damit ist man an der Schnittstellenfrage angelangt. Welche Schnittstellentechnologie soll verwendet werden? Gibt es eine existierende Komponententechnologie, die den Anforderungen gerecht werden kann? Entscheidungen aufgrund dieser Fragestellungen 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 Komponentendiagramm mit Schnittstellen zwischen den Komponenten einer einfachen Stereoanlage. Insbesondere die Schnittstellenbeziehung 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 Komponentendiagramm noch genannt werden. Die Aussage des Diagramms bezieht sich bisher auf Komponententypen 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 Systembestandteile und deren Beziehungen sichtbar machen. In diesem Sinne kann man Softwarekomponenten ohne weiteres mit Hardwarekomponenten vergleichen.
Die physische Sicht in der ArchitekturDie physische Sicht wird im Rahmen von Komponentenarchitektur wird auch oft die Rollensicht genannt. Es werden in dieser Sicht keine prinzipiellen Bestandteile, sondern konkrete gezeigt. Dabei tritt die Schnittstellendarstellung in den Hintergrund und wird durch die Port-Darstellung ersetzt. Diese muss nicht ohne die Darstellung der Schnittstellentypisierung 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 Komponentendiagramm das eine physische Systemsicht zeigt, stellen die Komponenten also keine abstrakten Typen dar, sondern real existierende Komponenteninstanzen in ihren jeweiligen Umweltbeziehungen. Man nennt ein solches Diagramm unter anderem auch Rollendiagramm, da die Instanzen für das Gesamtsystem bestimmte Rollen einnehmen.
Die Beziehungen der Komponenteninstanzen in einer solchen Systemsicht werden durch Ports realisiert. Ports sind eins-zu-eins-Sichten von Beziehungen. Keine typisierten Schnittstellen die eine Verbindungsmöglichkeit darstellen und eine Multiplizität beinhalten können, sondern exakt die Beziehung die im realen System zwischen den Komponenten aufgebaut wird. Dabei spielt die Schnittstellentypisierung bestenfalls eine untergeordnete Rolle.
In der UML werden die Ports als kleine Rechtecke an der Komponentengrenze dargestellt, die mit einer
Verbindungslinie zu einem ebensolchen Port an einer anderen Komponente verbunden sind
In vielen Softwareprojekten wird eine adäquate physische Sicht auf die Komponentenarchitektur vergessen. Man ist der Meinung, dass Komponentendiagramme 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 Laufzeitinteraktion mit anderen Komponenteninstanzen. Aus Architektursicht kann das schon einer Art Blindflug gleichen, da man keine wirkliche Vorstellung vom laufenden System entwickeln kann.
Die EntwicklungssichtIn der Entwicklungssicht stellt man sich auf die durch die Komponententechnologie veränderten Rahmenbedingungen 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 Konfigurationsmanagementprozess, um die Kombinationsmö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. 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.
Die Prozesssicht
Wie im Einführungskapitel bereits angedeutet, muss für den Einsatz von Komponententechnologie im
Bereich der Embedded Software eine ausgeprägter Architekturprozess existieren, da die Varianz der
Systeme dort nur selten den Einsatz einer Standardtechnologie 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:
Architektur und ProjektmanagementMan 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.
Konfigurationsmanagement und SystemtestKommen wir nun zum Konfigurationsmanagement und zur Software- bzw. Systemintegration: ein Vorteil aus komponentenbasierter Entwicklung ist natürlich die Möglichkeit der Rekombinierbarkeit der Teile und damit auch die Variantenvielfalt 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 Versionsinformationen 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.
Zunächst einmal lassen sich durch Komponentenorientierung verschiedene funktionale Aspekte in ihrer Entwicklung parallelisieren. Diese Parallelisierung führt zu einer ruhigeren und damit effektiveren Vorwärtsentwicklung unterschiedlicher Teile des Gesamtsystems, denn sie stören sich nicht gegenseitig, wie es bei monolithischen Systemen der Fall ist. Die Abgrenzung der Komponenten hat auf architektonischer Ebene bereits eine inhaltliche Abgrenzungsleistung 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 Formalisierungsschritt 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 Einzelkomponenten 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.
In den Abbildungen 4 bis 6 sind prinzipielle Release- oder Versionsstränge gezeigt, wie sie durch ein Versionierungswerkzeug (CVS, Subversion, GIT, usw.) verwaltet werden können. Abbildung 4 zeigt ein monolithisches System mit vielen Teilintegrationen, 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 Bugfixintegrationen berührt wird. Wenn etwas integriert wird, dann hat es auch inhaltlich mit der Komponente zu tun. Dafür wird ein zusätzlicher Integrationsschritt nötig, der die Einzelkomponenten 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 Schittstellenentwicklung 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:
| |||||||||||||||||||||||||||||||