Model View Controller Prasun Dewan Comp 114 Model

  • Slides: 78
Download presentation
Model View Controller Prasun Dewan Comp 114

Model View Controller Prasun Dewan Comp 114

Model View Controller Pattern • Issue – How to create user-interface objects like object

Model View Controller Pattern • Issue – How to create user-interface objects like object editor • Model-View-Controller Pattern – Observer sub-pattern

User Interface Objects • How to reuse code among different user interfaces? • How

User Interface Objects • How to reuse code among different user interfaces? • How to simultaneously create multiple user interfaces to same object? – Different views for same user. • Slide sorter vs. Outline – How to create same or different view for each user sharing an object • Distributed presentation

Counter • Can add arbitrary positive/negative value to an integer. • Different user interfaces.

Counter • Can add arbitrary positive/negative value to an integer. • Different user interfaces.

Console Input and Output

Console Input and Output

Console Input and JOption Output

Console Input and JOption Output

Console Input, Output and JOption Output

Console Input, Output and JOption Output

Implementation Constraint • Re-use as much code as possible in the three implementations

Implementation Constraint • Re-use as much code as possible in the three implementations

Pattern-free Implementation public class Console. UI { static int counter = 0; public static

Pattern-free Implementation public class Console. UI { static int counter = 0; public static void main(String[] args) { while (true) { int next. Input = read. Int(); if (next. Input == 0) return; counter += next. Input; System. out. println("Counter: " + counter); } } }

Pattern-free Implementation public class Console. UI { static int counter = 0; public static

Pattern-free Implementation public class Console. UI { static int counter = 0; public static void main(String[] args) { while (true) { int next. Input = read. Int(); if (next. Input == 0) return; counter += next. Input; JOption. Pane. show. Message. Dialog(null, "Counter: " + counter. Value); } } } Counter code duplicated

Model/Interactor Separation Interactor Model Interactor AConsole. UI Counter AMultiple. UI Interactor AMixed. UI Model

Model/Interactor Separation Interactor Model Interactor AConsole. UI Counter AMultiple. UI Interactor AMixed. UI Model has no UI code and only semantics!

Composing Model and Interactor public static main (String args[]) (new AConsole. UI()). edit (new

Composing Model and Interactor public static main (String args[]) (new AConsole. UI()). edit (new ACounter()); }

Counter Model package models; public class ACounter implements Counter { int counter = 0;

Counter Model package models; public class ACounter implements Counter { int counter = 0; public void add (int amount) { counter += amount; } public int get. Value() { return counter; } } • Code reusability • Less duplication • Fewer changes

Console Interactor package interactors; public class AConsole. UI implements Console. UI { public void

Console Interactor package interactors; public class AConsole. UI implements Console. UI { public void edit (Counter counter) { while (true) { int next. Input = read. Int(); if (next. Input == 0) return; counter. add(next. Input); System. out. println("Counter: " + counter. get. Value()); } } } Shared model code Input Output

Mixed Interactor package interactors; public class AConsole. UI implements Console. UI { public void

Mixed Interactor package interactors; public class AConsole. UI implements Console. UI { public void edit (Counter counter) { while (true) { int next. Input = read. Int(); if (next. Input == 0) return; counter. add(next. Input); JOption. Pane. show. Message. Dialog(null, "Counter: " + counter. get. Value()); } } } Shared model code Input Output

Multiple Interactor package interactors; public class AConsole. UI implements Console. UI { Shared public

Multiple Interactor package interactors; public class AConsole. UI implements Console. UI { Shared public void edit (Counter counter) { while (true) { model code int next. Input = read. Int(); Input if (next. Input == 0) return; counter. add(next. Input); Output System. out. println("Counter: " + counter. get. Value()); JOption. Pane. show. Message. Dialog(null, "Counter: " + counter. get. Value()); } } }

Drawbacks of Monolithic UI AConsole. UI AMixed. UI ACounter AMultiple. UI Duplicated input code

Drawbacks of Monolithic UI AConsole. UI AMixed. UI ACounter AMultiple. UI Duplicated input code Duplicated output code

Model/Interactor Pattern UI Code Interactor Arbitrary UI unaware methods Computation code Model

Model/Interactor Pattern UI Code Interactor Arbitrary UI unaware methods Computation code Model

Controller Write methods View Model Read methods Performs Output Performs Input MVC Pattern

Controller Write methods View Model Read methods Performs Output Performs Input MVC Pattern

Controller add() View Model get. Value() Performs Output Performs Input MVC Pattern in Counter

Controller add() View Model get. Value() Performs Output Performs Input MVC Pattern in Counter

Multiple Views and Controllers Controller 1 View 1 Controller 2 View 2 Model Controller

Multiple Views and Controllers Controller 1 View 1 Controller 2 View 2 Model Controller 3 View 3 Controller M View N

Syncing Controllers & View Controller 1 View 1 Controller 2 View 2 Model Controller

Syncing Controllers & View Controller 1 View 1 Controller 2 View 2 Model Controller 3 View 3 Controller M View N

Observer/Observable Pattern Controller 1 Observer Observable Controller 2 View 1 View 2 Model Controller

Observer/Observable Pattern Controller 1 Observer Observable Controller 2 View 1 View 2 Model Controller 3 View 3 Changed object notifies views Controller M View N

Observer/Observable Pattern Observer 1 Observer 2 Observable 1 Observer 3 Observable 2 Observer N

Observer/Observable Pattern Observer 1 Observer 2 Observable 1 Observer 3 Observable 2 Observer N

Observer with multiple Observables • A single battle simulation view observing – Multiple planes

Observer with multiple Observables • A single battle simulation view observing – Multiple planes – Multiple tanks

Notification Scheme • Each observer is registered with observable. Observer 1 • Each write

Notification Scheme • Each observer is registered with observable. Observer 1 • Each write method in observable calls a notification method in each observer. Observer 2 • Notification method in observer reads model. Observable 1 • Each student is registered with professor’s listserv. • When web page is updated Observable 2 mail sent to students. • Student reads web page. Observer 3 Observer N

General Notification Scheme • Observers may have multiple observerables with common notification method. •

General Notification Scheme • Observers may have multiple observerables with common notification method. • Notification method parameter indicates which observable. Observer 1 Observer 2 Observable 1 Observer 3 Observable 2 Observer N

Controller View Notification method Write methods Model Read methods Performs Output Performs Input MVC

Controller View Notification method Write methods Model Read methods Performs Output Performs Input MVC Pattern

Implementation dependent issues • How does controller know about model? – Model connection method

Implementation dependent issues • How does controller know about model? – Model connection method invoked on it. – By model or some other program • Main • How is observable registered with observer. – It registers itself if it knows about observable. – Model registers it if it knows about observer. – Some other code registers it • Main

Model, View and Controller (MVC) Performs Input Controller Performs Output Model connection method Observer

Model, View and Controller (MVC) Performs Input Controller Performs Output Model connection method Observer registration method Write method View Notification method Model Read method

Counter Observable and Observer? package models; public interface Observable. Counter extends Counter { }

Counter Observable and Observer? package models; public interface Observable. Counter extends Counter { } package models; public interface Counter. Observer { }

Counter Observable and Observer Console View, JOption View package models; public interface Observable. Counter

Counter Observable and Observer Console View, JOption View package models; public interface Observable. Counter extends Counter { public void add. Observer(Counter. Observer observer); public void remove. Observer(Counter. Observer observer); } Common interface of all views package models; public interface Counter. Observer { public void update(Observable. Counter counter); } Called whenever model is updated Updated model

Counter Model package models; import java. util. Vector; public class An. Observable. Counter extends

Counter Model package models; import java. util. Vector; public class An. Observable. Counter extends ACounter implements Observable. Counter { Vector observers = new Vector(); public void add. Observer(Counter. Observer observer) { Give this observers. add. Element(observer); observable initial observer. update(this); } value public void remove. Observer(Counter. Observer observer) { observers. remove. Element(observer); } void notify. Observers() { for (int observer. Num = 0; observer. Num < observers. size(); observer. Num++) ((Counter. Observer) observers. element. At(observer. Num)). update(this); } public void add (int amount) {Each write method notifies all. super. add(amount); notify. Observers(); } }

Console View package views; public class ACounter. Console. View implements Counter. Observer { public

Console View package views; public class ACounter. Console. View implements Counter. Observer { public void update(Observable. Counter counter) { System. out. println("Counter: " + counter. get. Value()); } }

Console View package views; import models. Observable. Counter; import models. Counter. Observer; public class

Console View package views; import models. Observable. Counter; import models. Counter. Observer; public class ACounter. Console. View implements Counter. Observer { public void update(Observable. Counter counter) { System. out. println("Counter: " + counter. Value); } }

JOption View package views; import models. Observable. Counter; import models. Counter. Observer; import javax.

JOption View package views; import models. Observable. Counter; import models. Counter. Observer; import javax. swing. JOption. Pane; public class ACounter. JOption. View implements Counter. Observer { public void update(Observable. Counter counter) { JOption. Pane. show. Message. Dialog(null, "Counter: " + counter. Value); } }

Console Controller Interface package controllers; import models. Counter; public interface Counter. Controller { public

Console Controller Interface package controllers; import models. Counter; public interface Counter. Controller { public void set. Model (Counter the. Counter); public void process. Input(); }

Console Controller Output method not called directly package controllers; import java. io. Buffered. Reader;

Console Controller Output method not called directly package controllers; import java. io. Buffered. Reader; import java. io. Input. Stream. Reader; import models. Counter; import java. io. IOException; public class ACounter. Controller implements Counter. Controller { Counter counter; public void set. Model (Counter the. Counter) { counter = the. Counter; } public void process. Input() { while (true) { int next. Input = read. Int(); if (next. Input == 0) return; counter. add(next. Input); } } //read. Int() …

Console Main public static main (String args[]) Counter model = new ACounter(); model. add.

Console Main public static main (String args[]) Counter model = new ACounter(); model. add. Observer (new ACounter. Console. View()); Counter. Controller controller = new ACounter. Controller(); controller. set. Model(model); controller. process. Input(); }

Console and JOption Main public static main (String args[]) Counter model = new ACounter();

Console and JOption Main public static main (String args[]) Counter model = new ACounter(); model. add. Observer (new ACounter. JOption. View()); Counter. Controller controller = new ACounter. Controller(); controller. set. Model(model); controller. process. Input(); } Input Code Shared

Console Main public static main (String args[]) Counter model = new ACounter(); model. add.

Console Main public static main (String args[]) Counter model = new ACounter(); model. add. Observer (new ACounter. Console. View()); Counter. Controller controller = new ACounter. Controller(); controller. set. Model(model); controller. process. Input(); }

Mixed UI Main public static main (String args[]) Counter model = new ACounter(); Composition

Mixed UI Main public static main (String args[]) Counter model = new ACounter(); Composition model. add. Observer(new AConsole. JOption. View()); code model. add. Observer (new ACounter. Console. View()); duplicated Counter. Controller controller = new ACounter. Controller(); controller. set. Model(model); controller. process. Input(); }

Façade Pattern Interactor Controller View Facade Notification method Write methods Model Read methods

Façade Pattern Interactor Controller View Facade Notification method Write methods Model Read methods

Facade pattern Facade Component 1 Component 2 Component. N

Facade pattern Facade Component 1 Component 2 Component. N

Façade: Compose objects into a single unit exposing methods relevant to that unit •

Façade: Compose objects into a single unit exposing methods relevant to that unit • E. g. scanner, parser, program tree, code generator objects combined into one compiler object – Takes program text as input – Produces code as output – Passes text to scanner, which passes tokens to parser, which creates program tree, which is processed by code generator, which produces output • Compiler user not aware of internal components • Componentizing code is a pain as components must be combined • Facades removes this problem, creating a simple façade to complex internals

Interactor Facade • Provides a single edit() method taking model as argument • Instantiates

Interactor Facade • Provides a single edit() method taking model as argument • Instantiates controller and view. • Makes view observer of model • Connects controller to model. • Starts controller.

Console. Controller. And. View Facade package interactors; import models. Observable. Counter; import models. Counter.

Console. Controller. And. View Facade package interactors; import models. Observable. Counter; import models. Counter. Observer; import controllers. ACounter. Controller; import controllers. Counter. Controller; import views. ACounter. Console. View; public class AConsole. Controller. And. View implements Counter. Interactor { public void edit(Observable. Counter model) { Counter. Observer view = new ACounter. Console. View(); model. add. Observer(view); Counter. Controller controller = new ACounter. Controller(); controller. set. Model(model); controller. process. Input(); } } Must be last action

Console Controller And JOption View Main package main; import models. An. Observable. Counter; import

Console Controller And JOption View Main package main; import models. An. Observable. Counter; import facades. AConsole. Controller. And. JOption. View; public class ACounter. Main { public static void main(String[] args) { (new AConsole. Contoller. And. JOption. View()). edit(new An. Observable. Counter()); } }

Console View + Controller An. Observable Counter ACounter. Console View ACounter. Controller AConsole. Controller.

Console View + Controller An. Observable Counter ACounter. Console View ACounter. Controller AConsole. Controller. And. View ACounter. JOption View

Console Controller + JOption. View An. Observable Counter ACounter. Console View ACounter. Controller ACounter.

Console Controller + JOption. View An. Observable Counter ACounter. Console View ACounter. Controller ACounter. JOption View AConsole. Controller. And. JView

(Console Controller + View) + JOption View An. Observable Counter ACounter. Console View ACounter.

(Console Controller + View) + JOption View An. Observable Counter ACounter. Console View ACounter. Controller ACounter. JOption View AConsole. Controller. And. View. And. JOption. View

Facade over facade package interactors; import models. Observable. Counter; import models. Counter. Observer; import

Facade over facade package interactors; import models. Observable. Counter; import models. Counter. Observer; import views. ACounter. JOption. View; public class AConsole. Contoller. And. View. And. JOption. View implements Counter. Interactor { public void edit(Observable. Counter model) { model. add. Observer(new ACounter. JOption. View()); (new AConsole. Contoller. And. View()). edit(model); } }

Main with two views and OE package main; import models. An. Observable. Counter; import

Main with two views and OE package main; import models. An. Observable. Counter; import bus. uigen. Object. Editor; import facades. AConsole. Controller. And. View. And. JOption. View; public class ACounter. Main { public static void main(String[] args) { Observable. Counter model = new An. Observable. Counter(); (new Object. Editor()). edit(model); (new Console. Controller. And. View. And. JOption. View()). edit(model); } }

Observers that are not views • Spreadsheet cell observes cells on which it depends.

Observers that are not views • Spreadsheet cell observes cells on which it depends. • Monitoring of appliance usage – Each time I do set. Channel() on TV event logged. • Any big brother app! • Counter observer?

Rocket Observer Rocket added observer before view

Rocket Observer Rocket added observer before view

Instances created and composed An. Observable Counter ACounter. Console View ACounter. Controller AConsole. Controller.

Instances created and composed An. Observable Counter ACounter. Console View ACounter. Controller AConsole. Controller. And. View ARocket. Launcher ARocket

Rocket Interface package models; import models. Counter. Observer; public interface Rocket extends Counter. Observer

Rocket Interface package models; import models. Counter. Observer; public interface Rocket extends Counter. Observer { public void launch() ; }

Rocket Launching Facade package models; import models. Observable. Counter; public class ARocket implements Rocket

Rocket Launching Facade package models; import models. Observable. Counter; public class ARocket implements Rocket { public void update(Observable. Counter counter) { if (counter. get. Value() == 0) launch(); } public void launch() { System. out. println("LIFT OFF!!!"); } }

Rocket Launching Facade package interactors; import models. Observable. Counter; import models. Counter. Observer; import

Rocket Launching Facade package interactors; import models. Observable. Counter; import models. Counter. Observer; import models. ARocket; import facades. AConsole. Contoller. And. View; public class ARocket. Launch. Count. Down implements Counter. Interactor { public final int INITIAL_COUNTER_VALUE = 10; public void edit(Observable. Counter counter) { counter. add(INITIAL_COUNTER_VALUE); Counter. Observer rocket = new ARocket(); counter. add. Observer(rocket); (new AConsole. Contoller. And. View()). edit(counter); } }

Rocket launching main package main; import models. An. Observable. Counter; import models. ARocket. Launcher;

Rocket launching main package main; import models. An. Observable. Counter; import models. ARocket. Launcher; import facades. ARocket. Launch. Count. Down; public class ACounter. Main { public static void main(String[] args) { (new ARocket. Launch. Count. Down()). edit(new An. Observable. Counter()); } }

Counter Observable and Observer Console View, JOption View package models; public interface Observable. Counter

Counter Observable and Observer Console View, JOption View package models; public interface Observable. Counter extends Counter { public void add. Observer(Counter. Observer observer); public void remove. Observer(Counter. Observer observer); } Common interface of all views package models; public interface Counter. Observer { public void update(Observable. Counter counter); } Called whenever model is updated Updated model

Basic Notification Observer 1 • Each observer is registered with observable. • Each write

Basic Notification Observer 1 • Each observer is registered with observable. • Each write method in observable calls a notification method in each Observable 1 observer. • Parameter indicates observervable • Notification method in observer reads model. Observer 2 Observer 3 Observable 2 Observer N

Basic Notification package models; public interface Counter. Observer { public void update(Observable. Counter counter);

Basic Notification package models; public interface Counter. Observer { public void update(Observable. Counter counter); } Called whenever observer is updated Updated observable

Implicit Observer package models; public interface Counter. Observer { public void set. Observable(Observable. Counter

Implicit Observer package models; public interface Counter. Observer { public void set. Observable(Observable. Counter counter); public void update(); } Implicit observable

Notification with Change Description package models; public interface Counter. Observer { public void update(Observable.

Notification with Change Description package models; public interface Counter. Observer { public void update(Observable. Counter counter, int new. Counter. Val); } No need to call read method after notification

Java Observable and Observer package java. util; public class Observable { public void add.

Java Observable and Observer package java. util; public class Observable { public void add. Observer(Observer observer) {…}’ public void delete. Observer(Observer observer) {…}’ void notify. Observers(Object arg) {…}; … } package java. util; public interface Observer { public void update(Observable o, Object arg); } A notifying class can subclass from Observable “Standard” observer interface

Notification with Changed Value package models; public interface Counter. Observer { public void update(Observable.

Notification with Changed Value package models; public interface Counter. Observer { public void update(Observable. Counter counter, int new. Counter. Val); } New value of observable attribute

Notification with Change package models; public interface Counter. Observer { public void update(Observable. Counter

Notification with Change package models; public interface Counter. Observer { public void update(Observable. Counter counter, int counter. Increment); } • Observer may display change to user. • Observer interested in change does not need to keep old value to determine change • Observer interested in absolute value must keep old value Difference between new and old value of observable attribute

Notification with New and Old Value package models; public interface Counter. Observer { public

Notification with New and Old Value package models; public interface Counter. Observer { public void update (Observable. Counter counter, int old. Counter. Value, int new. Counter. Value); } • Observer interested in change does not need to keep old value to determine change • Observer interested in absolute value need not keep old value • Makes observable harder to code Old and new value of observable attribute

Notification with Single Event Object package models; public interface Counter. Observer { public void

Notification with Single Event Object package models; public interface Counter. Observer { public void update(Counter. Change. Event event); } • package models; public interface Counter. Change. Event { Observable. Counter get. Counter(); int get. Old. Counter. Value(); int get. New. Counter. Value(); } • Easy to pass single object to different methods handling event. Can make event info very elaborate – – – • • Time when event occurred Unique ID for event …. Don’t have to declare parameters for event information fields not of interest. Can return object from a function.

Java Text. Field and Action. Listener package java. awt; public class Text. Field {

Java Text. Field and Action. Listener package java. awt; public class Text. Field { public void add. Action. Listener(Action. Listener l) {…}; public void remove. Action. Listener(Action. Listener l) {…}; … } package java. awt. Event; public interface Action. Listener { public void action. Performed(Action. Event e); } Button, Menu. Item and other components also define these methods Should have been put in an interface

Java Text. Field and Action. Listener package java. awt. event; public class Action. Event

Java Text. Field and Action. Listener package java. awt. event; public class Action. Event extends java. awt. AWTEvent { public String get. Action. Command() {…}; public param. String() {…}; long get. When() {…}; …. } package java. awt. event; public class AWTEvent extends java. util. Event. Object { int get. ID() {…}; …. } package java. util; public class Event. Object { Object get. Source() {…}; String to. String() {…}; }

Observing multiple attributes package bmi; public interface BMISpreadsheet { public double get. Height(); public

Observing multiple attributes package bmi; public interface BMISpreadsheet { public double get. Height(); public void set. Height (double new. Val); public double get. Weight() ; public void set. Weight(double new. Weight) ; public double get. BMI(); }

Multiple update methods package bmi; public interface BMISpreadsheet { public double get. Height(); public

Multiple update methods package bmi; public interface BMISpreadsheet { public double get. Height(); public void set. Height (double new. Val); public double get. Weight() ; public void set. Weight(double new. Weight) ; public double get. BMI(); } package bmi; public interface BMIObserver { public void update. Height (BMISpreadsheet bmi, int old. Height, int new. Height); public void update. Weight(BMISpreadsheet bmi, int old. Weight, int new. Weight); public void update. BMI(BMISpreadsheet bmi, double old. BMI, double new. BMI); }

Single update method package bmi; public interface BMISpreadsheet { public double get. Height(); public

Single update method package bmi; public interface BMISpreadsheet { public double get. Height(); public void set. Height (double new. Val); • public double get. Weight() ; public void set. Weight(double new. Weight) ; • public double get. BMI(); } Not type safe New methods not needed as new properties added • Can be generalized for arbitrary bean objects “Wght” package bmi; public interface BMIObserver { public void update (BMISpreadsheet bmi, String property. Name, Object old. Value, Object new. Value); } “One”

Property Changes package bmi; public class An. Observable. BMISpreadsheet extends ABMISpreadsheet{ public void add.

Property Changes package bmi; public class An. Observable. BMISpreadsheet extends ABMISpreadsheet{ public void add. Property. Change. Listener(Property. Change. Listener l) { …} … Must notify in } write methods package java. beans; public interface Property. Change. Listener extends java. util. Event. Listener { public void property. Change (Property. Change. Event evt); } package java. beans; public class Property. Change. Event extends java. util. Event. Object { public Property. Change. Event (Object source, String property. Name, Object old. Value, Object new. Value) {…} public Object get. New. Value() {…} public Object get. Old. Value() {…} public String get. Property. Name() {…} …. }

Property Changes • If Object defines – add. Property. Change. Listener(Property. Change. Listener l)

Property Changes • If Object defines – add. Property. Change. Listener(Property. Change. Listener l) { …} • Object. Editor automatically calls it to register itself. • Setter method can now call in listeners – public void property. Change Property. Change. Event evt); • Object. Editor reacts by updating display of property – Does not have to do polling refresh by calling all getter methods when a setter is invoked. – Can disable this polling refresh by invoking View Auto Refresh command

Multiple Observer/Observable Interfaces • An observable may need to register and notify multiple kinds

Multiple Observer/Observable Interfaces • An observable may need to register and notify multiple kinds of observers Observer 1 • An observer may need to interact with multiple kinds Observable 1 of observables. Observer 2 Observer 3 Observable 2 Observer N