Software-Architektur: Unterschied zwischen den Versionen

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche
(Structure Charts)
Zeile 74: Zeile 74:
* Datentyp: (Klasse/Struktur/Union)
* Datentyp: (Klasse/Struktur/Union)


und dem gedanklichen Trick, der die Objektorientierung ausmacht, dem Konzept der '''Datenkapsel''': ein Objekt beinhaltet also nur logisch irgendwie zusammenhängende Daten und Funktionen. Der Grad, in dem ein Objekt abhängig von anderen Komponenten ist, wird in der Softwaretechnik als '''Kopplung''' bezeichnet. Der Grad, wie stark logisch zusammenhängend ein Objekt zu seinen Member-Variablen ist, ist dagegen als '''Kohäsion''' bekannt. Ziel beim Entwurf einer Software-Architektur ist es, Kohäsion zu maximieren und Kopplung zu minimieren, sodass austauschbare Software-'Bausteine' entstehen.
und dem gedanklichen Trick, der die Objektorientierung ausmacht, dem Konzept der '''Datenkapsel''': ein Objekt beinhaltet also nur logisch irgendwie zusammenhängende Daten und Funktionen. Der Grad, in dem ein Objekt abhängig von anderen Komponenten ist, wird in der Softwaretechnik als '''Kopplung''' bezeichnet. Der Grad, wie stark logisch zusammenhängend ein Objekt zu seinen Member-Variablen ist, ist dagegen als '''Kohäsion''' bekannt. Ziel beim Entwurf einer Software-Architektur ist es, '''Kohäsion zu maximieren''' und '''Kopplung zu minimieren''', sodass austauschbare Software-'Bausteine' entstehen.


Entwurfsmuster sind dabei die Mittel, mit denen diese Minimierung von Kopplung und die Maximierung von Kohäsion erreicht werden kann.
Entwurfsmuster sind dabei die Mittel, mit denen diese Minimierung von Kopplung und die Maximierung von Kohäsion erreicht werden kann.

Version vom 5. Januar 2016, 19:59 Uhr

Grundlagen

Einleitung

Die vermutlich häufigste Problematik, die sich Programmierern bei der Entwicklung von Software stellt, ist die nach einer systematischen Erzeugung von 'gutem' Code und qualitativ hochwertiger Software - sprich nach der Entwicklung von Software als Qualitätsprodukt. Software Engineering ist also nach IEEE-Definition die Anwendung ingenieursmäßiger Entwurfsmethodiken auf den Bereich des Softwareentwicklung. Mit genau dieser Fragestellung beschäftigt sich der Teilbereich der Softwaretechnik in der Informatik.

Zweck dieses Artikels ist es, grundlegende Techniken vorzustellen, mit denen man als Entwickler seine Software etwas besser strukturieren und flexibler gegenüber Änderungen gestalten kann. Zum Verständnis dessen, wie das erreicht werden kann, müssen daher zunächst einige grundlegende Konzepte erklärt werden.

Daten

Essentiell für datenverarbeitende System ist der grundlegende Begriff des 'Datums' bzw. der Daten. Darunter versteht man die zeit- und wertdiskrete Repräsentation analoger Größen (Informationen). Beispielsweise resultiert aus der Messung einer analogen Größe (z.B. Temperatur) eine digitale Zahl (AD-Wandlung). Im Umkehrschluss können aus digitalen Daten auch wieder analoge Größen erzeugt werden (DA-Wandlung).

Daten besitzen immer ein Format, da ein diskretes Rechensystem selbst bestimmte Repräsentationen für die Berechnung an sich nutzen muss. Für Fließkommazahlen wird z.B. die IEEE-754 Repräsentation genutzt, aber selbst die Darstellung als Integer impliziert schon ein minimales und ein maximales Element und eine Zahlenauflösung dazwischen. Im Codebespiel dieses Artikels wäre ein internes 8.1 Bit-Format vom Sensor DS1621 in eine Komma-Darstellung zu konvertieren.

Das heißt in der Quintessenz: Konvertierungen zwischen Datenformaten sind essentieller Bestandteil jeder Software. Entsprechend wichtig ist Konsistenz dieser Daten im Gesamtsystem.

E/R Modell

Zur Modellierung von Daten eines Softwaresystems wird häufig das Entity-Relationship-Modell verwendet. Mit dieser Diagramm-Notation kann eindeutig ein gemeinsames und konsistentes Format von Daten spezifiziert werden. Das Codebeispiel dieses Artikels beschreibt nur die Verarbeitung eines Temperaturwerts, daher sei für kompliziertere Systeme auf die entsprechende Literatur verwiesen (Chen/IBM).

Erweiterte Backus-Naur-Form

Zur Darstellung von Datenformaten eignet sich sehr gut eine Notation in der EBNF. Diese kontextfreie Sprache dient zur eindeutigen Spezifikation von Daten und kann leicht durch Syntaxdiagramme visualisiert werden. Sie dient hauptsächlich zur Beschreibung von Transformationen von Daten durch Prozesse.

Datenfluss

Software fällt nur in den seltesten Fällen innerhalb von ein paar Stunden vom Himmel und ist dann für immer einfach da. Stattdessen unterliegt die Entwicklung derselbigen eines Prozesses, also eines zeitlich dauernden Vorgangs, durch den definierte Eingaben durch Arbeit zu Ausgaben transformiert werden.

Datenflussprinzip

Die Strukturierte Analyse und Design von Ed Yourdon u.a setzt darauf auf, zunächst zu identifizieren und zu spezifizieren, was die Software überhaupt leisten soll (Requirements bzw. Anforderungsanalyse) und mündet in einem Anforderungsdokument.

Aus diesem Anforderungsdokument werden nun die Funktionen abgeleitet, welche die Software tatsächlich während ihres Betriebs leisten muss.

Diese funktionalen Anforderungen werden innerhalb der Software auch als ein 'Prozess' betrachtet - einen Prozess, den die Software zur Durchführung der jeweiligen Funktion durchlaufen muss. Für das Code-Beispiel zum Artikel wäre die dort maßgebliche 'Funktion' der Software, die Werte eines I2C-Thermometers auf UART und LCD darzustellen. Der I2C-Slave ist also die Datenquelle, UART und LCD sind Datensenken.

Durch die Kombination der Definition eines 'Prozess' mit der hierarchischen Dekomposition eines komplizierten Vorgangs in kleinere Vorgänge ('Teile-und-Herrsche') kommen also also zwei Grundprinzipien der Informatik zum Tragen:

  • Prozess = Input-Processing-Output (Eingabe-Verarbeitung-Ausgabe)
  • Teile-und-Herrsche zur Unterteilung von Prozessen in Subprozesse

Yourdons Datenflussdiagramme (DFD) dienen dazu, diese Zerlegungen von Software-Funktionen zu beschreiben. Sie beschreiben, woher Daten kommen (Datenquelle), welche Transformationen sie in ihrer Verarbeitung durchlaufen und wohin die Daten am Ende ihrer Transformation fließen (Datensenke). Das wird letztlich als Datenflussprinzip formuliert. Diese DFDs werden immer im Zusammenhang mit Entity-Relationship-Diagrammen (ER-Diagramme) erstellt, beschreibt, was die eigentlichen Daten überhaupt sind und was relevant für die Datenhaltung ist.

Als Datenquellen/senken in DFDs dienen Datenbanken (Persistenz, Symbol: Ξ) und Terminals (Benutzerinteraktion, Symbol: Rechtecke). Verarbeitungen werden als Prozesse in Kreisen symbolisiert. Das Code-Beispiel verwendet keine Persistenz für die Sensordaten - dort müssten sowohl der I2C-Sensor als Datenquelle, sowie die Datensenken (UART, LCD) mit einem Terminal-Symbol verwendet werden. Zum Einen, weil die formatierten Daten direkt dem Anwender gezeigt werden (LCD) oder weil sie von anderen externen Teilnehmern stammen (I2C-Sensor) bzw. zu extern geliefert werden (z.B. UART).

Die Kombination aus DF- und ER-Diagrammen dient als erstes Design-Dokument, welches spezifiziert, was die Software überhaupt für Funktionen hat! Für den Praktiker ist an dieser Stelle wichtig, dass es gut ist, Daten fließen zu lassen und nicht zur Unkenntlichkeit zu zerstückeln.

Um es mit Mitteln der Elektrotechnik etwas griffiger zu formulieren: Datenflussdiagramme stellen im Prinzip die Leiterbahnen und die 'Verbraucher' dar. Analog zum 'Prozess' ist der Stromfluss zwischen den Potentialen (von Quellen zu den Senken) - die Dauer des Prozesses kann man als Äquivalent zur elektrischen Arbeit interpretieren (⇒ was sie auf etlichen Abstraktionsebenen unterhalb der Softwareebene letztlich auch ist).

Kontrollfluss

Zur Erklärung des Kontrollflusses soll das Beispiel des Stromflusses erweitert werden. Oft reicht bereits einfach ein geschlossener Stromkreis (zwischen Quelle und Senke) aus, sodass ein Strom fließt und an einem Verbraucher Arbeit verrichtet wird. Manchmal muss dieser Prozess aber abhängig von bestimmten Bedingungen gemacht werden - z.B. sollte ein Dämmerungsschalter bei Tag (in nicht-arktischen Gegenden) keinen Strom fließen lassen. Bedingungen werden mit Kontrolle gleichgesetzt - man benötigt eine Instanz zum Kontrollieren, ob die Bedingung eingehalten ist. Z.B. ob ein bestimmtes Flag gesetzt ist oder ein bestimmter Schwellwert überschritten wurde. Kontrollfluß ist also etwas, was kostet (im Sinne von 'den Datenfluss ausbremst') und was immer an Daten gekoppelt ist. Unter Kontrollmechanismen versteht man in etwa die klassischen if/for/while/switch-Konstrukte. Kontrolle ist immer schlecht - je mehr man hat, desto mehr Overhead kann man damit erzeugen (i.S.v. die Kontrolleure kontrollieren) und dementsprechend mehr Fehlerpotential in seine Software generieren. Ziel eines sauberen Designs ist es also, Kontrolle zu minimieren.

Datenflussdiagramme können diese Aufgabe der Kontrolllogik nicht übernehmen - sie stellen nur dar, wie Daten fließen sollen und geben somit noch kein direktes Design vor.

Structure Charts

Um aus den entstandenen ER- und DF-Diagrammen ein Design abzuleiten, wird eine Darstellung als Structure Chart verwendet. Diese entsprechen einem Baum aus Funktionsaufrufen und visualieren, zwischen welchen Komponenten Datenfluss und wo Kontrollfluss stattfindet.

Der Schwerpunkt eines Prozesses (also eines Top-Level-IPO-Vorgangs) wird auch als Zentrum der Transformation bzw. objektorientiert als Top-Level-Controller bezeichnet. Hat der Baum an dieser Stelle seine Wurzel, erhält man ein Design ohne unnötige Kontrollinstanzen.

Entwurfsmuster (Design Patterns)

In der Software-Architektur gibt es Konstrukte, die sich als gut funktionierend erwiesen haben und bei korrekter Anwendung ein Design drastisch verbessern können. An dieser Stelle verhält sich die Softwaretechnik als "praktisches Handwerk" analog zu anderen Tätigkeitsbereichen, wie etwa beim Design von Leiterplatten oder in der Mechanik - also eine Art Katalog von Do's und Don'ts, die ein Softwaredesign verbessern können.

Die hier vorgestellten Entwurfsmuster sind grundsätzlich objekt-orientierter Natur, sie setzen also ein grundlegendes Verständnis des Konzeptes eines 'Objekts' voraus, was aber die Verwendung der Muster für die rein prozedurale Programmierung (ANSI-C) nicht ausschließt.

Im Artikel wird unter einer 'Variable' also ein 4-Tupel verstanden, aus:

  • Adresse zur Laufzeit
  • Zustand (Wert)
  • Bezeichner
  • Datentyp

Ein Objekt ist also einfach eine spezielle Variable von einer Klasse/Struktur/Union (Datentyp) mit den speziellen Eigenschaften:

  • Adresse zur Laufzeit: 'this'-Pointer
  • Zustand: Zustände aller Member-Variablen
  • Datentyp: (Klasse/Struktur/Union)

und dem gedanklichen Trick, der die Objektorientierung ausmacht, dem Konzept der Datenkapsel: ein Objekt beinhaltet also nur logisch irgendwie zusammenhängende Daten und Funktionen. Der Grad, in dem ein Objekt abhängig von anderen Komponenten ist, wird in der Softwaretechnik als Kopplung bezeichnet. Der Grad, wie stark logisch zusammenhängend ein Objekt zu seinen Member-Variablen ist, ist dagegen als Kohäsion bekannt. Ziel beim Entwurf einer Software-Architektur ist es, Kohäsion zu maximieren und Kopplung zu minimieren, sodass austauschbare Software-'Bausteine' entstehen.

Entwurfsmuster sind dabei die Mittel, mit denen diese Minimierung von Kopplung und die Maximierung von Kohäsion erreicht werden kann.

Strategy-Pattern (Strategie)

State-Pattern

Observer-Pattern (Beobachter)

Architekturmuster

Model-View-Controller

Model-View-Controller

Model-View-Presenter

Model-View-Presenter

Kombinationen

MVC-MVP-Verschachtelung

Anwendung

Im Anhang des Artikels ist ein GitHub-Repository verlinkt, welches anhand eines Thermometer-ICs die Anwendung der beschriebenen Architekturmuster zeigen soll. Das Beispiel abstrahiert dabei den Sensor vollständig von der konkreten I2C-Slave-Implementierung, weswegen letztere als hardwareabhängiges Backend auch bewusst weggelassen wurde. Das Beispiel auf PIC, AVR oder ARM bzw. zu adaptieren bzw. in ein vorhandenes Framework zu integrieren ist nicht die direkte Aufgabe des Artikels.

Codechart-ähnliche Darstellung des hierarchischen Aufbaus der Thermometer-API

Links