440 likes | 603 Views
Gliederung: Teil 3 - Anpassungen. 1. Einführung Motivation Definition, Konzepte für Komponenten (klassische, kommerzielle, akademische) 2. Industrielle Komponentensysteme der 1. Generation 1. CORBA 2. Enterprise Java Beans 3. (D)COM 3. Anpassungen Daten und Funktion Interaktion
E N D
Gliederung: Teil 3 - Anpassungen 1. Einführung • Motivation • Definition, • Konzepte für Komponenten (klassische, kommerzielle, akademische) 2. Industrielle Komponentensysteme der 1. Generation 1. CORBA 2. Enterprise JavaBeans 3. (D)COM 3. Anpassungen • Daten und Funktion • Interaktion • Kommunikation • Synchronisation • Protokolle • Lebendigkeit
Problem 3.1 Anpassungen von Funktionen
Daten- als Funktionsanpassung • Datenanpassung • Transformation von Wertetypen • Transformation von Schnittstellen • Können Auswirkung auf Implementierung von Diensten haben • Sind damit spezielle Funktionsanpassungen • Modell: Herausziehen generischer Klassen • Funktionsanpassung • Hinzufügen, Entfernen und Ändern von Funktionalität • Allgemeiner
Quelle: Class X { R1m1(P1); R2 m2(P2); ... Rn mn(Pn); } Ziel: Class Y { R`1 m1(P‘1); R`2 m2(P‘2); ... R`n mn(P‘n); } Mittel: Class GX ( GP1<TS1, (von Impl.) GP2 < TS2, ..., GR1 <, (von Benutzung) GR2 <, ..., ) { GR1 m1(GP1); GR2 m2(GP2); ... GRn mn(GPn); } Domäne der Datenanpassung
Datenanpassung mit generischen Klassen • Konkrete Parameter und Ergebnisse • Als einen zusammengesetzten Typ auffassen • Durch generischen Typ ersetzen • Einsetzen neuer spezieller Typen bildet neue Instanz • Anpassungen von • Parametern • Resultaten • Methodenimplementierung (implizit) • Weitere nicht-generische Anpassungen • Methodennamen
Quelle:class SetOfComplex { void add(double re, double im); boolean has(double re, double im); int size(); } Mittel:class Set<Type T> { void add(T t); boolean has(T t); int size(); } Ziel:class SetOfComplex { void set(Complex c); boolean get(Complex c); boolean empty(); } Beispiel
Techniken revisited • XSLT & Co • Umformen von WSDL-Beschreibungen • Umformen konkreter Aufrufe, z.B. • Permutation von Parametern • Umbenennungen • Compost • Leistet Typanalyse • Identifiziert Stellen für innere Anpassung • Bietet Grundwerkzeuge zur Anpassung • Werkzeug mächtiger als (hier) benötigt
Beispiel: Umbenennen mit Compost public ProblemReport analyze() { transformations = new TwoPassTransformationArrayList(); CrossReferenceServiceConfiguration config = getServiceConfiguration(); ProblemReport report = EQUIVALENCE; AbstractTreeWalker tw = new ForestWalker(getSourceFileRepository().getKnownCompilationUnits()); while (tw.next()) { ProgramElement pe = tw.getProgramElement(); TwoPassTransformation t; if (pe instanceof TypeDeclaration) { TypeDeclaration td = (TypeDeclaration)pe; if (td.getName() != null) { t = new RenameType(config, td, createName(td)); report = t.analyze(); if (report instanceof Problem) return setProblemReport(report); transformations.add(t); } } else if (pe instanceof VariableSpecification) { // ...
Grenzen • Nur Anpassung einzelner Methoden • Keine Aggregation • Keine Zerlegung • Keine Rekombination • Anpassung von Feldern teilweise problematisch • Wann ist Typgleichheit Zufall? • Wie identifiziere ich diese Fälle? • Nur implizite Änderung von Funktionalität • Gezielte Änderungen unmöglich
Quelle:class SetOfComplex { void add(double re, double im); boolean has(double re, double im); int size(); double getReAt(int iter); double getImAt(int iter); void setLoad(double d); } Ziel:class SetOfComplex { void add(Complex c); booelan has(Complex c); int size(); Complex get(int iter); } Grenzen am Beispiel
Funktionsanpassungen • Datenanpassungen (subsumiert) • Neue Sichten auf Funktionalität • Aggregation • Zerlegung • Rekombination • Veränderungen der Funktionalität • Erweitern (mit neuem Code) • Einschränken (durch explizites Löschen) • Ändern (beides)
Quelle:class Konto { void abheben (double euro); void einzahlen(double euro); } Ziel:class Konto { void abheben (double euro); void einzahlen(double euro); void überweise(Konto k, double euro); } mit Semantik Void überweise(double euro) { this.abheben (euro); k .einzahlen(euro); } Beispiel Aggregation
Quelle:class Filter { Filter(Config c); void run(Input i); } Ziel:class Filter { Filter(); void setup(Config c); void run(Input i); } Beispiel Zerlegung
Quelle:class ModelView { void addAndEnable(Model m); } Ziel:class ModelView { void add (Model[] m); void enable(Model[] m); } Mittel:class ModelView { void add (Model m); void enable(Model m); } Beispiel Rekombination
Quelle:class Signal { void set(double[] data); void multiply(double s); void resample(double f); void haarWavelet(); void haarWaveletInv(); } Ziel:class Signal { void set(double[] data); void multiply(double s); void resample(double f); void wavelet (Wavelet w); void waveletInv(Wavelet w); } Implementierung spezifiziert durch Benutzer. Beispiel Erweiterung
Quelle:class GrooveFiles { Handle add (File f); File get (Handle h); void update (Handle h, File f); void setRights(Handle h, Rights r); void enterpriseFunc2(); void enterpriseFunc3(); } Ziel:class GroovePreviewFiles { Handle add (File f); File get (Handle h); void update(Handle h, File f); } Beispiel Einschränkung
Techniken • Anpassung ist Codetransformation • Einsatz von Compost • Analyse der Quellen • Spezifikation der Anpassungen als Metaprogramm • Umsetzung mit Grundbausteinen aus Bibliothek • Beispiele zeigen primär Anpassung auf Signaturenmengen • Erweiterungen/Einschränkungen denkbar, die quer zum OO-Modell liegen • Z.B. Hinzufügen/Entfernen von Tests, Persistenz etc. • Auch mit Compost möglich.
Beispiel: Erweitern mit Compost public ProblemReport execute() { MutableSet inserted = new IdentityHashSet(); CrossReferenceServiceConfiguration config = getServiceConfiguration(); ProgramFactory pf = getProgramFactory(); SourceInfo si = getSourceInfo(); CompilationUnitList units = getSourceFileRepository().getKnownCompilationUnits(); for (int i = 0; i < units.size(); i += 1, inserted.clear()) { CompilationUnit cu = units.getCompilationUnit(i); TreeWalker tw = new TreeWalker(cu); while (tw.next()) { ProgramElement pe = tw.getProgramElement(); if (pe instanceof MethodReference) { // [omitted: treat some special cases ...] MethodReference caller = (MethodReference)pe; MethodReference insert = (MethodReference) pf.parseExpression("System.out.println(\"\")"); // [omitted: build expression to insert ...] TwoPassTransformation t = new PrependExpressionWithStatements(config, caller, insert); ProblemReport report = t.analyze();
Warum Unterscheidung? • Datenanpassungen sind • Einfacher • Stärker lokalisiert • Mit einfacheren Werkzeugen (z.B. XSLT) behandelbar • Funktionsanpassungen sind • Komplexer • Weniger lokalisiert • Erfordern aufwendigere Werkzeuge. • Praktische Unterscheidung für Ingenieure sinnvoll • Theoretische Unterscheidung evtl. nicht sinnvoll • Jede Anpassung ist durch Wegwerfen und Neuschreiben möglich
Problem 3.2 Anpassungen von Interaktionen
Grau ist alle Theorie • Ansatz • Interaktion = Datenfluß + Kontrollfluß • Kollision mit der Realität • Häufigstes Interaktionsmittel ist der Prozeduraufruf • Datenfluß legt Kontrollfluß fest • Ebenso • RMI • Ereignisse • Synchrone Kommunikationskanäle • etc. • Variablenzugriffe sind Methodenaufrufe (bei Verteilung) • Auflösung in Bestandteile theoretisch denkbar • Spätestens bei Rückabbildung große Probleme!
Literatur • Understanding Software - Static and Dynamic Aspects(Löwe, Ludwig, Schwind) • Aspect-Oriented Configuration and Adaptation of Component Communication(Heuzeroth, Löwe, Ludwig, Aßmann) • Compost: siehe vorangehende Vorlesung
Definition • Interaktion = Kommunikation + Synchronisation • Kommunikation ist Daten- und Kontrollfluß • Lokaler Methodenaufruf • Java/Corba RMI • Ereignisse • etc. • Synchronisation stimmt Steuerflüsse ab • Semaphore • Kritischer Abschnitt • Monitor • Explizite Manipulation von Steuerflüssen • z.B. Threads • hier nicht betrachtet
Warum Kommunikation anpassen? • OO Entwurf • aktive Objekte • Relationen meist eigenständig • Kommunikation über Nachrichten • OO Umsetzung • passive Objekte • wenig aktive Threads • Relationen meist in Objekte eingemischt • Kommunikation und Aktivierung durch Methodenaufrufe • Folge • Große Spielräume bei der Umsetzung • Verwendung von Entwurfsmustern kann Kompatibilität nicht garantieren • Mangelnde Kompatibilität von Umsetzungen verschiedener Hersteller
Ein Beispiel • Produzent passiv • Konsument passiv • Registrierung in beiden • Anpassung nötig! Class Consumer{ void register(Producer p); void consume(Product p); } Class Producer { void register(Consumer c); Product produce(); }
Kommunikationsanpassung • Finde Kommunikationsports • Ersetze konkrete durch abstrakte/generische Aufrufe • Konfiguriere neu • Instanziiere gemeinsam (passfähig) Abstract Communication Model Program P1 Program P2 Configured Analysis Configuration (Re-)Production Abstract Model Source Code Source Code Abstract Model Instance of of Instance Transformation for Program P1 - adapted components - components for Program P1 - restructured systems - existing systems Programming Language
Analyse • Statische Analyse identifiziert Methodenaufrufe • Problem: modellgebundene uneigentliche Kommunikation • Z.B. Registrierung eines Listeners • Identifikation: Semantikwissen in Muster übertragen • Problem: unvollständige Information • M EventSources, N Listener • Alle erben/benutzen abstrakte Interfaces • Wer kommuniziert wirklich mit wem? • Lösung: Zusätzliche dynamische Analyse • Auflösung polymorpher Aufrufe aufzeichnen • Nur exemplarisch möglich (konkreter Programmlauf)
Analysewerkzeug VizzAnalyzer • Statische Analyse • Nutzt Compost • Konzepte: Bezug-auf, Enthält, Unterklasse-von • Dynamische Analyse • Nutzt Java Debug Interface • Zeichnet (Ausschnitt aus) Programmlauf auf • Konzepte: Ruft-auf, Zugriff-auf, Kennt, Instanz-von • Gefilterte Visualisierung • Ignorieren von Paketen • Hierarchische Aggregation • Automatisches Layout
Konfiguration • Semantik der Kommunikation festlegen • Gepuffert / ungepuffert • Koordination (FIFO, LIFO etc.) • Destruktives / nicht-destruktives Lesen • Aktivität • Verteilung • Basiskommunikation • Bibliokthek von Konnektoren • Deckt verbreitete Fälle ab • Erweiterbar • Konfigurator • Metaprogramm, das Anpassung an Konnektor vornimmt
Transformation • Ziel • Ersetze abstrakte durch konkrete Kommunikation • Abbildung auf Zielsprache • Mittel • Metaprogramme der Konfiguratoren ausführen • Mögliche Optimierungen • Einsparen von Puffern • Einsparen von Kanal-Objekten • etc. • Werkzeug • Compost
Ergebnis am Beispiel • Produzent aktiv • Konsument passiv • Registrierung beim Produzenten Class Consumer{ void consume(Product p); } Class Producer { void register(Consumer c); void produce() { Product p=myProduce(); for(int i=0; i<cs.length; i++) cs[i].consume(p); } }
Warum Anpassungen der Synchronisation? • Methodenausführung ist nicht atomar • Synchronisation nötig um Semantik zu erhalten • Viele Mechanismen (alle äquivalent) • Mutexe • Semaporen • Kritische Abschnitte • Barrieren • usw. • Abbildung aufeinander i.A. nicht einfach • Oft Entwicklung im threadfreiem Raum • Wiederverwendung in multi-threaded Umgebungen nicht gesichert
Synchronisation • Behandlung wie Kommunikation • Analyse der konkreten Synchronisation • Abbildung auf abstrakte Synchronisation • Konfiguration • Transformation auf neue konkrete Synchronisation • Kern • Abstrakter Synchronisations-Port • Evtl. erweitern um • Explizite und implizite Manipulation von Threads(fork / waitpid etc.)
Quelle: class SyncUser { Semaphore s; void insensitive() { s.wait(); sensitive(); s.post(); } void sensitive() { // perform some operations // that need synchronization } } Ziel: class SyncUser { synchronized void sensitive() { // perform some operations // that need synchronization } } Beispiel
Analyse • Erkennt abstrakte Synchronisation durch Muster • Keine Synchronisation • Exklusivzugriff auf Objekte • Exklusivzugriff auf Methodenmengen eines Objektes • Entweder N Leser oder 1 Schreiber • Einfach für implizite Synchronisation • Kritische Abschnitte (z.B. Java synchronized) • Barrieren • Schwieriger für explizite Synchronisation • Mutexe • Semaphoren • I.A. nicht statisch lösbar • Evtl. Einsatz dynamischer Analysen
Grenzen • Explizite Synchronisation kennt Objekte und Referenzen • Generelle Probleme • Gehört das Objekt zu einem speziellen Codeabschnitt? • Wird es für andere Abschnitte wiederverwendet? • Aliasing-Probleme • Worauf zeigt eine Referenz? • Ist sie die einzige, die dorthin zeigt? • Welche Aussagen über Synchronisationszustand sind möglich? • Lebendigkeit • Welche Auswirkungen auf das Gesamtsystem treten ein? • Selbststabilisierende Algorithmen • Was definiert die Sprache?
class SyncUser { Semaphore s; void insensitive() { s.wait(); sensitive(); s.post(); } void sensitive(); Semaphore get() { return s; } } class SyncRoulette { Vector us; Random r=new Random( System.currentTimeMillis() ); void add(SyncUser u) { us.add(v.get()); } void roulette() { int i=r.nextInt(us.size()); Semaphore s=(Semaphore) us.elementAt(I); if(r.nextBoolean()) s.wait(); else s.post(); } } Beispiel für Grenzen
Konfiguration • Festlegen der Semantik • Synchronisationsform (z.B. Exklusivzugriff auf Objekt) • Synchronisationsmechanismus(z.B. OS, Runtime, Dienstgeber) • etc. • Bibliothek von Synchronisatoren • deckt gängige Fälle ab • Konfigurator • Metaprogramm, das Anpassung an Synchronisator vornimmt
Transformation • Ziel • Ersetzen der abstrakten durch konkrete Synchronisation • Abbildung auf Zielsprache • Mittel • Metaprogramme der Konfiguratoren ausführen • Mögliche Optimierungen • Einsparen von Objekten durch implizite Synchronisation • Einsparen von Synchronisationen(z.B. durch Dominanz- und Escape-Analyse) • etc. • Werkzeug • Compost
Synchronisation und Transaktionen • Synchronisation • Ausführung einer Methode ist atomar • Transaktion • Ausführung mehrerer Methoden ist atomar(z.B. Abheben,Einzahlen = Überweisung) • Unterschiedliche Anforderungen je nach Einsatzkontext • Modellierung von Transaktionen • Abstrakten Sync- oder Lock-Ports • Statische Analyse schwer • Weitgehend ungelöst
Synchronisation in der Praxis • Theorie zeigt viele schwer beherrschbare Probleme auf • Abbildungen zwischen Mechanismen eher selten • In der Praxis entscheidend: Threadfähigkeit • Single threaded multi threaded • Funktional erforderlich • Beherrschbar mit einfachen Mitteln • Wrapping mit Synchronisationsbarriere ausreichend • Multi threaded single threaded • Funktional kein Problem • Geschwindigkeitsgewinne möglich • Einsatz alter Bekannter: Escape-Analyse etc.
Zusammenfassung • Unterscheidung nach Ingenieurskriterien sinnvoll • Gemeinsamer Ansatz • Konkrete Umsetzung analysieren • Abstrakte/Generische Anknüpfungspunkte identifizieren • Diese neu instantiieren • Unnötig bei völlig generischem Programmieren, aber • Keine Sprache kann alle Sichten vorsehen • Neue Sprachen helfen nicht beim Umgang mit Altsystemen • Es gibt immer mehr Altsysteme • Also • Analyse und Transformation sind der Schlüssel zum Reengineering • Übersetzerbau ist ein aktuelles Thema