Relevanz
Anforderungen, die die Robustheit gegenüber funktionalen Änderungen und Erweiterungen ausdrücken, werden als nicht-funktional bezeichnet, während die Notwendigkeit der Funktionalität selbst in funktionalen Anforderungen ausgedrückt wird. Funktionalität in Software funktioniert kompositorisch, d.h. sie wird durch die Kombination von Komponenten geschaffen, die eine Teilfunktionalität realisieren. Die von einer Komponente angebotene Teilfunktionalität wird durch solche Zusammenstellungen nicht beeinträchtigt. Man kann also funktionale Belange trennen. Eine solche Kompositionalität funktioniert jedoch nicht bei nicht-funktionalen Aspekten wie der Leistung und den ‚-ilities‘ (Skalierbarkeit, Wiederverwendbarkeit, Erweiterbarkeit usw.). Wenn wir z.B. zwei unabhängige Komponenten haben, die einzeln eine Frist einhalten, ist es alles andere als sicher, dass sie dieselbe Frist einhalten, wenn sie zusammen eingesetzt werden. Die Einhaltung einer Frist kann nicht lokalisiert werden (z.B. in einer einzigen Klasse) und erfordert die Anwendung ganz anderer Techniken als die Realisierung funktionaler Eigenschaften. Ein einfacher „Teile und herrsche“-Ansatz ist ineffektiv. Viele Autoren definieren Systemeigenschaften, die nicht lokalisierbar sind, als architektonisch.
In dem 4-tägigen Kurs am High Tech Institute werden die wichtigsten Ideen hinter der objektorientierten Entwicklung vorgestellt und erörtert, wie diese Technik zur Erfüllung der architektonischen oder nicht-funktionalen Anforderungen eingesetzt werden kann. Als Hauptwerkzeug wird die Modellierungssprache UML verwendet.
Dieser Kurs gliedert sich natürlich in zwei ineinandergreifende Teile: die objektorientierte Analyse und das objektorientierte Design. Während der Analyse wird eine objektorientierte Verhaltensspezifikation des Systems erstellt. Während des Entwurfsprozesses wird diese Spezifikation um die gewünschten nicht-funktionalen Systemeigenschaften erweitert. Die Analyse erfasst also die funktionalen Anforderungen und erstellt eine (möglicherweise ausführbare) funktionale Spezifikation, während der Entwurfsschritt die nicht-funktionalen Anforderungen erfasst und umsetzt.
Objektorientierte Analyse
Die objektorientierte Programmierung ist eine Form der modularen Programmierung mit der Klasse als grundlegendem Baustein. Klassen können aus den Objekttypen gewonnen werden, die wir in der Problemdomäne vorfinden: die Objekte in der zu steuernden Maschine oder in dem zu unterstützenden Geschäftsprozess. Die Objektorientierung steht im Gegensatz zu (älteren) prozeduralen Ansätzen. Im Allgemeinen ist es für Menschen einfacher, in Objekten zu denken als in Prozeduren.
Eine Klasse ist ein Softwaremodul, das über Operationen Zugriff auf seine Daten, auch Attribute genannt, bietet. Dies fördert die Idee, Daten an einem Ort zu halten und Informationen zu verstecken, was dazu beitragen kann, dass Software weniger empfindlich auf Änderungen der Datendarstellung und Erweiterungen reagiert. Bei richtiger Anwendung führt ein objektorientierter Ansatz zu einer besseren Lokalisierung von Softwareänderungen im Vergleich zur traditionellen prozeduralen Arbeitsweise.
Der Analyseprozess beginnt mit der Definition von so genannten Anwendungsfällen, szenarioähnlichen Beschreibungen von Benutzer-System-Interaktionen. Darüber hinaus wird die Systemstruktur in Form einer einfachen Klassenhierarchie erfasst. Anschließend werden die Anwendungsfälle analysiert und mithilfe von UML-Sequenzdiagrammen umgesetzt. Dies hilft dabei, den Systemobjekten das Verhalten so zuzuordnen, dass die in den Anwendungsfällen spezifizierten Interaktionen zustande kommen. Diese Verhaltenszuweisung führt zur Entdeckung von Operationen auf Klassen. Der letzte Schritt besteht darin, das Verhalten der einzelnen Klassen in UML-Zustandsautomatenbeschreibungen zu verfeinern, und zwar (höchstens) einen Zustandsautomaten pro Klasse.
Bei der Analyse wird das zu entwickelnde System modelliert und nicht die zu entwickelnde Software. Dies ist wichtig, da es zur Lokalisierung von Systemparametern und zugehörigem Verhalten führt und die Verkapselung fördert. Auf diese Weise werden Information Hiding und Kapselung auf natürliche Weise in das objektorientierte Modell eingebaut.
Objektorientiertes Design
Ein funktional korrektes (partielles) Analysemodell ist ein guter Ausgangspunkt für das Design. Der Designteil des Kurses basiert auf den 4+1 Sichten der Architektur von P. Krugten: die Logische, die Prozess-, die Komponenten- und die Implementierungssicht. Diese Sichten wurden eingeführt, um die angesprochenen nicht-funktionalen Aspekte und die zu ihrer Realisierung eingesetzten Techniken zu trennen.
Eine wichtige architektonische Technik, die in der logischen Sichtweise verwendet wird, ist die Modularität. Viele Teilnehmer dieses Kurses glauben, dass Modularität selbst eine Voraussetzung ist. Der Kurs plädiert dafür, dies anders zu sehen. Modularität dient immer dem Zweck, ein anderes, höheres Ziel zu erreichen (z.B. Wiederverwendung, Erweiterbarkeit, Wartbarkeit). Daher wird die Änderung der Softwarestruktur als ein Mittel zur Erreichung der gewünschten nichtfunktionalen Eigenschaften betrachtet. Es gibt zwei wichtige Möglichkeiten, die Softwarestruktur zu ändern: (1) Code-Refactoring und (2) die Anwendung von Entwurfsmustern. Beide Techniken können so angewendet werden, dass die Funktionalität erhalten bleibt.
Bei der Prozessbetrachtung geht es um Fragen wie Skalierbarkeit der Leistung und Korrektheit (Abwesenheit von Deadlocks und Race Conditions). Es wird gezeigt, wie die Verhaltensbeschreibungen in Form von UML-Zustandsautomaten eine Gleichzeitigkeit (Multi-Threading) ergeben, die für das Problem, das Systemverhalten, natürlich ist. Threading kann in der Prozesssicht ähnlich wie die Transformation von Klassen und deren Struktur in der logischen Sicht refaktorisiert werden. Einige einfache Transformationen auf der Grundlage von Thread-Kohäsionsargumenten werden diskutiert und illustriert.
Schließlich werden die Komponenten und die Bereitstellungsansichten ganz kurz besprochen. Diese Sichten befassen sich mit der Art und Weise, wie ausführbare Software-Bausteine für den Einsatz auf einer Ausführungsplattform angeboten werden. Typische Themen sind Skalierbarkeit und Softwareentwicklung. Der Schwerpunkt liegt hier hauptsächlich auf Notationen, aber auch einige Probleme mit Komponentenschnittstellen werden kurz angesprochen.
Das Kursmaterial enthält eine große Anzahl von Fallstudien, die als Beispiele und Übungen verwendet werden. Diese Fallstudien wurden detailliert ausgearbeitet, von den Anforderungen über Modelle bis hin zu ausführbaren (Java-)Implementierungen. Der letzte Schritt wird als „Proof of Concept“ durchgeführt. Die Kursteilnehmer erhalten diese Fallstudien auf einem USB-Stick.
Dieser Kurs richtet sich insbesondere an Softwareentwickler, die die oben genannten Probleme erkennen und das Gefühl haben, dass sie von einer klaren und zusammenhängenden Behandlung profitieren würden. Die klaren Lernziele und die vielen Übungen werden dazu beitragen, die vorgestellte Theorie und die Techniken zu festigen.
Eine natürliche Fortsetzung dieser Schulung ist „Design Patterns und emergente Architektur„. Es wird empfohlen, diesen OOAD-Kurs zuerst zu besuchen.