lexi case study (part 2)
Post on 11-Feb-2016
38 Views
Preview:
DESCRIPTION
TRANSCRIPT
Lexi case study (Part 2)
Presentation by Matt Deckard
User Operations Support different operations
Cut, copy, paste Formatting Printing Etc.
Support different Uis Menus Buttons Keyboard shortcuts Etc.
User Operations Avoid coupling operations and UIs
Use multiple UIs for single operations Change UIs in the future Avoid creating dependencies to classes operations
are defined in Undo and Redo
Want some, but not all operations to be undoable Un-undoable operations:
Saving Creating new documents Printing
Don’t want arbitrary limit on levels of undo
User Operations Encapsulate user operations
GUI menus are just glyphs that perform actions in response to user interaction
Create subclass of Glyph called MenuItem But don’t want to make each operation a subclass
of MenuItem (avoid decoupling!) Parameterize menu items by uper operations How?
User Operations How should user operations be
parameterized? Simple function call has drawbacks:
Doesn’t address undo/redo Hard to associate state with function Difficult to extend, reuse
Use objects instead Can store state, implement undo/redo Use inheritance to extend, reuse
Command class Command: abstract class representing user
operation Based on abstract method Execute() Subclasses will implement Execute() according to
the operation they represent Subclasses can delegate parts of user operation to
other objects Command objects are treated uniformly by
requestor MenuItem will store instance of Command
object to encapsulate its associated user operation
Command class Undoability
Add abstract Unexecute() method During Execute(), Commands will store whatever
information they need to undo later Add abstract Reversible() method
Returns true if and only if this Command is undoable Allows Commands to determine undoability at runtime i.e. redundant or vacuous Commands shouldn’t be
undoable Command history
Need list of Commands to support multiple undos Traverse back and forth through list to undo and redo Call Unexecute() when undoing, Execute() when redoing
Command class
Command class
Command Pattern Encapsulates a request (user operation) Prescribes uniform interface for requests Shields clients from request’s implementation Allows delegation of request to other objects Provides centralized access to functionality Allows to queue or log requests Supports undo, redo AKA “Action”, “Transaction” Similar to functor (but not quite the same)
Command Pattern Related patterns
Use with Composite and you’ve got: macros! Use with Memento to remember state (for undo) Use Prototype to copy command before putting in
undo list, to distinguish multiple invocations of same command
Spellchecking and Hyphenation Textual analysis Want to support multiple algorithms, addition
of new ones in future Avoid coupling to document structure
Add other types of analysis in future i.e. search, word count, etc.
Two pieces: Accessing information to be analyzed Performing the analysis
Accessing scattered information Data access
Glyphs may be stored in different ways Need mechanism to access all of them
Traversal Different analyses may access Glyphs in different
ways (i.e. forward vs. reverse search) Issues
Only Glyphs know their data structure Glyph interface shouldn’t be biased toward one
structure over another i.e. using integer index biased us toward arrays
Want to provide multiple access and traversal methods
Accessing scattered information One approach: adding methods to Glyph
void First(Traversal) – initializes specified traversal
void Next() – advances to next Glyph in traversal bool IsDone() – reports if traversal is over Glyph* GetCurrent() – accesses current glyph
Replaces Child() void Insert(Glyph*) – inserts Glyph at current
pos Replaces Insert(Glyph*, int)
Accessing scattered information Issues with this approach
Must extend Traversal enumeration to support new traversals
Would also have to change lots of subclasses Difficult to reuse mechanism for other object
structures Doesn’t support multiple traversals in parallel
Iterator class Better approach: encapsulate access and
traversal Iterator
Abstract class Defines interface for access and traversal Subclass contains reference to structure it traverses
CreateIterator() By default, will return NullIterator Subclasses can override, based on their structure Iterators can use CreateIterator() on their root
glyphs to support different traversal
Iterator class Example: PreorderIterator
First() Calls CreateIterator() on root Calls First() on returned Iterator Pushes Iterator onto stack
CurrentItem() Calls CurrentItem() on Iterator at top of stack Returns result
Next() Calls CreateIterator() on top Iterator Calls First() on returned Iterator Pushes Iterator onto stack Calls IsDone() on latest Iterator. If true, pops it off and
repeats
Iterator class
Iterator Pattern Abstracts traversal algorithm Shields clients from internal structure of
objects traversed Gain flexability, usability
Easy to extend Easy to reuse by parameterizing object type Can perform multiple traversals in parallel
Performing the analysis Want to distinguish analysis from traversal
Flexibility, reusability Different analyses might require same traversal
Want to distinguish different types of glyphs Different analyses consider different glyphs Could abstract in Glyph, have subclasses
implement Drawbacks to this approach:
Have to change multiple subclasses Obscures basic glyph interface
Performing the analysis Encapsulate the analysis
Create analysis classes For now, let’s consider a SpellChecker class
Iterator has instance of SpellChecker Uses SpellChecker instance as it traverses SpellChecker accumulates information as it is
used
Performing the analysis How to distinguish glyphs?
SpellChecker treats different glyph types differently
Don’t want to resort to type tests or casting (gross)
Instead, have the glyph object tell SpellChecker to check it Glyph has abstract CheckMe() method SpellChecker has methods for every glyph subclass,
i.e. CheckCharacter(), CheckRow(), CheckImage(), etc.
Glyph sublcasses will override CheckMe() to call the appropriate method in SpellChecker
Performing the analysis
Performing the analysis Adding new analyzers
Will be difficult if we define them as separate classes
Instead, abstract it as a Visitor class CheckMe() becomes the Accept() method
Takes any Visitor as parameter, so don’t have to touch glyph subclasses when adding new analyzers
CheckCharacter(), etc become the Visit() method Overloaded, takes visited subclass as parameter Need one for every subclass that implements Accept()
Visitor Pattern Can be applied to any object structure Visitees needn’t have common parent class Tradeoff:
When you add Visitors you don’t have to update object structure, BUT:
When you add subclasses to object structure, you DO have to update your Visitor classes
Sometimes can provide default “do nothing” operation in Visitor Helps avoid “polluting” classes with many operations Helps to separate groups of common operations Can accumulate state as they visit elements Sometimes compromises encapsulation of visitee elements
Summary Needed to support different user operations
Encapsulate the concept that varies (Command) Needed to support different methods of
accessing and traversing data Encapsulate the concept that varies (Iterator)
Needed to support different analysis algorithms Encapsulate the concept that varies (Visitor)
(Are we seeing a pattern here?)
top related