Berücksichtigung zukünftiger Anforderungen in der SW-Architektur
Top1 Links und Referenzen
Top2 Begriffe und Abkürzungen
- AOSD
aspect oriented software design
- KPI
"key performance indicator", Leistungskennzahl
- MDSD
model driven software development
- TDD
test driven development, testgetriebene Entwicklung
- USP
"unique selling proposition", Alleinstellungsmerkmal
Top3 Einleitung und Überblick
Eine zukunftsfähige SW-Architektur sollte die innerhalb der Lebenszeit der SW
zu erwartenden Änderungen und Erweiterungen bereits in ihrem grundlegenden
Design konzeptuell berücksichtigen. Bei aller Weitsicht lässt es sich jedoch
nicht vermeiden, dass für ein langlebiges SW-Produkt auch unerwartete neue
Anforderungen und Teilaspekte hinzukommen, die bei der ursprünglichen
Konzeption noch nicht betrachtet wurden.
Unerwartete und erwartete Änderungen werden prinzipiell gleich behandelt.
Der Hauptunterschied liegt darin, dass ein System auf die
Umsetzung bereits erwarteter Änderungen
(hoffentlich) besser vorbereitet ist als auf die Realisierung völlig
unerwarteter Änderungen.
Top3.1 Typische Auslöser für (unerwartete) neue Anforderungen
- dringender Bedarf beim Kunden
- notwendiges "Mithalten" mit neuen Features der Wettbewerber
- Erschliessen neuer Märkte, die andere Erwartungen an das
Produkt stellen
- neue Technologie-Standards, z.B. GUI, cloud computing
- unerwartete Beschränkungen aufgrund der gewählten SW-Architektur,
deren Folgen sich erst im Lauf der Entwicklung zeigen
Top3.2 Eigenschaften von Änderungen
- Änderungen können immer erforderlich werden:
- während der Entwicklung, z.B. ausgelöst durch größeres Detailwissen
- In der Betriebs- und Wartungsphase durch Änderungen im umgebenden
System/Markt
- Je länger ein System in Betrieb ist, desto höher ist die Wahrscheinlichkeit, dass
unerwartete Änderungen auftreten. Der Zeitpunkt und der betroffene Aspekt des Systems
können mit steigender Lebensdauer des Systems umso schwieriger vorhergesagt werden.
- Je später eine Änderung in einem System berücksichtigt wird, desto sorgfältiger
muss darauf geachtet werden, im Änderungsprozess den Wert des bestehenden Systems zu erhalten
- Ein Aufschieben notwendiger Änderungen ist nicht empfehlenswert, da die Schwierigkeiten
zu einem späteren Zeitpunkt eher zunehmen.
Top3.3 Vorbereitung und Durchführung von Änderungen an einem bestehenden System
- Notwendig: Kosten/Nutzenanalyse, Abwägung der
(wirtschaftlichen und technischen)
Vor- und Nachteile einer Änderung, Betrachtung der Kosten für den
gesamten Lebenszyklus des Systems, nicht nur die momentan anfallenden
Entwicklungskosten,
Durchführung von Analysen zur Entscheidungsfindung
(z.B. Commonality/Variability Analysis, Feature Models, siehe
Software Product Line Engineering with Feature Models)
- Reviews der Architektur ("Architekturtests") sollten Teil der regulären
Qualitätssicherung
sein. Dies ist Aufgabe für SW-Architekten und Modulverantwortliche.
- oberstes Ziel ist die Verständlichkeit der Architektur,
allzu generische Lösungen sind eher zu meiden
- Nicht erwünscht: Zerstörung der erreichten Design- und Code-Qualität
- Ziel: möglichst lokale Umsetzung der Änderung, unerwünschte Seiteneffekte
sollen vermieden werden
- Erhalt der Fähigkeit, zukünftige Änderungen einzubauen
- Freiheitsgrade sinken mit zunehmener Zahl an Veränderungen, deshalb
die wichtigsten Änderungen zuerst durchführen
Top3.4 Gliederung der nachfolgenden Kapitel
- Abschnitt 4 beschreibt eine Auswahl bewährter Verfahren zur Beurteilung
anstehender Änderungen. Ziel dabei ist es, eine wohlbegründete Entscheidung
für oder gegen die Realisierung einer Anforderung treffen zu können.
- Abschnitt 5 beschreibt Merkmale von SW-Architekturen, die
spätere Erweiterungen und Anpassungen erleichtern.
- Abschnitt 6 gibt einen Überblick, wie Änderungen in ein bestehendes System
eingebracht werden können.
Top4 Bewertung von Anforderungen/Änderungen
Als Vorbereitung für eine Entscheidungsfindung ist eine Bewertung
unterschiedlicher Aspekte erforderlich.
Bewertung des geschäftlichen Nutzens
- Wird in ausreichendem Maße zusätzliches Geschäft generiert durch die
geplante Änderung?
- Wie hoch ist der geschäftliche Verlust, wenn die Anforderung nicht
umgesetzt wird?
Bewertung der Auswirkung auf die bestehende Architektur / das bestehende System
- Kann die Anforderung unter Beibehaltung der bestehenden grundlegenden
Architektur/Systemstruktur umgesetzt werden?
- Besteht die Gefahr, dass die Qualitäten des bestehenden Kernsystem (= vorhandenes Investment)
beeinträchtigt werden, wenn die Anforderung umgesetzt wird?
- Abschätzung der erforderlichen Entwicklungskosten
Verantwortlichkeiten
Es ist Aufgabe des
Projektmanagements Anforderungen zu bewerten, um so eine wohlbegründete, dokumentierte und nachvollziehbare Entscheidungsfindung zu gewährleisten. Es ist aber auch Aufgabe des
verantwortlichen
SW-Architekten, sich Kenntnis über die Entscheidungsabläufe zu verschaffen
und diese ggf. nachzufordern. Nur so ist sichergestellt, dass sich das System in diejenige
Richtung entwickelt, die auch von allen Beteiligten getragen wird.
Die Entscheidung für oder gegen eine Änderung beruht dabei i.d.R. auf der Abwägung der (wirtschaftlich bewerteten) Kosten und Nutzen.
Nur in Ausnahmefällen sollte eine "strategische" Entscheidung zu einer Änderung führen, die nicht einen entsprechendem (unmittelbaren) geschäftlichen Vorteil nach sich zieht.
Die unterschiedlichen Stakeholder verfolgen dabei ihre jeweils eigenen Interessen
(Marketing: Flyer, Verkaufsprospekte; SW-Architekt: technische Auswirkungen auf das
System). Wichtig für die Abstimmung und Entscheidungsfindung ist es, eine für alle Stakeholder gleichermassen verständliche Darstellung des Systems und der zu diskutierenden Anforderungen zu finden.
Im nachfolgenden wird eine Reihe von Vorgehensweisen zur Bewertung möglicher
Änderungen und umzusetzender Anforderungen vorgestellt.
Top4.1 Requirement Trees
Über das Modell eines Anforderungsbaumes (requirement tree) werden bewertete Anforderungen
in hierarchische Beziehung zueinander und zu den zugehörigen Konstrukten in der SW-Architektur
gesetzt. Die Bewertung erfolgt im Hinblick darauf, wieviel der Kunde bereit ist, für das
jeweils betrachtete Merkmal zu bezahlen.
Entsprechend den verschiedenen Hierarchieebenen betrachtet man dabei:
- Problemraum mit nach dem Geschäftserfolg bewerteten Anforderungen
- Business requirements:
Wozu wird das System entwickelt? Was sind die geschäftlichen Hauptziele?
- Explizite Kunden-Anforderungen (user requirements):
Was fordern die Anwender? Wie wird das System von den Anwendern eingesetzt?
- Abgeleitete System-Merkmale (system requirements):
Wie gut muss die geforderte Funktionalität umgesetzt werden?
- Lösungsraum mit Zuordnung zu den Requirements
Einzelne Teilsysteme der SW-Architektur werden in Beziehung zu den Systemrequirements gesetzt,
die sie umsetzen.
Beurteilung geplanter Änderungen
- Änderungen, die wichtige Geschäftsfaktoren (business drivers), Leistungskennzahlen
(KPIs, key performance indicators) oder Alleinstellungsmerkmale (USPs, unique selling
propositions) unterstützen, tragen wesentlich zum Geschäftserfolg bei.
- Änderungen im Lösungsraum / in der SW-Architektur, die wenig zu den wesentlichen
Erfolgsfaktoren (d.h. zu den hoch bewerteten Anforderungen) beitragen,
haben dagegen auch keine besondere Auswirkung auf den Geschäftserfolg.
Top4.2 Kategorisierung von Anforderungen nach dem Kano-Modell
Das Kano-Modell ist ein Bewertungsmodell zur Einordnung von Kundenwünschen. Es wurde
entwickelt von Noriaki Kano, Universität Tokio.
Prinzip: Durch Interviews oder Fragebögen werden die Kundenanforderungen in mehrere Klassen eingeteilt:
Zur Beurteilung der Relevanz von Änderungen sind insbesondere folgende Klassen interessant:
- Begeisterungs-Merkmale ("delighters", "exciters", nicht bekannte Anforderungen),
eine kleine Leistungssteigerung im Bereich dieser Merkmale führt zu einer großen Steigerung des geschäftlichen Nutzens
- unerwartete Eigenschaften, die das Produkt gegenüber der Konkurrenz auszeichnen und die
beim Kunden Begeisterung auslösen
- unterstützen oder schaffen ein Alleinstellungsmerkmal (USP, unique selling proposition)
auf dem Markt
- stärken allgemein die Marktposition
- Leistungs-Merkmale ("satisfiers", bewusste Anforderungen)
werden vom Kunden explizit gefordert, die Zufriedenheit steigt proportional zum Erfüllungsgrad
der Leistungsmerkmale
- Basis-Merkmale ("dissatisfiers", unbewusste Anforderungen)
implizite Erwartungen des Kunden, die erst bei Nichtvorhandensein auffallen; um Unzufriedenheit
zu vermeiden, müssen diese Merkmale vollständig erfüllt werden. Eine
Leistungssteigerung in diesem Bereich führt aber nur zu einer minimalen Erhöhung
des geschäftlichen Nutzens.
Ein völliges
Fehlen dieser Eigenschaften führt aber zu geschäftlichem Verlust:
- Beeinträchtigung des Produktimages
- Konkurrenz kann durch die Umsetzung dieser Merkmale leicht "davonziehen"
- Schwächung der Markpositionierung
Achtung: implizite Erwartungen werden oft nicht formuliert,
dadurch Gefahr des Vergessens!
Darüberhinaus gibt es noch folgende Klassen von Anforderungen:
- unerhebliche Merkmale (indifferent):
Die Kundenzufriedenheit ist unabhängig vom Vorhandensein dieser Merkmale
- Rückweisungsmerkmale:
führen bei Vorhandensein zu Unzufriedenheit
Die Kategorisierung der Anforderungen ändert sich mit der Zeit:
Aus Begeisterungsfaktoren werden Leistungsmerkmale und schliesslich Basis-Merkmale. Was den Kunden früher begeistert hat,
nimmt er zunehmend als selbstverständlich hin.
Top4.3 Software Architecture Analysis Method (SAAM)
Ziele
- szenariobasierte Architekturbewertung
- Untersuchung der qualitativen Eigenschaften einer SW-Architektur: Modifizierbarkeit,
Erweiterbarkeit, Portierbarkeit, Performanz
- Beurteilung des Funktionsumfangs einer SW-Architektur, dabei
Aufdeckung problematischer Situationen:
- eine Komponente realisiert viele Szenarios
- ein Szenario betrifft sehr viele Systemkomponenten
Ablauf
Nebeneffekte
- Verbesserung der bestehenden Architekturdokumentation (sofern diese nicht zur
Bewertung der Auswirkung geplanter Änderungen bereits ausreicht)
- Verbesserung der Kommunikation zwischen den Projektbeteiligten. Die zugrunde liegende
Architekturbeschreibung muss für alle Beteiligten verständlich sein
- Weiterverwendung der erstellten Szenarios als wertvolle Abnahme- und
Testkriterien
Top4.4 Architecture Tradeoff Analysis Method (ATAM)
- Ziele:
Erweiterung von SAAM um geschäftsrelevante Aspekte, Betrachtung
von Qualitätszielen, frühzeitige Identifikation von Risiken,
Abwägen des Schadens/Nutzens einer geplanten Änderung.
- Methode:
Analyse von Szenarios und der zugehörigen notwendigen Architektur-
Entscheidungen, dabei Klassifikation nach:
- Tradeoffs
Identifizierte Schwachpunkte im Design, Umsetzung eines betrachteten
Szenarios führt zur Beeinträchtigung erwünschter
Architektureigenschaften oder zur Verminderung der Fähigkeit,
andere Szenarios zu erfüllen.
- Sensitivy points
Identifizierte Schwachpunkte im Design, die bei einem Szenario auftreten.
- Non risks
Risikolose Aspekte. Die Szenarios und zugehörigen Architekturentscheidungen
sind ohne Probleme umsetzbar.
- Risks
Die Szenarios und zugehörigen Architekturentscheidungen stellen ein Risiko dar.
Zur Verminderung der Risiken sind ggf. Änderungen der geschäftlichen Ziele
und damit der Szenario-Anforderungen bzw. Korrekturen an der SW-Architektur
erforderlich.
Top4.5 Effort-Impact Grid Analysis
Grundforderung: für ein neues Feature / eine anstehende Änderung soll der erwartete
geschäftliche Nutzen deutlich größer sein als die aufzuwendenden Kosten.
Die zu betrachtenden Kosten müssen dabei den gesamten Lebenszyklus des Systems
betrachten: Entwicklung, Wartung, Update im Feld.
- Die Gitterdarstellung setzt Kosten und Nutzen zueinander in Beziehung.
Mit der Positionierung einer geplanten Änderung im Gitter erhält man
eine erste Entscheidungshilfe für oder gegen die Änderung.
- Als Ergebnis der gesamten Analyse erhält man als Grundlage für die Release-Planung:
- Priorisierte Liste der geplanten Änderungen
- vorrangig Änderungen, die mit kleinem Aufwand großen Nutzen bringen
- Änderungen mit kleinem Nutzen bei kleinem Aufwand eher zur späteren
Umsetzung vorsehen
- Änderungen mit großem Aufwand und großem Nutzen bedürfen ggf.
einer strategischen Entscheidung, wann sie umgesetzt werden
- Liste der abgelehnten Änderungen
Änderungen, die bei hohem Aufwand nur einen geringen geschäftlichen
Nutzen bringen, werden abgelehnt. Nur in Ausnahmefällen (z.B. langfristige
strategische Planung und Bezug zu anderen Zielsetzungen) werden sie umgesetzt.
Top5 Vorbereitung einer SW-Architektur auf Änderungen
Motivation
- Architektur = "everything that is costly to change"
- Beispiel aus der klassischen Architektur (Stewart Brand:
"How Buildings Learn: What Happens After They're Built")
- nur sehr wenige Gebäude können an veränderte Umgebungsbedingugen und
veränderte Nutzungszwecke angepasst werden.
- langlebige "alte" Architektur (z.B. Klöster) wird bis heute
laufend an veränderte Einsatzzwecke (Kloster, Lazarett, Lager, Schulungszentrum)
angepasst.
- Manch moderne Hochhaus-Architektur ist dagegen sehr kurzlebig
und bereits nach wenigen Jahrzehnten nicht mehr zu gebrauchen.
- Was ist das Kennzeichen langlebiger Architektur?
Ziele:
- Die bestehenden Eigenschaften und Qualitätsmerkmale einer Architektur
sollen beim Umsetzen neuer Anforderungen erhalten bleiben.
- Das Entwicklungsteam soll während des Änderungsprozesses
die Kontrolle behalten. Dazu gehört auch die Entscheidungsfreiheit
über Zeitpunkt und Ort der Änderung.
- Vermeidung von "rippling effects": eine Änderung an einer Stelle
zieht eine Reihe nachfolgender Änderungen nach sich, die Änderungswelle
pflanzt sich durch das ganze System fort
- Vermeidung von "shotgun surgeries": Um die neue Anforderung
umzusetzen, sind Änderungen an vielen Stellen des Systems notwendig.
- häufiges Prinzip: Begrenzung der Variabilität, dadurch Sicherung des
bisher Erreichten
Eine gut gewählte Architektur erhöht die Chance, dass kommende Änderungen
gut im bestehenden System umgesetzt werden können.
Im Folgenden werden wichtige Architektur-Eigenschaften und Entwurfsmethoden aufgeführt,
die die Änderbarkeit unterstützen.
Top5.1 Expressive modularization
- Die Komponenten der SW-Architektur sollen sowohl die
Grundstrukturen als auch die Details des tatsächlichen
Systems widerspiegeln.
- Komponenten, Interfaces und ihre Beziehungen sollten ausdrucksstarke Namen
aus der Anwendungsdomäne erhalten.
- Komponenten sollten eindeutige Verantwortlichkeiten besitzen.
- Jede Komponente sollte nur eine Verantwortung besitzen (jedenfalls nicht mehr als 3).
Tipp: als Entwurfshilfsmittel CRC-Karten verwenden, die nur
wenig Platz zum Anhäufen von Verantwortlichkeiten bieten und
somit die Definition weiterer Klassen/Komponenten fördern.
- Eine Komponente sollte mehr tun als nur zu delegieren,
d.h. nur andere Komponenten aufzurufen
- Beziehungen zu anderen Komponenten explizit definieren
- Neue Anforderungen sollten möglichst lokal implementiert werden können
Die beschriebenen Merkmale erhöhen die Wahrscheinlichkeit, dass Änderungen innerhalb
einer Komponente bleiben.
Top5.2 Design by contract (DbC)
Bei der Spezifikation von Schnittstellen wird nicht nur die statische
Struktur (Methodensignatur) festgelegt, sondern es wird auch die
Semantik und das Verhalten über formale Verträge (contracts) spezifiziert.
Für jede Methode wird festgelegt:
- Vorbedingungen (preconditions)
Zusicherungen, die der Aufrufer einzuhalten hat
- Nachbedingungen (postconditions)
Zusicherungen, die der Aufgerufene sicherstellt
- Invarianten
relevante Eigenschaften des Systems, die durch den Methodenaufruf
nicht verändert werden
Diese Festlegungen sind Teil der Methodenspezifikation.
Um das Einhalten des Vertrages im Sinne einer defensiven Programmierung
sicherzustellen, ist folgende Vorgehensweise empfehlenswert:
- öffentliche Methoden (Aufruf durch andere Komponenten) überprüfen im regulären
Produktivcode das Einhalten der Vorbedingungen und geben bei Nichteinhalten einen Fehler (
z.B. Exception) zurück.
- private Methoden sichern mit Assertions das Einhalten der Vorbedingungen ab. Durch
geeignete Compilerswitches entfallen diese Überprüfungen in der Release-Version, so dass
für den Produktivcode kein Overhead entsteht.
Überschreiben von Methoden - "principle of subcontracting"
Wird eine Methode in einer abgeleiteten Klasse überschrieben,
so müssen nach dem
Liskov'schen Substitutionsprinzip
alle wesentlichen Eigenschaften der Basisklasse auch in abgeleiteten Klassen erfüllt werden
(Vererbung = "ist-ein"-Relation).
Die Neuimplementierung der Methode ist somit an den Kontrakt der Methode der Basisklasse gebunden.
Das bedeutet:
- die neue Methode darf die Vorbedingungen nicht verschärfen, d.h. vom aufrufenden Code
darf nicht mehr verlangt werden
- die neue Methode darf die Nachbedingungen nicht aufweichen, d.h. sie muss mindestens soviel
garantieren wie die Basismethode
Vertragsbruch - Fehlerbehandlung
Kommt es bei der Ausführung einer Methode zu Problemen (HW-Fehler, Fehlschlagen einer
intern aufgerufenen Methode, ...), so sind folgende Reaktionen möglich:
- Retry
Es wird versucht auf alternativem Lösungsweg die gewünschte Funktion
mit Einhaltung aller Nachbedingungen und Invarianten auszuführen.
- "organized panic"
Die Funktion kann nicht ausgeführt werden. Es werden alle Invarianten wiederhergestellt
und die rufende Funktion wird über den Fehlschlag benachrichtigt (z.B. Exception,
Fehlercode). Die rufende Funktion muss dann ihrerseits versuchen, das Problem zu lösen.
Weitere Informationen siehe
Building bug-free O-O software: An introduction to Design by Contract (Eiffel Software)
Top5.3 Separation of Concerns
Top5.3.1 Grundprinzipien
- establishment of boundaries
"intuitive" Aufteilung der Verantwortlichkeiten auf einzelne Teilsysteme/Komponenten
- exclusivity and singularity of purpose
Jedes Systemelement sollte nur einen einzigen (Haupt)zweck erfüllen. Es sollten
keine weiteren Systemelemente für diesen Zweck mitverantwortlich sein.
Daraus resultierende Vorteile:
- leichtere Wartbarkeit, höhere Stabilität:
Änderung eines Aspektes betrifft mit höherer Wahrscheinlichkeit nur eine einzige
Systemkomponente.
- natürliche Erweiterungspunkte (extensibility points)
- verbesserte Chancen zur Wiederverwendung
Da jede Komponente sich auf nur einen Aspekt konzentriert, hat sie auch weniger
Abhängigkeiten zu den restlichen Systemteilen und kann leichter auch in anderem
Kontext eingesetzt werden.
- beschleunigte Bearbeitungszeiten
Identifikation von Problemfällen und Zuordnung auf verantwortliche Bereiche/Systemteile/verantwortliche
Bearbeiter fällt leichter.
Top5.3.2 Horizontal Separation
Typische Gliederung in mehrere Schichten:
- Presentation Layer
Benutzeroberfläche und ggf. erforderliche Controller-Schichten zur Ansteuerung/Versorgung der GUI,
die gewährleisten, dass
tieferliegende Schichten unabhängig von den Belangen der Benutzeroberfläche sind.
- Business Layer
enthält die zentrale Applikationslogik
- entweder als herkömmliches objektorientiertes Modell der Domäne aus Objekten
mit eingebetteten Daten und zugehörigem Verhalten
- oder aus speziellen Komponenten, die den Workflow, die Geschäftsprozesse und
zugehörige "entities" repräsentieren
- Resource Access Layer
abstrahiert den Zugriff auf externe Informationen und Daten, ist verantwortlich für das genaue
Datenformat
Top5.3.3 Vertical Separation
- Auftrennung nach Features / Themenbereichen
- Gliederung nach der Verantwortung durch unterschiedliche evtl. räumlich getrennte Entwicklungsteams
- Innerhalb einer vertikalen Schicht ist zusätzlich eine horizontale Schichtung (presentation/business/
resource access layer) möglich und sinnvoll
Top5.3.4 Aspect Separation - aspektorientierte Programmierung
Neben den Kernthemen einer Applikation, die z.B. unterschiedlichen Schichten zugeordnet
sind, gibt es oft eine Reihe von Querschnittsthemen ("Aspekten"), die
mehrere Systemteile/Schichten/Objekte betreffen. Mit der Separierung dieser Aspekte von den
Kernthemen können beide Bereiche unabhängig voneinander geändert werden.
Beispiel für einen Aspekt: Logging, Tracing.
Es gibt verschiedene Vorgehensweisen um die separierten Aspekte mit der Applikationslogik
zu "verweben" (weaving): Präprozessor, Compiler, Runtime-Verknüpfung. Es gibt Programmiersprachen und Tools, die aspektorientiertes Programmieren direkt unterstützen.
Weitere Informationen zu Aspect Separation siehe
Taosad: Aspect-Oriented Software Development
Weitere Informationen zu Separation of Concerns siehe
separation of concerns (Aspiring Craftsman)
Top5.3.5 Vermischtes
- gerichtete Abhängigkeiten
Abhängigkeiten sollten stets von spezifischen Komponenten zu generischen Komponenten verlaufen und
nicht umgekehrt. Auf diese Weise können die allgemeineren Komponenten leichter wiederverwendet werden.
- Delegating concerns
Eine bestimmte (Teil-)Aufgabe wird an eine untergeordnete Komponente delegiert, die spezifische
technische Details berücksichtigt, die von der jeweiligen Anwendungssituation
abhängen können (z.B. Datenformat). Mögliche Umsetzung: Strategie-Pattern.
- Inverting concerns - Inversion of Control
Ein Teilaspekt einer Funktionalität wird nach "aussen" verlagert.
Beispiel:Template Method: Verallgemeinertes Verhalten wird in der Basisklasse in einer
sogenannten Template-Methode realisiert. Abgeleitete Klassen
erlauben Variation des Verhaltens durch Überschreiben der in der Template-Methode aufgerufenen
Funktionen.
- Exaggeration Excercise
Um mögliche Schwachstellen eines Designs hinsichtlich der Skalierbarkeit oder Wiederverwendbarkeit
besser erkennen zu können, wird probehalber angenommen, dass die Zahl der angeschlossenen externen
Systeme/Nutzer wesentlich größer wird, als momentan gefordert. Aus den resultierenden Auswirkungen
auf das bestehende Design zur Umsetzung der hypothetischen Anforderung, können ggf. falsch zugeordnete
Verantwortlichkeiten besser erkannt werden.
- Ordered complexity
Aus der Anwendung der Prinzipien der "separation of concerns" resultiert zunächst gegenüber der simplen
und direkten Umsetzung der augenblicklichen Domänen-Anforderungen eine gewisse Komplexitätssteigerung.
Betrachtet man jedoch einen längeren Zeithorizont, so handelt es sich dabei um "geordnete" Komplexität
gegenüber einer eher "ungeordneten" Komplexität, wie sie in unstrukturierteren Systemen durch laufende
Änderungen zwangsweise entsteht.
Top5.4 Layering for change
Neben der Gliederung nach verschiedenen Abstraktionsebenen kann auch eine Gliederung nach den erwarteten
Änderungsraten (rate of change) sinnvoll sein. Beispiele:
- GUI wird ständig modernisiert (WEB, WEB 3.0), die zugrunde liegende
Applikationslogik bleibt erhalten.
- Trennung von Geschäftsprozessen und Geschäftsobjekten. Motivation: Prozesse können sich ändern,
Daten/Objekte sind oft wesentlich längerlebig als das System, das sie verwendet/erzeugt und
müssen vor Veränderung daher mehr geschützt werden.
- Datenbank-Schicht: Das Speicherformat / die verwendete Datenbank kann sich ändern.
- Eine Verbindungsschicht zu externen Systemen verkapselt Änderungen in deren spezifischen
Schnittstellen.
- Eine Schicht zur Verkapselung betriebssystem-spezifischer Ansteuerungen erleichtert die
Portierbarkeit.
- Open-Source-SW kann in einem eigenen Layer vom restlichen System separiert werden
Top5.5 Konfigurierbarkeit
- Jede Komponente besitzt eine Konfigurationsschnittstelle
- Lebenszyklusmodell für alle Komponenten: Hochfahren, Initialisierung, In-Betrieb-gehen,
Ausser-Betrieb-gehen, Herunterfahren, ...
- Konfigurations-Rahmensystem, das die Einbindung geänderter/neuer Komponenten
auf generische Weise unterstützt
- von Bedeutung insbesondere für Produktplattformen und -Linien
- Wichtiger Aspekt: Änderung auf die Strasse / zu den Kunden bringen
- zeitlich gestreckte Upgrades: zunächst nur einige Systeme upgraden, dann
prüfen ob alles funktioniert, danach weiterer Upgrade, Ziel: Begrenzung eines
durch die Änderung ausgelösten wirtschaftlichen Schadens
- häufiges Requirement: Upgrade im laufenden Betrieb (z.B. zu realisieren durch Herunterfahren
der alten Teilkomponente und Hochfahren der neuen Teilkomponente)
Top5.6 Open/Closed Principle
Das Design eines SW-Systems muß von Anfang an berücksichtigen, dass über einen längeren
Entwicklungs- und Wartungszeitraum hinweg, immer wieder neue Funktionalitäten ergänzt werden
müssen. Nach dem open/closed-Prinzip sollte eine SW-Komponente (Klasse, Modul) dabei so konzipiert werden, dass sie
offen für Erweiterungen ist und gleichzeitig möglichst
abgeschlossen für Modifikationen.
Motivation
- bestehender Code ist bereits getestet und hat sich im Einsatz bewährt
- Änderungen/Erweiterungen sollen möglichst geringe Auswirkungen auf bestehenden Code
haben, dadurch Begrenzung des Risikos unerwünschter Seiteneffekte, Sicherung der bereits
erbrachten Investitionen und erreichten Qualitätseigenschaften des Systems
- Mögliche Vorgehensweisen
- neue Funktionalität durch Erstellung neuer Klassen
- Ableiten von einer Basisklasse (Implementierungs-Vererbung), Modifikationen
werden dann in der abgeleiteten Klasse umgesetzt, grundlegende Algorithmen
sind in der Basisklasse vorgegeben und können nur eingeschränkt variiert werden
- Ableiten von abstrakten Interfaces (Schnittstellenvererbung),
neue Implementierungen sind an die Semantik der bestehenden Schnittstelle gebunden
Weitere Informationen siehe
Top5.7 Eingebaute Änderungs-Mechanismen
Top5.7.1 Interpreter
Prinzip
Das Verhalten eines SW-Systems kann zumindest in Teilen durch
das Interpretieren von extern änderbaren Scripts (z.B. XML) zur Laufzeit beeinflusst werden.
Vorteile
- größte Freiheit hinsichtlich Änderbarkeit: gewünschte Änderungen
werden ausserhalb des SW-Systems in Scripts formuliert
- SW muß nicht geändert/angepasst werden
Nachteile/Risiken
- schlechtere Performance
- möglicher Verlust an Kontrolle und Sicherheit, da Abläufe
ohne Einflussnahme durch SW-Architekten variiert/abgeändert werden können
Anwendungsbeispiel: EDDL
Die spezifischen Einstellmöglichkeiten für Geräte der Fertigungs- und
Prozessautomatisierung können in der EDDL (electronic device description language)
formuliert werden. Anstatt für jedes Gerät
spezifische SW zu schreiben, kann die steuernde SW allgemeiner gehalten werden, indem
sie die spezifischen Gerätebeschreibungen zur Laufzeit interpretiert.
siehe auch Wikipedia-EDDL
Top5.7.2 Stable design centers - built-in changeability
Prinzip
- feste, nicht änderbare Codierung des SW-Kernsystems
- explizite Erweiterungspunkte (extension hooks) für die
erforderliche Variation des Verhaltens vorsehen
- alle variablen Aspekte als Objekte anlegen, die in das Kernsystem
"eingesteckt" werden können
- zusätzlicher Schutz vor den Interna durch Verwendung
abstrakter Interfaces für alle Beziehungen zum Kernsystem
- typischerweise werden bei der Realisierung Patterns verwendet (z.B.
Visitor, Iterator, Strategy)
Top5.7.3 Generative Ansätze: MDSD, AOSD
Prinzip
- Lösungen werden in domänenspezifischen Modellen umgesetzt
- aus den Modellen wird der Code erst generiert
- ggf. ausführbare/validierbare/simulierbare Modelle zur Absicherung durchgeführter
Änderungen
- geeignet für eher größere Änderungsintervalle
siehe auch
Modellgetriebene Softwareentwicklung / model driven sw development (Wikipedia)
Aspect-Oriented Software Development (TAOSAD)
Top6 Umsetzung von (unerwarteten) Änderungen
Top6.1 Sicherung des bisher Erreichten
Insbesondere wenn (unerwartete) Änderungen/Requirements sehr spät
im Lebenszyklus eines Produktes umgesetzt werden müssen, sollten
folgende Aspekte stets beachtet werden:
- Erhalt der positiven Architektur-Eigenschaften
Unbedingt vermeiden: Einführung beliebiger zusätzlicher Beziehungen,
die zwar kurzfristig die gewünschten Änderungen erleichtern aber
wesentlichen Architekturprinzipien zuwiderlaufen.
- Erhalt der bereits erreichten Funktions/Systemqualitäten
Die Einführung
eines neuen Features oder Änderung eines bestehenden Features sollte
möglichst keine
negativen Auswirkungen auf andere bereits erreichte und bewährte
Funktionsmerkmale und Systemeigenschaften haben.
- Absicherung durch Planung und Reviews (change-scenario driven)
- Überprüfung und Anpassung der Teststrategie hinsichtlich der
geplanten Änderungen für Unit-, Regressions- und Systemtests
- Reviews für Code, Architektur und Test im Hinblick auf die geplanten /
bereits durchgeführten Änderungen
- Kundensicht: Eine Migration bestehender Systeme auf die neu
einzuführende Version sollte unterstützt werden.
- Die Kosten für die Migration dürfen nicht den zusätzlichen
Nutzen der neuen Version übertreffen
- Rückwärtskompatibilität z.B. für Datenmodelle und Scripting gewährleisten,
ggf. automatische Konversion anbieten
- Ein schrittweiser Upgrade im laufenden Betrieb muss ggf. unterstützt werden.
Dabei sind ggf. Komponenten mit unterschiedlichen Versionen parallel in
Betrieb.
Top6.2 Einbettung in die bestehende Architektur
Architektur-Änderungen verursachen Kosten (Umfang der Implementierungsarbeiten,
resultierende Aufwände für Anpassung/Durchführung von Tests).
Aus wirtschaftlichen Gründen
sollte also ein möglichst großer Teil des bestehenden Systems erhalten bleiben.
- Realisierung neuer Features als zusätzliche Komponenten und
Services, die
entsprechend der vorhandenen Komponenten-Infrastruktur in das Gesamtsystem
eingebettet werden. Die bestehende Modularisierung bleibt erhalten.
- Änderung/Erweiterung bestehender Komponenten, falls die Änderung in natürlicher
Weise die Komponente erweitert, ohne die bestehenden Beziehungen
zwischen den Komponenten zu beeinträchtigen.
- Wichtige Erweiterungen, die nicht mit der bestehenden Architektur
realisiert werden können, sollten einen möglichst großen Teil
der bestehenden Architektur erhalten und die notwendigen Anpassungen
über Refactoring/Reenginering und notfalls Rewriting durchführen (siehe unten).
Top6.3 Unterscheidung zwischen Refactoring, Reengineering und Rewriting
Top6.3.1 Refactoring
- Hauptziel: Verbesserung der internen Struktur
Vereinfachung, Lesbarkeit, Wartbarkeit, Erweiterbarkeit, Fehlerkorrekturen
- Nicht ändern: externe Schnittstellen
Schnittstellen, Semantik und funktionales Verhalten einer
System-Komponente nach aussen bleiben erhalten
- Folge: nach aussen hin nicht sichtbar
Eine Beeinflussung anderer
Systemteile ist damit ausgeschlossen
- dient ggf. als Vorbereitung nachfolgender größerer Änderungen
- Empfehlung: ständig durchführen
kontinuierliche Maßnahme zur Erhaltung bzw. Steigerung der
Code-Qualität
- Hilfreich: bestehende (automatisierte) Testfälle
Absicherung der Bereinigungsmassnahmen. Es sind keine
zusätzlichen Testfälle notwendig, da bei diesem
Verfahren keine funktionalen Änderungen zugelassen sind.
Top6.3.2 Reengineering
- Hauptziel: bevorzugte Methode für die Umsetzung neuer Anforderungen
- Wichtig: Beibehaltung der Grundstruktur
- Analyse und Überarbeitung der bestehenden System-Komponenten
und ihrer Beziehungen zueinander.
- Verbesserung der internen Struktur (wie bei Refactoring)
- Für funktionale Erweiterungen und
zur Verbesserung der Code-Qualität bzw. des allgemeinen
Systemverhaltens:
Veränderung der Semantik,
Ergänzung von Neuem, dabei aber Beibehaltung der Grundstruktur
- Folge: ist nach aussen sichtbar
- Erforderlich: Zusatzaufwand für Qualitätssicherung
Eigenschaften und Qualität des Alt-Systems sollen erhalten bleiben.
- für neue Funktionalitäten sind neue Testfälle erforderlich
- bestehende Testfälle müssen ggf. angepasst werden
- betroffene/relevante Testfälle identifizieren und durchführen
Top6.3.3 Rewriting
- Hauptziel: Verbesserung der Gesamtstruktur
- Methode: vollständige Ersetzung eines Systems oder einer Gruppe von
Teilkomponenten durch neues Design und Implementierung
- Verbesserungen für: Design- und Code-Stabilität, innere und äussere Struktur,
allgemeines System-Verhalten
- Durchführung funktionaler Erweiterungen
- Folge: ist nach aussen sichtbar
- Einschätzung: sehr risikoreich
- Gefahr: Aufwand (Zeit, Kosten) kann wesentlich höher sein
als der Nutzen aus dem resultierenden Funktionszuwachs
- hoher Qualitätssicherungsbedarf, neue Teststrategien erforderlich
- konservatives, abgesichertes Vorgehen notwendig
- Empfehlung: nur durchführen, wenn Refactoring/Reengineering
zur Umsetzung von Anforderungen nicht ausreichen
- Indizien für notwendiges und lohnendes Rewriting:
- lange Einarbeitungszeit für neue Mitarbeiter
- Behebung eines Fehlers verursacht häufig mehrere Folgefehler
In der Realität wird zur Realisierung von anstehenden Änderungen häufig
eine Kombination der drei Vorgehensweisen eingesetzt. Unter Umständen ist es
dabei sinnvoller
eine geforderte Funktionalität mit Hilfe von Reengineering nur teilweise umzusetzen,
wenn dafür das risikoreichere und i.d.R. kostenintensivere Rewriting
vermieden werden kann.
Top6.4 Testgetriebene Entwicklung (TDD)
In der testgetriebenen Entwicklung werden
automatisierte Tests
als Grundlage der SW-Entwicklung eingesetzt. TDD (test driven development) ist aber mehr
als eine Anleitung zum Schreiben von Tests. TDD wurde ursprünglich als Teil der agilen Methode
"eXtreme Programming" (Kent Beck) konzipiert und stellt eine vollständige Entwicklungs-Methode
zur Erstellung von Software dar.
Prinzipien der Durchführung
- "Write the test first"
Zunächst wird der Testfall für die neue Funktionalität geschrieben.
Beabsichtiger Effekt: Entwickler muss sich mit den Requirements
und deren Nachweisbarkeit beschäftigen, bevor er die Änderung umsetzt.
- "First fail the test cases"
Wiederholung aller Tests. Der neue Test muss fehlschlagen, falls er die
neue, noch fehlende Funktionalität tatsächlich nachweisen kann. Die
unveränderten Testfälle müssen erfolgreich durchlaufen werden
- Minimale Implementierung
Ergänzung von gerade soviel Code, dass der neue/geänderte Testfall klappt
- Berücksichtigung von Fehlerfällen
Ergänzung weiterer Testfälle, die auch die Fehlerfälle
der neuen Funktionalität prüfen und
Ergänzung des zugehörigen Quellcodes. Ablauf jeweils
entsprechend den Schritten (1) - (3)
- Refaktorierung/"Aufräumen"
Überarbeitung der betroffenen/geänderten Codestellen (z.B. insbesondere
Beseitigung von Duplikaten). Dabei wird stets nach wenigen Änderungen
die automatisierte Testsuite wiederholt. Dadurch ist gewährleistet,
dass bei gefundenen Fehlern einfach die letzten Änderungen zurückgenommen
oder korrigiert werden können, ohne eine zeitraubende Debug-Session
starten zu müssen.
Vorteile der Vorgehensweise
- Beschleunigte Implementierungszeit
Obwohl zusätzlicher Testcode geschrieben werden muss, wird die
benötigte Implementierungszeit bis zur (annähernden) Fehlerfreiheit
typischerweise verkürzt. Zeitaufwändiges Debugging
kann weitgehend entfallen.
- Höhere Testabdeckung
Da nur soviel Code geschrieben wird, wie zur Erfüllung eines neu
erstellten Testfalles notwendig ist, wird tendenziell (fast)
jeder Code-Zweig im Rahmen der Tests durchlaufen.
- Vermeidung ungewollter Seiteneffekte
Durch die wachsende Zahl automatisierter Tesfälle werden ungewollte
Seiteneffekte auf andere Anwendungsfälle schnell aufgedeckt.
Die automatisierten Tests bilden eine Voraussetzung für
sicheres Refactoring/Reengineering.
- Modularisierter, testbarer Code
Da der Entwickler gezwungen ist, gleich Testfälle zu erstellen
(und dazu ggf. auch entsprechende Mockobjekte der Umgebung
realisieren muss), besteht die Tendenz, dass kleinere Module/Klassen
mit einfachen Schnittstellen entstehen, die eine Ansteuerung oder
Simulation im Test vereinfachen.
- „Usage specification better than documentation“
Die Testfälle geben wertvolle Hinweise für den Anwender, wie der Code
benutzt werden kann. Im Unterschied zur Dokumentation ist der Testfall immer aktuell.
Referenzen und weiterführende Literatur