Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
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
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
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”
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
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.
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
Paket
expr aritmetiska uttryck – modifieras ej gui användargränssnittet – modifieras menu menyer – modifieras
… egna paket util ”avvecklingspaket”
Paketet expr
Expr ExprParser
Num Add
…
AddressExpr
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
Paketet gui
Gui med 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
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
Paketet menu - menyerna
Paketet util
Address Environment Adjustment
NumberAdjustment XLException XLBufferedReader XLPrintStream
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
XL - Gui
JFrame ger ett eget fönster på skärmen.
import javax.swing.JFrame;
import java.awt.BorderLayout;
public class Gui extends JFrame{
public Gui(int count) {
super("Untitled-" + count);
setLayout(new BorderLayout());
// omissions pack();
setVisible(true);
} }
BorderLayout
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
Structure
Structure
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
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() }
}
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
WindowMenu
WindowMenuItem
class WindowMenuItem extends JMenuItem
implementsActionListener { private Gui gui;
public WindowMenuItem(Gui gui) { super(gui.getTitle());
this.gui = gui;
addActionListener(this);
}
public void actionPerformed(ActionEvent event) { gui.toFront();
} }
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
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);
}
public void update(Observable observable, Object object) { removeAll();
for (Gui gui : guiList) {
add(new WindowMenuItem(gui));
} } }
GuiList
public class GuiList extends Observable
implementsIterable<Gui> { private List<Gui> list = new ArrayList<Gui>();
public void add(Gui gui) { list.add(gui);
setChanged();
notifyObservers();
}
public Iterator<Gui> iterator() { return list.iterator();
}
//omissions }
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
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){}
}
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
MouseListenerLabel
public class MouseListenerLabel extends JLabel { private class ClickListener extends MouseAdapter {
public void mouseClicked(MouseEvent event) { setBackground(Color.YELLOW);
} }
public MouseListenerLabel() { setBackground(Color.WHITE);
addMouseListener(new ClickListener());
} }
Felhantering
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
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);
} }
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
Felhantering
… try {
value = expr.value();
} catch (XLException e) {
report(e.getMessage(), e.getObject());
}
…
Model/View/Control
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
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
View Model Control
JFrame JButton
View
<<interface>>
ActionListener
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
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.
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
Observer & Observable
Finns i java.util
public class Observable {
public void addObserver(Observer observer) public void deleteObserver(Observer observer) protected void setChanged()
public void notifyObservers(Object object) public void notifyObservers()
// omissions }
public interface Observer {
public void update(Observable observable, Object object);
}
Observer-mönstret
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
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 Model extends Observable { private int state;
public void changeState() { state++;
setChanged();
notifyObservers();
}
public String state() {
return String.valueOf(state);
} }
Observer-mönstret
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());
}
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
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);
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
Java.util.Observable implementering…
public class Observable {
private boolean changed = false;
private Vector<Observer> vector = new Vector<Observer>();
public synchronized void addObserver(Observer observer) { if (!vector.contains(observer)) {
vector.add(observer);
} }
protected synchronized void 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 void notifyObservers(Object arg) { if (!changed) {
return;
} changed = false;
for (Observer observer: vector) { observer.update(this, arg);
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
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.
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
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.
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
Update(…, object)
Object kan användas för att skicka information från modellen till vyn.
Fråga: Hur kan modellen veta vad vyn vill ha?
Svar: Det kan den inte veta.
Fråga: Det kan finnas flera observatörer som vill veta olika saker. Hur skall informationen förpackas?
Svar: 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.
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
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.
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
Konkret grammatik - BNF
Expr ::= term (addop term)∗
Term ::= factor (mulop factor)∗
Factor::= number | name | ’(’ expr ’)’
Addop::= ’+’ | ’−’
Mulop::= ’∗’ | ’/’
number ::= unsignedNumber | ’−’ unsignedNumber unsignedNumber ::= digit (digit)∗
digit ::= ’0’ | ’1’ | ’2’ | ’3’ | ’4’ | ’5’ | ’6’ | ’7’ | ’8’ | ’9’
name ::= letter (letter)∗
letter ::= ’a’ | ’b’ | ... | ’z’
Operatorer
* upprepa 0 eller flera gånger
| eller
’ ’ literalt
Abstrakt representation – abstrakt grammatik
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
Factory Method
SomeApp
Square
<<interface>>
Shape
<<interface>>
Shape Factory + make(String):Shape
Circle
<<creates>>
ShapeFactory Implementation
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)
public int nextToken() throws IOException public void ordinaryChar(int ch)
\\ omissions }
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
Java.io.StreamTokenizer
public class ExprParser extends StreamTokenizer { private int 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:
Datavetenskap/LTH | EDAF10/EDA061 HT2015 | Ulf Asklund
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;
}