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

Post on 07-Aug-2020

0 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Softwareproduktlinien - Laufzeitvariabilität

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

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

1

Dom

ain

Eng.

Ap

plic

atio

n En

g.

Feature-Auswahl

Feature-Modell Wiederverwendbare Implementierungs- artefakte

Generator Fertiges Program 2

Wie Variabilität implementieren?

Agenda

3

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

Ein Beispiel

4

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 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

Laufzeitparameter

8

Parameter

9

Parameter –i in grep

10

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

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

Propagierte Parameter

13

durch viele Aufrufe propagiert statt globaler Variable

Konfiguration

14

Kommandozeilenparameter Config-Datei Dialog Quelltext …

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

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

Wiederholung: Modularität

17

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

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

Kohäsion/Kopplung – Beispiel

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

20

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)

Zerlegung und Wiederverwendung

22

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

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

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

Design Patterns für Variabilität

26

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

Observer Pattern

28

VIEW

A = 50%

B = 30%

C = 20%

Obs

erve

rs

Subj

ect

[Quelle: Meyer/Bay]

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)

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

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

Strategy Pattern

32

Strategy Pattern: Beispiel

33

Problem: Unflexible Erweiterungsmechanismen

34

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

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

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

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

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

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

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

43

Konfiguration mit Generator zur Übersetzungszeit Flexiblere Erweiterungsmechanismen Modularisierung von Features

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

top related