Mittwoch, 27. November 2013

Modularisierung - aber wie?

Modularisierung hat sich als fundamentales Prinzip für die Konstruktion von Software-Systemen weitgehend durchgesetzt. In modernen Programmiersprachen ist das Prinzip tief verankert und kann vom Entwickler praktisch nicht ignoriert werden. Die Problemstellung besteht nicht in der Frage, ob modularisiert werden soll oder nicht, sondern im Entwerfen einer geeigneten Modularisierung. In diesem Kontext sollen hier einige Probleme besprochen werden, die sich wie folgt gruppieren:
  • Wahl der Modularisierungs-Ebene
  • Bestimmung der dominanten Modularisierungs-Dimension
  • Berücksichtigung von Conways' Gesetz
  • Einsatz von Zerlegungskriterien

Die geeignete Modularisierungs-Ebene

Wie bereits im vorigen Blogartikel angedeutet, kann Modularisierung auf verschiedenen Ebenen erfolgen. Für eine in Java entwickelte Software könnte eine gewisse Funktionionalität beispielsweise auf folgenden Ebenen "modularisiert" werden:
  • Methoden-Ebene: Die gesamte Funktionalität wird innerhalb einer Methode umgesetzt.
  • Klassen-Ebene ohne Interface: Die Funktionalität wird auf verschiedene Methoden und Felder einer Klasse verteilt. Das Geheimnisprinzip kann annähernd mittels der Access-Modifier berücksichtigt werden.
  • Klassen-Ebene mit Interface: Die Klasse implementiert zur Verstärkung des Geheimnisprinzips zusätzlich ein Interface.
  • Package-Ebene: Die Funktionalität wird auf verschiedene Klassen/Interfaces eines Packages verteilt. Zur Berücksichtigung des Geheimnisprinzips kann die Package-Sichtbarkeit der Member der Klassen genutzt werden. Die Möglichkeit zur expliziten Definition einer öffentlichen Schnittstelle besteht auf dieser Ebene nicht.
  • Jarfile-Ebene: Die Funktionalität wird auf verschiedene Klassen/Interfaces eines Jarfiles verteilt. Damit löst man sich allerdings von einer Modularisierung auf Sprach-Ebene und begibt sich in den Bereich des Build-Prozesses. Auch hier besteht nicht die Möglichkeit zur expliziten Definition einer öffentlichen Schnittstelle.
  • Komponenten-Ebene: Java kennt syntaktisch keine Komponenten, so dass zu diesem Zweck ein bestimmter technischer Kontext gewählt werden müsste. Dies könnte z. B. ein OSGi-Container sein, der dann auch Mechanismen zur Definition der gewünschten öffentlichen Schnittstelle bereitstellt. Auch hier verlässt man die Sprach-Ebene und nimmt zudem auch einen komplexeren technischen Kontext in Kauf, um die gewünschte Modularisierung zu erreichen.
  • Anwendungs-Ebene: Die gewünschte Funktionalität könnte durch eine oder mehrere Anwendungen realisiert werden.
Soll die Funktionalität in einer anderen Programmiersprache als Java realisiert werden, so stehen ggf. andere syntaktische oder technische Konstrukte für verschiedene Ebenen zur Verfügung. Die grundlegende Problemstellung, die passende Ebene zu bestimmen, bleibt jedoch bestehen.

Die geeignete Modularisierungs-Dimension

Wie [Barth2012] ausführt, erfolgt Modularisierung oftmals entlang einer dominanten Dimension. Die Funktionalität dieser Dimension wird dann angemessen in verschiedene Module aufgeteilt, da sich die Modularisierung an dieser Dimension orientiert. Die gewählte Aufteilung kann jedoch dazu führen, dass die Funktionalität (einer oder mehrerer) anderer Dimensionen sich unerwünscht auf verschiedene Module verteilt. Versucht man dieses Problem zu beheben, indem man die Modularisierung entlang der anderen Dimension vornimmt, so führt dies zu einer unerwünschten Verteilung der Funktionalität der ersten Dimension. In Anlehnung an [D'Hondt2002] bezeichnet Barth dieses Problem als "Tyranny of Dominant Decomposition". 

In vielen Fällen wird die Wahl der dominanten Dimension naheliegen und die unerwünschte Verteilung der Funktionalität der sekundären Dimensionen verschmerzbar sein. Hat man es jedoch mit mehreren gleichberechtigten Dimensionen zu tun, so steht man vor einem konzeptionenellen Problem, das sich innerhalb der üblichen Moduarisierungs-Strategien nicht zufriedenstellend lösen lässt. Für einige typische Konstellationen dieser Art bieten sich dann andere Strategien wie z. B. Aspektorientierung an. Die unterlegenen Dimensionen werden dann als Cross-Cutting Concern behandelt und in sogenannten Aspekten modularisiert. Freilich erhält man diese Möglichkeit nicht ohne einen Preis, der im Fall der Aspektorientierung in einer erschwerten Nachvollziehbarkeit und in der Einführung eines neuen Kopplungstyps besteht. Zudem bietet sich Aspektorientierung nur für bestimmte querschnittliche Funktions-Dimensionen an. Weiterführende Ansätze wie z. B. Multidimensional Separation of Concerns [Batory2003] haben bislang keine weite Verbreitung oder auch nur technische Unterstützung gefunden.

Das Gesetz von Conway

Ein eher praktischer Hinderungsgrund für das Auffinden einer der Problemstellung angemessenen Modularisierung wird durch das Gesetz von Conway beschrieben:
"Organisationen, die Systeme modellieren, […] sind auf Modelle festgelegt, welche die Kommunikationsstrukturen dieser Organisationen abbilden." [Conway1968]
Da komplexe Systeme immer in Arbeitsteilung entstehen und diese Arbeitsteilung sich oftmals eher an organisatorischen und persönlichen Eigenschaften der beauftragten Personen richtet (z. B. organisatorische Verantwortlichkeiten und Interessen, persönliche Kompetenzen und Vorlieben etc.), entsteht oftmals eine Modularisierung, die nicht optimal an der eigentlichen Problemstellung ausgerichtet ist. Die einzelnen Teams oder Personen versuchen, ihren Arbeitsbereich gegeneinander abzugrenzen und definieren zu diesem Zweck Schnittstellen und Module. War die eher zufällige Abgrenzung der Arbeitsbereiche nicht passend, so wird damit die falsche Modularisierungs-Dimension dominant.

Zerlegungskritierien

Im Rahmen der Kategorisierung der Prinzipien habe ich bereits dargelegt, dass eine Reihe von Prinzipien der Softwaretechnik als Zerlegungskriterien einzuordnen sind. Die Suche nach geeigneten Zerlegungskriterien beginnt mit Parnas' On the Criteria To Be Used in Decomposing Systems into Modules [Parnas1972]. Das Geheimnisprinzip, die Prinzipien der hohen Kohäsion und losen Kopplung, das Single-Responsibility Prinzip, das Separation of Concerns-Prinzip und "Don't Repeat Yourself!" sind nur einige Beispiele für Prinzipien, die als Zerlegungskriterien das Problem der richtigen Modularisierung adressieren. 

Zusammenfassung

Das Auffinden einer geeigneten Modularisierung gehört zu den eigentlichen Herausforderungen bei der Entwicklung eines Software-Systems. Das Bestimmen der richtigen Dimension, der richtigen Ebene und die Aufteilung an Hand verschiedener Zelegungskriterien sind oftmals ein kreativer, auf Änderungsannahmen basierender Prozess. Es ist eines der Ziele dieser Artikelserie, die Abhängigkeiten der vielen Prinzipien aufzuzeigen, die in diesem Kontext eine Rolle spielen, und so ggf. eine systematischere Vorgehensweise beim Auffinden der richtigen Zerlegung zu ermöglichen.

Quellen

[Barth2012] - Prinzipien der Modularisierung, Barth, Michael (2012)
[Conway1968] - How Do Committees Invent?, Melvin E. Conway: . In: F. D. Thompson Publications, Inc. (Hrsg.): Datamation. 14, Nr. 5, April 1968, S. 28–31
[D'Hondt2002] - The Tyranny of the Dominant Model Decomposition, Maja und Theo D'Hondt (2002)
[Parnas1972] – On the Criteria To Be Used in Decomposing Systems into Modules, David L. Parnas, in: Communications of the ACM, 1972