Tooled Composite Tooled Composite Design PatternDesign Pattern
Andy BulkaAusthink Software
By GOF authorBy GOF authorJohn VlissidesJohn Vlissides
http://www.research.ibm.com/designpatterns/pubs/ph-sep99.pdf
The ProblemThe Problem
““direct manipulation” direct manipulation” metaphor.metaphor.
How do you represent shapes? How do you represent tools? How do tools and shapes interact? How do you enhance the editor with
new shapes and tools?
Representing shapesRepresenting shapes
COMPOSITE – design pattern
Representing “Tools”
usually a palette of tools editor’s behavior changes with the
current tool E.g. when drawing tool is active we create shapes;
E.g. when the selection tool is active we select shapes
Representing “Tools”
STATE – design pattern
How tools and shapes How tools and shapes interactinteract
How a tool interacts with each shape is usually different
m x n possible interactions between m tools and n shapes.
How do we manage and organise this complexity?
Answer -> with the visitor pattern
VisitorVisitor
Shapes don’t have tool knowledge – Shapes don’t have tool knowledge – tools do all the work. Shapes just tools do all the work. Shapes just implement implement AcceptVisitor(v)AcceptVisitor(v) and then and then do a v.VisitShape() or v.VisitEdge() or do a v.VisitShape() or v.VisitEdge() or whatever they themselves are.whatever they themselves are.
Each tool has a Each tool has a VisitShape VisitShape VisitEdge VisitEdge VisitShapeRhEdgeZone VisitShapeRhEdgeZonemethod method that gets triggered in this that gets triggered in this way.way.
Example SequenceExample Sequence
Start in Hover ToolStart in Hover Tool All mouseMove events go to Hover All mouseMove events go to Hover
tooltool As hover over shapes/edges you ask As hover over shapes/edges you ask
what is under me and change cursor. what is under me and change cursor. You “visit” the shape and change You “visit” the shape and change cursor accordinglycursor accordingly
ToolHover--------------
VisitShape() cursor = HAND
VisitEdge() cursor = ARROW
Example Sequence Example Sequence continuedcontinued
User Left ClicksUser Left Clicks HoverTool.OnLeftClick sees that you HoverTool.OnLeftClick sees that you
are over a resize zone shape, so are over a resize zone shape, so switches to the Resize toolswitches to the Resize tool Zones (e.g. resize zone) within shapes are also Zones (e.g. resize zone) within shapes are also ‘shapes’‘shapes’
Resize tool.OnMouseMove resizes the Resize tool.OnMouseMove resizes the shape you are on. Repeatedly (as shape you are on. Repeatedly (as MouseMove events arrive).MouseMove events arrive).
Resize tool.OnMouseUp switches back Resize tool.OnMouseUp switches back to the hover tool.to the hover tool.
RTTI as RTTI as alternative to Visitoralternative to Visitor
Have each tool use RTTI (runtime type info) to Have each tool use RTTI (runtime type info) to see what the type of the shape is and do see what the type of the shape is and do something.something.
Thus instead of each tool with numerous Thus instead of each tool with numerous VisitSOMETHING() method, just have a single VisitSOMETHING() method, just have a single Visit() method with an Visit() method with an if if statement based on rtti statement based on rtti inside.inside.
ToolHover--------------
Visit() if target == Shape // use of RTTI
… else if target == Edge …..
ToolHover--------------
VisitShape() …VisitEdge() ...
RTTI as RTTI as alternative to Visitoralternative to Visitor
When visitor was invented double dispatch When visitor was invented double dispatch was the only way to get around the lack of was the only way to get around the lack of RTTI in C++RTTI in C++
RTTI approach is simpler than visitorRTTI approach is simpler than visitor Easier to reuse and specialise tools since don’t Easier to reuse and specialise tools since don’t
have to modify visitor class every time add have to modify visitor class every time add new shape – just subclass a tool and use RTTI new shape – just subclass a tool and use RTTI
Also - instead of RTTI just have each tool Also - instead of RTTI just have each tool return an enum or string from its GetMyType() return an enum or string from its GetMyType() method or something similar – avoids method or something similar – avoids overhead (if any) of RTTI and is under overhead (if any) of RTTI and is under programmers complete control.programmers complete control.
EventsEvents Funnel all Funnel all
events events through to through to the the current current tooltool..
Each tool has Each tool has custom custom handling for all handling for all the gui events the gui events e.g. e.g. mouseDown, mouseDown, mouseClick, mouseClick, mouseMove etc.mouseMove etc.
Classic STATE pattern, passing through method invocations to the current state object
Event HandlingEvent Handling
MouseUp might trigger exiting a MouseUp might trigger exiting a tool and reverting to another tool tool and reverting to another tool e.g. back to Hover.e.g. back to Hover.
State pattern – each state knows State pattern – each state knows when to switch to another state – OR when to switch to another state – OR – outer class e.g. canvas knows– outer class e.g. canvas knows
State State Pattern Pattern
– – switchiswitchi
ng ng statestate
Notice calls to “SetTool”
Prototype PatternPrototype Pattern
Use for creation toolUse for creation tool Create a copy of an instance of an Create a copy of an instance of an
objectobject Could create a new instance rather Could create a new instance rather
than prototype – depends on how than prototype – depends on how complex the prototypical object iscomplex the prototypical object is
Command PatternCommand Pattern
Hook in command manager for Hook in command manager for undo/redoundo/redo
We use tool to generate a command We use tool to generate a command and then run the command, which and then run the command, which redoes the gui action, except redoes the gui action, except through “official channels” through “official channels”
Final PatternFinal Pattern
ReflectionsReflections
Classic approach -> visitor.Classic approach -> visitor.Practical approachPractical approach -> use RTTI (or -> use RTTI (or equivalent e.g. have each shape return a equivalent e.g. have each shape return a shapeType enum) for better shapeType enum) for better comprehensibility.comprehensibility.
Classic approach -> 3D table of possibilities, Classic approach -> 3D table of possibilities, with events, shapes, tools on each axis.with events, shapes, tools on each axis.Practical approachPractical approach -> table too sparse and -> table too sparse and complex, so just code for the cases you complex, so just code for the cases you want.want.
ReflectionsReflections Classic approach -> some blend of Classic approach -> some blend of
visitShape() / visitEdge() etc methods and visitShape() / visitEdge() etc methods and mouse event methods, within each toolmouse event methods, within each toolPractical approachPractical approach -> Skip most of the -> Skip most of the visit methods and do the logic in the visit methods and do the logic in the mouse handling methods. Generalise the mouse handling methods. Generalise the mouse handling into one event mouse handling into one event (mouseAction) and use if statements to (mouseAction) and use if statements to catch the situations of interest. You know catch the situations of interest. You know what the current shape is by having a what the current shape is by having a pointer to it (set up for you by the tool or pointer to it (set up for you by the tool or something).something).