Komponenten und Frameworks - Fachbereich Mathematik und

Transcription

Komponenten und Frameworks - Fachbereich Mathematik und
Softwareproduktlinien
Teil 6: Komponenten und Frameworks
Christian Kästner (Universität Marburg)
Sven Apel (Universität Passau)
Gunter Saake (Universität Magdeburg)
1
Wie Variabilität modular implementieren?
Wiederverwendbare
Implementierungsartefakte
Application Eng.
Domain Eng.
Feature-Modell
Feature-Auswahl
2
Generator
Fertiges Program
Komponenten
3
Komponenten
Abgeschlossene modulare Implementierungseinheit mit
Schnittstelle (black box), bietet ein „Dienst“ an
Wird zusammen mit anderen Komponenten – auch von
anderen Herstellern – „zusammengesetzt“ zu
Softwaresystemen (Komposition)
Einzeln vermarktbar und lauffähig
Kontext (z. B. JavaEE, CORBA, COM+/DCOM, OSGi) und
Abhängigkeiten (imports, exports) explizit spezifiziert
Klein genug für Erzeugung und Wartung in einem Stück,
groß genug um sinnvolle Funktion bieten zu können





4
Komponenten vs. Objekte/Klassen
Ähnliche Konzepte: Kapselung, Interfaces,
Geheimnisprinzip



Objekte strukturieren ein Problem
Komponenten bieten anwendbare Teilfunktionalität
Objekte sind üblicherweise kleiner als Komponenten:
„Komponenten skalieren OOP“
Objekte haben häufig viele Abhängigkeiten zu anderen
Objekten; Komponenten haben wenig Abhängigkeiten
Interfaces von Objekten sind häufig
implementierungsnah; Komponenten abstrahieren mehr



5
Vision: Märkte für Komponenten
Komponenten können gekauft und in eigene Produkte
integriert werden
Best of Breed: Entwickler kann für jedes Teilsystem den
besten/billigsten Anbieter auswählen
Anbieter können sich auf Kernkompetenz beschränken
und diese als Komponente anbieten



6
Komponenten eines Webshops
(UML-Notation)

Bestellen
Kunde anlegen
Großhändler
Kundenverwaltung
Adresse ändern
Einkäufe
Rechnung drucken
Reportgenerator
Webshop
Katalog verwalten
Entnehmen
Rechnung schreiben
Lagerhaltung
Finanzbuchhaltung
Einlagern
Szenario: Kunde anlegen -> Einkauf -> Rechnung schreiben -> Rechnung drucken
7
Produktlinien aus Komponenten
Features werden in verschiedenen Komponenten
implementiert



z. B. die Komponenten Transaktionsverwaltung, Log/Recovery,
Pufferverwaltung, Optimierer
Komponenten können ggf. Laufzeitvariabilität enthalten
Durch Feature-Auswahl werden Komponenten
ausgewählt (mapping)
Der Entwickler muss Komponenten verbinden (glue
code)


8
Beispiel: Komponente Farbe in Java
package modules.colorModule;
//public interface
public class ColorModule {
public Color createColor(r:Int,g:Int,b:Int) { …}
public void printColor(color: Color) {colorPrint… }
public void mapColor(elem: Object, col: Color)
{ colorMapping…}
public Color getColor(elem: Object)
{ colorMapping…}
//just one module instance
public static ColorModule getInstance()
{ return module; }
private static ColorModule module =
new ColorModule();
private ColorModule() { super(); }

Facade Pattern



Versteckt
Implementierungsdetails
Gemeinsames Interface
für viele Klassen
Singleton Pattern

Nur eine Instanz des
Moduls
}
public interface Color { … }
//hidden implementation
class ColorPrinter { … }
class 9ColorMapping {…}
ColorModule.getInstance().createColor(…)
Produktlinien aus Komponenten
Mapping von Features
zu Komponenten
Komponentensammlung
Feature-Modell
Component10
Component6
Component2
Component12
Component5
Component1
Component9
Component3
Component7
Component11
Component4
Component8
Feature-Auswahl
als Eingabe
Entwickler baut
fertiges Programm
aus Komponenten
Component6
Component2
Component9
Component8
Fertiges Program
Feature-Auswahl
10
Komponentenauswahl
Menge von
Komponenten
Wie Komponenten bestimmen?




Markt für beliebige Komponenten funktioniert nicht
Zu kleine Komponenten  hoher Aufwand
Zu große Komp.  kaum wiederverwendbar
Produktlinien liefern
nötige Domänenanalyse


11
Welche Teilfunktionalität
wird in welcher Granularität wiederverwendet
Systematische
Wiederverwendung
(Tangram)
Exkurs
12
Bewertung Produktlinien aus Komponenten





In der Industrie üblich (Beispiel Heimelektronik mit
Koala-Komponenten von Phillips)
Produktlinien zum Domain Engineering, zur Organisation
der Entwicklung, …
Systematische (geplante) Wiederverwendung von
Komponenten
Keine vollständige Automatisierung, hoher
Entwicklungsaufwand (glue code) im Application
Engineering
Keine freie Feature-Auswahl
13
Diskussion Modularität





Komponenten verstecken interne Implementierung
Idealerweise kleine Interfaces
Feature-Kohäsion
aber …
Grobe Granularität



Seitenwechselstrategien, Suchalgorithmen, Locking im B-Baum,
oder VARCHAR als Komponente?
Farben oder gewichtete Kanten im Graph als Komp.?
Funktionalität ggf. schwer als Komponente zu kapseln

14
Transaktionsverwaltungskomponente?
Services


Ähnlich zu Komponenten: kapseln Teilfunktionalität
(Dienste)
i.d.R. verteiltes Szenario



Bus-Kommunikation, Web Services, SOAP, REST…
Produktlinien durch Verbinden von Services, i.d.R. mit
scripting (glue code)
Viele Werkzeuge verfügbar (teils “Managementgerecht”)
15
Frameworks
16
Frameworks


Menge abstrakter und konkreter Klassen
Abstrakte Struktur, die für einen bestimmten Zweck
angepasst/erweitert werden kann




vgl. Template Method Pattern und Strategy Pattern
Wiederverwendbare Lösung für eine Problemfamilie in
einer Domäne
Punkte an denen Erweiterungen vorgesehen sind: hot
spots (auch variation points, extension points)
Umkehrung der Kontrolle, das Framework bestimmt die
Ausführungsreihenfolge

17
Hollywood Prinzip: „Don’t call us, we’ll call you.“
Plug-ins




Erweiterung eines Frameworks
Spezielle Funktionen bei Bedarf hinzufügen
Üblicherweise getrennt kompiliert; third-party
Beispiele: Emailprogramme, Graphikprogramme, MediaPlayer, Webbrowser
18
Web Portal


WebapplikationFrameworks wie Struts,
die grundlegende
Konzepte vorgeben und
vorimplementieren
Entwickler konzentrieren
sich auf Anwendungslogik
statt Navigation zwischen
Webseiten
19
<?php
class WebPage {
function getCSSFiles();
function getModuleTitle();
function hasAccess(User u);
function printPage();
}
?>
<?php
class ConfigPage extends WebPage {
function getCSSFiles() {…}
function getModuleTitle() {
return “Configuration”;
}
function hasAccess(User u) {
return user.isAdmin();
}
function printPage() {
print “<form><div>…”;
}
}
?>
Eclipse

Eclipse als Framework für IDEs



20
Nur sprachspezifische Erweiterungen (Syntax Highlighting,
Compiler) müssen implementiert werden
Der gemeinsame Teil (Editoren, Menüs, Projekte,
Verzeichnisbaum, Copy & Paste & Undo Operationen, CVS,
uvm.) ist durch das Framework vorgegeben
Framework aus vielen kleineren Frameworks
21
JUnit
Refactor
Launch
Cheat Sheets
Search
Debug
OSGi
Rich Client Platform
Help
Compare
Text Editors
Forms
Editors
Console
Resources
Weitere Erweiterungen…
Workbench
JFace
Core Runtime
SWT
Prerequisite C.
Meta-Dialogs
Dialogs
Resourcepool
Messages
Script
Table
Component
Control Items
Web
WTP
Views
Update
Help
J2EE
Edit
IDE
JDT
Team
Debug
Platform
Ant
Build
Eclipse
SAPinst Workbench
Weitere Framework-Beispiele




Frameworks für graphische Benutzeroberflächen, z.B.
MacApp, Swing, SWT, MFC
Multimedia-Frameworks, z.B. DirectX
Instant Messenger-Frameworks, z.B. Miranda, Trillian, ...
Compiler-Frameworks, z.B. Polyglot, abc
22
Framework-Implementierung: Mini-Beispiel

Familie von Dialogen, bestehend aus Textfeld und Button

90% des Quelltexts sind gleich





23
Main Methode
Initialisierung von Fenster, Textfeld und Button
Layout
Schliessen des Fensters
…
Taschenrechner
public class Calc extends JFrame {
private JTextField textfield;
public static void main(String[] args) { new Calc().setVisible(true); }
public Calc() { init(); }
protected void init() {
JPanel contentPane = new JPanel(new BorderLayout());
contentPane.setBorder(new BevelBorder(BevelBorder.LOWERED));
JButton button = new JButton();
button.setText("calculate");
contentPane.add(button, BorderLayout.EAST);
textfield = new JTextField("");
textfield.setText("10 / 2 + 6");
textfield.setPreferredSize(new Dimension(200, 20));
contentPane.add(textfield, BorderLayout.WEST);
button.addActionListener(/* code zum berechnen */);
this.setContentPane(contentPane);
this.pack();
this.setLocation(100, 100);
this.setTitle("My Great Calculator");
// code zum schliessen des fensters
}
}
24
Quelltext für alle Varianten fast gleich, nur roter Quelltext unterscheidet sich (hot spots)
White-Box Frameworks


Erweiterung durch überschreiben und hinzufügen von
Methoden (vgl. Template Method Pattern)
Interna des Framework müssen verstanden werden





-> schwierig zu lernen
Flexible Erweiterung
Viele Subklassen nötig -> ggf. unübersichtlich
Status direkt verfügbar durch Superklasse
Keine Plug-ins, nicht getrennt kompilierbar
25
Taschenrechner als Whitebox-Framework
public abstract class Application extends JFrame {
protected abstract String getApplicationTitle();
//Abstrakte Methoden
protected abstract String getButtonText();
protected String getInititalText() {return "";}
protected void buttonClicked() { }
private JTextField textfield;
public Application() { init(); }
protected void init() {
JPanel contentPane = new JPanel(new BorderLayout());
contentPane.setBorder(new BevelBorder(BevelBorder.LOWERED));
JButton button = new JButton();
button.setText(getButtonText());
contentPane.add(button, BorderLayout.EAST);
textfield = new JTextField("");
textfield.setText(getInititalText());
textfield.setPreferredSize(new Dimension(200, 20));
contentPane.add(textfield, BorderLayout.WEST);
button.addActionListener(/* … buttonClicked(); … */);
this.setContentPane(contentPane);
this.pack();
this.setLocation(100, 100);
this.setTitle(getApplicationTitle());
// code zum schliessen des fensters
}
protected String getInput() { return textfield.getText();}
}
26
Taschenrechner als Whitebox-Framework
public abstract class Application extends JFrame {
protected abstract String getApplicationTitle();
//Abstrakte Methoden
protected abstract String getButtonText();
protected String getInititalText() {return "";}
protected void buttonClicked() { }
private JTextField textfield;
public Application() { init(); }
protected void
init() class
{
public
Calculator extends Application {
JPanel contentPane
= new
JPanel(new BorderLayout());
protected String
getButtonText()
{ return "calculate"; }
contentPane.setBorder(new
BevelBorder(BevelBorder.LOWERED));
protected String getInititalText()
{ return "(10 – 3) * 6"; }
JButton protected
button = new
JButton();
void
buttonClicked() {
button.setText(getButtonText());
JOptionPane.showMessageDialog(this, "The result of "+getInput()+
contentPane.add(button,
" is BorderLayout.EAST);
"+calculate(getInput())); }
textfield protected
= new JTextField("");
String getApplicationTitle() { return "My Great Calculator"; }
textfield.setText(getInititalText());
public static void main(String[] args) {
textfield.setPreferredSize(new
Dimension(200, 20));
new Calculator().setVisible(true);
contentPane.add(textfield,
BorderLayout.WEST);
}
} button.addActionListener(/* … buttonClicked(); … */);
this.setContentPane(contentPane);
this.pack();
this.setLocation(100,
100);Application {
public
class Ping extends
this.setTitle(getApplicationTitle());
protected String getButtonText() { return "ping"; }
// code zum
schliessen
des
fensters
protected
String
getInititalText()
{ return "127.0.0.1"; }
}
protected void buttonClicked() { /* … */ }
protected String getInput()
{ return
textfield.getText();}
protected
String
getApplicationTitle() { return "Ping"; }
}
public static void main(String[] args) {
new Ping().setVisible(true);
}
27
}
Black-Box Frameworks

Einbinden des anwendungsspezifischen Verhalten durch
Komponenten mit speziellem Interface (plug-in)


Nur das Interface muss verstanden werden




vgl. Strategy Pattern, Observer Pattern
einfacher zu lernen, aber aufwendiger zu entwerfen
Flexibilität durch bereitgestellte Hot Spots festgelegt,
häufig Design Pattern
Status nur bekannt wenn durch Interface verfügbar
Insgesamt besser wiederverwendbar (?)
28
Taschenrechner
public class Application extends JFrame {
private JTextField textfield;
private Plugin plugin;
public Application(Plugin p) { this.plugin=p; p.setApplication(this); init(); }
protected void init() {
JPanel contentPane = new JPanel(new BorderLayout());
contentPane.setBorder(new BevelBorder(BevelBorder.LOWERED));
JButton button = new JButton();
public interface Plugin {
if (plugin != null)
String getApplicationTitle();
button.setText(plugin.getButtonText());
String getButtonText();
else
String getInititalText();
button.setText("ok");
void buttonClicked() ;
contentPane.add(button, BorderLayout.EAST);
void setApplication(Application app);
textfield = new JTextField("");
}
if (plugin != null)
textfield.setText(plugin.getInititalText());
textfield.setPreferredSize(new Dimension(200, 20));
contentPane.add(textfield, BorderLayout.WEST);
if (plugin != null)
button.addActionListener(/* … plugin.buttonClicked();… */);
this.setContentPane(contentPane);
…
}
public String getInput() { return textfield.getText();}
}
29
Taschenrechner
public class Application extends JFrame {
private JTextField textfield;
private Plugin plugin;
public Application(Plugin p) { this.plugin=p; p.setApplication(this); init(); }
protected void init() {
JPanel contentPane = new JPanel(new BorderLayout());
contentPane.setBorder(new BevelBorder(BevelBorder.LOWERED));
JButton button = new JButton();
public interface Plugin {
if (plugin != null)
String getApplicationTitle();
button.setText(plugin.getButtonText());
String getButtonText();
else
String getInititalText();
button.setText("ok");
void buttonClicked() ;
contentPane.add(button, BorderLayout.EAST);
void setApplication(Application app);
textfield = new JTextField("");
}
if (plugin != null)
textfield.setText(plugin.getInititalText());
public class CalcPlugin
implements20));
Plugin {
textfield.setPreferredSize(new
Dimension(200,
private BorderLayout.WEST);
Application application;
contentPane.add(textfield,
public
void
setApplication(Application app) { this.application = app; }
if (plugin != null)
public String getButtonText()
{ return "calculate";*/);
}
button.addActionListener(/*
… plugin.buttonClicked();…
public String getInititalText() { return "10 / 2 + 6"; }
this.setContentPane(contentPane);
public void buttonClicked() {
…
JOptionPane.showMessageDialog(null, "The result of "
}
+ application.getInput() + " is "
public String getInput() { return textfield.getText();}
+ calculate(application.getText())); }
}
public String getApplicationTitle() { return "My Great Calculator"; }
}
class30CalcStarter { public static void main(String[] args) { new Application(new CalcPlugin()).setVisible(true); }}
Weitere Entkopplung moeglich
public class Application extends JFrame implements InputProvider {
private JTextField textfield;
private Plugin plugin;
public Application(Plugin p) { this.plugin=p; p.setApplication(this); init(); }
public interface InputProvider {
protected void init() {
JPanel contentPane = new JPanel(new BorderLayout()); String getInput();
}
contentPane.setBorder(new BevelBorder(BevelBorder.LOWERED));
JButton button = new JButton();
public interface Plugin {
if (plugin != null)
String getApplicationTitle();
button.setText(plugin.getButtonText());
String getButtonText();
else
String getInititalText();
button.setText("ok");
void buttonClicked() ;
contentPane.add(button, BorderLayout.EAST);
void setApplication(InputProvider app);
textfield = new JTextField("");
}
if (plugin != null)
textfield.setText(plugin.getInititalText());
public class CalcPlugin
implements20));
Plugin {
textfield.setPreferredSize(new
Dimension(200,
private BorderLayout.WEST);
InputProvider application;
contentPane.add(textfield,
public
void
setApplication(InputProvider app) { this.application = app; }
if (plugin != null)
public String getButtonText()
{ return "calculate";*/);
}
button.addActionListener(/*
… plugin.buttonClicked();…
public String getInititalText() { return "10 / 2 + 6"; }
this.setContentPane(contentPane);
public void buttonClicked() {
…
JOptionPane.showMessageDialog(null, "The result of "
}
+ application.getInput() + " is "
public String getInput() { return textfield.getText();}
+ calculate(application.getInput())); }
}
public String getApplicationTitle() { return "My Great Calculator"; }
}
class31CalcStarter { public static void main(String[] args) { new Application(new CalcPlugin()).setVisible(true); }}
Plugins laden

Typisch für viele Frameworks: Plugin-Loader …






Häufig zusätzlich GUI für Plugin-Konfiguration
Beispiele:



… sucht in Verzeichnis nach DLL/Jar/XML Dateien
… testet ob Datei ein Plugin implementiert
… prüft Abhängigkeiten
… initialisiert Plugin
Eclipse (plugin-Verzeichnis + Jar)
Miranda (plugins-Verzeichnis + DLL)
Alternativ: Plugins in Konfigurationsdatei festlegen oder
StarterProgramm generieren
32
Beispiel Plugin Loader (benutzt Java Reflection)
public class Starter {
public static void main(String[] args) {
if (args.length != 1)
System.out.println("Plugin name not specified");
else {
String pluginName = args[0];
try {
Class<?> pluginClass = Class.forName(pluginName);
new Application((Plugin) pluginClass.newInstance())
.setVisible(true);
} catch (Exception e) {
System.out.println("Cannot load plugin " + pluginName
+ ", reason: " + e);
}
}
}
}
33
Mehrere Plugins




vgl. Observer Pattern
Mehrere Plugins laden und registrieren
Bei Ereigniss alle Plugins informieren
Für unterschiedliche
Aufgaben: spezifische
Plugin-Interfaces
34
public class Application {
private List<Plugin> plugins;
public Application(List<Plugin> plugins) {
this.plugins=plugins;
for (Plugin plugin: plugins)
plugin.setApplication(this);
}
public Message processMsg (Message msg) {
for (Plugin plugin: plugins)
msg = plugin.process(msg);
...
return msg;
}
}
Frameworks fuer Produktlinien
Mapping von Features
zu Plugins
Framework + Plugins
Application Eng.
Domain Eng.
Feature-Modell
Feature-Auswahl
als Eingabe
Feature-Auswahl
35
Pluginauswahl
(und ggf. Startkonfiguration
generieren)
Anwendung =
Framework mit
passenden Plugins
Frameworks für Produktlinien – Bewertung







Vollautomatisierung möglich
Modularität
Praxiserprobt
Erstellungsaufwand und Laufzeit-Overhead für
Framework/Architektur
Vorplanung nötig, Frameworkdesign erfordert Erfahrung
Schwierige Wartung, Evolution
Grobe Granularität oder riesige Interfaces

36
Plugin für Transaktionsverwaltung, VARCHAR oder gewichtete
Kanten?
Querschneidende Belange
37
Querschneidende Belange




Engl. crosscutting concerns
Behauptung: Nicht alle Belange (Features) in einem
Programm können mittels Objekten (Komponenten,
Plugins) modularisiert werden
Belange sind semantisch zusammenhängende Einheiten
Aber ihre Implementierung ist manchmal verstreut,
vermischt und repliziert im Code
Weitere Querschneidende Belange
class DatabaseApplication
//... Datenfelder
//... Logging Stream
//... Cache Status
public void authorizeOrder(
Data data, User currentUser, ...){
// prüfe Autorisierung
// Objekt sperren für Synchronisation
// Aktualität des Puffers prüfen
// Start der Operation loggen
// eigentliche Operation ausführen
// Ende der Operation loggen
// Sperre auf Objekt freigeben
}
public void startShipping(
OtherData data, User currentUser, ...){
// prüfe Autorisierung
// Objekt sperren für Synchronisation
// Aktualität des Puffers prüfen
// Start der Operation loggen
// eigentliche Operation ausführen
// Ende der Operation loggen
// Sperre auf Objekt freigeben
}
}



Code für verschiedene
Belange vermischt
Code repliziert
Im Beispiel
Operationen modular,
aber Sperren, Logging,
Puffer und
Authentifizierung
nicht
Scattering und Tangling

Verstreuter Code (code scattering)




Code, der zu einem Belang gehört, ist nicht modularisiert,
sondern im gesamten Programm verteilt
Häufig kopierter Code (auch wenn es je nur ein einzelner
Methodenaufruf ist)
Oder stark verteilte Implementierung von (komplementären)
Teilen eines Belangs
Vermischter Code (code tangling)

Code, der zu verschiedenen Belangen gehört, ist in einem
Modul (oder einer Methode) vermischt
Eine Frage der Größe
Zeitmanagement
von Sessions
l Beispiel:
Example:
Session expiration
in
im Apache Tomcat Server
the
Apache der
Tomcat
Server
als
Bestandteil
Session-Verwaltung
Probleme querschneidender Belange

Belange verschwinden in der Implementierung



Schwierige Arbeitsteilung


Was gehört alles zu einem Belang?
Bei Wartungsaufgaben muss der ganze Quelltext durchsucht
werden
Für unterschiedliche Belange kann es unterschiedliche Experten
geben; alle müssen am gleichen Code-Fragment arbeiten
Geringere Produktivität, schwierige Evolution

Beim Hinzufügen neuer Funktionalitäten muss sich der
Entwickler um diverse andere Belange kümmern, die erstmal
nur ablenken (Lesbarkeit, Erfassbarkeit)
Alternative Implementierung (Command Pattern)
class SecureSystem extends System
private User currentUser;
public void login() { /* ... */ }
public void executeOperation(Operation o) {
if (o instanceof AuthorizeOrder)
if (!currentUser.isAdmin())
denyAccess();
else
o.execute();
if (o instanceof StartShipping) {
if (!o.hasWriteAccess())
denyAccess();
else
o.execute();
}
}


Authentifizierung
wurde modularisiert
Dadurch sind aber
die anderen Belange
(hier Operationen)
nicht mehr modular
Weiterer Versuch – Methodenaufrufe

Belange wie Sperren, Logging, Puffer und
Authentifizierung werden in eigene Module ausgelagert



Scattering und Tangling nur noch von Methodenaufrufen
Übersichtlicher, aber immer noch explizite Aufrufe im Code
-> Viele Extension Points in Framework nötig; große
Interfaces für Komponenten
class BusinessClass
public void importantOperation(
Data data, User currentUser, ...){
checkAuth(currentUser);
startSynchronization();
checkCache();
logStart();
// eigentliche Operation ausfuehren
logEnd();
endSynchronization();
}
}
Weiterer Versuch – Middleware

Middleware kann sich um querschneidende Belange
kümmern; Entwickler implementiert nur noch eigentliche
Operationen (inversion of control)



Beispiel: Enterprise Java Beans stellen zur Verfügung: verteilte
Objekte, Persistenz, Transaktionsverwaltung, Authentifizierung
und Autorisierung, Synchronisation
Aufwendige Architektur
Nicht alle Belange von Middleware erfassbar, insbesondere
Belange der Businesslogik
Tyrannei der Dominanten Dekomposition

Viele Belange können modularisiert werden, jedoch nicht
immer gleichzeitig





Problemstellung nur in einer Richtung modularisierbar
Im Graph können Farben modularisiert werden…
…dann sind die Datenstrukturen (Node, Edge) verteilt
Entwickler wählen eine Dekomposition aus (z.B.
Operationen, Authentifizierung, Datenstrukturen), aber
einige andere Belange „schneiden quer“
Gleichzeitige Modularisierung entlang verschiedener
Dimensionen nicht möglich
Exkurs: Expression Problem

Frage: Wie weit kann man Datenstrukturen und
Methoden abstrahieren, so dass man beide unabhängig
erweitern kann…



ohne bestehenden Code zu ändern (oder sogar ohne neucompilieren des bestehenden Codes)
mehrfach, in beliebiger Reihenfolge und
ohne (nicht-triviale) Code-Replikation
Ausdrücke (expressions)

Aufgabe: Mathematische Ausdrücke werden in einer
Baumstruktur gespeichert und können ausgerechnet und
ausgegeben werden
*
1,73
+
5
ln
50
Implementierung 1: Daten-zentriert


Rekursive Klassenstruktur (Composite Pattern)
Für jede Operation eine Methode in jeder Klasse
definiert
«interface»
Term
+eval()
+print()
Number
-value
+eval()
+print()
Plus
-term1
-term2
+eval()
+print()
Ln
Product
-term1
-term2
+eval()
+print()
-term
+eval()
+print()
Problem von Implementierung 1




Ausdrücke sind modular
Neue Operationen, z. B. drawTree oder simplify, können
nicht einfach hinzugefügt werden
Alle bestehenden Klassen müssen angepasst werden!
Operationen sind querschneidend zu den Ausdrücken
Implementierung 2: Methoden-zentriert


Nur eine Methode accept pro Klasse
Methoden werden mit dem Visitor-Pattern
implementiert
«interface»
Visitor
+visitNumber()
+visitSum()
+visitProduct()
«interface»
Term
+accept(in Visitor)
Sum
Product
-term1
-term2
+accept(in visitor)
-term1
-term2
+accept(in visitor)
Number
-value
+accept(in visitor)
(Ln Klasse aus Platzgründen ausgelassen)
PrintVisitor
EvalVisitor
+visitNumber()
+visitSum()
+visitProduct()
+visitNumber()
+visitSum()
+visitProduct()
Codebeispiel Methoden-zentriert
interface Term {
void accept(Visitor v);
}
class Number {
float value;
void accept(Visitor v) {
v.visitNumber(this);
}
}
class Sum {
Term term1, term2;
void accept(Visitor v) {
v.visitSum(this);
}
}
class Product {
Term term1, term2;
void accept(Visitor v) {
v.visitProduct(this);
}
}
interface Visitor {
void visitNumber(Number n);
void visitSum(Sum s);
void visitProduct(Product p);
}
class PrintVisitor {
void visitNumber(Number n) {
System.out.print(n.value);
}
void visitSum(Sum s) {
System.out.print('(');
s.term1.accept(this);
System.out.print('+');
s.term2.accept(this);
System.out.print(')');
}
void visitProduct(Product p) {
s.term1.accept(this);
System.out.print('*');
s.term2.accept(this);
}
}
// Main:
// term.accept(new PrintVisitor());
Problem von Implementierung 2




Operationen sind modular
Neue Ausdrücke, z. B. Min oder Power können nicht
einfach hinzugefügt werden
Für jede neue Klasse müssen alle Visitor-Klassen
angepasst werden
Ausdrücke sind querschneidend zu den Operationen
Expression Problem


Nur sehr schwer möglich Ausdrücke und Operationen
darauf gleichzeitig zu modularisieren
(komplizierte Lösungen mit Java 1.5 Generics existieren)
Daten-zentrierter Ansatz



Neue Ausdrücke können direkt hinzugefügt werden: modular
Neue Operationen müssen in alle Klassen eingefügt werden:
nicht modular
Methoden-zentrierter Ansatz


Neue Ausdrücke können als weiterer Visitor hinzugefügt
werden: modular
Für neue Klassen müssen alle bestehenden Visitors erweitert
werden: nicht modular
Expression Problem – Grafisch
Daten-zentriert
plus
Methoden-zentriert (Visitor)
plus
power
eval
eval
print
print
(a)
plus
power
power
(b)
ln
plus
eval
eval
print
print
simplify
simplify
(c)
Ausblick: Neue Sprachansätze
power
ln
(d)
Ausblick: Feature-Interaktionen
Typische Beispiele für querschneidende Belange




Logging: Meldung nach jeder Methode
Caching/Pooling: Code bei jedem Erzeugen eines
Objektes
Synchronisierung/Locking: Erweiterung vieler Methoden
mit lock/unlock-Aufrufen
Features in Produktlinien!
Dilemma




Es ist nicht immer möglich alle Belange zu modularisieren
(Tyrannei …)
Ein Grundmaß an verstreutem und vermischtem Code in
OOP-Implementierungen ist normal
Einige Belange sind immer „orthogonal“ zu anderen:
querschneidende Belange
Features in Produktlinien sind häufig betroffen
Preplanning Problem
58
Preplanning Problem


Erweiterungen sind nicht ad-hoc möglich, sondern
müssen voraus geplant werden
Es müssen explizit Erweiterungsmöglichkeiten
vorgesehen werden



Extension Points in Frameworks
Interfaces/Parameter in Komponenten
Ohne passenden Extension Point keine modulare
Erweiterung möglich
Preplanning Problem - Beispiel


Stack-Methoden sollen synchronisiert werden
Modulare Erweiterung mittels Subklasse oder Delegation
Basis-Code
class Stack { /* ... */ }
class Main {
public static void main(
String[] args) {
Stack stack = new Stack();
stack.push('foo');
stack.push('bar');
stack.pop();
}
}
Spätere ungeplante Erweiterung
class LockedStack extends Stack {
private void lock() { /* ... /* }
private void unlock() { /* ... /* }
public void push(Object o) {
lock();
super.push(o);
unlock();
}
public Object pop() {
lock();
Object result = super.pop();
unlock();
return result;
}
}
Preplanning Problem - Beispiel II

Problem: Instantiierung des Stacks im Basiscode muss
angepasst werden


Alternative



Keine Möglichkeit ohne Änderung des Basiscode (nichtmodular)
Design Pattern: Factory statt direkter Instantiierung (erlaubt
modulare Erweiterung)
Framework mit passendem Extension Point
Erweiterungsmöglichkeiten müssen antizipiert werden
(preplanning) oder nachträglich dem Basiscode
hinzugefügt werden (nicht-modular)
Zusammenfassung





Modularisierung von Features mit Komponenten und
Frameworks
Keine Vollautomatisierung, Laufzeitoverhead, grobe
Granularität
Grenzen bei querschneidenen Belangen und feiner
Granularität
Modularität erfordert Planung
Nicht für alle Produktlinien geeignet (z.b.
Graphbibliothek, eingebettete Datenbanken)
62
Ausblick

Neue Programmierkonzepte



63
Analyse Objekt-Orientierung und deren Grenzen
Feature-Orientierung
Aspekt-Orientierung
Literatur



C. Szyperski: Component Software: Beyond ObjectOriented Programming. Addison-Wesley, 1998
[Standardwerk Komponentenorientierte
Softwareentwicklung]
R. Johnson and B. Foote, Desiging reusable classes,
Journal of Object-Oriented Programming, 1(2):22-35,
1988
[OOP Wiederverwendung, insb. Frameworks]
L. Bass, P. Clements, R. Kazman, Software Architecture in
Practice, Addison-Wesley, 2003
[Architekturgetriebene Produktlinien, typischerweise
Frameworks]
64

Documents pareils