You are on page 1of 17

Lecture week 11

Model-View-Controller

1. The MVC pattern


2. Basic communication
Code outlines
3. Building the system
The Observer pattern
4. Putting it all together
The root class
Simple and compound views
A controller
A model
5. A concert booking system
Model classes: Stadium, Group
Standard classes: Viewable, View
Standard classes: Root, Window
Panel classes: Panel, GroupPanel
6. Input validation

1
1. The MVC pattern
The Model-View-Controller (MVC) architecture is the
basis for all modern GUI systems. It is designed to
• separate the GUI from the domain model
• separate the control of the GUI from the visible display
• allow multiple views to be updated when needed
• achieve low coupling and high cohesion
• achieve clean code and high reuse

The model consists of the classes that deal with the


domain objects. The view is defined by the windows, panels,
and any other GUI components. The controllers are classes
that respond to user input, such as button presses. The most
common form of controller implements the Java class
ActionListener and handles ActionEvents; there are
other types of event as well. The separation of these three
layers allow changes to be made to the view, without any
changes to the model.

Web-based systems use a web browser to show the view


(the user interface). The browser runs the view and controller
on your machine, but the model runs on some other machine,
possibly on the other side of the world.

2
2. Basic communication
1. The user generates an event. The controller receives the
event and calls methods in the model object.

2. The model object calls methods in the model layer.

3. The model calls a view method update().

4. update() gets data from the model and updates the view.
command
Model

query

Controller

event
update data

View

A model object knows nothing about its views, except that


they exist. It has a list of the view objects to be updated when
a value changes, and calls the update() method in each view.
The update method in a view then gets the new data values
from the model object and displays them.

3
Code outlines
A controller responds to view events and calls model
methods. It may also access the view to get new values and
change the view (such as clearing a text field).

A controller receives an event and calls model methods.

class Listener extends ActionListener


{ private Model model;

public void actionPerformed(actionEvent e)


{ model.doSomething() }

A model has a list of views to update.

class Model
{ private LList<View> views;

private void update()


{ for (View view: views)
view.update(); }

A view calls its model for any new data values.


class View
{ private Model model;

public void update()


{ get model data
show new data }

4
3. Building the system
The process to build an MVC system may be summarised
as MMMVCVCVC.

1. Create the model layer.


2. Create the first view and pass the root model object to it.
3. Each view object
• attaches itself to the model object
• creates a controller and passes the relevant model object
• creates the next view object
4. Each controller object stores the model object.

Model

view view
Controller
model model

View model model model

In this diagram, a solid arrow means "creates" and a dashed


arrow means "attach”. The name next to an arrow shows the
argument that is passed.

5
The Observer pattern
A model updates its views by calling update().

class Viewable
{ private LList<View> views =
new LList<View>();

public void attach(View view)


{ views.add(view); }

private void update()


{ for (View view: views)
view.update(); }

class Model extends Viewable


{ ...

public abstract interface View


{ public abstract void update(); }

public class Panel implements View


{ private Model model;

public Panel(Model model)


{ this.model = model;
model.attach(this);

public void update()


{ model.getData();
show the new data }

All the model does is call update(), so you can change the
GUI completely with no change at all to the model!

6
4. Putting it all together

A model
A model class looks like

public class A extends Viewable


{
public A()
{ ... }

public void method(data)


{ store the data
update(); }
}

The root class


public class Root
{ public static void main(String[] args)
{ new ViewA(new Model()); }
}

The root class uses a static main method so we don't need


to create a root object. It creates the model layer, and the first
GUI. After the whole system has been created, the code
finishes and the main method exits. The GUI runs on its own
thread (threads are not covered in this subject) and stays on the
screen until the user closes the window,

7
A view
public class ViewA implements View
{ private A a;
private view attributes;

public ViewA(A a)
{ this.a = a;
setup();
build();
...
add(new ViewB(a.b)); }

private void build()


{ add(new Jlabel(...);
add(new JTextField(...);
...
Button button = new Button(...);
button.addActionListener(listener);
add(button); }

...

public void update()


{ a.getSomeData();
change view }
}

8
A controller
A controller looks like

class ListenerA extends ActionListener


{ private A a;

public ListenerA(A a)
{ this.a = a; }

public void actionPerformed(ActionEvent e)


{ a.methodCalls;
... }
}

The controller may also affect the view directly, such as


clearing text fields.

Common errors
The symptom is that nothing changes when you click on
the button. The probable cause is

• the view did not attach itself to the model


model.attach(this);

• the model did not update the view


class Model
{ ...
void doSomething(somedata)
{ useData
update(); }

9
5. A concert booking system
This system sells seats for a concert. The GUI has a panel
for each group of seats; it looks like

The user enters a number of seats to sell at right and then


clicks on the OK button to sell seats for that group.

The actionPerformed method in the button's action


listener calls a method on the model object (the group of seats)
to add the number of seats sold: group.sell(n);

The model object (the group) adds the number of seats to


the total number sold and calls update to change the display:

public void sell(int number)


{ sold += number;
update(); }

The panel's update method gets and displays the new


number sold and the number left, and clears the sell field.

10
viewer classes
public abstract interface View
{ public abstract void update();
}

public class Viewable


{ private LinkedList<View> views =
new LinkedList<View>();

public void attach(View view)


{ views.add(view); }

public void update()


{ for (View view: views)
view.update(); }
}

Any number of views can be attached to a model class. A


view can be attached to any number of model classes.

class Stadium
public class Stadium
{ private LL<Group> seats = new LL<Group>();

public Stadium()
{ seats.add(new Group("Back", 2000, 35.00));
seats.add(new Group("Middle", 4000, 60.00));
seats.add(new Group("Front", 200, 300.00));}
}

11
class Group

public class Group extends Viewable


{ private String name;
private int forSale;
private double price;
private int sold = 0;

public Group(String name, int forSale,


double price)
{ this.name = name;
this.forSale = forSale;
this.price = price; }

public void sell(int number)


{ sold += number; }

public int left()


{ return forSale - sold; }

public double income()


{ return sold * price; }

public String name()


{ return name; }

public double price()


{ return price; }

public int sold()


{ return sold; }
}

12
The root class
The root class creates the stadium and passes it to the window.

public class Root


{ public static void main(String[] args)
{ new Window(new Stadium()); }
}

The window class


The window class receives the stadium and passes it on to the
top-level panel.
public class Window extends JFrame
{ public Window(Stadium stadium)
{ setup();
build(stadium);
pack();
setVisible(true); }

private void setup()


{ setLocation(500, 500); }

private void build(Stadium stadium)


{ add(new Panel(stadium)); }
}

13
class Panel
The top-level panel (the panel that covers the window)
adds a group panel for each group.
public class Panel extends JPanel
{
public Panel(Stadium stadium)
{ setup(stadium.groups());
build(stadium); }

private void setup(int groups)


{ setLayout(new GridLayout(groups, 1)); }

private void build(Stadium stadium)


{ for (Group group: stadium.seats())
add(new GroupPanel(group)); }
}

14
class GroupPanel
The group panel stores (a pointer to) the group, attaches
itself to the group, and creates a listener for the button.
public class GroupPanel
extends JPanel implements View
{ private JTextField sold = new JTField(5);
private JTextField left = new JField(5);
private JTextField sell = new JField(5);
private Group group;

public GroupPanel(Group group)


{ this.group = group;
group.attach(this);
setup();
build(group); }

private void setup()


{ ... }

private void build(Group group)


{ addPair("Sold", sold);
addPair("Left", left);
addPair("Sell", sell);
JButton button = new JButton("OK");
button.addActionListener
(new Listener());
add(button);
update(); }

public void addPair


(String label, JTextField field)
{ add(new JLabel(label));
add(field); }

15
public void update()
{ sold.setText(group.sold() + "");
left.setText(group.left() + "");
sell.setText(""); }

private int sale()


{ return Integer.parseInt
(sell.getText()); }

The listener calls the group's sell method. The listener is


a private class here, so it can use the model object group
stored in the view class.

private class Listener


implements ActionListener
{ public void actionPerformed(ActionEvent e)
{ group.sell(sale()); }
}
}

The Group sell method increments the number sold, and


calls update() to update the display.

public class Group extends Viewer


{ ...
public void sell(int number)
{ sold += number;
update(); }

16
Input validation
The controller can change the view directly. Consider the
case where the user enters an invalid value, such as too many
seats to sell. The controller checks that there are enough seats
before calling sell, and changes the view to tell the user.
Here, I just show an error message in the input text field.

public void actionPerformed(ActionEvent e)


{ int seats = sale();
if (group.canSell(seats))
group.sell(seats);
else
sell.setText(“Too many!”); }

If the user enters a non-number, parseInt throws a non-


fatal exception that is caught in actionPerformed:

private int sale() throws NumberFormatException


{ return Integer.parseInt(sell.getText()); }

public void actionPerformed(ActionEvent e)


{ int seats;
try
{ seats = sale(); }
catch (NumberFormatException e)
{ sell.setText(“That’s not a number”);
return; }
if (group.canSell(seats))
… }

If the local is declared in the try-catch block, its scope is


that block and it cannot be used after that block exits.

17

You might also like