Wie Beispiel 8 zeigt, müssen Entwickler mit den Abhängigkeiten umgehen, die sich aus der Zerlegung eines Problems und seiner Lösung in eine Reihe von Modulen ergeben. Wir sagen, dass ein Modul eines Systems von einem anderen abhängt, wenn es möglich ist, dass eine Änderung an einem Modul eine Änderung an einem anderen erfordert. Wenn beispielsweise ein Unternehmen seine Produktionsmethoden ändert, kann dies zu einer Änderung der Art und Weise führen, wie es die für die produzierten Waren erforderlichen Zahlungen berechnet.
Ein Entwickler muss sich nicht nur mit der Art der einzelnen Abhängigkeiten befassen, sondern auch mit der Anzahl der Abhängigkeiten. In der Softwaretechnik wird der Begriff „Kopplung“ verwendet, um den Grad der gegenseitigen Abhängigkeit zwischen den verschiedenen Teilen eines Systems zu beschreiben. Es ist leicht zu erkennen, dass bestimmte Systeme Ketten von voneinander abhängigen Modulen haben können, bei denen zum Beispiel Modul A von Modul B abhängt, das wiederum von Modul C abhängt, und so weiter. In einigen Fällen können sich diese Ketten verbinden und eine zirkuläre Abhängigkeit schaffen, was eine besondere Form der starken (oder hohen) Kopplung ist.
Entwickler versuchen, lose gekoppelte Systeme zu konstruieren, weil sie leichter zu verstehen und zu warten sind. Ein gutes Softwaresystem hat also eine geringe Kopplung, was bedeutet, dass sich Änderungen an einem Teil weniger wahrscheinlich auf den Rest des Systems auswirken. Ein weiterer Vorteil einer geringen Kopplung besteht darin, dass Komponenten leicht ausgetauscht und möglicherweise wiederverwendet werden können.
Unabhängig vom Grad der Kopplung in einem Softwaresystem ist es wichtig zu wissen, welche Module gekoppelt sind. Gäbe es keine Aufzeichnungen über die Kopplung zwischen den Modulen, müsste ein Entwickler Zeit damit verbringen, die Module durchzuarbeiten, um festzustellen, ob jedes einzelne von einer Änderung betroffen ist oder nicht. Das Ergebnis wäre ein hoher Prüfaufwand, selbst wenn keine Änderungen erforderlich wären. Beispiel 9 veranschaulicht die Gefahr, wenn mehr als ein Modul gemeinsame oder gemeinsam genutzte Daten verwendet.
Beispiel 9
Die Handhabung von Daten war schon immer ein Problem für Softwareentwickler. Für Anwendungen eines bestimmten Alters war das am besten geeignete Speicherformat für die Darstellung einer Jahreszahl eine Zahl zwischen 0 und 99. Das war sinnvoll, weil 1966 als 66, 1989 als 89 usw. gespeichert wurde, so dass weniger Platz für die Speicherung von nur zwei Ziffern benötigt wurde. Wenn Daten als Zahlen gespeichert wurden, waren außerdem Aufgaben, die eine Sortierung nach der Reihenfolge des Datums erforderten, einfach – der 22. Januar 1989 wurde als 890122 gespeichert, der 22. Dezember 1966 als 661222.
Unglücklicherweise waren einige dieser Anwendungen immer noch im Einsatz, als das Jahr 2000 näher rückte, so dass jedes Modul in jeder Anwendung, die die Kurzform des Jahres verwendete, untersucht werden musste.
Ein wichtiger Aspekt des Problems in Beispiel 9 war, dass verschiedene Entwickler unterschiedliche Wege hatten, die in Variablen gespeicherten Werte zu lesen und zu manipulieren, die das sechsstellige Datumsformat verwendeten. Dies erhöhte den Aufwand für die Behebung des sogenannten Millennium-Bugs. Hätten die Entwickler eine konsistente Methode zur Manipulation von Datumswerten gehabt, die sich nicht auf das Speicherformat stützt, wäre der Millennium-Bug kein Problem gewesen.
Kohäsion ist eine Möglichkeit, zu beschreiben, wie eng die Aktivitäten innerhalb eines einzelnen Moduls miteinander verbunden sind. Kohäsion ist ein allgemeines Konzept – zum Beispiel kann eine Abteilung in einer Organisation eine Reihe von zusammenhängenden Verantwortlichkeiten haben (z.B. Buchhaltung) oder auch nicht (verschiedene Dienstleistungen). In Softwaresystemen führt ein hochgradig kohärentes Modul eine einzige Aufgabe aus oder erreicht ein einziges Ziel – „do one thing and do it well“ ist ein nützliches Motto, das man anwenden kann. Ein Modul sollte eine einzige logische Aufgabe oder eine einzige logische Einheit implementieren.
Niedrige Kopplung und hohe Kohäsion sind konkurrierende Ziele. Wenn jedes Modul nur eine Sache auf einer niedrigen Abstraktionsebene ausführt, benötigen wir möglicherweise ein komplexes Gebilde aus stark gekoppelten Modulen, um eine Aktivität auf höheren Abstraktionsebenen auszuführen. Ein Entwickler sollte versuchen, das beste Gleichgewicht zwischen den Ebenen der Kopplung und der Kohäsion für ein Softwaresystem zu erreichen. Ein Beispiel: Hotels erzielen Einnahmen, indem sie ihre Zimmer an Gäste vermieten. Das Konzept des Zimmers wird wahrscheinlich irgendwo im Softwaresystem für die Reservierung eines Hotels vertreten sein. Es mag praktisch sein, ein Modul oder eine Klasse zu verwenden, die das Konzept des Zimmers repräsentiert, um Daten über die durch die Vermietung von Zimmern erzielten Einnahmen zu sammeln und zu speichern. Eine bessere Lösung ist jedoch ein separates Rechnungs- oder Zahlungsmodul, da es in sich schlüssiger ist, insbesondere wenn ein Hotel auf andere Weise Einnahmen erzielt, z.B. durch das Servieren von Mahlzeiten an Personen, die keine Gäste sind.
Aktivität 5 Aufteilen und erobern
- a.Warum sollten Sie ein großes Projekt in kleinere Teile aufteilen?
- b.Wie wirkt sich die Komplexität eines Softwaresystems auf die Wartungsaufgabe aus?
- c.Was ist ein Modul?
- d.Warum ist eine geringe Kopplung in einem Softwaresystem hilfreich?
- e.Nennen Sie Beispiele für die Arten von Informationen, die wertvoll wären, wenn man eine Änderung an einem bestimmten Modul in Betracht zieht.
- f.Was sind die Kontextabhängigkeiten eines Moduls? Wie hängen sie mit der Schnittstelle eines Moduls zusammen?
- g.Welche Vorteile bietet die Verwendung von Modulen mit definierten Schnittstellen?
- h.Warum ist es hilfreich, wenn die Module eines Softwaresystems eine hohe Kohäsion aufweisen?
- i.Welche Eigenschaften sollte ein Modul aufweisen, um sicherzustellen, dass es einfach und billig zu entwickeln und zu warten ist und dass Fehler auf ein Minimum beschränkt werden?
- j.Warum ist es wichtig, ein Gleichgewicht zwischen Kopplung und Kohäsion zu erreichen?
Antwort
- a.Es gibt eine Grenze dafür, wie viel eine Person zu einem bestimmten Zeitpunkt verstehen kann. Es gibt also eine Grenze für die Größe eines Softwaresystems, die eine Person bewältigen kann. Durch die Aufteilung eines großen Projekts in kleinere Teile ist es möglich, eine Reihe von überschaubaren Aufgaben für die Beteiligten festzulegen.
- b.Es ist wichtig, ein Softwaresystem ändern zu können, ohne alles über dieses System wissen zu müssen. Jede Änderung wird schwierig, wenn der Kontrollfluss und die Abhängigkeiten innerhalb von Programmen komplex sind. Je größer die Anzahl und die Art der Abhängigkeiten, desto schwieriger ist es, ein Softwaresystem zu warten.
- c.Ein Modul ist jeder identifizierbare Teil eines Softwaresystems, der separat betrachtet wird. Module können zum Beispiel Unterprogramme (in einer prozeduralen Sprache äquivalent zu Methoden), Klassen (in einer objektorientierten Sprache), Bibliotheksfunktionen oder andere Konstrukte sein, die unabhängig voneinander behandelt werden können.
- d.Bei geringer Kopplung gibt es nur wenige Abhängigkeiten zwischen Modulen. Daher ist es weniger wahrscheinlich, dass sich Änderungen, die an einem Teil (einem oder mehreren Modulen) eines Softwaresystems vorgenommen werden, auf das gesamte System ausbreiten. (Eine klare Aufzeichnung der Abhängigkeiten zwischen Modulen hilft Ihnen, die Auswirkungen einer vorgeschlagenen Änderung an einem Softwaresystem vorherzusagen.)
- e.Es gibt zwei Arten von Informationen, die zur Analyse einer vorgeschlagenen Änderung beitragen:
- Welche Module sind Kunden des fraglichen Moduls? Diese Information gibt an, wie weit sich eine Änderung im Softwaresystem ausbreiten kann.
- Welche Annahmen wurden in den Client-Modulen des fraglichen Moduls getroffen? Ein Verständnis der erwarteten Dienste eines Moduls hilft bei der Bewertung der mit einer bestimmten Änderung verbundenen Risiken.
- f.Die Kontextabhängigkeiten für ein Modul sind die Dienste anderer Module, die das Modul benötigt, um korrekt zu funktionieren. Sie können die Kontextabhängigkeiten für ein Modul in Form von anderen Schnittstellen ausdrücken. In der Tat können Sie die Verantwortlichkeiten eines Moduls in Form seiner Schnittstelle und seiner Kontextabhängigkeiten ausdrücken. Wenn der Kontext die Dienste bereitstellt, die das Modul benötigt, und die Clients alle in der Schnittstelle angegebenen Bedingungen erfüllen, kann das Modul die Bereitstellung der in seiner Schnittstelle beschriebenen Dienste garantieren.
- g.Die Vorteile sind wie folgt:
- Entwickler müssen nur die Schnittstelle des Moduls kennen (seine Syntax und das, was es erfordert und erreicht – seine Semantik), nicht aber, wie es diese Dienste bereitstellt. Folglich können Entwickler produktiver arbeiten.
- Entwickler können Aspekte des Softwaresystems gründlicher verstehen, so dass weniger Fehler eingeführt werden.
- Es sollte einfacher sein, Fehler zu finden, da irrelevante Module vermieden werden.
- Die Möglichkeit der Wiederverwendung von Modulen wird erhöht, wenn bekannt ist, was das Modul bereitstellt und benötigt.
- h.Bei hoher Kohäsion führt ein Modul eine sinnvolle Reihe von Operationen oder Aktivitäten aus. Idealerweise impliziert hohe Kohäsion nur eine Hauptabstraktion pro Modul. Die Schnittstelle abstrahiert von dem, was ein Entwickler wissen muss, um ein Modul zu verwenden. Dadurch wird es für Entwickler einfacher, den Zweck des Moduls zu verstehen und zu wissen, wie es zu verwenden ist. Darüber hinaus macht eine hohe Kohäsion ein Modul in anderen Anwendungen wiederverwendbar, da es eine Reihe von Operationen bietet, die auf natürliche Weise zusammengehören.
- i.Ein Modul sollte eine geringe Kopplung und eine hohe Kohäsion aufweisen, eine gute Abstraktion darstellen und eine gut definierte Schnittstelle haben, die eine gekapselte Abstraktion eines gut verstandenen Konzepts ist.
- j.Bei der Konstruktion eines Systems kann man die Wahl haben zwischen einer kleineren Menge von lose gekoppelten, weniger kohäsiven Modulen oder einer größeren Menge von eng gekoppelten, kohäsiven Modulen. Im ersten Fall kann jedes Modul schwer zu verstehen sein, während im zweiten Fall die Beziehungen zwischen ihnen zu komplex sein können. Sie müssen ein angemessenes Gleichgewicht finden.