Freitag, 20. September 2013

Von der logischen Komponente zur technischen Komponente.

These: Gut kommunizierte Software ist ein Segen für die Zukunft.

Artikelübersicht
1. Teil Ordnung für Use Cases und User Stories durch Funktionsgruppen.
2. Teil Von den Funktionsgruppen zu logischen Komponenten.
3. Teil Von der logischen Komponente zur technischen Komponente.


Im letzten Post dieser Reihe haben wir die gefundenen funktional motivierten Komponenten durch die Konzepte von Tier, Layer und Softwarekategorie weiter in logisch motivierte Komponenten unterteilt. Dabei wurde eine strenge Kommunikationsrichtung, immer von der oberen Komponente auf die untere Komponente, und ein strenger Zugriffsort, immer über die Fassade, vorgegeben.

Im folgenden Post stellen wir uns der Aufgabe, aus den logisch motivierten Komponenten technische Komponenten zu konstruieren. In dieser Betrachtungsebene wird die Basistechnologie und damit auch die Programmiersprache festgelegt. In diesem Beispiel wird die verwendete Programmiersprache Java sein.

Technische Architekturen "verfeinern die Komponenten aus einer logischen ...Architektur, indem sie Schnittstellen präzise definieren, beispielsweise mit den Mitteln einer Programmiersprache." [S.350, REUS09] Am Ende wollen wir deploybare Komponenten konstruieren.

Eines der wichtigsten Aufgaben innerhalb der technischen Komponentenbildung ist also die genaue Beschreibung der Schnittstelle. In unseren Projekten konzentrieren wir den Zugriff auf die Komponente in der Komponentenfassade. "Die Fassadenklasse definiert eine abstrakte Schnittstelle, welche die Benutzung des Subsystems vereinfacht." [GOF11] In Java ist es möglich, diese Fassade als Schnittstelle zu definieren. Zugriffe auf die Komponente dürfen nur über diese Schnittstellenmethoden erfolgen. Als Rückgabewert und als Übergabeparameter dürfen nur einfache Basistypen oder Data Transfer Objects verwendet werden. Ein Data Transfer Object ist "ein Objekt, das Daten zwischen Prozessen überträgt, um die Anzahl der Methodenaufrufe zu verringern." [S. 443, FOWL03]

Die im letzten Post erwähnte Beispielanwendung, in der man bestimmte Testuntersuchungen vornimmt, hatte drei funktionale Komponenten: Testerstellung, Testausführung und Testauswertung. Jede dieser Komponenten unterteilt sich wiederum in drei logische Komponenten: View, Businesss und Persistence. Betrachten wir nun nur die logische Komponente Business-Testerstellung. Ein Test besteht aus Tests und diese jeweils aus mehreren Testschritten. Diese Einheiten können wir anlegen, ändern und löschen. Damit sind die Möglichkeiten der Fassade beschrieben.

  

/**
 * Fassade zum Zugriff auf die
 * Komponente Business-Testerstellung
 */
public interface TestCreationFacade {
  /** Erzeugt einen neuen Test */
  void createTest(TestDTO testDto);
  /** Erzeugt einen neuen Testschritt */
  void createTestStep(TestStepDTO testStepDto);
  /** Fügt einem Test einen Testschritt hinzu */
  void addTestStep(TestDTO testDto, TestStepDTO testStepDto);
  ...
}



Der Code soll die Methode nur andeuten. In die Konstruktion einer Schnittstelle sollte sehr viel Zeit fließen. Außerdem sollte es eine ausgiebige Kommunikation der Hersteller mit den Anwendern dieser Schnittstelle geben. Ist die Komponente mit der entsprechenden Fassade ausgeliefert, sind Änderungen nur noch in einem begrenzten Umfang möglich. Bei Änderungen an der Schnittstelle, die Neuprogrammierungen in Anwenderkomponenten nach sich ziehen, geht das Vertrauen in die Komponente schnell verloren. Die Schnittstelle sollte stabil sein. Erweiterungen der angebotenen Funktionalität sind ohne weiteres möglich. Die Abwärtskompatibilität muss jedoch gewahrt bleiben.

Zukünftiges Entfallen von Funktionalität kann jedoch nicht ausgeschlossen werden. Eine noch so sorgfältige Konstruktion einer Schnittstelle kann trotzdem ein Streichen von Methoden erforderlich machen. Zum Beispiel hat sich eine Methode und der in ihr angewendete Algorithmus als Berechnungsrisiko herausgestellt. Diese Methode kann in Java als Deprecated gesetzt werden. Damit kündigen wir eine entsprechende Streichung in einer zukünftigen Version an. Dieses Mittel sollte jedoch sehr sparsam angewendet werden.

Das Methodenangebot der Komponente haben wir somit in einer Fassade konzentriert. Damit haben wir einen zentralen Ort geschaffen, in dem wir die angebotene Funktionalität erfahren und darauf zugreifen können. Wir müssen nicht in einer Vielzahl von Schnittstellen und Klassen suchen. Ebenso konzentrieren wir alle Zugriffe auf eine andere Komponenten in einer Delegate. Die dazu benutzten Interfaces wurden Delegator genannt. Wieder haben wir einen zentralen Ort, wo wir eine Verantwortlichkeit konzentriert haben. Wird ein Austausch notwendig, ist dieser einfach und schnell zu realisieren.

Im Folgenden möchte ich, der Vollständigkeit halber, die UML-Diagramme für Fassade und Delegation nochmals darstellen.

Facade Pattern

Delegation Pattern

Mit den Mitteln des Delegators, der Fassade und der Data Transfer Objects haben wir einen hohen Grad an Unabhängigkeit für unsere Komponente gewonnen. Auf diese Art und Weise haben wir wieder ein Werkzeug gegen allzu große Komplexität. Aber wie überall muss man auch hier Augenmaß behalten. Für eine Anwendung mit 100 Zeilen hat dieses Denken keinen Sinn, die Grundlagen dieses Denkens sind für Enterprise-Applikationen entstanden. Trotzdem bleibt die Erkenntnis: Teile und herrsche. Data Transfer Objects werde ich in einem späteren Posts noch genauer diskutieren. Dort komme ich auch darauf zu sprechen, warum Verfechter der OO dieses Design Pattern für ein Antipattern halten. Dafür werden gute Gründe angeführt und wenn sie mir alle einleuchten, lasse ich sie als Rückgabewerte weg und ersetze sie durch ...

  • [FOWL03] Martin Fowler : "Patterns für Enterprise-Application-Architekturen", 1. Auflage, mitp-Verlag, Bonn, 2003
  • [GOF11] Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides: "Entwurfsmuster, Addison Wesley Verlag, 2011"
  • [REUS09] Ralf Reussner, Wilhelm Hasselbring: "Handbuch der Softwarearchitektur", 2. überarbeitete und erweiterte Auflage, dpunkt.verlag GmbH, Heidelberg, 2009


vorheriger Post dieses Themas


Print Friendly Version of this page Print Get a PDF version of this webpage PDF

Kommentare:

  1. > Ist die Komponente mit der entsprechenden Fassade ausgeliefert, sind Änderungen nur noch in einem begrenzten Umfang möglich. Bei Änderungen an der Schnittstelle...

    Änderungen können (sollten) intensiv dokumentiert werden. Dies kann unterstützt werden durch "Semantic Versioning". Entweder manuell, oder automatisiert über Tools.

    Siehe hierzu auch:
    http://semver.org/

    bzw. für die Java-Welt mit OSGi
    http://www.osgi.org/wiki/uploads/Links/SemanticVersioning.pdf

    CU
    Boeffi

    AntwortenLöschen
  2. > In Java ist es möglich, diese Fassade als //Schnittstelle// zu definieren

    Mir fehlt bei dieser Art Hinweisen auf "Schnittstellen", dass der "Vertrag" einer Schnittstelle aus einem "Syntax-Teil" (in Java s. "interface") und einem Semantik-Teil (Protokoll der Schnittstelle, Kontext-Verhalten, Wertebereich der formalen Parameter, ...) besteht.

    Die Syntax kann einfach über Tools (IDE, Compiler, ...) verifiziert werden; die Semantik über explizite Tests (Stichworte "ausführbare Spezifikationen").

    CU
    Boeffi

    AntwortenLöschen
  3. Anbei ein paar Erläuterungen zu "Semantic Versioning" im Java/OSGi-Umfeld:
    http://blog.osgi.org/2013/09/baselining-semantic-versioning-made-easy.html

    CU
    Boeffi

    AntwortenLöschen