Sonntag, 9. Januar 2011

Software complexity by example

Die Entwicklung von Software wird hauptsächlich von fachlichen Anforderungen getrieben. Der fachliche Nutzen einer Software ist dabei wesentlich für die Benutzerakzeptanz eines Softwareproduktes. Die Benutzerfreundlichkeit im Kontext der nicht funktionalen Anforderungen erhöht die Komplexität einer Software. Das Datenvolumen beeinflusst stark die Antwortzeiten einer Applikation. und ist deshalb eng mit der Benutzerakzeptanz verbunden. Die Stabilität einer Applikation und die Verlässlichkeit spielen eine wichtige Rolle für die Benutzerakzeptanz und den Erfolg eines Softwareproduktes.

Eine fachliche Funktionalität ist bei schlechter Antwortzeit und/oder Stabilität nahezu wertlos und ein Ärgernis für die Mitarbeiter der Fachabteilung. Das Spannungsfeld zwischen der Softwareentwicklung und der Fachabteilung kann dabei zum Schmelztiegel werden. Wie die Komplexität eines einfachen Dialogbeispiels bereits den Tiegel aufheizt, versucht dieser Artikel zu erläutern.

Beschreibung des Dialogbeispiels:

Das Beispiel beinhaltet einen Dialog zum Anzeigen und Quittieren von Alarmmeldungen. 

Folgende fachliche Funktionalitäten bietet der Dialog:

  • Laden einer Alarmmenge
  • Anzeigen des Ladezustandes mit einem Fortschrittsbalken
  • Anzeigen der Alarme in einem Listenfeld (Listbox) mit einem Renderer
  • Filtermöglichkeit zur Reduzierung der Alarmmenge
  • Speicherung des Filters zur schnellen Anzeige einer Alarmmenge
  • Anzeige und Quittiermöglichkeit für einen selektierten Alarm

Beschreibung weiterer Anforderungen:

Der Dialog soll jederzeit geschlossen werden können. Der aktuelle Ladezustand spielt dabei keine Rolle.  Die Benutzeroberfläche des Dialoges darf nicht einfrieren. Die Interaktionsmöglichkeiten des Benutzers sollen reibungslos zu jedem beliebigen Zeitpunkt durchführbar sein. Der Dialog darf die Benutzeroberfläche der zugrundeliegenden Applikation nicht beeinträchtigen. Im Hintergrund eingehende Alarmmeldungen sollen verarbeitet und angezeigt werden. Der Dialog soll für beliebige Services zur Anzeige der Alarmmeldungen aufschaltbar sein.

Tipp: Bei der Programmierung von Benutzeroberflächen sollte stets nach dem Kernprinzip Separation of Concerns implementiert werden. Es lohnt sich deshalb das Command Pattern einzusetzen, weil die Aktionen in einer Benutzeroberfläche über Menüs, die Werkzeugleiste (Toolbar) und Hot-Keys ausgelöst werden können. Die Anwendung des Command Patterns ist eine wesentliche Voraussetzung für die Einhaltung des DRY-Prinzips. Die Möglichkeiten von Java Generics in Kombination mit einer Ellipse, erlauben es, parametrisierbare Kommandos anzuwenden, sodass einer lose gekoppelten und erweiterbaren Implementierung nichts mehr im Wege steht. Die Abhängigkeiten zwischen den aktionsauslösenden Elementen der Benutzeroberfläche sind mit einem Mediator und/oder Visitor synchronisierbar. Der Status eines Menüeintrages ist auf diese Art und Weise mit dem Status einer Schaltfläche der Werkzeugleiste abgleichbar.

Erste Hürde - Anzeigen des Ladezustandes

Der Fortschrittsbalken zeigt beim Ladevorgang für eine beliebige Menge von Alarmmeldungen den Ladezustand an. Das sieht nach einem mathematischen Problem aus. Die variable Menge der Alarmmeldungen stellt hierbei die erste Hürde dar. Das Problem ist mit einem einfachen Dreisatz nicht zu lösen. Der Dreisatz gibt allerdings die Richtung der Problemlösung vor. Mehr soll an dieser Stelle nicht verraten werden. Eine kleine Rechenaufgabe vor dem Weiterlesen beugt der Langeweile vor.

Ladezustand anzeigen

Zweite Hürde - Alarmmenge laden

In dem Dialog wird über ein Business Delegate auf die Session Facade der Serverapplikation zum Laden der Alarmmenge zugegriffen. Der Aufruf zwischen dem Business Delegate und der Session Facade erfolgt synchron. Die Benutzeroberfläche blockiert deshalb während dem Laden der Alarmmenge.

Eine brauchbare Lösung für das Problem ist, die Alarmmenge in kleineren Teilmengen in einem Hintergrundthread zu laden. Durch diese Variante kann der Ladevorgang unterbrochen werden und die Benutzeroberfläche friert nicht ein. In Java Swing Applikationen bietet sich der in Java SE 6 integrierte Swing Worker zur Problemlösung an. Der Swing Worker startet einen Hintergrundthread, der unterbrechbar ist und bietet darüber hinaus die Möglichkeit an, die Daten nach der Ladeoperation auszulesen, um anschließend die Benutzeroberfläche zu initialisieren.

Alarmmenge anzeigen

Anmerkung: Die in dem Dialog angezeigten Alarme sind mit einem Unit-Test erzeugt worden und haben keinen fachlichen Anspruch.

Die Anzeige der Icons in dem Listenfeld ist keine Standardfunktionalität. Für die Icon-Darstellung ist ein spezieller Renderer zu implementieren. Das Listenfeld wird initialisiert, nachdem der Hintergrundthread des Swing Workers seine Arbeit erledigt hat. Die Anwendung des Swing Workers in GUI-Applikationen ist ratsam. Dem Event Dispatch Thread (EDT) des Swing GUI-Systems sollte man nicht in die Quere kommen, weil sich dabei Refresh-Probleme ergeben können. Refresh-Probleme sind neben dem Einfrieren der Benutzeroberflächen ein weiteres Problem, das bei der GUI-Programmierung zu vermeiden ist.

Dritte Hürde - Alarmmenge filtern

Das Listenfeld verwaltet die Alarmmenge in einem eigenen Datenmodell. Die Alarmmeldungen werden als  eine Collection von Data Transfer Objects (DTOs) über das Business Delegate geladen und im Datenmodell des Listenfeldes verwaltet. Zur Filterung der Modelldaten reicht das einfache Modell des Listenfeldes nicht aus. Das Modell des Listenfeldes ist deshalb um die Filterfunktionalität erweitert worden. Dem Filtermodell liegt eine Model View Controller (MVC) Struktur zugrunde, sodass das Filtermodell in der Folge erweiterbar ist.

Alarmmenge filtern
Der Alarmfilter reagiert bei der der Eingabe eines Suchstrings in dem Filtereingabefeld. Gibt man ein einfaches "q" ein, werden alle Alarme angezeigt die bereits quittiert worden sind. Der Alarmfilter ist speicherbar, sodass dieser ohne Texteingabe ausgelöst werden kann.

Das einfache Dialogbeispiel veranschaulicht, warum die Entwicklung von Softwareprodukten komplex ist. Die Balance zwischen den fachlichen Anforderungen eines Softwareproduktes und den nicht funktionalen Anforderungen ist häufig schwierig. Manchmal ist es eine Gratwanderung große Datenmengen zu verarbeiten und dabei die Benutzerakzeptanz aufrecht zu erhalten. 

Der Dialog ist nur ein sehr kleiner Teil einer komplexen Java EE 6 Anwendung, die Daten im Hintergrund verarbeitet, selbst bei hoher Last auf Benutzerinteraktionen reagiert und keine Refresh-Probleme aufweist. Das solche Applikationen und deren Benutzeroberflächen nicht mit einem "Single Thread Model" umsetzbar sind, dürfte klar sein. Parallele Threads in Benutzeroberflächen zu synchronisieren, ohne dem EDT in die Quere zu kommen, keine Deadlocks zu verursachen und die Performanz auszubremsen, sind Herausforderungen, die es bei der Programmierung von Benutzeroberflächen zu meistern gilt. Wie man Synchronisationsprobleme in Benutzeroberflächen löst ist nicht Gegenstand des Artikels, aber eine der wesentlichen Herausforderungen bei der Programmierung von interaktiven Benutzeroberflächen.

Wenn ein einzelner Dialog einer Anwendung offensichtlich bereits eine nicht zu unterschätzende Komplexität besitzt, lässt sich der Schluss ziehen, dass die Entwicklung von Softwareprodukten eine komplexe Angelegenheit ist.