Objektorienterad modellering och diskreta strukturer (EDAF10) HT2 2015, FÖRELÄSNING 15 (XL-PROJEKTET) Dagens agenda Introduktion till projekt 2 ( XL ) EDA061 Repetition MVC-mönstret (Observer-mönstret) Factory Method-mönstret Felhantering Glöm inte skapa grupp och boka redovisningstid EDAF10/EDA061 HT2015, Ulf Asklund 1
Vecka 1-3 Vecka EDAF10 1 Föreläsning XL XL design 2 XL redovisningsmöte 1 XL implementation 3 XL implementation XL redovisningsmöte 2 4 XL slutinlämning (vid behov) Möte 1: användningsfall, paketindelning, klassdiagram 3 min presentation Möte 2: klassdiagram, källkod (fungerande program), kort beskrivning 3 min presentation Projekt 2 XL EDAF10/EDA061 HT2015, Ulf Asklund 2
Projekt 2 - XL GUI-modellering Den oerfarne gör allt i en stor klass. Den agile gör lite i många klasser med beaktande av principen om enkelt ansvar. EDAF10/EDA061 HT2015, Ulf Asklund 3
Paket expr gui menu util aritmetiska uttryck modifieras ej användargränssnittet modifieras menyer modifieras egna paket avvecklingspaket Paketet expr Expr Num Add AddressExpr ExprParser EDAF10/EDA061 HT2015, Ulf Asklund 4
Paketet gui Guimed main GuiList CurrentView StatusArea Editor RowNumberPanel SheetView SlotView Varför inte javax.swing.jtable? JTable tillhandahåller mycket som skulle göra projektet lättare, men döljer mycket av det som projektet vill lära ut javadoc omfattar 68+ sidor EDAF10/EDA061 HT2015, Ulf Asklund 5
Paketet menu - menyerna Paketet util Address Environment Adjustment NumberAdjustment XLException XLBufferedReader XLPrintStream En del av dessa klasser bör flyttas till andra paket. EDAF10/EDA061 HT2015, Ulf Asklund 6
XL - Gui JFrame ger ett eget fönster på skärmen. import javax.swing.jframe; import java.awt.borderlayout; public class Guiextends JFrame{ public Gui(int count) { super("untitled-" + count); setlayout(new BorderLayout()); // omissions pack(); setvisible(true); BorderLayout EDAF10/EDA061 HT2015, Ulf Asklund 7
Structure Structure EDAF10/EDA061 HT2015, Ulf Asklund 8
statusarea Editor - JTextField JTextField används för textinmatning. public class Editor extends JTextField implements ActionListener { public Editor() { setbackground(color.white); addactionlistener(this); public void actionperformed(actionevent event) { // activated by Return key // contents returnedby gettext() EDAF10/EDA061 HT2015, Ulf Asklund 9
WindowMenu WindowMenuItem class WindowMenuItem extends JMenuItem implementsactionlistener { private Guigui; public WindowMenuItem(Guigui) { super(gui.gettitle()); this.gui = gui; addactionlistener(this); public void actionperformed(actionevent event) { gui.tofront(); EDAF10/EDA061 HT2015, Ulf Asklund 10
JMenu public class WindowMenu extends JMenuimplements Observer { private GuiList guilist; public WindowMenu(GuiList guilist) { super("window"); this.guilist = guilist; guilist.addobserver(this); update(null, null); publicvoid update(observable observable, Object object) { removeall(); for (Guigui : guilist) { add(new WindowMenuItem(gui)); GuiList public class GuiList extends Observable implementsiterable<gui> { private List<Gui> list = new ArrayList<Gui>(); public void add(guigui) { list.add(gui); setchanged(); notifyobservers(); public Iterator<Gui> iterator() { return list.iterator(); //omissions EDAF10/EDA061 HT2015, Ulf Asklund 11
MouseListener Några swing-komponenter kan ha en ActionListener, t ex: JButton, JMenuItem, JTextField. Alla komponenter kan ha en MouseListener. Den läggs till med public void addmouselistener(mouselistener listener); public interface MouseListener { void mouseclicked(mouseevent event); void mouseentered(mouseeventevent); void mouseexited(mouseevent event); void mousepressed(mouseevent event); void mousereleased(mouseevent event); MouseAdapter I regel vill man bara reagera på en av händelserna. Då är det bekvämt med public abstract class MouseAdapter { public void mouseclicked(mouseevent event){ public void mouseentered(mouseevent event){ public void mouseexited(mouseevent event){ public void mousepressed(mouseevent event){ public void mousereleased(mouseevent event){ EDAF10/EDA061 HT2015, Ulf Asklund 12
MouseListenerLabel public class MouseListenerLabel extends JLabel{ private class ClickListenerextends MouseAdapter { public void mouseclicked(mouseevent event) { setbackground(color.yellow); public MouseListenerLabel() { setbackground(color.white); addmouselistener(new ClickListener()); Felhantering EDAF10/EDA061 HT2015, Ulf Asklund 13
Felhantering, forts. public class XLException extendsruntimeexception { private Object object; public XLException(String message, Object object) { super(message); this.object = object; public Object getobject() { return object; Feldetektering public class Div extends BinExpr { public Div(Expr expr1, Expr expr2) { super(expr1, expr2); protected double op(double op1, double op2) { if (op2!= 0) return op1 / op2; else throw new XLException( division by zero, this); EDAF10/EDA061 HT2015, Ulf Asklund 14
Felhantering try { value = expr.value(); catch (XLException e) { report(e.getmessage(), e.getobject()); Model/View/Control EDAF10/EDA061 HT2015, Ulf Asklund 15
Model/View/Control-arkitektur Modellen beskriver systemets tillstånd. Vyn visar upp systemets tillstånd. Control förändrar systemets tillstånd. View Model Control Model/View/Control-implementering JFrame <<interface>> ActionListener JButton View Model Control Paketberoenden View Model Control EDAF10/EDA061 HT2015, Ulf Asklund 16
Model/View/Control Varför? Principen om enkelt ansvar. Integritet: Modellen behöver inte känna till vyn. Flera vyer av en modell. Om Control saknar tillstånd slår vi ofta ihop vyn och control till ett grafiskt användargränssnitt, GUI. Observer Observer-mönstret används för att separera modellen från användargränssnittet, vyn. Vyn implementerar gränssnittet Observer. Modellen utvidgar klassen Observable. När modellens tillstånd förändras informeras alla observatörer om att det skett en förändring och vyerna uppdaterar sig genom att hämta information från modellen. Observer/Observable är ett ramverk; klasserna finns färdiga. EDAF10/EDA061 HT2015, Ulf Asklund 17
Observer & Observable Finns i java.util public class Observable { public void addobserver(observer observer) public void deleteobserver(observer observer) protected voidsetchanged() public void notifyobservers(object object) publicvoid notifyobservers() // omissions public interface Observer { public void update(observable observable, Object object); Observer-mönstret Metoden state() i modellen är en sorts getter. Det är bra om denna inte avslöjar hur tillståndet representeras. EDAF10/EDA061 HT2015, Ulf Asklund 18
Observer-mönstret public class Model extends Observable { private int state; public void changestate() { state++; setchanged(); notifyobservers(); public String state() { return String.valueOf(state); Metoden state() i modellen är en sorts getter. Det är bra om denna inte avslöjar hur tillståndet representeras. Observer-mönstret Metoden state() i modellen är en sorts getter. Det är bra om denna inte avslöjar hur tillståndet representeras. public class View extends JLabel implements Observer { private Model model; public View(Model model) { this.model = model; model.addobserver(this); public void update(observable observable, Object object) { settext(model.state()); Tydlig koppling till modellen. EDAF10/EDA061 HT2015, Ulf Asklund 19
Observer-mönstret Metoden state() i modellen är en sorts getter. Det är bra om denna inte avslöjar hur tillståndet representeras. public class View extends JLabel implements Observer { public View(Model model) { model.addobserver(this); public void update(observable observable, Object object) { Model model = (Model) observable; settext(model.state()); Mindre tydlig koppling till modellen. Ihopkoppling av vy och modell Skapa model och vy enligt exemplet ovan: Model model = new Model(); View view = new View(model); Kopplingen mellan vy och modell kan också göras där vy och modell skapas: Model model = new Model(); View view = new View(); model.addobserver(view); Någon nackdel? EDAF10/EDA061 HT2015, Ulf Asklund 20
Java.util.Observable implementering public class Observable { private boolean changed = false; private Vector<Observer> vector = new Vector<Observer>(); public synchronized voidaddobserver(observer observer) { if (!vector.contains(observer)) { vector.add(observer); protected synchronizedvoid setchanged() { changed = true;... java.util.observable Något förenklad: public class Observable { private boolean changed = false; private Vector<Observer> vector = new Vector<Observer>(); public void notifyobservers() { notifyobservers(null); public synchronized voidnotifyobservers(object arg) { if (!changed) { return; changed = false; for (Observer observer: vector) { observer.update(this, arg); EDAF10/EDA061 HT2015, Ulf Asklund 21
MVC med Observer En eller flera vyer registrerar sig som observatörer av modellen via addobserver. OCP Open-Closed Principle Ett kommando modifierar modellens tillstånd. Modellen informerar Observable att tillståndet förändrats via setchanged. Modellen begär att observatörerna informeras genom notifyobservers. notifyobservers i Observable informerar observatörerna via update om att modellen förändrats. Observatörerna hämtar modellens nya tillstånd och uppdaterar vyerna. Sekvensdiagram Anrop av en metod i modellen som ändrar dess tillstånd så att vyerna ska uppdateras. EDAF10/EDA061 HT2015, Ulf Asklund 22
Observer, update-parametrarna public interface Observer { public void update(observable observable, Object object); När man anropar notifyobservers(object) i modellen kommer update(observable, Object) att anropas i alla observatörer. Vad skall man använda argumenten till? Update(observable, ) Observable kan användas för att komma åt modellen. Det är tydligare att ge observatören tillgång till modellen via observatörens konstruerare. EDAF10/EDA061 HT2015, Ulf Asklund 23
Update(, object) Object kan användas för att skicka information från modellen till vyn. Fråga: Svar: Hur kan modellen veta vad vyn vill ha? Det kan den inte veta. Fråga: Svar: Det kan finnas flera observatörer som vill veta olika saker. Hur skall informationen förpackas? Det är svårt att veta om man inte känner till alla observatörer som kan vara aktuella. Det finns ingen naturlig plats att dokumentera förpackningen. Designprincip för Observer.update Undvik att använda parametrarna. EDAF10/EDA061 HT2015, Ulf Asklund 24
Exempel Aritmetiska uttryck Aritmetiska uttryck Konkret syntax Ett uttryck består av en eller flera termer separerade av enkla plus- eller minus-tecken. En term består i sin tur av en eller flera faktorer separerade av enkla multiplikations- eller divisionstecken. En faktor är ett tal, en variabel eller ett uttryck inom parenteser. Ett tal består av en eller flera siffror och får inledas med ett minustecken. En variabel består av en eller flera bokstäver bland a z. EDAF10/EDA061 HT2015, Ulf Asklund 25
Konkret grammatik - BNF Expr ::= term (addop term) Term ::= factor (mulop factor) Factor::= number name ( expr ) Addop::= + Mulop::= / Operatorer * upprepa 0 eller flera gånger eller literalt number ::= unsignednumber unsignednumber unsignednumber ::= digit (digit) digit ::= 0 1 2 3 4 5 6 7 8 9 name ::= letter (letter) letter ::= a b... z Abstrakt representation abstrakt grammatik EDAF10/EDA061 HT2015, Ulf Asklund 26
Factory Method SomeApp <<interface>> Shape Factory + make(string):shape <<interface>> Shape ShapeFactory Implementation Square Circle <<creates>> Vad måste nu SomeApp känna till? Java.io.StreamTokenizer public class StreamTokenizer { public doublenval; public String sval; public int ttype = 4; public static final int TT EOF = 1, TT EOL = 10, TT NUMBER = 2, TT WORD = public StreamTokenizer(Reader r) publicint nexttoken() throwsioexception public void ordinarychar(int ch) \\ omissions EDAF10/EDA061 HT2015, Ulf Asklund 27
Java.io.StreamTokenizer public class ExprParser extends StreamTokenizer { privateint token; public ExprParser(String string) throws IOException { super(new StringReader(string)); ordinarychar( ); ordinarychar( / ); token = nexttoken(); Analys av faktorer private Expr factor() { Expr e; switch (token) { case ( : token = nexttoken(); e = expr(); token = nexttoken(); return e; case TT NUMBER: double x = nval; token = nexttoken(); return new Num(x); case TT WORD: String s = sval; token = nexttoken(); return new Variable(s); EDAF10/EDA061 HT2015, Ulf Asklund 28
Analys av termer private Expr term() { Expr result, factor; result = factor(); while (token == token == / ) { int op = token; token = nexttoken(); factor = factor(); switch (op) { case : result = new Mul(result, factor); break; case / : result = new Div(result, factor); break; return result; EDAF10/EDA061 HT2015, Ulf Asklund 29