580 likes | 592 Views
Learn how to create user interface objects like an object editor using the Model-View-Controller pattern. Discover how to reuse code among different user interfaces and simultaneously create multiple interfaces for the same object. Explore different views for the same user and different views for different users sharing the same object.
E N D
Model View Controller Prasun Dewan Comp 114
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 to simultaneously create multiple user interfaces to same object? • Different views for same user. • Slide sorter vs. Outline • How to create different view for different views sharing same object • Distributed presentation
Example: Counter • Can add arbitrary positive/negative value to it. • Different UIs sharing component. • Multiple UIs
Example: Counter package models; publicclass ACounter implements Counter { int counter = 0; publicvoid add (int amount) { counter += amount; } publicint getValue() { return counter; } }
AConsoleUIMain AMixedUIMain ACounter AMultipleUIMain Benefits of Object/UI Separation
Model/Interactor Pattern UI Code Interactor Arbitrary UI unaware methods Model Computation code
Monolithic Main for Console I/O publicclass ACounterWithConsoleControllerAndView { publicstaticvoid main(String[] args) { Counter counter = new ACounter(); appendToConsole(counter.getValue()); processInput(counter); } staticvoid appendToConsole(int counterValue) { System.out.println("Counter: " + counterValue); } publicstaticvoid processInput(Counter counter) { while (true) { int nextInput = readInt(); if (nextInput == 0) return; counter.add(nextInput); appendToConsole (counter.getValue()); } } //readInt() ... }
Monolithic Main for Mixed I/O publicclass ACounterWithConsoleControllerAndJOptionView { publicstaticvoid main(String[] args) { Counter counter = new ACounter(); displayMessage(counter.getValue()); processInput(counter); } staticvoid displayMessage(int counterValue) { JOptionPane.showMessageDialog(null, "Counter: " + counterValue); } publicstaticvoid processInput(Counter counter) { while (true) { int nextInput = readInt(); if (nextInput == 0) return; counter.add(nextInput); displayMessage (counter.getValue()); } } //readInt() ... }
Monolithic Main for Mixed I/O publicclass ACounterWithConsoleControllerAndViewAndJOptionView { publicstaticvoid main(String[] args) { Counter counter = new ACounter(); displayOutput(counter.getValue()); processInput(counter); } staticvoid appendToConsole(int counterValue) { System.out.println("Counter: " + counterValue); } staticvoid displayMessage(int counterValue) { JOptionPane.showMessageDialog(null, "Counter: " + counterValue); } staticvoid displayOutput(int counterValue) { appendToConsole(counterValue); displayMessage(counterValue); } publicstaticvoid processInput(Counter counter) { while (true) { int nextInput = readInt(); if (nextInput == 0) return; counter.add(nextInput); displayOutput(counter.getValue()); } } //readInt() ... }
AConsoleUIMain AMixedUIMain ACounter AMultipleUIMain Duplicated input code Duplicated output code Drawbacks of Monolithic UI
Model/Interactor Pattern UI Code Interactor Arbitrary UI unaware methods Model Computation code
Performs Input Performs Output Model, View and Controller (MVC) View Controller Read methods Model Write methods
Performs Input Performs Output Interactor Pattern Special Case View Controller Interactor Facade Read methods Model Write methods
Example: Counter package models; publicclass ACounter implements Counter { int counter = 0; publicvoid add (int amount) { counter += amount; } publicint getValue() { return counter; } }
Performs Input Performs Output Counter with MVC Pattern View Controller Model getValue() add()
Multiple Views and Controllers Controller 1 View 1 Controller 2 View 2 Model Controller 3 View 3 Controller M View N
Could be instances of same class (multiple users) Views and Controllers are instances 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 3 View 3 Controller M View N Syncing Controllers and Views?
Observer Observable Observer/Observable Pattern Controller 1 View 1 Controller 2 View 2 Model Controller 3 View 3 Changed object notifies views Controller M View N
Observable 2 Observer/Observable Pattern Observer 1 Observer 2 Observable 1 Observer 3 Observer N
Observer with multiple Observables • A single battle simulation view observing • Multiple planes • Multiple tanks
Observable 2 Notification Scheme • Each observer is registered with observable. • Each write method in observable calls a notification method in each observer. • Notification method in observer reads model . Observer 1 Observer 2 Observable 1 Observer 3 • Each student is registered with professor’s listserv. • When web page is updated mail sent to students. • Student reads web page. Observer N
Observable 2 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 Observer N
Performs Input Performs Output Notification method Write method Read method Model, View and Controller (MVC) View Controller Model
Implementation dependent issues • How does controller know about model? • Model connection method invoked on it. • By model or some other program • Main • Façade • 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 • Facade
Performs Input Performs Output Model connection method Observer registration method Notification method Write method Read method Model, View and Controller (MVC) View Controller Model
Counter Observable and Observer (edit) package models; publicinterface ObservableCounter extends Counter { } package models; publicinterface CounterObserver { }
Counter Observable and Observer (edited) package models; publicinterface ObservableCounter extends Counter { public void register(CounterObserver counterObserver); public void unregister(CounterObserver counterObserver); } package models; publicinterface CounterObserver { public void notified(ObservableCounter counter); }
Console View, JOption View Common interface of all views Updated model Called whenever model is updated Counter Observable and Observer package models; publicinterface ObservableCounter extends Counter { publicvoid addObserver(CounterObserver observer); publicvoid removeObserver(CounterObserver observer); } package models; publicinterface CounterObserver { publicvoid update(ObservableCounter counter); }
Each write method notifies all. Give this observable initial value Counter Model package models; import java.util.Vector; publicclass AnObservableCounter extends ACounter implements ObservableCounter { Vector observers = new Vector(); publicvoid addObserver(CounterObserver observer) { observers.addElement(observer); observer.update(this); } publicvoid removeObserver(CounterObserver observer) { observers.removeElement(observer); } void notifyObservers() { for (int observerNum = 0; observerNum < observers.size(); observerNum++) ((CounterObserver) observers.elementAt(observerNum)).update(this); } publicvoid add (int amount) { super.add(amount); notifyObservers(); } }
Console View package views; import models.ObservableCounter; import models.CounterObserver; publicclass ACounterConsoleView implements CounterObserver { publicvoid update(ObservableCounter counter) { appendToConsole (counter.getValue()); } void appendToConsole(int counterValue) { System.out.println("Counter: " + counterValue); } }
JOption View package views; import models.ObservableCounter; import models.CounterObserver; import javax.swing.JOptionPane; publicclass ACounterJOptionView implements CounterObserver { publicvoid update(ObservableCounter counter) { displayMessage(counter.getValue()); } void displayMessage(int counterValue) { JOptionPane.showMessageDialog(null, "Counter: " + counterValue); } }
Console Controller Interface package controllers; import models.Counter; publicinterface CounterController { publicvoid setModel (Counter theCounter); publicvoid processInput(); }
Output method not called directly Console Controller package controllers; import java.io.BufferedReader; import java.io.InputStreamReader; import models.Counter; import java.io.IOException; publicclass ACounterController implements CounterController { Counter counter; publicvoid setModel (Counter theCounter) { counter = theCounter; } publicvoid processInput() { while (true) { int nextInput = readInt(); if (nextInput == 0) return; counter.add(nextInput); } } //readInt() … }
Classes we have AnObservable Counter ACounterJOptionView ACounterConsole View ACounterController
AConsoleControllerAndView Instances created and composed AnObservable Counter ACounterJOptionView ACounterConsole View ACounterController
Classes we have AnObservable Counter ACounterJOptionView ACounterConsole View ACounterController
AConsoleControllerAndJView Instances created and composed AnObservable Counter ACounterJOptionView ACounterConsole View ACounterController
Classes we have AnObservable Counter ACounterJOptionView ACounterConsole View ACounterController
AConsoleControllerAndView AConsoleControllerAndViewAndJOptionView Instances created and composed AnObservable Counter ACounterJOptionView ACounterConsole View ACounterController
Console Controller And View Main package main; import models.AnObservableCounter; import facades.AConsoleControllerAndView; publicclass ACounterMain { publicstatic void main(String[] args) { (new AConsoleControllerAndView()).edit(new AnObservableCounter()); } }
Must be last action ConsoleControllerAndView Facade package facades; import models.ObservableCounter; import models.CounterObserver; import controllers.ACounterController; import controllers.CounterController; import views.ACounterConsoleView; publicclass AConsoleControllerAndView implements CounterInteractor { publicvoid edit(ObservableCounter model) { CounterObserver view = new ACounterConsoleView(); model.addObserver(view); CounterController controller = new ACounterController(); controller.setModel(model); controller.processInput(); } }
Console Controller And JOption View Main package main; import models.AnObservableCounter; import facades.AConsoleControllerAndJOptionView; publicclass ACounterMain { publicstatic void main(String[] args) { (new AConsoleContollerAndJOptionView()).edit(new AnObservableCounter()); } }
ConsoleControllerAndJView Facade package facades; import models.ObservableCounter; import models.CounterObserver; import controllers.ACounterController; import controllers.CounterController; import views.ACounterJOptionView; publicclass AConsoleControllerAndJOptionView implements CounterInteractor { publicvoid edit(ObservableCounter model) { CounterObserver view = new ACounterJOptionView(); model.addObserver(view); CounterController controller = new ACounterController(); controller.setModel(model); controller.processInput(); } }
Main with two views package main; import models.AnObservableCounter; import facades.AConsoleControllerAndViewAndJOptionView; publicclass ACounterMain { publicstatic void main(String[] args) { (new AConsoleControllerAndViewAndJOptionView()).edit(new AnObservableCounter()); } }
Facade over facade package facades; import models.ObservableCounter; import models.CounterObserver; import views.ACounterJOptionView; publicclass AConsoleContollerAndViewAndJOptionView implements CounterInteractor { publicvoidedit(ObservableCounter model) { model.addObserver(new ACounterJOptionView()); (new AConsoleContollerAndView()).edit(model); } }