780 likes | 1.08k Views
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?
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 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.
Implementation Constraint • Re-use as much code as possible in the three implementations
Pattern-free Implementation publicclass ConsoleUI { staticint counter = 0; publicstaticvoid main(String[] args) { while (true) { int nextInput = readInt(); if (nextInput == 0) return; counter += nextInput; System.out.println("Counter: " + counter); } } }
Pattern-free Implementation publicclass ConsoleUI { staticint counter = 0; publicstaticvoid main(String[] args) { while (true) { int nextInput = readInt(); if (nextInput == 0) return; counter += nextInput; JOptionPane.showMessageDialog(null, "Counter: " + counterValue); } } } Counter code duplicated
Model Interactor Interactor AMultipleUI AConsoleUI Counter Interactor AMixedUI Model has no UI code and only semantics! Model/Interactor Separation
Composing Model and Interactor publicstatic main (String args[]) (new AConsoleUI()).edit (new ACounter()); }
Counter Model package models; publicclass ACounter implements Counter { int counter = 0; publicvoid add (int amount) { counter += amount; } publicint getValue() { return counter; } } • Code reusability • Less duplication • Fewer changes
Shared model code Input Output Console Interactor package interactors; publicclass AConsoleUI implements ConsoleUI { publicvoid edit (Counter counter) { while (true) { int nextInput = readInt(); if (nextInput == 0) return; counter.add(nextInput); System.out.println("Counter: " + counter.getValue()); } } }
Shared model code Input Output Mixed Interactor package interactors; publicclass AConsoleUI implements ConsoleUI { publicvoid edit (Counter counter) { while (true) { int nextInput = readInt(); if (nextInput == 0) return; counter.add(nextInput); JOptionPane.showMessageDialog(null, "Counter: " + counter.getValue()); } } }
Shared model code Input Output Multiple Interactor package interactors; publicclass AConsoleUI implements ConsoleUI { publicvoid edit (Counter counter) { while (true) { int nextInput = readInt(); if (nextInput == 0) return; counter.add(nextInput); System.out.println("Counter: " + counter.getValue()); JOptionPane.showMessageDialog(null, "Counter: " + counter.getValue()); } } }
AConsoleUI AMixedUI ACounter AMultipleUI Duplicated input code Duplicated output code Drawbacks of Monolithic UI
Model/Interactor Pattern UI Code Interactor Arbitrary UI unaware methods Model Computation code
MVC Pattern Controller View Performs Output Performs Input Read methods Model Write methods
MVC Pattern in Counter Controller View Performs Output Performs Input Model getValue() add()
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 3 View 3 Controller M View N
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
MVC Pattern Controller View Performs Output Performs Input Notification method Read methods Model Write methods
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
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? package models; publicinterface ObservableCounter extends Counter { } package models; publicinterface CounterObserver { }
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; publicclass ACounterConsoleView implements CounterObserver { publicvoid update(ObservableCounter counter) { System.out.println("Counter: " + counter.getValue()); } }
Console View package views; import models.ObservableCounter; import models.CounterObserver; publicclass ACounterConsoleView implements CounterObserver { publicvoid update(ObservableCounter counter) { 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) { 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() … }
Console Main publicstatic main (String args[]) Counter model = new ACounter(); model.addObserver (new ACounterConsoleView()); CounterController controller = new ACounterController(); controller.setModel(model); controller.processInput(); }
Input Code Shared Console and JOption Main publicstatic main (String args[]) Counter model = new ACounter(); model.addObserver (new ACounterJOptionView()); CounterController controller = new ACounterController(); controller.setModel(model); controller.processInput(); }
Console Main publicstatic main (String args[]) Counter model = new ACounter(); model.addObserver (new ACounterConsoleView()); CounterController controller = new ACounterController(); controller.setModel(model); controller.processInput(); }
Composition code duplicated Mixed UI Main publicstatic main (String args[]) Counter model = new ACounter(); model.addObserver(new AConsoleJOptionView()); model.addObserver (new ACounterConsoleView()); CounterController controller = new ACounterController(); controller.setModel(model); controller.processInput(); }
Facade Façade Pattern Interactor Controller View Notification method Read methods Model Write methods
Facade pattern Facade Component1 Component2 ComponentN
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 controller and view. • Makes view observer of model • Connects controller to model. • Starts controller.
Must be last action ConsoleControllerAndView Facade package interactors; 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()); } }
AConsoleControllerAndView Console View + Controller AnObservable Counter ACounterJOptionView ACounterConsole View ACounterController
AConsoleControllerAndJView Console Controller + JOptionView AnObservable Counter ACounterJOptionView ACounterConsole View ACounterController