Logo der aldebaran Programmierung & IT-Lösungen GmbH

+49 - 511 - 270 416 0

info@aldebaran.de

Was ist modulare Softwareentwicklung?

In der modularen Softwareentwicklung werden die Aufgaben der Anwendung in voneinander getrennten Modulen gekapselt. Es gibt eine Anwendung, die die Module einbindet und nutzbar macht. Am Beispiel einer Kaffeemaschine könnte man sich das so vorstellen: Ein Modul mahlt die Bohnen und ein Modul erwärmt das Wasser. Die beiden Module können unabhängig voneinander ihre Aufgaben erfüllen und auch unabhängig voneinander entwickelt und getestet werden. Wenn die Kaffeemaschine später auch noch Milch aufschäumen soll, kann dies ebenfalls unabhängig von den anderen Funktionen umgesetzt werden.

Wieso modulare Softwareentwicklung?

Bei der Planung eines größeren Projektes hat sich immer mehr heraus kristallisiert, dass die Anwendung teils recht unterschiedliche Anforderungen erfüllen soll. Hinzu kam, dass die Anwendung so schnell wie möglich zum Einsatz kommen und dann kontinuierlich erweitert werden soll. Aus diesen Gründen hat sich eine modulare Umsetzung fast schon aufgedrängt.

Die Anwendung soll in C# und WPF (Windows Presentation Foundation) umgesetzt werden.

Mit einer monolithisch aufgebauten Softwarelösung wäre die Umsetzung mit einem höheren Aufwand - vor allem bei den Tests - verbunden gewesen, und eine schnelle Inbetriebnahme hätte sich schwierig gestaltet.

Der modulare Aufbau hat hier also mehrere Vorteile:

  • Separate Entwicklung der unterschiedlichen Anforderungen
  • Separates Tests der unterschiedlichen Module
  • Frühe Nutzung der Anwendung und stetige Erweiterung der Features ohne Änderungsnotwendigkeit für den existierenden Quellcode

PRISM

Bei der Recherche nach einem Framework, welches uns bei diesen Anforderungen unterstützen kann, sind wir auf PRISM (PResentation Integration SysteMs) for WPF gestoßen. Nein - dieses PRISM hat nichts mit der gleichnamigen Überwachungs-Software zu tun!

Das Framework bietet einen sehr guten Weg, eine modulare Anwendung zu entwickeln, indem es die benötigte Logik zum Einbinden der Module liefert. Dies geht so weit, dass Regionen in der Oberfläche unterstützt werden. Regionen werden in der Hauptansicht definiert und jede View in einem Modul kann festlegen, in welcher dieser Regionen sie angezeigt werden soll.

Für unseren Kunden sollte eine Anwendung geschaffen werden, über die Hardware administriert und über die u. a. Einstellungen in Form von Parametern ausgelesen, eingespielt und verglichen werden können. Da diese Anforderungen unterschiedliche Oberflächen und Logik erfordern, haben wir sie in getrennten Modulen umgesetzt.

Wir haben dafür die Oberfläche in zwei Regionen unterteilt. In Region 1 werden die Module zur Arbeit mit den Parametern angezeigt. Jedes Modul bekommt automatisch einen eigenen Tab zugewiesen; die Logik zum Erzeugen der Tabs übernimmt PRISM für uns. In Region 2 wird ein weiteres Modul angezeigt, in dem Logmeldungen visualisiert werden.

Die Definition der Regionen ist in der WPF-Anwendung im XAML-Code des Hauptfensters hinterlegt und wird an einer zentralen Stelle gepflegt.

Um eine Region zu definieren, muss lediglich der 'RegionName' festgelegt werden. Am Beispiel von Region 2 könnte das wie folgt aussehen:

Oops, an error occurred! Code: 20240426221348d5bf8289

Zusätzlich muss innerhalb des Moduls definiert werden, in welcher Region es angezeigt werden soll.

Hierfür reicht es, wenn es innerhalb der dll, in der das Modul definiert ist, eine Implementation des Interfaces 'IModule' aus dem 'Microsoft.Practices.Prism.Modularity Namespace' angelegt wird und in der 'IRegionViewRegistry Instance' die Region zugewiesen wird.

Oops, an error occurred! Code: 202404262213480c10291e

Der Vorteil dieser Lösung ist, dass die Zugehörigkeit zu einer Region in den Modulen definiert wird. Dadurch muss die eigentliche WPF-Anwendung die Module nicht kennen oder referenzieren. Es ist denkbar, dass verschiedene Anwender die gleiche Anwendung mit unterschiedlichen Modulen installieren und somit der Funktionsumfang beeinflusst werden kann - ohne Anpassungen am Quellcode.

Aber was ist, wenn es doch mal nötig wird, dass die Module miteinander kommunizieren?

Ein Anwendungsbeispiel könnte sein, dass es ein Modul gibt, das Logmeldungen der anderen Module sammelt und dem Benutzer anzeigt. Da die Module sich untereinander nicht kennen, gestaltet sich dies auf den ersten Blick als schwierig. Aber auch hier liefert PRISM eine gute Lösung: den 'IEventAggregator'. Über dieses Interface können Nachrichten zwischen den Modulen und der WPF-Anwendung ausgetauscht werden. Eine Nachricht kann z.B. in einer dll, die von allen Modulen verwendet wird, definiert werden. Die Klasse muss von 'CompositePresentationEvent' abgeleitet werden, und es wird der Nachrichten-Typ angegeben. In unserem Fall reicht ein String aus:

Oops, an error occurred! Code: 2024042622134833dc01ee

An der Stelle, an der die Nachricht verschickt werden soll, wird über den 'IEventAggregator' auf das 'LogEvent' zugegriffen und die 'Methode Publish' aufgerufen.

Oops, an error occurred! Code: 20240426221348f5ef89db

Die Klasse, die die Nachricht verarbeiten soll, muss dann nur noch definieren, wie vorzugehen ist, wenn ein Nachricht dieses Typs ausgelöst worden ist.

Oops, an error occurred! Code: 202404262213480c184151

Des Weiteren bietet das PRISM Framework weitere Unterstützung wie 'Dependency Injection'. Hier werden mehrere Dependency Injection Frameworks unterstützt. Wir haben uns für 'Unity' entschieden und sind damit sehr gut gefahren.

Fazit

Wer sich mit dem Thema modulare Softwareentwicklung näher beschäftigen möchte, kommt meiner Meinung nach nicht an PRISM vorbei. Zumindest einen Blick sollte man riskieren. Nach einer überschaubaren Einarbeitungszeit können relativ schnell erste Ergebnisse erzielt werden. Ein weiterer Pluspunkt ist, dass mittlerweile sehr viele Beispiele im Internet vorhanden sind.