Einschub: Testen mit JUnit • Framework, um den Unit-Test eines Java-Programms zu automatisieren • einfacher Aufbau • leicht erlernbar • geht auf SUnit (Smalltalk) zurück • mittlerweile für viele Sprachen verfügbar (NUnit, CPPUnit) Software-Qualität Prof. Dr. Stephan Kleuker 54 Testfall • Vor dem Testen müssen Testfälle spezifiziert werden • Vorbedingungen – Zu testende Software in klar definierte Ausgangslage bringen (z. B. Objekte mit zu testenden Methoden erzeugen) – Angeschlossene Systeme in definierten Zustand bringen – Weitere Rahmenbedingungen sichern (z. B. HW) • Ausführung – Was muss wann gemacht werden (einfachster Fall: Methodenaufruf) • Nachbedingungen – Welche Ergebnisse sollen vorliegen (einfachster Fall: Rückgabewerte) – Zustände anderer Objekte / angeschlossener Systeme Software-Qualität Prof. Dr. Stephan Kleuker 55 Aufbau einer Testklasse (1/8) import import import import import import junit.framework.Assert; junit.framework.Assert; org.junit.After; org.junit.After; org.junit.AfterClass; org.junit.AfterClass; org.junit.Before; org.junit.Before; org.junit.BeforeClass; org.junit.BeforeClass; org.junit.Test; org.junit.Test; public class AnschauungTest { private int wert; private static int klasse; Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker Beliebiger Klassenname, Endung „Test“ üblich Nutzung von normalen Exemplarvariablen Klassenvariablen, außer als Konstanten, unüblich 56 Aufbau einer Testklasse (2/8) Verhaltensbeschreibung mit Annotationen Methode wird einmal vor vor allen Tests ausgeführt, z.B. Aufbau DB-Verbindung @BeforeClass public static void setUpBeforeClass() setUpBeforeClass() throws Exception { System.out.println(" System.out.println("setUpBeforeClass ("setUpBeforeClass"); setUpBeforeClass"); klasse = 99; } einmal nach allen Tests (aufräumen) @AfterClass public static void tearDownAfterClass() tearDownAfterClass() throws Exception { System.out.println(" System.out.println("tearDownAfterClass ("tearDownAfterClass"); tearDownAfterClass"); } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 57 Aufbau einer Testklasse (3/8) Methode wird vor jedem Test ausgeführt, Idee: einheitliche Ausgangssituation schaffen @Before public void setUp() setUp() throws Exception { System.out.println(" System.out.println("setUp ("setUp"); setUp"); wert = 42; klasse = klasse + 1; System.out.println("klasse System.out.println("klasse ist "+klasse); } einmal nach jeden Tests (lokal aufräumen) @After public void tearDown() tearDown() throws Exception { System.out.println(" System.out.println("tearDown ("tearDown"); tearDown"); } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 58 Aufbau einer Testklasse (4/8) Test ist beliebige mit @Test annotierte Methode Methodenname ist beliebig, beginnt typischerweise mit „test“ und beinhaltet Name der @Test Methode oder Sinn des Tests public void test1() { System.out.println("test1"); System.out.println("test1"); Experimente wert = wert + 1; Assert.assertTrue("Erwartet Assert.assertTrue("Erwartet 43 gefunden: "+wert , wert == 43); } Prüfmethode, Parameter Text und Boolesche Bedingung; ist Bedingung „false“ wird Test als gescheitert festgehalten und Text ausgegeben Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 59 Aufbau einer Testklasse (5/8) @Test public void test2() { System.out.println("test2"); System.out.println("test2"); wert = wert + 2; Assert.assertTrue(wert Assert.assertTrue(wert == 44); } Kurzform ohne Text (gibt assert-Varianten) wenn Testfall scheitert ist entweder das Programm oder der Test fehlerhaft @Test public void test3() { System.out.println("test3"); System.out.println("test3"); wert = wert + 3; Assert.assertTrue("Erwartet Assert.assertTrue("Erwartet 44 gefunden: "+wert , wert == 44); } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 60 Aufbau einer Testklasse (6/8) @Test Test scheitert, wenn ausgeführt; public void test4() { markiert Stellen, die nicht System.out.println("test4"); System.out.println("test4"); erreicht werden sollen try{ try{ if(42/0 if(42/0 == 0){ gewünschte Exception } Assert.fail(); Assert.fail(); } catch(ArithmeticException catch(ArithmeticException e){ } catch(Exception catch(Exception e){ ungewünschte Exception Assert.fail(); Assert.fail(); } } @Test public void test5() { System.out.println("test5"); System.out.println("test5"); throw new IllegalArgumentException(); IllegalArgumentException(); } } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 61 Aufbau einer Testklasse (7/8) setUpBeforeClass setUp klasse ist 100 test4 tearDown setUp klasse ist 101 test5 tearDown setUp klasse ist 102 test1 tearDown setUp klasse ist 103 test2 tearDown setUp klasse ist 104 test3 tearDown tearDownAfterClass Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 62 Aufbau einer Testklasse (8/8) • Error: Fehler durch Assert • Failure: Fehler durch Abbruch Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 63 Tests in NetBeans erstellen (1/2) • JUnit einbinden (eine Variante) Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 64 Tests in NetBeans erstellen (2/2) • Location: Test Packages, Start mit Rechtsklick auf Test Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 65 Mini-Testbeispiel (1/5) • Spezifikation: Ein Tarif wird über das Alter und die Eigenschaft „Raucher“ bestimmt. Für unter 30-jährige Nichtraucher ist der Tarif „A“. Für alle sonstigen Nichtraucher „X“. Ansonsten darf die Tarifberechnung nicht durchgeführt werden. • Design: Klasse Tarif hat Methode public char gruppierungBerechnen( gruppierungBerechnen(int alter , boolean istRaucher) istRaucher) throws UnversichbarException • die den Tarif als Buchstaben zurück liefert und bei unerwünschten Daten eine spezielle Exception wirft Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 66 Mini-Testbeispiel (2/5) • Erstellung der Testfälle mit Äquivalenzklassen und Grenzwertanalyse • „alter“ hat zwei Bereiche: „< 30“ und „>= 30“ • „istRaucher“ hat zwei Bereiche „true“, „false“ • Testfälle – alter = 29, istRaucher = false Ergebnis: ‚A‘ – alter = 30, istRaucher = false Ergebnis: ‚X‘ – alter = 30, istRaucher = true Ergebnis: Exception • Hinweis: Exceptions werden immer einzeln getestet Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 67 Mini-Testbeispiel (3/5) package test; test; import import import import import business.Tarif; business.Tarif; exceptions.UnversichbarException; exceptions.UnversichbarException; org.junit.Assert; org.junit.Assert; org.junit.Before; org.junit.Before; org.junit.Test; org.junit.Test; public class TarifTest { private Tarif tarif; tarif; • Hinweis: Tests können auch vor der Programmierung nach dem Klassendesign entstehen („test first“) Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 68 Mini-Testbeispiel (4/5) @Before public void setUp(){ setUp(){ tarif = new Tarif(); } @Test public void testGruppierungBerechnenA(){ testGruppierungBerechnenA(){ try { char ergebnis = tarif.gruppierungBerechnen(29, tarif.gruppierungBerechnen(29, false); false); Assert.assertTrue( Assert.assertTrue(ergebnis == 'A'); } catch (UnversichbarException (UnversichbarException ex) { Assert.fail(); Assert.fail(); } } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 69 Mini-Testbeispiel (5/5) @Test public void testGruppierungBerechnenX(){ testGruppierungBerechnenX(){ try { char ergebnis = tarif.gruppierungBerechnen(30, tarif.gruppierungBerechnen(30, false); false); Assert.assertTrue( Assert.assertTrue(ergebnis == 'X'); } catch (UnversichbarException (UnversichbarException ex) { Assert.fail(); Assert.fail(); } } @Test public void testGruppierungBerechnenException(){ testGruppierungBerechnenException(){ try { char ergebnis = tarif.gruppierungBerechnen(30, tarif.gruppierungBerechnen(30, true); true); Assert.fail(); Assert.fail(); } catch (UnversichbarException (UnversichbarException ex) { } } }Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 70 viele weitere Möglichkeiten • typisch eine Testklasse pro entwickelter Klasse • Tests zusammenfassbar in Test-Suites • Tests mit Parameterlisten aus Datei • viele Test-Frameworks basieren auf JUnit • Web-Tests mit Selenium • DB-Tests mit DBUnit • Ansatz zur Testfallerstellung mit Äquivalenzklassen (welche unterschiedlichen Eingaben führen zu unterschiedlichen Arten von Ergebnissen) Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 71 2. JavaBeans als Komponenten • • • • • • Reflexion Java-GUI-Elemente sind Beans Typischer Aufbau von Beans Bound properties Constrained Properties Speichern Literatur: • Oracle, Trail: JavaBeans™, http://docs.oracle.com/javase/tutorial/javabeans/ • C. Ullenboom, Java ist auch eine Insel, 8. Auflage, Galileo Press [Abschnitt 7.4] Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 72 Reflexion • Reflexion erlaubt in Java Meta-Programmierung; Klassen selbst als Objekte nutzen • Paket java.lang.reflect • Zu jeder Klasse gibt es ein Klassenobjekt der Klasse Class Class cl1 = String.class; String.class; try { // oder Class cl2 = Class.forName(" Class.forName("java.lang.String ("java.lang.String"); java.lang.String"); } catch (ClassNotFoundException (ClassNotFoundException e) { System.out.println(" System.out.println("gibs nich") ("gibs nich") } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 73 Nutzung von Class • Klassenobjekt erlaubt – Abfrage von Variablen, Konstruktoren, Methoden (mit Sichtbarkeiten, Typen, Parametern, Exceptions,…) – Abfrage und Änderung von Objektwerten – Nutzung von Konstruktoren und Methoden – Abfrage von Oberklassen • Insgesamt können damit zur Laufzeit beliebige Klassen analysiert und genutzt werden • Zentrale Reflection-Klassen: Field, Constructor, Method, Modifier Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 74 Exemplarische Methoden der Klasse Class Annotation[] getDeclaredAnnotations() present on this element. Returns all annotations that are directly Constructor[] getDeclaredConstructors() Returns an array of Constructor objects reflecting all the constructors declared by the class represented by this Class object. Field[] getDeclaredFields() Returns an array of Field objects reflecting all fields declared by the class or interface represented by this Class object. Method[] getDeclaredMethods() Returns an array of Method objects reflecting all the methods declared by the class or interface represented by this Class object. Class[] getInterfaces() Determines the interfaces implemented by the class or interface represented by this object. int getModifiers() Returns the Java language modifiers for this class or interface, encoded in an integer. boolean isArray() Determines if this Class object represents an array class. T newInstance() this Class object. Komponentenbasierte SoftwareEntwicklung Creates a new instance of the class represented by http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html Prof. Dr. Stephan Kleuker 75 Einsatzbereiche von Reflexion • Eingesetzt zur Entwicklung von Entwicklungswerkzeugen – Debugger – Class Browser, UML-Diagrammableitung – GUI Builder – IDEs wie z.B. Eclipse und Netbeans • Eingesetzt in Frameworks – Einheitliche Klassenbehandlung ohne Interfaces – Finden bestimmter Methoden (z. B. getXXX) und Eigenschaften – Meist Verwaltung unterschiedlicher Klassen • Oft verknüpft mit anderen fortgeschrittenen Ansätzen (ClassLoader, Annotationen, Bytecode-Manipulation) Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 76 Ungewöhnliches Beispiel (1/2) • Praktisch nicht nutzbare Klasse package model; model; public class Heimlich { private int x; private Heimlich(){ Heimlich(){ } private void ausgeben(){ System.out.println("x System.out.println("x ist "+x); } } • Hinweis: Folgender Ansatz geht nicht immer, hängt von Security-Einstellungen ab Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 77 Ungewöhnliches Beispiel (2/2) public static void main(String[] main(String[] s) throws Exception{ Exception{ Class cl = Class.forName(" Class.forName("model.Heimlich ("model.Heimlich"); model.Heimlich"); Constructor[] Constructor[] cons = cl.getDeclaredConstructors(); cl.getDeclaredConstructors(); Constructor con = cons[0]; cons[0]; System.out.println( System.out.println(con); con); if(! if(!con.isAccessible (!con.isAccessible()) con.isAccessible()) // evtl. SecurityManagerSecurityManager-Exception con.setAccessible( con.setAccessible(true); true); Object[] Object[] parameter = {}; Heimlich h = (Heimlich) con.newInstance( con.newInstance(parameter); parameter); System.out.println("" System.out.println("" + h); private model.Heimlich() model.Heimlich() Field f=cl.getDeclaredFields f=cl.getDeclaredFields()[0]; cl.getDeclaredFields()[0]; model.Heimlich@19821f f.setAccessible( f.setAccessible(true); true); x ist 42 f.set(h f.set(h, (h, 42); Method m=cl.getDeclaredMethods m=cl.getDeclaredMethods()[0]; cl.getDeclaredMethods()[0]; m.setAccessible( m.setAccessible(true); true); m.invoke(h m.invoke(h, (h, parameter); parameter); } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 78 Hintergrund und Motivation von JavaBeans • JavaBeans sind als frühes Komponentenmodell entstanden • Objekte einfach serialisierbar (damit einfach im Netz austauschbar) • Beans-Framework ermöglicht einfache Kommunikation unter Beans • Haupteinsatz: GUI-Komponenten, Swing nutzt Technologie (ermöglicht u. a. GUI-Builder [gute/schlechte]) • Aktuelle Spezifikation: JavaBeans 1.01 • Ideen für JavaBeans Spec 2.0 (JSR-273) wurden verworfen • Zitat: “A Java Bean is a reusable software component that can be manipulated visually in a builder tool.” • Achtung: nicht mit Enterprise JavaBeans verwechseln Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 79 Erinnerung: JButton (Nutzung) public class ButtonFrame extends JFrame { public ButtonFrame() ButtonFrame() { setSize(200, setSize(200, 100); JButton rot = new JButton("rot"); JButton("rot"); rot.addActionListener( rot.addActionListener(new MeineAction( MeineAction(getContentPane(), getContentPane(), Color.RED)); Color.RED)); add(rot, add(rot, BorderLayout.EAST); BorderLayout.EAST); JButton blau = new JButton("blau"); JButton("blau"); blau.addActionListener( blau.addActionListener(new MeineAction( MeineAction(getContentPane(), getContentPane(), Color.BLUE)); Color.BLUE)); add(blau, add(blau, BorderLayout.WEST); BorderLayout.WEST); setDefaultCloseOperation( setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); WindowConstants.EXIT_ON_CLOSE); } public static void main(String main(String args[]) args[]) { java.awt.EventQueue.invokeLater( java.awt.EventQueue.invokeLater(new Runnable() Runnable() { public void run() run() {new {new ButtonFrame(). ButtonFrame().setVisible ().setVisible( setVisible(true);} true);} }); } } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 80 Erinnerung: JButton (ActionListener) import import import import java.awt.Color; java.awt.Color; java.awt.Component; java.awt.Component; java.awt.event.ActionEvent; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.event.ActionListener; public class MeineAction implements ActionListener { private Component komponente; komponente; private Color farbe; farbe; public MeineAction( MeineAction(Component komponente, komponente, Color farbe){ farbe){ this.komponente = komponente; komponente; this.farbe = farbe; farbe; } } public void actionPerformed( actionPerformed(ActionEvent e) { komponente.setBackground( komponente.setBackground(farbe); farbe); } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 81 Typische Beanmethoden (1/2) public class Knopf { public static void main(String[] main(String[] s){ JButton jb= jb= new JButton(); JButton(); jb.addPropertyChangeListener( jb.addPropertyChangeListener(new PCL()); jb.setText(" jb.setText("touch ("touch me"); me"); jb.setToolTipText("bin jb.setToolTipText("bin ne Bean"); jb.setBackground( jb.setBackground(Color.red); Color.red); jb.setBorderPainted(false); jb.setBorderPainted(false); JFrame jf = new JFrame(); JFrame(); jf.addPropertyChangeListener( jf.addPropertyChangeListener(new PCL()); jf.add( jf.add(jb); jb); jf.setSize(200, jf.setSize(200, 100); jf.setTitle("Knopf"); jf.setTitle("Knopf"); jf.setDefaultCloseOperation( jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); WindowConstants.EXIT_ON_CLOSE); jf.setVisible(true); jf.setVisible(true); } } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 82 Typische Beanmethoden (2/2) public class PCL implements PropertyChangeListener{ PropertyChangeListener{ public PCL() {} public void propertyChange( propertyChange(PropertyChangeEvent evt) evt) { System.out.println( System.out.println(evt.getPropertyName() evt.getPropertyName() +" alt:"+evt.getOldValue alt:"+evt.getOldValue() evt.getOldValue() +" neu:"+evt.getNewValue neu:"+evt.getNewValue()); evt.getNewValue()); } text alt: neu:touch me } ToolTipText alt:null neu:bin ne Bean background alt:javax.swing.plaf.ColorUIResource[r=238,... borderPainted alt:true neu:false title alt: neu:Knopf defaultCloseOperation alt:1 neu:3 foreground alt:null neu:java.awt.Color[r=0,g=0,b=0] font alt:null neu:java.awt.Font[family=Dialog,name=Dia... ancestor alt:null neu:javax.swing.JPanel[null.contentP... Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 83 Elementare Anforderungen an JavaBean • Default-Konstruktor (keine Parameter) • Alle Exemplarvariablen (Properties, Attribute) über get- und set-Methoden erreichbar • Variable private <Typ> xx; public void setXx(<Typ> xx) { this.xx = xx; } public <Typ> getXx() { return xx; } • Methoden können weiteren Code enthalten • Jedwede Typen von Variablen sind serialisierbar Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 84 Minimales Beispiel import java.io.Serializable; public class Student implements Serializable{ private String name; private int cp; public Student(){} public int getCp() { return cp; } public void setCp(int cp) { this.cp = cp; } public String getName() { return name; } public void setName(String name) { this.name = name; } } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 85 Indizierte Eigenschaften • Für Eigenschaften, die in Arrays gespeichert werden, besteht zusätzlich folgende Möglichkeit (Ausschnitt) private String[] vorlesung; public String[] getVorlesung() { //bekannt return vorlesung; } public void setVorlesung(String[] vorlesung) { //bekannt this.vorlesung = vorlesung; } public String getVorlesung(int index) { return vorlesung[index]; } public void setVorlesung(String vl, int index) { vorlesung[index]=vl; } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 86 Einschub: typischer schlechter Stil • Arrays sind für dynamische Datenmengen unflexibel • Collections sind auch nutzbar (auch serialisierbar) private Set<String> vorlesung= vorlesung= new HashSet<String>(); HashSet<String>(); mit üblichen get- und set-Methoden • In JavaBeans können weitere Methoden ergänzt werden public void weitereVorlesung(String weitereVorlesung(String v){ vorlesung.add(v); vorlesung.add(v); } • Leider nicht unüblich der Stil „Innereien herausreißen, verändern, Innereien wieder hinschieben“ (verletzt eigentlich Geheimnisprinzip) Student st = new Student(); st.getVorlesung().add(" st.getVorlesung().add("KomSE ().add("KomSE"); KomSE"); Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 87 Gebundene Eigenschaften • Bound properties sind Exemplarvariablen, die Änderungen interessierten Beans mitteilen (Observer-Observable) • Wer Änderungen mitteilen will, – benötigt Objekt der Klasse PropertyChangeSupport – muss Methoden add/removePropertyChangeListener implementieren • Interessenten für Änderungen müssen sich mit add… anmelden • Bei Anmeldung muss Objekt übergeben werden, das das Interface PropertychangeListener implementiert • normales Observer-Observable-Pattern Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 88 Beispiel-Erweiterung in Student (1/2) import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; … private PropertyChangeSupport pcs=new PropertyChangeSupport(this); public void setCp(int cp) { int alt=this.cp; this.cp = cp; pcs.firePropertyChange("cp", alt, cp); } public void addPropertyChangeListener(PropertyChangeListener l){ pcs.addPropertyChangeListener(l); } public void removePropertyChangeListener( PropertyChangeListener l){ pcs.removePropertyChangeListener(l); } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 89 Beispiel-Erweiterung in Student (2/2) import java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; public class Studentnutzer { public static void main(String[] args) { Student s = new Student(); s.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent e) { System.out.println("Eigenschaft "+e.getPropertyName() +" alt:"+e.getOldValue()+" neu:"+e.getNewValue()); } }); s.setCp(42); s.setCp(s.getCp()+5); s.setCp(47); Eigenschaft cp alt:0 neu:42 } Eigenschaft cp alt:42 neu:47 } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 90 Veto-Eigenschaften • Spezielle Möglichkeit von JavaBeans: Vetoable (constrained) – Bean meldet angemeldeten Bean-Interessenten, dass eine Exemplarvariable geändert wird – Jede Bean kann „Einspruch“ gegen die Änderung (PropertyVetoException) einlegen • Konkretes Szenario: Kundenbestellung soll durchgeführt werden, Kontoverwaltung und Lagerverwaltung lauschen und können Einspruch einlegen • Nach Einspruch werden alle angemeldeten Beans erneut mit altem Wert informiert Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 91 Constrained und Bound • Listener für eine vetoable Property erhalten Änderung mit public void vetoableChange( vetoableChange(PropertyChangeEvent e) • gibt e.getNewValue() e.getNewValue() e.getOldValue() e.getOldValue() e.getPropertyName() e.getPropertyName() • Problem: Listener macht kein Veto und rechnet mit Daten weiter, aber anderer Listener hat Veto (vgl. dirty read) • sauberer Ansatz, bei versuchter Änderung – erst nach Vetos fragen – wenn Veto, dann Änderung abbrechen – wenn kein Veto, dann Änderung als bound property behandeln (propertyChange), also zwei Listener zusammen • sauberes Beispiel: s. Praktikum • schmuddeliges Beispiel: nächste Folien • Anmerkung: Parameterinhalte kann man ändern Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 92 Beispiel ohne Bound Property (1/3) private VetoableChangeSupport vcs=new VetoableChangeSupport(this); public void setVorlesung(Set<String> vorlesung) { Set<String> old = this.vorlesung; in Student try { vcs.fireVetoableChange("vl", old, vorlesung); this.vorlesung = vorlesung; } catch (PropertyVetoException ex) { System.out.println("abgelehnt: "+ex.getMessage()); } } public void addVetoableChangeListener(VetoableChangeListener l){ vcs.addVetoableChangeListener(l); } public void removeVetoableChangeListener( VetoableChangeListener l) { vcs.removeVetoableChangeListener(l); } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 93 Beispiel ohne Bound Property (2/3) public class MeinVeto implements VetoableChangeListener{ VetoableChangeListener{ private String schlecht; private int aenderung; aenderung; public MeinVeto(String MeinVeto(String schlecht) { this.schlecht = schlecht; } @Override public void vetoableChange( vetoableChange(PropertyChangeEvent evt) evt) throws PropertyVetoException { Set<String> neu = (Set<String>) evt.getNewValue(); evt.getNewValue(); if (neu.contains(schlecht)) neu.contains(schlecht)) throw new PropertyVetoException("nicht PropertyVetoException("nicht "+schlecht, evt); evt); System.out.println("für System.out.println("für "+schlecht+": "+(++aenderung "+(++aenderung)); aenderung)); } } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 94 Beispiel ohne Bound Property (3/3) public static void main(String[] args) { Student stu = new Student(); Set<String> vls = new HashSet(); vls.add("C"); vls.add("Java"); stu.addVetoableChangeListener(new MeinVeto("BWL")); stu.addVetoableChangeListener(new MeinVeto("C++")); stu.setVorlesung(vls); Set<String> tmp = (Set<String>)vls.clone(); für BWL: 1 tmp.add("C++"); für C++: 1 stu.setVorlesung(tmp); System.out.println(stu.getVorlesung()); für BWL: 2 für BWL: 3 } für C++: 2 abgelehnt: nicht C++ [C, Java] Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 95 Wesentlicher Nachrichtenfluss stu:Student (+vcs) setVorlesung ((C,Java)) :MeinVeto(BWL) :MeinVeto(C++) vetoableChange BWL:1 (vl,null,(C,Java)) vetoableChange (vl,null,(C,Java)) C++:1 vorlesung=(C,Java) setVorlesung ((C,Java,C++)) vetoableChange BWL:2 (vl,(C,Java),(C,Java,C++)) vetoableChange(vl,(C,Java),(C,Java,C++)) PropertyVetoException vetoableChange BWL:3 (vl,(C,Java,C++),(C,Java)) vetoableChange(vl,(C,Java,C++),(C,Java)) C++:2 Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 96 Einfaches Speichern (von JavaBeans) public static void main main(String[] (String[] args) args) throws FileNotFoundException { Student stu = new Student(); HashSet<String> HashSet<String> vls = new HashSet<>(); HashSet<>(); vls.add("C"); vls.add("C"); vls.add("Java"); vls.add("Java"); stu.setVorlesung( stu.setVorlesung(vls); vls); stu.setName("Erwin"); stu.setName("Erwin"); try (XMLEncoder out = new XMLEncoder(new XMLEncoder(new BufferedOutputStream( BufferedOutputStream( new FileOutputStream("stu.xml")))) FileOutputStream("stu.xml")))) { out.writeObject( out.writeObject(stu); stu); } try (XMLDecoder in = new XMLDecoder( XMLDecoder(new BufferedInputStream( BufferedInputStream( new FileInputStream("stu.xml")))) FileInputStream("stu.xml")))) { Student st = (Student) (Student) in.readObject(); in.readObject(); System.out.println( System.out.println(st.getVorlesung()); st.getVorlesung()); } [C, Java] } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 97 stu.xml <?xml <?xml version="1.0" version="1.0" encoding="UTF encoding="UTF="UTF-8"?> <java version=" version="1.7.0_07" ="1.7.0_07" class=" class="java.beans.XMLDecoder ="java.beans.XMLDecoder"> java.beans.XMLDecoder"> <object class=" class="beans.Student ="beans.Student"> beans.Student"> <void property=" property="name ="name"> name"> <string>Erwin</ string>Erwin</string >Erwin</string> string> </void> <void property=" property="vorlesung ="vorlesung"> vorlesung"> <void method=" method="add ="add"> add"> <string>C</ string>C</string >C</string> string> </void> <void method=" method="add ="add"> add"> <string>Java</ string>Java</string >Java</string> string> </void> </void> </object </object> object> </java </java> java> Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 98 Analyse des Beispiels • Wesentliche Funktionalität wurde an Java (genauer die Virtual Machine VM) abgegeben; diese arbeitet als Container • VM kann direkt auf Exemplarvariablen zugreifen (Ansätze mit Introspection, Reflection) – Gib mir Exemplarvariablen – Gib mir Exemplarmethoden – (Textanalyse, Beginn mit get/set) – Rufe auf Objekt mit Methode mit Namen … und Parametern … auf Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 99 Weiterführend • Zu jeder Bean kann es eine BeanInfo-Klasse geben, die die Bean beschreibt • Ursprünglich sind Beans für visuelle Komponenten entwickelt worden; GUIBuilder erlauben die direkte Bearbeitung von Properties • Java-Bean für GUI-Builder in jar-Datei mit Manifest-Datei (.mf) • Es gibt eigene Werkzeuge zur Entwicklung mit Beans Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 100