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:

Kano-Modell

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
  • (1) Erhebung von Szenarios
    alle Projektbeteiligten (Stakeholder) erstellen gemeinsam eine Beschreibung der verschiedenen Tätigkeiten, die das System unterstützen soll bzw. bereits unterstützt.
    • jedes Szenario beschreibt eine konkrete Interaktion eines Stakeholders (User, Tester, Developer) mit dem System bzw. mit bestimmten Teilkomponenten des Systems
    • jedes Szenario enthält eine Beschreibung des Stimulus, der Systemumgebung und der erwarteten messbaren Reaktionen des Systems
  • (2) Architekturbeschreibung
    für alle Beteiligten verständliche Beschreibung der Komponenten/Datenelemente, ihrer Beziehungen zueinander und des Systemverhaltens.

    Die Schritte 1 und 2 werden iterativ ausgeführt: Eine Detaillierung der Architekturbeschreibung kann neue Szenarios anregen, neue Szenarios führen zu einer Erweiterung der Architekturbeschreibung.

  • (3) Klassifikation und Priorisierung der Szenarios
    • Unterscheidung nach direkten Szenarios (Umsetzung mit bestehender Architektur möglich) und indirekten Szenarios (erfordern Änderungen der Architektur)
    • Vergabe von Prioritäten an die Szenarios entsprechend ihrer geschäftlichen Relevanz, nur die wichtigsten 30% werden im Nachfolgenden näher untersucht
  • (4) Einzelbewertung
    • Zuordnung eines Szenarios auf die betroffenen Elemente der Architektur bzw. Identifikation notwendiger SW-Änderungen/Erweiterungen
    • Schätzung des zugehörigen Änderungsaufwandes
  • (5) Untersuchung von Szenariointeraktionen
    Identifikation unterschiedlicher Szenarios, die an derselben Komponente Änderungen erfordern. Bei funktional nicht zusammengehörigen Szenarios deutet dies auf eine ungünstige Architekturgliederung hin ggf. auf weiteren Detaillierungsbedarf für die Architekturbeschreibung (Schritt 2)
  • (6) Gesamtbewertung
    Gewichtung der bewerteten Szenarios.

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.

Effort impact grid analysis

  • 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
  1. "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.
  2. "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
  3. Minimale Implementierung
    Ergänzung von gerade soviel Code, dass der neue/geänderte Testfall klappt
  4. 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)
  5. 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