Softwareproduktlinien - Laufzeitvariabilität Christian Kästner (Universität Marburg) Sven Apel (Universität Passau) Gunter Saake, Thomas Thüm (Universität Magdeburg) 1
Wie Variabilität implementieren? Domain Eng. Feature-Modell Wiederverwendbare Implementierungsartefakte Application Eng. 2 Feature-Auswahl Generator Fertiges Program
Agenda Graph-Beispiel Variabilität mit Laufzeitparametern Wiederholung: Modularität Design Patterns für Variabilität Grenzen herkömmlicher Techniken 3
4 Ein Beispiel
Beispiel: Graph-Bibliothek Durchgängiges Beispiel in Vorlesung (Chat-Client in der Übung) Bibliothek von Graph-Datenstrukturen und Algorithmen Gewichtete/ungewichtete Kanten Gerichtete/ungerichtete Kanten Gefärbte Knoten Algorithmen: kürzester Pfad, minimale Spannbäume, Transitive Hülle, 5
Graph-Feature-Modell 6
Beispiel: Graph-Implementierung class Graph { class Node { Vector nv = new Vector(); Vector ev = new Vector(); int id = 0; Edge add(node n, Node m) { Color color = new Color(); Edge e = new Edge(n, m); void print() { nv.add(n); nv.add(m); ev.add(e); Color.setDisplayColor(color); e.weight = new Weight(); System.out.print(id); return e; Edge add(node n, Node m, Weight w) Edge e = new Edge(n, m); nv.add(n); nv.add(m); ev.add(e); class Edge { e.weight = w; return e; Node a, b; Color color = new Color(); void print() { Weight weight = new Weight(); for(int i = 0; i < ev.size(); i++) { Edge(Node _a, Node _b) { a = _a; b = _b; ((Edge)ev.get(i)).print(); void print() { Color.setDisplayColor(color); a.print(); b.print(); weight.print(); class Color { static void setdisplaycolor(color c) {... class Weight { void print() {... 7
8 Laufzeitparameter
Parameter 9
Parameter i in grep 10
Globale Konfigurationsoptionen 11 class Conf { public static boolean Logging = false; public static boolean Windows = false; public static boolean Linux = true; class Main { public void foo() { if (Conf.Logging) log( running foo() ); if (Conf.Windows) callwindowsmethod(); else if (Conf.Linux) calllinuxmethod(); else throw RuntimeException();
Graph-Implementierung class Conf { public static boolean COLORED = true; public static boolean WEIGHTED = false; class Graph { Vector nv = new Vector(); Vector ev = new Vector(); Edge add(node n, Node m) { Edge e = new Edge(n, m); nv.add(n); nv.add(m); ev.add(e); if (Conf.WEIGHTED) e.weight = new Weight(); return e; Edge add(node n, Node m, Weight w) if (!Conf.WEIGHTED) throw RuntimeException(); Edge e = new Edge(n, m); nv.add(n); nv.add(m); ev.add(e); e.weight = w; return e; void print() { for(int i = 0; i < ev.size(); i++) { ((Edge)ev.get(i)).print(); class Node { int id = 0; Color color = new Color(); void print() { if (Conf.COLORED) Color.setDisplayColor(color); System.out.print(id); class Edge { Node a, b; Color color = new Color(); Weight weight = new Weight(); Edge(Node _a, Node _b) { a = _a; b = _b; void print() { if (Conf. COLORED) Color.setDisplayColor(color); a.print(); b.print(); if (Conf.WEIGHTED) weight.print(); class Color { static void setdisplaycolor(color c) {... class Weight { void print() {... 12
Propagierte Parameter durch viele Aufrufe propagiert statt globaler Variable 13
Konfiguration Kommandozeilenparameter Config-Datei Dialog Quelltext 14
Diskussion Variabilität im gesamten Program verteilt Globale Variablen oder lange Parameterlisten Konfiguration geprüft? Änderungen zur Laufzeit möglich? Geschützt vor Aufruf deaktivierter Funktionalität? Kein Generator; immer alle Variabilität ausgeliefert Codegröße, Speicherverbrauch, Performance Ungenutzte Funktionalität als Risiko 15
Domain Eng. Parameterliste (Feature-Modell) Program mit Laufzeitparametern Application Eng. Parameterauswahl (Feature-Auswahl) Setzen der Startparameter Programmausführung mit gewünschtem Verhalten 16
17 Wiederholung: Modularität
Was ist Modularität? Modularität ist Kohäsion plus Kapselung Kapselung: Verstecken von Implementierungsdetails hinter einer Schnittstelle Kohäsion: Gruppieren verwandter Programmkonstrukte in einer adressierbaren Einheit (z.b. Paket, Klasse, ) Kohäsive und schwach gekoppelte Module können isoliert verstanden werden Reduziert die Komplexität des Softwareentwicklungsprozesses 18
Kapselung public class ArrayList<E> { public void add(int index, E element) { if (index > size index < 0) throw new IndexOutOfBoundsException( "Index: "+index+", Size: "+size); ensurecapacity(size+1); System.arraycopy(elementData, index, elementdata, index + 1, size - index); elementdata[index] = element; size++; public int indexof(object o) { if (o == null) { for (int i = 0; i < size; i++) if (elementdata[i]==null) return i; else { for (int i = 0; i < size; i++) if (o.equals(elementdata[i])) return i; return -1;. 19 public interface List<E> { void add(int index, E element); int indexof(object o);. Implementierungsdetails werden versteckt Schnittstelle beschreibt Verhalten Implementierung wird austauschbar
Kohäsion/Kopplung Beispiel Gruppierung von Methoden/Aufgaben Viele Aufrufe über Grenzen der Gruppe Gruppe implementiert verschiedene Belange 20
Warum Modularität? Software leicht verständlich (divide and conquer) Versteckt Komplexität von Teilstücken hinter Schnittstellen (information hiding) Einfacher zu warten, da Änderungen lokal erfolgen können (maintainability) Teile der Software können wiederverwendet werden (reusability) Module können auch in neuem Kontext in anderen Projekte neu zusammengestellt werden (variability) 21
Zerlegung und Wiederverwendung 22
Probleme? Verstreuter Code class Graph { Vector nv = new Vector(); Vector ev = new Vector(); Edge add(node n, Node m) { Edge e = new Edge(n, m); nv.add(n); nv.add(m); ev.add(e); if (Conf.WEIGHTED) e.weight = new Weight(); return e; Edge add(node n, Node m, Weight w) if (!Conf.WEIGHTED) throw RuntimeException(); Edge e = new Edge(n, m); nv.add(n); nv.add(m); ev.add(e); e.weight = w; return e; void print() { for(int i = 0; i < ev.size(); i++) { ((Edge)ev.get(i)).print(); Code Scattering class Node { int id = 0; Color color = new Color(); void print() { if (Conf.COLORED) Color.setDisplayColor(color); System.out.print(id); class Edge { Node a, b; Color color = new Color(); Weight weight; Edge(Node _a, Node _b) { a = _a; b = _b; void print() { if (Conf. COLORED) Color.setDisplayColor(color); a.print(); b.print(); if (!Conf.WEIGHTED) weight.print(); class Color { static void setdisplaycolor(color c) {... class Weight { void print() {... 23
Probleme? Vermischter Code class Graph { Vector nv = new Vector(); Vector ev = new Vector(); Edge add(node n, Node m) { Edge e = new Edge(n, m); nv.add(n); nv.add(m); ev.add(e); if (Conf.WEIGHTED) e.weight = new Weight(); return e; Edge add(node n, Node m, Weight w) if (!Conf.WEIGHTED) throw RuntimeException(); Edge e = new Edge(n, m); nv.add(n); nv.add(m); ev.add(e); e.weight = w; return e; void print() { for(int i = 0; i < ev.size(); i++) { ((Edge)ev.get(i)).print(); Code Tangling class Node { int id = 0; Color color = new Color(); void print() { if (Conf.COLORED) Color.setDisplayColor(color); System.out.print(id); class Edge { Node a, b; Color color = new Color(); Weight weight; Edge(Node _a, Node _b) { a = _a; b = _b; void print() { if (Conf. COLORED) Color.setDisplayColor(color); a.print(); b.print(); if (!Conf.WEIGHTED) weight.print(); class Color { static void setdisplaycolor(color c) {... class Weight { void print() {... 24
Probleme? Replizierter Code class Graph { Vector nv = new Vector(); Vector ev = new Vector(); Edge add(node n, Node m) { Edge e = new Edge(n, m); nv.add(n); nv.add(m); ev.add(e); if (Conf.WEIGHTED) e.weight = new Weight(); return e; Edge add(node n, Node m, Weight w) if (!Conf.WEIGHTED) throw RuntimeException(); Edge e = new Edge(n, m); nv.add(n); nv.add(m); ev.add(e); e.weight = w; return e; void print() { for(int i = 0; i < ev.size(); i++) { ((Edge)ev.get(i)).print(); Code Replication class Edge { class Node { int id = 0; Color color = new Color(); void print() { if (Conf.COLORED) Color.setDisplayColor(color); System.out.print(id); Node a, b; Color color = new Color(); Weight weight; Edge(Node _a, Node _b) { a = _a; b = _b; void print() { if (Conf. COLORED) Color.setDisplayColor(color); a.print(); b.print(); if (!Conf.WEIGHTED) weight.print(); class Color { static void setdisplaycolor(color c) {... class Weight { void print() {... 25
26 Design Patterns für Variabilität
Design Patterns Muster für den Entwurf von Lösungen für wiederkehrende Probleme Viele Design Patterns für Variabilität, Entkoppelung und Erweiterbarkeit Hier Auswahl: Observer Template Method Strategy Decorator 27
Observer Pattern Observer s VIEW Subject A = 50% B = 30% C = 20% [Quelle: Meyer/Bay] 28
Observer Pattern Define[s] a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. [GoF, p 293] Observer 1) Bekundet Interesse Subject 3) Informiert über Event Interfaces fügen zusätzliche Flexibilität hinzu 2) Merke Interessenten In Implementierung Klasse für Observer Klasse für Subject Liste in Subject für Observer Subject.addToObservers(Observer) (wird vom Observer aufgerufen) Observer.notify() (wird von Subject aufgerufen) 29 29
Template Method Pattern public abstract class BubbleSorter{ protected int length = 0; protected void sort() { if (length <= 1) return; for (int nexttolast= length-2; nexttolast>= 0; nexttolast--) for (int index = 0; index <= nexttolast; index++) if (outoforder(index)) swap(index); protected abstract void swap(int index); protected abstract boolean outoforder(int index); 30
IntBubbleSorter public class IntBubbleSorter extends BubbleSorter{ private int[] array = null; public void sort(int[] thearray) { array = thearray; length = array.length; super.sort(); protected void swap(int index) { int temp = array[index]; array[index] = array[index+ 1]; array[index+1] = temp; protected boolean outoforder(int index) { return (array[index] > array[index+ 1]); 31
Strategy Pattern 32
Strategy Pattern: Beispiel 33
34 Problem: Unflexible Erweiterungsmechanismen
Unflexible Erweiterungsmechanismen Subklassen für Erweiterungen: modular, aber unflexibel Kein mix & match Stack Stack SecureStack UndoStack SecureStack SynchronizedStack SynchronizedStack UndoStack 35 z. B. White-Box-Framework
Lösung I Kombinierte Klassenhierarchien Kombinatorische Explosion der Varianten Massive Code-Replikation Stack SynchronizedStack UndoStack SecureStack SynchronizedUndoStack UndoSecureStack Mehrfachvererbung Kombinatorische Explosion SynchronizedUndoSecureStac Aufgrund diverser Probleme (u. a. Diamant-Problem) in nur wenigen Sprachen verfügbar 36
Diamant-Problem Stack -values +push() +pop() +size() Was passiert? new LockedUndoStack().pop() UndoStack -log +push() +pop() +undo() LockedStack +push() +pop() +size() +lock() +unlock() Multiple inheritance is good, but there is no good way to do it. A. Synder LockedUndoStack 37
Delegation statt Vererbung class LockedStack implements IStack { final IStack _delegate; public LockedStack(IStack delegate) { this._delegate = delegate; private void lock() { /*... /* private void unlock() { /*... /* public void push(object o) { lock(); _delegate.push(o); unlock(); public Object pop() { lock(); Object result = _delegate.pop(); unlock(); return result; public int size() { return _delegate.size(); 38 class UndoStack implements IStack { final IStack _delegate; public UndoStack(IStack delegate) { this._delegate = delegate; public void undo() { /*... /* public void push(object o) { remember(); _delegate.push(o); public Object pop() { remember(); return _delegate.pop(); public int size() { return _delegate.size(); Main: IStack stack = new UndoStack( new LockedStack(new Stack()));
Decorator Pattern «interface» IStack +push() +pop() +size() 1 1 Stack StackDecorator -values -delegate +push() +pop() +size() +push() +pop() +size() LockedStack UndoStack SecureStack 39 +push() +pop() +size() +lock() +unlock() -log +push() +pop() +undo() -keyphrase +push() +pop() +encrypt() +decrypt()
Beispiel: Decorator in java.io java.io enthält verschiedene Funktionen zur Ein- und Ausgabe: Programme operieren auf Stream-Objekten... Unabhängig von der Datenquelle/-ziel und der Art der Daten 40
Delegation statt Vererbung Diskussion Dynamische Kombination möglich Erweiterungen müssen alle unabhängig sein Kann keine Methoden hinzufügen, nur bestehende erweitern Kein spätes Binden (keine virtuellen Methoden) Viele Indirektionen in der Ausführung (Performance) Mehrere Objektinstanzen bilden ein Objekt (Objektschizophrenie) 41
Flexible Erweiterungsmechanismen???? 42
Ausblick Konfiguration mit Generator zur Übersetzungszeit Flexiblere Erweiterungsmechanismen Modularisierung von Features 43
Literatur Gamma, Erich; Richard Helm, Ralph Johnson, and John Vlissides (1995). Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley. ISBN 0-201- 63361-2. [Standardwerk Design Patterns] Bertrand Meyer, Object-Oriented Software Construction, Prentice Hall, 1997 Chapters 3, 4 [Zu Modularität] 44
Quiz Mit welchen Design Pattern könnte man das Feature Color modular implementieren? (kurz begründen) Visualisiere Module, die (a) sehr kohäsiv + stark gekoppelt 45 (b) sehr kohäsiv + schwach gekoppelt (c) wenig kohäsiv + stark gekoppelt (d) wenig kohäsiv + schwach gekoppelt sind (a) (b) (c) (d)