QUESTIONS LAST TIME: - I don't know. MVC, back there somewhere.. TODAY: - Eew, substitute - Design patterns: MVC, Double dispatching HOMEWORK: - I don't know. Final project.. NEXT TIME: - Back to your regularly scheduled program DEFINITION OF THE DAY PERFECT PROGRAMMER SYNDROME n. Arrogance; the egotistical conviction that one is above normal human error. Most frequently found among programmers of some native ability but relatively little experience (especially new graduates; their perceptions may be distorted by a history of excellent performance at solving toy problems). "Of course my program is correct, there is no need to test it." "Yes, I can see there may be a problem here, but I'LL never type "rm -rf /' while root." DESIGN PATTERNS - Based on Gamma, Helm, Johnson, Vlissides (1995) : Design Patterns: Elements of Reusable Object-Oriented Software >From pg 1-3: --- Designing object-oriented software is hard, and designing *reusable* object-oriented software is even harder. You must find pertinent objects.. define class interfaces and inheritance hierarchies... When [expert designers] find a good solution they use it again and again... Consequently, you'll find recurring patterns of classes and communicating objects in many object-oriented systems... A designer who is familiar with such patterns can apply them immediately to design problems without having to rediscover them... Once you know a pattern, a lot of design decisions follow automatically. DESIGN PATTERNS - Teaching design patterns vs preaching to the choir -> Patterns for stuff you already know tend to make sense -> Patterns for novel issues often don't, at least at first. DESIGN PATTERNS - Teaching design patterns vs preaching to the choir -> Patterns for stuff you already know tend to make sense -> Patterns for novel issues often don't, at least at first. - E.g., basic stuff like the idea of delegation class Foo { public int func() { .. } } class Bar { public int func() { return f.func(); } private Foo f; } 'Bar delegates Func to Foo'. = Delegation relates classes together without inheritance. (Attack/Defend) DESIGN PATTERNS - Teaching design patterns vs preaching to the choir -> Patterns for stuff you already know tend to make sense -> Patterns for novel issues often don't, at least at first. - E.g., basic stuff like the idea of delegation class Foo { public int func() { .. } } class Bar { public int func() { return f.func(); } private Foo f; } 'Bar delegates Func to Foo'. = Delegation relates classes together without inheritance. (Attack/Defend) - Patterns at different levels of architecture and design => 'Low end', close-to-the-implementation patterns, like 'delegation' => 'Mid range' patterns like 'Model-View-Controller' => 'High end' architectures like .. ? DESIGN PATTERNS - Teaching design patterns vs preaching to the choir -> Patterns for stuff you already know tend to make sense -> Patterns for novel issues often don't, at least at first. - E.g., basic stuff like the idea of delegation class Foo { public int func() { .. } } class Bar { public int func() { return f.func(); } private Foo f; } 'Bar delegates Func to Foo'. = Delegation relates classes together without inheritance. (Attack/Defend) - Patterns at different levels of architecture and design => 'Low end', close-to-the-implementation patterns, like 'delegation' => 'Mid range' patterns like 'Model-View-Controller' => 'High end' architectures like .. ? Proprietary lock-in & break out? DESIGN PATTERNS - PREACHING TO THE CONVERTED - MODEL-VIEW-CONTROLLER DESIGN PATTERNS - MODEL-VIEW-CONTROLLER - Breaking up monolithic interactive applications - Developed by Smalltalk people at PARC DESIGN PATTERNS - MODEL-VIEW-CONTROLLER - Breaking up monolithic interactive applications - Developed by Smalltalk people at PARC - Components: = MODEL: The 'guts' of the program, that actually represents (or models) something and performs processing = VIEW: A component that gets/takes data from the MODEL and presents it in some fashion (e.g., on a display) = CONTROLLER: A component that injects commands into the MODEL, causing it to do something or change in some way. DESIGN PATTERNS - MODEL-VIEW-CONTROLLER - Breaking up monolithic interactive applications - Developed by Smalltalk people at PARC - Components: = MODEL: The 'guts' of the program, that actually represents (or models) something and performs processing = VIEW: A component that gets/takes data from the MODEL and presents it in some fashion (e.g., on a display) = CONTROLLER: A component that injects commands into the MODEL, causing it to do something or change in some way. - VIEWs and CONTROLLERs are sometimes hard to separate = Sometimes done as two-part design, Model & View/control DESIGN PATTERNS - MODEL-VIEW-CONTROLLER - Breaking up monolithic interactive applications - Developed by Smalltalk people at PARC - Components: = MODEL: The 'guts' of the program, that actually represents (or models) something and performs processing = VIEW: A component that gets/takes data from the MODEL and presents it in some fashion (e.g., on a display) = CONTROLLER: A component that injects commands into the MODEL, causing it to do something or change in some way. - VIEWs and CONTROLLERs are sometimes hard to separate = Sometimes done as two-part design, Model & View/control - Lots of variations! It's a design PATTERN! It's MEANT to be VAGUE about the details! DESIGN PATTERNS - MODEL-VIEW-CONTROLLER - UML +-----------------+ | Model | |-----------------| | | |-----------------| | | | | | | | | | | | | +-----------------+ DESIGN PATTERNS - MODEL-VIEW-CONTROLLER +-----------------+ +-----------------+ | Model | | View | |-----------------| |-----------------| | | | | |-----------------| | | | | |-----------------| | | | | | | | | | | | | | | +-----------------+ | | +-----------------+ DESIGN PATTERNS - MODEL-VIEW-CONTROLLER +-----------------+ +-----------------+ | Model | | View | |-----------------| |-----------------| | | | | |-----------------|<---------->| | | | |-----------------| | | | | | | | | | | | | | | +-----------------+ | | +-----------------+ DESIGN PATTERNS - MODEL-VIEW-CONTROLLER +-----------------+ +-----------------+ | Model | | View | |-----------------| |-----------------| | |1 *| | |-----------------|<---------->| | | | |-----------------| | | | | | | | | | | | | | | +-----------------+ | | +-----------------+ DESIGN PATTERNS - MODEL-VIEW-CONTROLLER +-----------------+ +-----------------+ | Model | | View | |-----------------| |-----------------| | |1 *| | |-----------------|<---------->| | |register(View) | |-----------------| |unregister(View) | | | | | | | | | | | | | +-----------------+ | | +-----------------+ DESIGN PATTERNS - MODEL-VIEW-CONTROLLER +-----------------+ +-----------------+ | Model | | View | |-----------------| |-----------------| | |1 *| | |-----------------|<---------->| | |register(View) | |-----------------| |unregister(View) | |abstract | |notify(Event) | | notify(Event) | | | | | | | +-----------------+ | | +-----------------+ DESIGN PATTERNS - MODEL-VIEW-CONTROLLER +-----------------+ +-----------------+ | Model | | View | |-----------------| |-----------------| | |1 *| | |-----------------|<---------->| | |register(View) | |-----------------| |unregister(View) | |abstract | |notify(Event) | | notify(Event) | | o | | | | . | +-----------------+ | . | +--------.--------+ +-------------------------\ . . . . . . |foreach View v in Model |\ | v.notify(Event) |-\ +---------------------------+ DESIGN PATTERNS - MODEL-VIEW-CONTROLLER +-----------------+ +-----------------+ | Model | | View | |-----------------| |-----------------| | |1 *| | |-----------------|<---------->| | |register(View) | |-----------------| |unregister(View) | |abstract | |notify(Event) | | notify(Event) | | | | | | | +-----------------+ | | +-----------------+ DESIGN PATTERNS - MODEL-VIEW-CONTROLLER +-----------------+ +-----------------+ | Model | | View | |-----------------| |-----------------| | |1 *| | |-----------------|<---------->| | |register(View) | |-----------------| |unregister(View) | |abstract | |notify(Event) | | notify(Event) | | | | | | | +-----------------+ | | ^ +-----------------+ / \ --- |----------------| +--------------+ +------ | DebugView | |OtherV |--------------| |------ |--------------| |------ |notify(Event e) | | System.err. | | | println(e); | | +--------------+ +------ DESIGN PATTERNS - MODEL-VIEW-CONTROLLER +-----------------+ +-----------------+ | Model | | View | |-----------------| |-----------------| | |1 *| | |-----------------|<---------->| | |register(View) | |-----------------| |unregister(View) | 1 |abstract | |notify(Event) |<-\ | notify(Event) | | | \ | | | | \ +-----------------+ | | | ^ +-----------------+ v / \ +-------------+ --- | Controller | |----------------| |-------------| +--------------+ +------ | | | DebugView | |OtherV |-------------| |--------------| |------ | | |--------------| |------ | | |notify(Event e) | | | | System.err. | | +-------------+ | println(e); | | +--------------+ +------ DESIGN PATTERNS - MODEL-VIEW-CONTROLLER +-----------------+ +-----------------+ | Model | | View | |-----------------| |-----------------| | |1 *| | |-----------------|<---------->| | |register(View) | |-----------------| |unregister(View) | 1 |abstract | |notify(Event) |<-\ | notify(Event) | | | \ | | | | \ +-----------------+ | | |1? ^ +-----------------+ v / \ +-------------+ --- | Controller | |----------------| |-------------| +--------------+ +------ | | | DebugView | |OtherV |-------------| |--------------| |------ | | |--------------| |------ | | |notify(Event e) | | | | System.err. | | +-------------+ | println(e); | | +--------------+ +------ DESIGN PATTERNS - MODEL-VIEW-CONTROLLER +-----------------+ +-----------------+ | Model | | View | |-----------------| |-----------------| | |1 *| | |-----------------|<---------->| | |register(View) | |-----------------| |unregister(View) | 1 |abstract | |notify(Event) |<-\ | notify(Event) | | | \ | | | | \ +-----------------+ | | |1? ^ +-----------------+ v / \ +-------------+ --- | Controller | |----------------| |-------------| +--------------+ +------ |-------------| | DebugView | |OtherV |somehow makes| |--------------| |------ |Commands and | |--------------| |------ |calls perform| |notify(Event e) | |on the Model | | System.err. | | +-------------+ | println(e); | | +--------------+ +------ DESIGN PATTERNS - MODEL-VIEW-CONTROLLER +-----------------+ +-----------------+ | Model | | View | |-----------------| |-----------------| | |1 *| | |-----------------|<---------->| | |register(View) | |-----------------| |unregister(View) | 1 |abstract | |notify(Event) |<-\ | notify(Event) | |abstract | \ | | | perform(Command)| \ +-----------------+ | | |1? ^ +-----------------+ v / \ +-------------+ --- | Controller | |----------------| |-------------| +--------------+ +------ |-------------| | DebugView | |OtherV |somehow makes| |--------------| |------ |Commands and | |--------------| |------ |calls perform| |notify(Event e) | |on the Model | | System.err. | | +-------------+ | println(e); | | +--------------+ +------ DESIGN PATTERNS - MODEL-VIEW-CONTROLLER +-----------------+ +-----------------+ | Model | | View | |-----------------| |-----------------| | |1 *| | |-----------------|<---------->| | |register(View) | |-----------------| |unregister(View) | 1 |abstract | |notify(Event) |<-\ | notify(Event) | |abstract | \ | | | perform(Command)| \ +-----------------+ | | |1? ^ +-----------------+ v / \ ^ +-------------+ --- / \ | Controller | |----------------| --- |-------------| +--------------+ +------ | |-------------| | DebugView | |OtherV ----------+ |somehow makes| |--------------| |------ uperModel | |Commands and | |--------------| |------ ----------| |calls perform| |notify(Event e) | ----------| |on the Model | | System.err. | | perform(Co| +-------------+ | println(e); | | mmand).. | +--------------+ +------ DESIGN PATTERNS - MODEL-VIEW-CONTROLLER +-----------------+ +-----------------+ | Model | | View | |-----------------| |-----------------| | |1 *| | |-----------------|<---------->| | |register(View) | |-----------------| |unregister(View) | 1 |abstract | |notify(Event) |<-\ | notify(Event) | |abstract | \ | | | perform(Command)| \ +-----------------+ | | |1? ^ +-----------------+ v / \ ^ +-------------+ --- / \ /----| Controller | |----------------| --- ^ |-------------| +--------------+ +------ | / \ |-------------| | DebugView | |OtherV ----------+ --- |somehow makes| |--------------| |------ uperModel | | |Commands and | |--------------| |------ ----------|+-------+|calls perform| |notify(Event e) | ----------||MyContr||on the Model | | System.err. | | perform(Co||oller |+-------------+ | println(e); | | mmand).. ||-------| +--------------+ +------ DESIGN PATTERNS - MODEL-VIEW-CONTROLLER +-----------------+ +-----------------+ | Model | | View | |-----------------| |-----------------| | |1 *| | |-----------------|<---------->| | |register(View) | |-----------------| |unregister(View) | 1 |abstract | |notify(Event) |<-\ | notify(Event) | |abstract | \ | | | perform(Command)| \ +-----------------+ | | |1? ^ ^ +-----------------+ v |? / \ ^ +-------------+ | --- / \ /----| Controller | | |----------------| --- ^ |-------------| | +--------------+ +------ | / \ |-------------| | | DebugView | |OtherV ----------+ --- |somehow makes|<--/ |--------------| |------ uperModel | | |Commands and | ? |--------------| |------ ----------|+-------+|calls perform| |notify(Event e) | ----------||MyContr||on the Model | | System.err. | | perform(Co||oller |+-------------+ | println(e); | | mmand).. ||-------| +--------------+ +------ DESIGN PATTERNS - MODEL-VIEW-CONTROLLER + Decouples input from processing from display DESIGN PATTERNS - MODEL-VIEW-CONTROLLER + Decouples input from processing from display + Supports multiple views, heterogeneous views, can add views later (ditto controllers) DESIGN PATTERNS - MODEL-VIEW-CONTROLLER + Decouples input from processing from display + Supports multiple views, heterogeneous views, can add views later (ditto controllers) - Extra hair compared to simpleminded monolithic design: if (temp > 1200) { // Uh oh, time to KYAG.. System.err.println("Alert: Meltdown imminent, core temp="+temp); } // Thar, that'll fix it vs if (temp > 1200) { // Uh oh, time to KYAG.. EventAlertMeltdown ev = new EventAlertMeltdown(temp); notify(ev); // Notify all registered Views } DESIGN PATTERNS - MODEL-VIEW-CONTROLLER + Decouples input from processing from display + Supports multiple views, heterogeneous views, can add views later (ditto controllers) - Extra hair compared to simpleminded monolithic design: if (temp > 1200) { // Uh oh, time to KYAG.. System.err.println("Alert: Meltdown imminent, core temp="+temp); } // Thar, that'll fix it vs if (temp > 1200) { // Uh oh, time to KYAG.. EventAlertMeltdown ev = new EventAlertMeltdown(temp); notify(ev); // Notify all registered Views } + VASTLY more scalable and maintainable than monolithic code DESIGN PATTERNS - MODEL-VIEW-CONTROLLER + Decouples input from processing from display + Supports multiple views, heterogeneous views, can add views later (ditto controllers) - Extra hair compared to simpleminded monolithic design: if (temp > 1200) { // Uh oh, time to KYAG.. System.err.println("Alert: Meltdown imminent, core temp="+temp); } // Thar, that'll fix it vs if (temp > 1200) { // Uh oh, time to KYAG.. EventAlertMeltdown ev = new EventAlertMeltdown(temp); notify(ev); // Notify all registered Views } + VASTLY more scalable and maintainable than monolithic code ? Often view and controller are closely coupled -- eg checkbutton how to represent that? ? If views can be controllers, can notify()s lead to more commands? Doesn't that get complicated? DESIGN PATTERNS - MODEL-VIEW-CONTROLLER + Decouples input from processing from display + Supports multiple views, heterogeneous views, can add views later (ditto controllers) - Extra hair compared to simpleminded monolithic design: if (temp > 1200) { // Uh oh, time to KYAG.. System.err.println("Alert: Meltdown imminent, core temp="+temp); } // Thar, that'll fix it vs if (temp > 1200) { // Uh oh, time to KYAG.. EventAlertMeltdown ev = new EventAlertMeltdown(temp); notify(ev); // Notify all registered Views } + VASTLY more scalable and maintainable than monolithic code ? Often view and controller are closely coupled -- eg checkbutton how to represent that? ? If views can be controllers, can notify()s lead to more commands? Doesn't that get complicated? ..It Can.. MVC SAMPLE CODE - Lots and lots of variations: M-V-C, M-VC, etc etc MVC SAMPLE CODE - Lots and lots of variations: M-V-C, M-VC, etc etc - Abstract base class Model - the thing that does something, that perform()s Command's and generates Event's - Abstract base class View - a thing that gets notify()d by a Model when an Event occurs - Abstract base class Controller - a thing that creates Commands and tells a Model to perform() them MVC SAMPLE CODE - Lots and lots of variations: M-V-C, M-VC, etc etc - Abstract base class Model - the thing that does something, that perform()s Command's and generates Event's - Abstract base class View - a thing that gets notify()d by a Model when an Event occurs - Abstract base class Controller - a thing that creates Commands and tells a Model to perform() them - Abstract base class Command - a thing that a Controller uses to tells a Model what to perform() - Abstract base class Event - a thing that a Model uses to notify() a View about something that happened MVC SAMPLE CODE - Lots and lots of variations: M-V-C, M-VC, etc etc - Abstract base class Model - the thing that does something, that perform()s Command's and generates Event's - Abstract base class View - a thing that gets notify()d by a Model when an Event occurs - Abstract base class Controller - a thing that creates Commands and tells a Model to perform() them - Abstract base class Command - a thing that a Controller uses to tells a Model what to perform() - Abstract base class Event - a thing that a Model uses to notify() a View about something that happened - Let's make one.. MVC SAMPLE CODE - FRAMEWORK - Command, Event, Model import java.util.HashSet; import java.util.Iterator; import java.util.Set; // Message from Controller to Model abstract class Command { abstract public String toString(); } // Message from Model to View abstract class Event { abstract public String toString(); } MVC SAMPLE CODE - FRAMEWORK - Command, Event, Model import java.util.HashSet; import java.util.Iterator; import java.util.Set; // Message from Controller to Model abstract class Command { abstract public String toString(); } // Message from Model to View abstract class Event { abstract public String toString(); } abstract class Model { // Abstract base class for models private Set views = new HashSet(); abstract public void perform(Command c) ; // Make model behave public void register(View v) { views.add(v); } public void unregister(View v) { views.remove(v); } public void notify(Event e) { for (Iterator i = views.iterator(); i.hasNext(); ) { View v = (View) i.next(); v.notify(e); } } } MVC SAMPLE CODE - FRAMEWORK - View, Controller abstract class View { // Something that can be notify'd of events private Model model; public View(Model model) { this.model = model; } abstract public void notify(Event e); Model getModel() { return model; } } abstract class Controller { // Something that calls Model.perform(Command) private Model model; public Controller(Model model) { this.model = model; } public Model getModel() { return model; } } MVC SAMPLE CODE - INSTANTIATION - So, to use the framework, we subclass everything = Extend Model to make our model that actually can perform something = Extend Command to make some things our model will perform = Extend Controller to something that injects our Commands into our Model = Extend Event to make some things our Views will view = Extend View to make some things that can display our Events - Goals: = 'Nothing' in the base M-V-C classes should change = Our extensions should be as natural and compact as possible = Further extensions down the road should be natural and compact as well MVC SAMPLE CODE - INSTANTIATION - Command & Event subclasses // Command the model to do something class StepCommand extends Command { public String toString() { return "Step"; } } // Command the model to do something else class HopCommand extends Command { public String toString() { return "Hop"; } } // Only thing happening in my stupid model is a number gets bigger.. class CountEvent extends Event { private int count; public CountEvent(int num) { count = num; } public String toString() { return "Count "+count; } public int getCount() { return count; } } MVC SAMPLE CODE - INSTANTIATION - The model class MyModel extends Model { private int state = 0; public void perform(Command c) { if (c instanceof StepCommand) ++state; else if (c instanceof HopCommand) state *= 2; else throw new IllegalArgumentException("Unhandled: "+c); notify(new CountEvent(state)); } } MVC SAMPLE CODE - INSTANTIATION - Some views class DebugView extends View { public DebugView(Model m) { super(m); } public void notify(Event e) { System.err.println("DEBUG: "+e); } } class GUIView extends View { private JLabel jl; public GUIView(Model m) { super(m); JFrame jf = new JFrame("GUIView"); jl = new JLabel("Count unknown"); jf.getContentPane().add(jl); jf.pack(); jf.setVisible(true); } public void notify(Event e) { if (e instanceof CountEvent) { CountEvent ec = (CountEvent) e; jl.setText("Count: "+ec.getCount()); } } } MVC SAMPLE CODE - INSTANTIATION - A controller class GUIController extends Controller { public GUIController(Model m) { super(m); JFrame jf = new JFrame("GUIView"); JButton bs = new JButton("Step"); JButton bh = new JButton("Hop"); bs.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { getModel().perform(new StepCommand()); }}); bh.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { getModel().perform(new HopCommand()); }}); jf.getContentPane().setLayout(new FlowLayout()); jf.getContentPane().add(bs); jf.getContentPane().add(bh); jf.pack(); jf.setVisible(true); } } MVC SAMPLE CODE - INSTANTIATION - An application class Test { public static void main(String[] args) { Model m = new MyModel(); Controller c = new GUIController(m); m.register(new DebugView(m)); m.register(new GUIView(m)); } } MVC SAMPLE CODE - INSTANTIATION - An application class Test { public static void main(String[] args) { Model m = new MyModel(); Controller c = new GUIController(m); m.register(new DebugView(m)); m.register(new GUIView(m)); } } - OK, so we made a framework and instantiated it. Where's the win supposed to be? = Scalability, extensibility, maintainability MVC SAMPLE CODE - INSTANTIATION - An application class Test { public static void main(String[] args) { Model m = new MyModel(); Controller c = new GUIController(m); m.register(new DebugView(m)); m.register(new GUIView(m)); } } - OK, so we made a framework and instantiated it. Where's the win supposed to be? = Scalability, extensibility, maintainability = So let's add a command.. MVC SAMPLE CODE - INSTANTIATION - EXTENSIBILITY/MAINTAINABILITY - Add a new command to clear the counter MVC SAMPLE CODE - INSTANTIATION - EXTENSIBILITY/MAINTAINABILITY - Add a new command to clear the counter 1. Command: Define it class ClearCommand extends Command { public String toString() { return "Clear"; } } 2. Event: No new event needed? Depends on design and intended semantics of CountEvent: 'Count up'? MVC SAMPLE CODE - INSTANTIATION - EXTENSIBILITY/MAINTAINABILITY - Add a new command to clear the counter 1. Command: Define it class ClearCommand extends Command { public String toString() { return "Clear"; } } 2. Event: No new event needed? Depends on design and intended semantics of CountEvent: 'Count up'? 3. Model: Handle new command class MyModel extends Model { private int state = 0; public void perform(Command c) { if (c instanceof StepCommand) ++state; else if (c instanceof HopCommand) state *= 2; else throw new IllegalArgumentException("Unhandled: "+c); notify(new CountEvent(state)); } } MVC SAMPLE CODE - INSTANTIATION - EXTENSIBILITY/MAINTAINABILITY - Add a new command to clear the counter 1. Command: Define it class ClearCommand extends Command { public String toString() { return "Clear"; } } 2. Event: No new event needed? Depends on design and intended semantics of CountEvent: 'Count up'? 3. Model: Handle new command class MyModel extends Model { private int state = 0; public void perform(Command c) { if (c instanceof StepCommand) ++state; else if (c instanceof HopCommand) state *= 2; else if (c instanceof ClearCommand) state = 0; else throw new IllegalArgumentException("Unhandled: "+c); notify(new CountEvent(state)); } } MVC SAMPLE CODE - INSTANTIATION - EXTENSIBILITY/MAINTAINABILITY - Add a new command to clear the counter 1. Command: Define it class ClearCommand extends Command { public String toString() { return "Clear"; } } 2. Event: No new event needed? Depends on design and intended semantics of CountEvent: 'Count up'? 3. Model: Handle new command class MyModel extends Model { private int state = 0; public void perform(Command c) { if (c instanceof StepCommand) ++state; else if (c instanceof HopCommand) state *= 2; else if (c instanceof ClearCommand) state = 0; else throw new IllegalArgumentException("Unhandled: "+c); notify(new CountEvent(state)); } } 4. View: No change (unless new ClearEvent or something) 5. Controller: JButton bc=new JButton("Clear"); etc.. MVC SAMPLE CODE - INSTANTIATION - EXTENSIBILITY/MAINTAINABILITY - Add a new command to clear the counter 1. Command: Define it CLEAN: ISOLATED CHANGE class ClearCommand extends Command { public String toString() { return "Clear"; } } 2. Event: No new event needed? CLEAN: NO CHANGE Depends on design and intended semantics of CountEvent: 'Count up'? 3. Model: Handle new command class MyModel extends Model { private int state = 0; NOT VERY CLEAN - Had public void perform(Command c) { to hack inside an if (c instanceof StepCommand) ++state; existing method else if (c instanceof HopCommand) state *= 2; else if (c instanceof ClearCommand) state = 0; else throw new IllegalArgumentException("Unhandled: "+c); notify(new CountEvent(state)); } } 4. View: No change (unless new ClearEvent or something) CLEAN: NO CHANGE 5. Controller: JButton bc=new JButton("Clear"); etc.. CLEAN: ISOLATED CHANGE MVC SAMPLE CODE - INSTANTIATION - EXTENSIBILITY/MAINTAINABILITY - UPSHOTS - Did well in some respects; most needed changes were natural and compact - 'if/else if/else if' in MyModel.perform is ugly (Though at least it dies if we forget to handle a new command) - Would like a more robust way of dispatching for different commands.. class MyModel extends Model { private int state = 0; NOT VERY CLEAN - Had public void perform(Command c) { to hack inside an if (c instanceof StepCommand) ++state; existing method else if (c instanceof HopCommand) state *= 2; else if (c instanceof ClearCommand) state = 0; else throw new IllegalArgumentException("Unhandled: "+c); notify(new CountEvent(state)); } } MVC SAMPLE CODE - INSTANTIATION - EXTENSIBILITY/MAINTAINABILITY - UPSHOTS - Did well in some respects; most needed changes were natural and compact - 'if/else if/else if' in MyModel.perform is ugly (Though at least it dies if we forget to handle a new command) - Would like a more robust way of dispatching for different commands.. abstract class MyCommand extends Command { abstract public int newState(int currentState) ; } MVC SAMPLE CODE - INSTANTIATION - EXTENSIBILITY/MAINTAINABILITY - UPSHOTS - Did well in some respects; most needed changes were natural and compact - 'if/else if/else if' in MyModel.perform is ugly (Though at least it dies if we forget to handle a new command) - Would like a more robust way of dispatching for different commands.. abstract class MyCommand extends Command { abstract public int newState(int currentState) ; } class StepCommand extends MyCommand { public int newState(int currentState) { return currentState+1; } .. } .. MVC SAMPLE CODE - INSTANTIATION - EXTENSIBILITY/MAINTAINABILITY - UPSHOTS - Did well in some respects; most needed changes were natural and compact - 'if/else if/else if' in MyModel.perform is ugly (Though at least it dies if we forget to handle a new command) - Would like a more robust way of dispatching for different commands.. abstract class MyCommand extends Command { abstract public int newState(int currentState) ; } class StepCommand extends MyCommand { public int newState(int currentState) { return currentState+1; } .. } .. class MyModel extends Model { private int state = 0; public void perform(Command c) { if (!(c instanceof MyCommand)) throw new IllegalArgumentException(); MyCommand mc = (MyCommand) c; state = mc.newState(state); notify(new CountEvent(state)); } } MVC SAMPLE CODE - INSTANTIATION - EXTENSIBILITY/MAINTAINABILITY - UPSHOTS - Did well in some respects; most needed changes were natural and compact - 'if/else if/else if' in MyModel.perform is ugly (Though at least it dies if we forget to handle a new command) - Would like a more robust way of dispatching for different commands.. abstract class MyCommand extends Command { abstract public int newState(int currentState) ; } class StepCommand extends MyCommand { public int newState(int currentState) { return currentState+1; } .. } .. class MyModel extends Model { private int state = 0; public void perform(Command c) { if (!(c instanceof MyCommand)) throw new IllegalArgumentException(); MyCommand mc = (MyCommand) c; state = mc.newState(state); notify(new CountEvent(state)); } } Defend & Attack DOUBLE DISPATCHING - THE PROBLEM - Wasn't it gross, when we added ClearCommand, and we had to go grovel inside of MyModel.perform(Command)? Wasn't that just too gross? - Problem was: = We have subclasses of two different classes: MyModel extends Model, and ClearCommand extends Command and we basically want the ability to do something different in each _combination_ of cases. class MyModel extends Model { private int state = 0; public void perform(Command c) { if (c instanceof StepCommand) ++state; else if (c instanceof HopCommand) state *= 2; else if (c instanceof ClearCommand) state = 0; else throw new IllegalArgumentException("Unhandled: "+c); notify(new CountEvent(state)); } } DOUBLE DISPATCHING - THE PROBLEM - Wasn't it gross, when we added ClearCommand, and we had to go grovel inside of MyModel.perform(Command)? Wasn't that just too gross? - Problem was: = We have subclasses of two different classes: MyModel extends Model, and ClearCommand extends Command and we basically want the ability to do something different in each _combination_ of cases. class MyModel extends Model { private int state = 0; public void perform(Command c) { if (!(c instanceof MyCommand)) throw new IllegalArgumentException(); MyCommand mc = (MyCommand) c; state = mc.newState(state); notify(new CountEvent(state)); } } DOUBLE DISPATCHING - THE PROBLEM - Wasn't it gross, when we added ClearCommand, and we had to go grovel inside of MyModel.perform(Command)? Wasn't that just too gross? - Problem was: = We have subclasses of two different classes: MyModel extends Model, and ClearCommand extends Command and we basically want the ability to do something different in each _combination_ of cases. = Or: Varied ship types getting hit with varied weapon types... abstract class Ship { .. } abstract class Weapon { .. } DOUBLE DISPATCHING - THE PROBLEM - Wasn't it gross, when we added ClearCommand, and we had to go grovel inside of MyModel.perform(Command)? Wasn't that just too gross? - Problem was: = We have subclasses of two different classes: MyModel extends Model, and ClearCommand extends Command and we basically want the ability to do something different in each _combination_ of cases. = Or: Varied ship types getting hit with varied weapon types... abstract class Ship { .. } abstract class Weapon { .. } class Scout extends Ship { .. } class Rock extends Weapon { .. } class DeathStar extends Ship { .. } class SonicMine extends Weapon { .. } DOUBLE DISPATCHING - THE PROBLEM - Wasn't it gross, when we added ClearCommand, and we had to go grovel inside of MyModel.perform(Command)? Wasn't that just too gross? - Problem was: = We have subclasses of two different classes: MyModel extends Model, and ClearCommand extends Command and we basically want the ability to do something different in each _combination_ of cases. = Or: Varied ship types getting hit with varied weapon types... abstract class Ship { .. } abstract class Weapon { .. } class Scout extends Ship { .. } class Rock extends Weapon { .. } class DeathStar extends Ship { .. } class SonicMine extends Weapon { .. } class SpaceWar { ..if intersection detected between Ship s and Weapon w, would like: s.hitBy(w) DOUBLE DISPATCHING - THE PROBLEM - Wasn't it gross, when we added ClearCommand, and we had to go grovel inside of MyModel.perform(Command)? Wasn't that just too gross? - Problem was: = We have subclasses of two different classes: MyModel extends Model, and ClearCommand extends Command and we basically want the ability to do something different in each _combination_ of cases. = Or: Varied ship types getting hit with varied weapon types... abstract class Ship { .. abstract void hitBy(Weapon m) ; } abstract class Weapon { .. } class Scout extends Ship { .. } class Rock extends Weapon { .. } class DeathStar extends Ship { .. } class SonicMine extends Weapon { .. } class SpaceWar { ..if intersection detected between Ship s and Weapon w, would like: s.hitBy(w) DOUBLE DISPATCHING - THE PROBLEM - Wasn't it gross, when we added ClearCommand, and we had to go grovel inside of MyModel.perform(Command)? Wasn't that just too gross? - Problem was: = We have subclasses of two different classes: MyModel extends Model, and ClearCommand extends Command and we basically want the ability to do something different in each _combination_ of cases. = Or: Varied ship types getting hit with varied weapon types... abstract class Ship { .. } abstract class Weapon { .. abstract void hits(Ship s) ; } class Scout extends Ship { .. } class Rock extends Weapon { .. } class DeathStar extends Ship { .. } class SonicMine extends Weapon { .. } class SpaceWar { ..if intersection detected between Ship s and Weapon w, would like: s.hitBy(w) or w.hits(s) DOUBLE DISPATCHING - THE PROBLEM - Wasn't it gross, when we added ClearCommand, and we had to go grovel inside of MyModel.perform(Command)? Wasn't that just too gross? - Problem was: = We have subclasses of two different classes: MyModel extends Model, and ClearCommand extends Command and we basically want the ability to do something different in each _combination_ of cases. = Or: Varied ship types getting hit with varied weapon types... abstract class Ship { .. } abstract class Weapon { .. } class Scout extends Ship { .. } class Rock extends Weapon { .. } class DeathStar extends Ship { .. } class SonicMine extends Weapon { .. } class SpaceWar { ..if intersection detected between Ship s and Weapon w, would like: s.hitBy(w) or w.hits(s) or interact(s,w) -- but where would we put it? In SpaceWar? DOUBLE DISPATCHING - THE PROBLEM abstract class Ship { .. } abstract class Weapon { .. } class Scout extends Ship { .. } class Rock extends Weapon { .. } class DeathStar extends Ship { .. } class SonicMine extends Weapon { .. } class SpaceWar { ..if intersection detected between Ship s and Weapon w, would like: s.hitBy(w) or w.hits(s) or interact(s,w) But any way you slice it.. class Scout extends Ship { .. void hitBy(Rock r) { hitPoints--; } void hitBy(SonicMine sm) { hitPoints -= 1< HitResolver public static void add(Class ship, Class weapon, HitResolver hr) { registry.put(new Key(ship,weapon), hr); } public static void resolve(Ship s,Weapon w) { HitResolver hr = (HitResolver) registry.get(new Key(s.getClass(),w.getClass())); if (hr == null) throw new IllegalStateException("miss: "+s+" "+w); hr.interact(s,w); } } DOUBLE DISPATCHING - RUNTIME REGISTRATION class Test { static { Registry.add(Scout.class,Rock.class,new HitResolver() { public void interact(Ship s, Weapon w) { System.out.println("SR"); } }); Registry.add(Scout.class,SonicMine.class,new HitResolver() { public void interact(Ship s, Weapon w) { System.out.println("SS"); } }); } public static void main(String[] args) { Ship s = new Scout(); Weapon w = new SonicMine(); Registry.resolve(s,w); } } $ java Test SS $ DOUBLE DISPATCHING - RUNTIME REGISTRATION - UPSHOTS - REFLECTION - Reflection is head-exploding stuff = There is an object of type 'Class' for each class defined in a program DOUBLE DISPATCHING - RUNTIME REGISTRATION - UPSHOTS - REFLECTION - Reflection is head-exploding stuff. = There is an object of type 'Class' for each class defined in a program = Several ways to get at Class objects - Given a class name, the .class 'field' gets a Class object ref class Test { public static void main(String[] args) { System.out.println(Test.class); System.out.println(System.class); } } DOUBLE DISPATCHING - RUNTIME REGISTRATION - UPSHOTS - REFLECTION - Reflection is head-exploding stuff. = There is an object of type 'Class' for each class defined in a program = Several ways to get at Class objects - Given a class name, the .class 'field' gets a Class object ref class Test { public static void main(String[] args) { System.out.println(Test.class); System.out.println(System.class); } } - Given an instance of a class, the getClass() method gets a Class object class Test { public static void main(String[] args) { System.out.println((new Test()).getClass()); System.out.println(System.out.getClass()); } } DOUBLE DISPATCHING - RUNTIME REGISTRATION - UPSHOTS - REFLECTION - Reflection is head-exploding stuff. = There is an object of type 'Class' for each class defined in a program = Several ways to get at Class objects - Given a class name, the .class 'field' gets a Class object ref class Test { public static void main(String[] args) { System.out.println(Test.class); System.out.println(System.class); } } - Given an instance of a class, the getClass() method gets a Class object class Test { public static void main(String[] args) { System.out.println((new Test()).getClass()); System.out.println(System.out.getClass()); } } - The forName method looks up classes by name class Test { public static void main(String[] args) throws Exception { // Gah! :( Class c = Class.forName(args[0]); // Find the class Object o = c.newInstance(); // MAKE ONE of that class! System.out.println(o); } } = Yow! DOUBLE DISPATCHING - RUNTIME REGISTRATION - UPSHOTS - Java (like C++, etc) only supports 'single dispatching' - Some languages (like CLOS, Dylan, ..) directly support 'multiple dispatch' - Double dispatching is a special case of multiple dispatch - Using reflection, can add tremendous runtime flexibility to Java = But are sacrificing compile time safety checks and efficiencies in the process - Double dispatching by runtime registration: = Make a table of type-pairs -> code to run = Look up the object types at invocation time, find the code to run, and run it