Folien ab 2.10.12

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