softwareproduktlinien - laufzeitvariabilitätobserver pattern 29 “ define [s] a one-to-many...

44
Softwareproduktlinien - Laufzeitvariabilität Christian Kästner (Universität Marburg) Sven Apel (Universität Passau) Gunter Saake, Thomas Thüm (Universität Magdeburg) 1

Upload: others

Post on 07-Aug-2020

0 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Softwareproduktlinien - Laufzeitvariabilität

Christian Kästner (Universität Marburg) Sven Apel (Universität Passau)

Gunter Saake, Thomas Thüm (Universität Magdeburg)

1

Page 2: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Dom

ain

Eng.

Ap

plic

atio

n En

g.

Feature-Auswahl

Feature-Modell Wiederverwendbare Implementierungs- artefakte

Generator Fertiges Program 2

Wie Variabilität implementieren?

Page 3: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Agenda

3

Graph-Beispiel Variabilität mit Laufzeitparametern Wiederholung: Modularität Design Patterns für Variabilität Grenzen herkömmlicher Techniken

Page 4: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Ein Beispiel

4

Page 5: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

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

Page 6: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Graph-Feature-Modell

6

Page 7: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Beispiel: Graph-Implementierung

class Edge { Node a, b; Color color = new Color(); Weight weight = new Weight(); Edge(Node _a, Node _b) { a = _a; b = _b; } void print() { Color.setDisplayColor(color); a.print(); b.print(); weight.print(); } }

class Edge { Node a, b; Color color = new Color(); Weight weight = new Weight(); Edge(Node _a, Node _b) { a = _a; b = _b; } void print() { Color.setDisplayColor(color); a.print(); b.print(); weight.print(); } }

class Edge { Node a, b; Color color = new Color(); Weight weight Edge(Node _a, Node _b) { a = _a; b = _b; } void print() { Color.setDisplayColor(color); a.print(); b.print(); weight.print(); } }

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); e.weight = new Weight(); 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); e.weight = w; return e; } void print() { for(int i = 0; i < ev.size(); i++) { ((Edge)ev.get(i)).print(); } } }

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); e.weight = new Weight(); 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); 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() { Color.setDisplayColor(color); System.out.print(id); } }

class Node { int id = 0; Color color = new Color(); void print() { Color.setDisplayColor(color); System.out.print(id); } }

class Color { static void setDisplayColor(Color c) { ... } } class Weight { void print() { ... } }

7

Page 8: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Laufzeitparameter

8

Page 9: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Parameter

9

Page 10: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Parameter –i in grep

10

Page 11: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Globale Konfigurationsoptionen 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(); } }

11

Page 12: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Graph-Implementierung

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 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 Color { static void setDisplayColor(Color c) { ... } } class Weight { void print() { ... } }

class Conf { public static boolean COLORED = true; public static boolean WEIGHTED = false; }

12

Page 13: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Propagierte Parameter

13

durch viele Aufrufe propagiert statt globaler Variable

Page 14: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Konfiguration

14

Kommandozeilenparameter Config-Datei Dialog Quelltext …

Page 15: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Diskussion

15

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

Page 16: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Dom

ain

Eng.

Ap

plic

atio

n En

g.

16

Parameterauswahl (Feature-Auswahl)

Parameterliste (Feature-Modell) Program mit

Laufzeitparametern

Setzen der Startparameter Programmausführung mit gewünschtem Verhalten

Page 17: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Wiederholung: Modularität

17

Page 18: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Was ist Modularität?

18

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

Page 19: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Kapselung

Implementierungsdetails werden versteckt

Schnittstelle beschreibt Verhalten

Implementierung wird austauschbar

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; } ……. }

public interface List<E> { void add(int index, E element); int indexOf(Object o); …. }

19

Page 20: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Kohäsion/Kopplung – Beispiel

Gruppierung von Methoden/Aufgaben Viele Aufrufe über Grenzen der Gruppe Gruppe implementiert verschiedene Belange

20

Page 21: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Warum Modularität?

21

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)

Page 22: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Zerlegung und Wiederverwendung

22

Page 23: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

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); } }

Probleme? – Verstreuter Code

Code Scattering

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

Page 24: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

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(); } } }

Probleme? – Vermischter Code

Code Tangling

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 Node { int id = 0; Color color = new Color(); void print() { if (Conf.COLORED) Color.setDisplayColor(color); System.out.print(id); } }

class Color { static void setDisplayColor(Color c) { ... } } class Weight { void print() { ... } }

24

Page 25: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

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(); } } }

Probleme? – Replizierter Code

Code Replication 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 Node { int id = 0; Color color = new Color(); void print() { if (Conf.COLORED) Color.setDisplayColor(color); System.out.print(id); } }

class Color { static void setDisplayColor(Color c) { ... } } class Weight { void print() { ... } }

25

Page 26: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Design Patterns für Variabilität

26

Page 27: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Design Patterns

27

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

Page 28: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Observer Pattern

28

VIEW

A = 50%

B = 30%

C = 20%

Obs

erve

rs

Subj

ect

[Quelle: Meyer/Bay]

Page 29: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Observer Pattern

29

“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

Subject

3) Informiert über Event

1) Bekundet Interesse

2) Merke Interessenten

29

• Interfaces fügen zusätzliche Flexibilität hinzu • 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)

Page 30: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

30

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); }

Template Method Pattern

Page 31: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

IntBubbleSorter

31

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]); } }

Page 32: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Strategy Pattern

32

Page 33: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Strategy Pattern: Beispiel

33

Page 34: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Problem: Unflexible Erweiterungsmechanismen

34

Page 35: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Unflexible Erweiterungsmechanismen Subklassen für Erweiterungen: modular, aber unflexibel Kein “mix & match”

Stack

SecureStack

SynchronizedStack

UndoStack

Stack

SecureStack SynchronizedStackUndoStack

35 z. B. White-Box-Framework

Page 36: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Lösung I Kombinierte Klassenhierarchien Kombinatorische Explosion der Varianten Massive Code-Replikation

Mehrfachvererbung Kombinatorische Explosion Aufgrund diverser Probleme

(u. a. Diamant-Problem) in nur wenigen Sprachen verfügbar

36

Stack

SecureStack

SynchronizedUndoSecureStac

UndoStack

UndoSecureStackSynchronizedUndoStack

SynchronizedStack

Page 37: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Diamant-Problem

Was passiert? new LockedUndoStack().pop()

+push()+pop()+size()

-valuesStack

+push()+pop()+size()+lock()+unlock()

LockedStack

+push()+pop()+undo()

-logUndoStack

LockedUndoStack

“Multiple inheritance is good, but there is no good way to do it.”

A. SYNDER

37

Page 38: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

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(); } }

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(); } }

38

Main: IStack stack = new UndoStack( new LockedStack(new Stack()));

Page 39: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Decorator Pattern

+push()+pop()+size()

«interface»IStack

+push()+pop()+size()

-valuesStack

+push()+pop()+size()

-delegateStackDecorator

+push()+pop()+size()+lock()+unlock()

LockedStack

+push()+pop()+undo()

-logUndoStack

+push()+pop()+encrypt()+decrypt()

-keyphrase

SecureStack

1

1

39

Page 40: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Beispiel: Decorator in java.io

40

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

Page 41: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

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

Page 42: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Flexible Erweiterungsmechanismen?

??? 42

Page 43: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

Ausblick

43

Konfiguration mit Generator zur Übersetzungszeit Flexiblere Erweiterungsmechanismen Modularisierung von Features

Page 44: Softwareproduktlinien - LaufzeitvariabilitätObserver Pattern 29 “ Define [s] a one-to-many dependency between objects so that when one object changes state, all its dependents are

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