Einführung in Software Engineering Übung 9: Design Patterns Richard Bubel & Martin Hentschel 31. Januar 2013 130131 | TUD | M. Hentschel | 1 Aufgabe 9.1 (7 Punkte) I Ziel: Entwurfsmuster verstehen und anwenden I Beschreibung: Anwendung Registrierkasse (Cash Register) erfasst Positionen eines Kassenbelegs und berechnet den Gesamtbetrag 130131 | TUD | M. Hentschel | 2 Aufgabe 9.1a (2 Punkte) I Ziel: Entwurfsmuster „Strategy“ anwenden I Beschreibung: Angezeigte Positionen anhand Beschreibung filterbar I Aufgabe: Anforderung mit Entwurfmuster „Strategy“ implementieren 130131 | TUD | M. Hentschel | 3 Aufgabe 9.1a (Lösungsvorschlag) Struktur VisualbParadigmbforbUMLbStandardbEditionwTUbDarmstadtv CashRegisterFrame 1updateShownReceiptwvb:bvoid <<create>> ReceiptPrinter filter 1ReceiptPrinterwfilterb:bIReceiptPositionFilterv 1printReceiptwreceiptb:bReceiptvb:bString DescriptionReceiptPositionFilter -filterb:bString 1DescriptionReceiptPositionFilterwfilterb:bStringv 1acceptwpositionb:bReceiptPositionvb:bboolean 130131 | TUD | M. Hentschel | 4 <<create>> 1 <<Interface>> IReceiptPositionFilter 1acceptwpositionb:bReceiptPositionvb:bboolean AllReceiptFilter 1AllReceiptFilterwv 1acceptwpositionb:bReceiptPositionvb:bboolean Aufgabe 9.1a (Lösungsvorschlag) Quellcode public interface IReceiptPositionFilter { public boolean accept(ReceiptPosition position); } 130131 | TUD | M. Hentschel | 5 Aufgabe 9.1a (Lösungsvorschlag) Quellcode public class AllReceiptFilter implements IReceiptPositionFilter { @Override public boolean accept(ReceiptPosition position) { return true; } } 130131 | TUD | M. Hentschel | 6 Aufgabe 9.1a (Lösungsvorschlag) Quellcode public class DescriptionReceiptPositionFilter implements IReceiptPositionFilter { private String filter; public DescriptionReceiptPositionFilter(String filter) { this.filter = filter; } @Override public boolean accept(ReceiptPosition position) { if (position != null) { String description = position.getDescription(); return description != null && description.contains(filter); } else { return false; } } } 130131 | TUD | M. Hentschel | 7 Aufgabe 9.1a (Lösungsvorschlag) Quellcode public class ReceiptPrinter { private IReceiptPositionFilter filter; public ReceiptPrinter(IReceiptPositionFilter filter) { this.filter = filter; } } public String printReceipt(Receipt receipt) { // ... for (ReceiptPosition position : positions) { if (filter == null || filter.accept(position)) { // ... } } // ... } 130131 | TUD | M. Hentschel | 8 Aufgabe 9.1a (Lösungsvorschlag) Quellcode public class CashRegisterFrame extends JFrame { protected void updateShownReceipt() { // Get filter text String filterText = filterField.getText(); // Create new text to show in receipt area IReceiptPositionFilter filter = filterText != null && !filterText.isEmpty() ? new DescriptionReceiptPositionFilter(filterText) : new AllReceiptFilter(); ReceiptPrinter printer = new ReceiptPrinter(filter); String receiptText = printer.printReceipt(receipt); // Update shown text receiptArea.setText(receiptText); } } 130131 | TUD | M. Hentschel | 9 Aufgabe 9.1b (2,5 Punkte) I Ziel: Entwurfsmuster „Observer“ anwenden I Beschreibung: Kassenbeleg in zwei Fenstern editierbar I Aufgabe: Anforderung mit Entwurfmuster „Observer“ implementieren 130131 | TUD | M. Hentschel | 10 Aufgabe 9.1b (Lösungsvorschlag) Struktur (Observable) Visual:Paradigm:for:UML:Standard:EditionbTU:DarmstadtO CashRegisterFrame receiptObserver jCashRegisterFramebtitle:::String$:receipt:::ReceiptO jdisposebO:::void pupdateShownReceiptbO:::void 0 CashRegisterFramev0 jupdatebo:::Observable$:arg:::ObjectO:::void <<Interface>> Observer ReceiptPosition ><<y receipt jupdatebo:::Observable$:arg:::ObjectO:::void ><<y positions 0 Receipt jaddPositionbposition:::ReceiptPositionO:::void 130131 | TUD | M. Hentschel | 11 obs Observable 1changed:::boolean jaddObserverbo:::ObserverO:::void jdeleteObserverbo:::ObserverO:::void psetChangedbO:::void jnotifyObserversbarg:::ObjectO:::void Aufgabe 9.1b (Lösungsvorschlag) Quellcode (Observable) import java.util.LinkedList; import java.util.List; import java.util.Observable; public class Receipt extends Observable { private List<ReceiptPosition> positions = new LinkedList<ReceiptPosition>(); public void addPosition(ReceiptPosition position) { if (positions.add(position)) { setChanged(); notifyObservers(position); // Resets changed flag } } } 130131 | TUD | M. Hentschel | 12 Aufgabe 9.1b (Lösungsvorschlag) Quellcode (Observable) import java.util.Observable; public class CashRegisterFrame extends JFrame { private Observer receiptObserver = new Observer() { @Override public void update(Observable o, Object arg) { updateShownReceipt(); } }; public CashRegisterFrame(String title, Receipt receipt) { /* ... */ this.receipt.addObserver(receiptObserver); /* ... */ } @Override public void dispose() { this.receipt.deleteObserver(receiptObserver); super.dispose(); } protected void updateShownReceipt() { /* ... */ } } 130131 | TUD | M. Hentschel | 13 Aufgabe 9.1b (Lösungsvorschlag) Struktur (Listener) Visual:Paradigm:for:UML:Standard:EditionATU:Darmstadt< CashRegisterFrame receiptListener 0 -CashRegisterFrameAtitle:::StringO:receipt:::Receipt< -disposeA< pupdateShownReceiptA<:::void 0 receipt Receipt -addPositionAposition:::ReceiptPosition<:::void -addReceiptListenerAlistener:::IReceiptListener<:::void -removeReceiptListenerAlistener:::IReceiptListener<:::voidA< -fireReceiptPositionAddedAe:::ReceiptEvent<:::void 1jj> positions ReceiptPosition CashRegisterFrame#0 -receiptPositionAddedAe:::ReceiptEvent<:::void listeners 1jj0 <<Interface>> IReceiptListener -receiptPositionAddedAe:::ReceiptEvent<:::void <<use>> <<create>> ReceiptEvent baddedPosition:::ReceiptPosition -ReceiptEventAsource:::ReceiptO:addedPosition:::ReceiptPosition< -getSourceA<:::Receipt -getAddedPositionA<:::ReceiptPosition 130131 | TUD | M. Hentschel | 14 <<Interface>> EventListener EventObject Aufgabe 9.1b (Lösungsvorschlag) Quellcode (Listener) import java.util.EventListener; public interface IReceiptListener extends EventListener { public void receiptPositionAdded(ReceiptEvent e); } 130131 | TUD | M. Hentschel | 15 Aufgabe 9.1b (Lösungsvorschlag) Quellcode (Listener) import java.util.EventObject; public class ReceiptEvent extends EventObject { private ReceiptPosition addedPosition; public ReceiptEvent(Receipt source, ReceiptPosition addedPosition) { super(source); this.addedPosition = addedPosition; } @Override public Receipt getSource() { return (Receipt)super.getSource(); } public ReceiptPosition getAddedPosition() { return addedPosition; } } 130131 | TUD | M. Hentschel | 16 Aufgabe 9.1b (Lösungsvorschlag) Quellcode (Listener) import javax.swing.event.EventListenerList; public class Receipt { private EventListenerList listeners = new EventListenerList(); } public void addReceiptListener(IReceiptListener listener) { if (listener != null) { listeners.add(IReceiptListener.class, listener); } } public void removeReceiptListener(IReceiptListener listener) { if (listener != null) { listeners.remove(IReceiptListener.class, listener); } } 130131 | TUD | M. Hentschel | 17 Aufgabe 9.1b (Lösungsvorschlag) Quellcode (Listener) import javax.swing.event.EventListenerList; public class Receipt { private EventListenerList listeners = new EventListenerList(); } protected void fireReceiptPositionAdded(ReceiptEvent e) { IReceiptListener[] toInform = listeners.getListeners( IReceiptListener.class); for (IReceiptListener l : toInform) { l.receiptPositionAdded(e); } } 130131 | TUD | M. Hentschel | 18 Aufgabe 9.1b (Lösungsvorschlag) Quellcode (Listener) import java.util.LinkedList; import java.util.List; public class Receipt { private List<ReceiptPosition> positions = new LinkedList<ReceiptPosition>(); public void addPosition(ReceiptPosition position) { if (positions.add(position)) { fireReceiptPositionAdded(new ReceiptEvent(this, position)); } } // ... } 130131 | TUD | M. Hentschel | 19 Aufgabe 9.1b (Lösungsvorschlag) Quellcode (Listener) public class CashRegisterFrame extends JFrame { private IReceiptListener rcptListener = new IReceiptListener() { @Override public void receiptPositionAdded(ReceiptEvent e) { updateShownReceipt(); } }; public CashRegisterFrame(String title, Receipt receipt) { /* ... */ receipt.addReceiptListener(rcptListener); /* ... */ } @Override public void dispose() { receipt.removeReceiptListener(rcptListener); super.dispose(); } protected void updateShownReceipt() { /* ... */ } } 130131 | TUD | M. Hentschel | 20 Aufgabe 9.1b (Lösungsvorschlag) Struktur (Bean) Visual:Paradigm:for:UML:Standard:Edition_TU:DarmstadtI CashRegisterFrame receiptListener yCashRegisterFrame_title:::Stringx:receipt:::ReceiptI ydispose_I:::void pupdateShownReceipt_I:::void < CashRegisterFramev< ypropertyChange_evt:::PropertyChangeEventI:::void <<Interface>> PropertyChangeListener receipt ypropertyChange_evt:::PropertyChangeEventI:::void < Receipt 1jjN yPROP_POSITIONS:::String yaddPosition_position:::ReceiptPositionI:::void yaddPropertyChangeListener_propertyName:::Stringx:listener:::PropertyChangeListenerI:::void yremovePropertyChangeListener_propertyName:::Stringx:listener:::PropertyChangeListenerI:::void ReceiptPosition 1jjN ReceiptPosition 130131 | TUD | M. Hentschel | 21 < pcs < source <<use>> listeners PropertyChangeEvent <<create>> PropertyChangeSupport yaddPropertyChangeListener_propertyName:::Stringx:listener:::PropertyChangeListenerI:::void yremovePropertyChangeListener_propertyName:::Stringx:listener:::PropertyChangeListenerI:::void yfireIndexedPropertyChange_propertyName:::Stringx:index:::intx:oldValue:::Objectx:newValue:::ObjectI:::void yfirePropertyChange_propertyName:::Stringx:oldValue:::Objectx:newValue:::ObjectI:::void Aufgabe 9.1b (Lösungsvorschlag) Quellcode (Bean) import java.beans.PropertyChangeSupport; public class Receipt { public static final String PROP_POSITIONS = "positions"; private PropertyChangeSupport pcs = new PropertyChangeSupport(this); private List<ReceiptPosition> positions = new LinkedList<ReceiptPosition>(); } public void addPosition(ReceiptPosition position) { int index = positions.size(); if (positions.add(position)) { pcs.fireIndexedPropertyChange(PROP_POSITIONS, index, null, position); } } 130131 | TUD | M. Hentschel | 22 Aufgabe 9.1b (Lösungsvorschlag) Quellcode (Bean) import java.beans.PropertyChangeListener; public class Receipt { public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { if (listener != null) { pcs.addPropertyChangeListener(propertyName, listener); } } } public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { if (listener != null) { pcs.removePropertyChangeListener(propertyName, listener); } } 130131 | TUD | M. Hentschel | 23 Aufgabe 9.1b (Lösungsvorschlag) Quellcode (Bean) import java.beans.PropertyChangeListener; public class Receipt { public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { if (listener != null) { pcs.addPropertyChangeListener(propertyName, listener); } } } public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { if (listener != null) { pcs.removePropertyChangeListener(propertyName, listener); } } 130131 | TUD | M. Hentschel | 24 Aufgabe 9.1b (Lösungsvorschlag) Quellcode (Bean) import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; public class CashRegisterFrame extends JFrame { private PropertyChangeListener receiptListener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt){ updateShownReceipt(); } }; public CashRegisterFrame(String title, Receipt receipt) { /* ... */ receipt.addPropertyChangeListener(Receipt.PROP_POSITIONS, receiptListener); /* ... */ } } 130131 | TUD | M. Hentschel | 25 Aufgabe 9.1b (Lösungsvorschlag) Quellcode (Bean) import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; public class CashRegisterFrame extends JFrame { @Override public void dispose() { receipt.removePropertyChangeListener(Receipt.PROP_POSITIONS, receiptListener); super.dispose(); } protected void updateShownReceipt() { /* ... */ } } 130131 | TUD | M. Hentschel | 26 Aufgabe 9.2 (2 Punkte) I Ziel: Angewendete Entwurfsmuster erkennen I Beschreibung: Ausschnitt der Java 6 API Spezifikation einer Klasse gegeben I Aufgabe: I I Entwurfsmuster des Ausschnitts nennen Elemente der Java API den Bestandteilen des Entwurfmusters zuordnen 130131 | TUD | M. Hentschel | 27 Aufgabe 9.2a (Lösungsvorschlag) Class javax.swing.AbstractListModel 130131 | TUD | M. Hentschel | 28 Aufgabe 9.2a (Lösungsvorschlag) Class javax.swing.AbstractListModel I Observer Design Pattern Subjekt Beobachter verwalten Beobachter informieren Konkretes Subjekt Abstrakter Beobachter Konkrete Beobachter 130131 | TUD | M. Hentschel | 29 AbstractListModel addListDataListener(ListDataListener), getListDataListeners(), removeListDataListener(ListDataListener) fireContentsChanged(... ), fireIntervalAdded(... ), fireIntervalRemoved(... ) BasicDirectoryModel, DefaultComboBoxModel, ... ListDataListener JComboBox Aufgabe 9.2b (Lösungsvorschlag) Class javax.swing.JFileChooser extends javax.swing.JComponent 130131 | TUD | M. Hentschel | 30 Aufgabe 9.2b (Lösungsvorschlag) Class javax.swing.JFileChooser extends javax.swing.JComponent I Strategy Design Pattern Kontext Abstrakte Strategie Strategie-Methode Konkrete Strategien 130131 | TUD | M. Hentschel | 31 JFileChooser FileFilter accept(File) BasicFileChooserUI.AcceptAllFileFilter, FileNameExtensionFilter