1 Expression Trees
1 Expression Trees
[email protected] www.dre.vanderbilt.edu/~schmidt Professor of Computer Science Institute for Software Integrated Systems Vanderbilt University Nashville, Tennessee, USA
Douglas C. Schmidt
Douglas C. Schmidt
Unary Node
Leaf Nodes
Douglas C. Schmidt
Douglas C. Schmidt
Component_Node
Composite_ Unary_Node
Leaf_Node
Composite_Binary _Node
Composite_Negate _Node
Douglas C. Schmidt
www.cs.iastate.edu/~cs309/references/CoplienHoffmanWeiss_CommonalityVariability.pdf 5
Douglas C. Schmidt
ExpressionTree exprTree = ; ETVisitor printVisitor = new PrintVisitor(); Java for-each loop for (ComponentNode node : exprTree) node.accept(printVisitor);
Douglas C. Schmidt
Douglas C. Schmidt
Douglas C. Schmidt
Douglas C. Schmidt
10
Douglas C. Schmidt
11
Douglas C. Schmidt
12
Douglas C. Schmidt
13
Douglas C. Schmidt
Douglas C. Schmidt
Douglas C. Schmidt
Summary
The expression tree processing app can be run in multiple modes, e.g.: Succinct mode % tree-traversal > 1+4*3/2 7 > (8/4) * 3 + 1 7 ^D Verbose mode % tree-traversal -v format [in-order] expr [expression] print [in-order|pre-order|postorder|level-order] eval [post-order] quit > format in-order > expr 1+4*3/2 > print post-order 143*2/+ > eval post-order 7 > quit
16
Douglas C. Schmidt
Douglas C. Schmidt
Initialize Prompt User Read Expr Build Tree Process Tree No EOF? Yes End
18
Douglas C. Schmidt
A
Read Expr Build Tree Process Tree
B
No EOF? Yes End
19
Douglas C. Schmidt
A
Yes Verbose? No Verbose Prompt Succinct Prompt
A
Read Expr Build Tree Process Tree
B
Yes Print? No Print Tree Yes Eval Tree
B
No EOF? Yes End
Eval? No
20
Douglas C. Schmidt
Douglas C. Schmidt
Douglas C. Schmidt
typedef struct Tree_Node { enum { NUM, UNARY, BINARY } tag_; short use_; union { char op_[2]; int num_; } o; #define num_ o.num_ #define op_ o.op_ union { struct Tree_Node *unary_; struct { struct Tree_Node *l_, *r_;} binary_; } c; #define unary_ c.unary_ #define binary_ c.binary_ } Tree_Node; 23
unary_
binary_
Douglas C. Schmidt
Douglas C. Schmidt
Douglas C. Schmidt
Douglas C. Schmidt
Douglas C. Schmidt
Summary
Limitations with algorithmic decomposition Little/no encapsulation
Implementation details available to clients
typedef struct Tree_Node { enum { NUM, UNARY, BINARY } tag_; short use_; union { char op_[2]; int num_; Small changes ripple } o; through entire program #define num_ o.num_ #define op_ o.op_ union { struct Tree_Node *unary_; struct { struct Tree_Node *l_, *r_;} binary_; Use of macros pollutes } c; global namespace #define unary_ c.unary_ #define binary_ c.binary_ } Tree_Node; 28
Douglas C. Schmidt
Summary
Limitations with algorithmic decomposition Incomplete modeling of application domain
typedef struct Tree_Node { enum { NUM, UNARY, BINARY } tag_; short use_; union { Tight coupling char op_[2]; between int num_; nodes/edges } o; in union #define num_ o.num_ #define op_ o.op_ union { struct Tree_Node *unary_; struct { struct Tree_Node *l_, *r_;} binary_; Wastes space by } c; making worst-case #define unary_ c.unary_ assumptions wrt #define binary_ c.binary_ structs & unions } Tree_Node; 29
tag_ use_ op_ num_
unary_
binary_
Douglas C. Schmidt
Summary
Limitations with algorithmic decomposition
Tree_Node data structure is passive & functions do all the real work
Douglas C. Schmidt
Douglas C. Schmidt
Composite_ Unary_Node
Leaf_Node
Composite_Binary _Node
Composite_Negate _Node
32
Douglas C. Schmidt
Binary Nodes
Unary Node
Leaf Nodes
33
Douglas C. Schmidt
ET_Iterator_Impl
Level_Order_ET _Iterator_Impl
Pre_Order_ET _|Iterator_Impl
34
Douglas C. Schmidt
35
Douglas C. Schmidt
Reactor
Event_Handler
Singleton
Options
ET_Command_Factory
ET_Event_Handler
ET_Context
Verbose_ET_ Event_Handler
Succinct_ET_ Event_Handler
ET_Command
frameworks
36
Douglas C. Schmidt
37
Douglas C. Schmidt
38
Douglas C. Schmidt
Model a tree as a collection of nodes Represent nodes as a hierarchy, capturing properties of each node e.g., arities
Composite_ Unary_Node
Leaf_Node
Composite_Binary _Node
Composite_Negate _Node
39
Douglas C. Schmidt
Bridge
Composite_ Unary_Node
Leaf_Node
Composite_Binary _Node
Composite_Negate _Node
Composite_ Add_Node
Composite_ Multiply_Node
Composite_ Subtract_Node
Composite_ Divide_Node
Applicationindependent steps
Conduct Scope, Commonality, & Variability analysis to determine stable interfaces & extension points Apply Gang of Four (GoF) patterns to guide efficient & extensible development of framework components Integrate pattern-oriented language/library features w/frameworks
Douglas C. Schmidt
41
Douglas C. Schmidt
for (Expression_Tree::iterator iter = expr_tree.begin(); iter != expr_tree.end(); Traditional STL ++iter) iterator loop (*iter).accept(print_visitor);
42
Douglas C. Schmidt
std::for_each (expr_tree.begin(), expr_tree.end(), [&print_visitor] (const Expression_Tree &t) { t.accept(print_visitor);}); C++11 lambda expression
43
Douglas C. Schmidt
std::for_each (expr_tree.begin(), expr_tree.end(), [&print_visitor] (const Expression_Tree &t) { t.accept(print_visitor);}); for (auto &iter : expr_tree) iter.accept(print_visitor);
Douglas C. Schmidt
45
Douglas C. Schmidt
46
Douglas C. Schmidt
OO designs are characterized by structuring software architectures around objects/classes in domains Rather than on actions performed by the software
Summary
<< accept >>
ET_Visitor
Evaluation_Visitor
Print_Visitor
Component_Node
ET_Iterator
ET_Iterator_Impl
LQueue
std::stack
47
Douglas C. Schmidt
OO designs are characterized by structuring software architectures around objects/classes in domains Rather than on actions performed by the software
Summary
Systems evolve & functionality changes, but well-defined objects & class roles & relationships are often relatively stable over time
48
Douglas C. Schmidt
OO designs are characterized by structuring software architectures around objects/classes in domains Rather than on actions performed by the software
Summary
Expression_Tree
Component_Node
Systems evolve & functionality changes, but well-defined objects & class roles & relationships are often relatively stable over time To obtain flexible & reusable software, therefore, its better to base the structure on objects/classes rather than on actions
Composite_ Unary_Node
Leaf_Node
Composite_Binary _Node
Composite_Negate _Node
49
Douglas C. Schmidt
Douglas C. Schmidt
51
Douglas C. Schmidt
Creational Class Scope: Domain Where Pattern Applies Object Factory Method Abstract Factory Builder Prototype Singleton
Structural Adapter (class) Adapter (object) Bridge Composite Decorator Flyweight Faade Proxy
Behavioral Interpreter Template Method Chain of Responsibility Command Iterator Mediator Memento Observer State Strategy Visitor
Douglas C. Schmidt
Design Problem Extensible expression tree structure Encapsulating variability & simplifying memory management Parsing expressions & creating expression tree Extensible tree operations Implementing STL iterator semantics Consolidating user operations
Consolidating creation of Abstract Factory variabilities for commands, & Factory iterators, etc. Method
53
Douglas C. Schmidt
Design Problem Extensible expression tree structure Encapsulating variability & simplifying memory management Parsing expressions & creating expression tree Extensible tree operations Implementing STL iterator semantics Consolidating user operations
Consolidating creation of Abstract Factory variabilities for commands, & Factory iterators, etc. Method
54
Douglas C. Schmidt
Design Problem Extensible expression tree structure Encapsulating variability & simplifying memory management Parsing expressions & creating expression tree Extensible tree operations Implementing STL iterator semantics Consolidating user operations
Consolidating creation of Abstract Factory variabilities for commands, & Factory iterators, etc. Method
55
Douglas C. Schmidt
Design Problem Extensible expression tree structure Encapsulating variability & simplifying memory management Parsing expressions & creating expression tree Extensible tree operations Implementing STL iterator semantics Consolidating user operations
Consolidating creation of Abstract Factory variabilities for commands, & Factory iterators, etc. Method
56
Douglas C. Schmidt
Design Problem Extensible expression tree structure Encapsulating variability & simplifying memory management Parsing expressions & creating expression tree Extensible tree operations Implementing STL iterator semantics Consolidating user operations
Consolidating creation of Abstract Factory variabilities for commands, & Factory iterators, etc. Method
57
Douglas C. Schmidt
Design Problem Extensible expression tree structure Encapsulating variability & simplifying memory management Parsing expressions & creating expression tree Extensible tree operations Implementing STL iterator semantics Consolidating user operations
Consolidating creation of Abstract Factory variabilities for commands, & Factory iterators, etc. Method
58
Douglas C. Schmidt
Design Problem Extensible expression tree structure Encapsulating variability & simplifying memory management Parsing expressions & creating expression tree Extensible tree operations Implementing STL iterator semantics Consolidating user operations
Consolidating creation of Abstract Factory variabilities for commands, & Factory iterators, etc. Method
59
Douglas C. Schmidt
Design Problem
Pattern(s)
Ensuring correct protocol State for processing commands Structuring the application event flow Reactor Template Method & Strategy Singleton Adapter
Supporting multiple operation modes Centralizing access to global resources Eliminating loops via the STL std::for_each() algorithm
60
Douglas C. Schmidt
Design Problem
Pattern(s)
Ensuring correct protocol State for processing commands Structuring the application event flow Reactor Template Method & Strategy Singleton Adapter
Supporting multiple operation modes Centralizing access to global resources Eliminating loops via the STL std::for_each() algorithm
Douglas C. Schmidt
Design Problem
Pattern(s)
Ensuring correct protocol State for processing commands Structuring the application event flow Reactor Template Method & Strategy Singleton Adapter
Supporting multiple operation modes Centralizing access to global resources Eliminating loops via the STL std::for_each() algorithm
62
Douglas C. Schmidt
Design Problem
Pattern(s)
Ensuring correct protocol State for processing commands Structuring the application event flow Reactor Template Method & Strategy Singleton Adapter
Supporting multiple operation modes Centralizing access to global resources Eliminating loops via the STL std::for_each() algorithm
63
Douglas C. Schmidt
Design Problem
Pattern(s)
Ensuring correct protocol State for processing commands Structuring the application event flow Reactor Template Method & Strategy Singleton Adapter
Supporting multiple operation modes Centralizing access to global resources Eliminating loops via the STL std::for_each() algorithm
64 Naturally, these patterns apply to more than expression tree processing apps!
Douglas C. Schmidt
GoF patterns provide elements of reusable object-oriented software that address limitations with algorithmic decomposition
Summary
65
Douglas C. Schmidt
Douglas C. Schmidt
Composite_ Unary_Node
Leaf_Node
Composite_Binary _Node
Composite_Negate _Node
67
Douglas C. Schmidt
Expression_Tree
Component_Node
Composite_ Unary_Node
Leaf_Node
Composite_Binary _Node
Composite_Negate _Node
Composite_ Add_Node
Composite_ Multiply_Node
Composite_ Subtract_Node
Composite_ Divide_Node
Bridge
Composite 68
Douglas C. Schmidt
69
Douglas C. Schmidt
70
Douglas C. Schmidt
71 Eliminate need for type tags & switch statements, cf. algorithmic decomposition
Douglas C. Schmidt
72
Douglas C. Schmidt
73
Douglas C. Schmidt
74
Douglas C. Schmidt
75
Douglas C. Schmidt
~Component_Node()=0 item() const left() const right() const accept(ET_Visitor &visitor) const
This hook method plays an essential role in I terator & Visitor patterns (covered later)
Commonality: Base class interface used by all nodes in an expression tree Variability: Each subclass defines state & method implementations that
are specific for the various types of nodes 76
Douglas C. Schmidt
Composite_ Unary_Node
Leaf_Node
Composite_Binary _Node
Composite_Negate _Node
Composite_ Add_Node e.g., Composite_Binary_Node is a Composite_Unary_Node & also has a Component_Node Composite_ Multiply_Node
Eliminates need for type tags & switch 77 statements by treating nodes uniformly!
Douglas C. Schmidt
Composite
Intent Applicability Objects must be composed recursively
and no distinction between individual & composed elements and objects in structure can be treated uniformly Structure
e.g., Component_Node
e.g., Leaf_Node
78
Douglas C. Schmidt
Composite
Composite example in C++
Build an expression tree based on recursively-composed objects Component_Node *l1 = new Leaf_Node(5); Component_Node *l2 = new Leaf_Node(3); Component_Node *l3 = new Leaf_Node(4);
l1
l2
l3
79
Douglas C. Schmidt
Composite
Composite example in C++
Build an expression tree based on recursively-composed objects Component_Node *l1 = new Leaf_Node(5); Component_Node *l2 = new Leaf_Node(3); u1 Component_Node *l3 = new Leaf_Node(4); Component_Node *u1 = new Composite_Negate_Node(l1); Component_Node *b1 = new Composite_Add_Node(l2, l3);
b1
l1
l2
l3
80
Douglas C. Schmidt
Composite
Composite example in C++
Build an expression tree based on recursively-composed objects Component_Node *l1 = new Leaf_Node(5); Component_Node *l2 = new Leaf_Node(3); u1 Component_Node *l3 = new Leaf_Node(4); Component_Node *u1 = new Composite_Negate_Node(l1); Component_Node *b1 = new Composite_Add_Node(l2, l3); Component_Node *b2 = new Composite_Multiply_Node(u1, b1); b2
b1
l1
l2
l3
Douglas C. Schmidt
Composite
Composite example in Java
Build an expression tree based on recursively-composed objects ComponentNode l1 = new LeafNode(5); ComponentNode l2 = new LeafNode(3); u1 ComponentNode l3 = new LeafNode(4); ComponentNode u1 = new CompositeNegateNode(l1); ComponentNode b1 = new CompositeAddNode(l2, l3); ComponentNode b2 = new CompositeMultiplyNode(u1, b1); b2
b1
l1
l2
l3
Douglas C. Schmidt
Composite
Consequences + Uniformity: Treat components the same regardless of complexity & behavior + Extensibility: New component subclasses work wherever existing ones do
+ Parsimony: Classes only include fields they need Perceived complexity: May need what seems like a prohibitive numbers of classes/objects Awkward designs: May need to treat leaves as lobotomized composites in some cases
83
Douglas C. Schmidt
Composite
Consequences + Uniformity: Treat components the same regardless of complexity & behavior
+ Extensibility: New Component subclasses work wherever existing ones do + Parsimony: Classes only include fields they need Perceived complexity: May need what seems like a prohibitive numbers of classes/objects Awkward designs: May need to treat leaves as lobotomized composites in some cases Implementation Do components know their parents? Uniform interface for both leaves & composites? Dont allocate storage for children in component base class (big problem with algorithmic decomposition solution)
84 Who is responsible for deleting children?
Douglas C. Schmidt
Composite
Consequences + Uniformity: Treat components the same regardless of complexity & behavior
+ Extensibility: New Component subclasses work wherever existing ones do + Parsimony: Classes only include fields they need Perceived complexity: May need what seems like a prohibitive numbers of classes/objects Awkward designs: May need to treat leaves as lobotomized composites in some cases Implementation Do components know their parents? Uniform interface for both leaves & composites?
Dont allocate storage for children in component base class (big problem with algorithmic decomposition solution)
85 Who is responsible for deleting children?
Douglas C. Schmidt
86
Douglas C. Schmidt
Print_Visitor print_visitor; for (auto iter = expr_tree.begin(); iter != expr_tree.end(); ++iter) Iterate through all elements in expression tree without concern (*iter).accept(print_visitor); for how tree is structured or what traversal has been designated
87
Douglas C. Schmidt
Print_Visitor print_visitor; for (auto iter = expr_tree.begin(); iter != expr_tree.end(); ++iter) Note how C++11 auto keyword can deduce type of Expression (*iter).accept(print_visitor);
_Tree iterator
88
Douglas C. Schmidt
89
Douglas C. Schmidt
Douglas C. Schmidt
91
Douglas C. Schmidt
Component _Node
Composite_ Unary_Node
Leaf_Node
Composite_ Binary_Node
Composite_ Negate_Node
Composite_ Add_Node
Composite_ Multiply_Node
92
Douglas C. Schmidt
Component _Node
Composite_ Unary_Node
Leaf_Node
Composite_ Binary_Node
Composite_ Negate_Node
The Abstract Factory pattern can be used to produce the right implementor hierarchy (as seen later)
Composite_ Add_Node
Composite_ Multiply_Node
93
Douglas C. Schmidt
void
bool const int Expression_Tree Expression_Tree Forward to void implementor iterator hierarchy iterator const_iterator const_iterator
94
Douglas C. Schmidt
Commonality: Provides common interface for expression tree operations Variability: The contents of the composite nodes in the expression tree
will vary depending on the user input expression 95
Douglas C. Schmidt
Bridge
Intent
Separate an interface from its implementation(s) Applicability When interface & implementation should vary independently Require a uniform interface to interchangeable implementor hierarchies Structure
e.g., Expression_Tree e.g., Component_Node
Douglas C. Schmidt
Bridge
Bridge example in C++
Separate expression tree interface from the composite node implementations class Expression_Tree { root_ manages lifecycle of pointer parameter public: Expression_Tree(Component_Node *root): root_(root) {}
Interface forwards to implementor via shared_ptr
Douglas C. Schmidt
Bridge
Bridge example in C++
Separate expression tree interface from the composite node implementations class Expression_Tree { public: Expression_Tree(Component_Node *root): root_(root) {} ... void accept(ET_Visitor &v) { root_->accept(v); } private: std::shared_ptr <Component_Node> root_; ...
expr_tree manages lifecycle of composite via Bridge pattern
Douglas C. Schmidt
Bridge
Consequences + Abstraction interface & implementor hierarchy are decoupled + Implementors can vary dynamically + Enables efficient use of value-based STL algorithms & containers
99
Douglas C. Schmidt
Bridge
Consequences + Abstraction interface & implementor hierarchy are decoupled + Implementors can vary dynamically + Enables efficient use of value-based STL algorithms & containers
Creating the right implementor Often addressed by using factories Not as widely used in Java as in C++
100
Douglas C. Schmidt
Bridge
Consequences + Abstraction interface & implementor hierarchy are decoupled + Implementors can vary dynamically + Enables efficient use of value-based STL algorithms & containers
AWT Component/
ComponentPeer
Creating the right implementor Often addressed by using factories Not as widely used in Java as in C++
101
Douglas C. Schmidt
Expression_Tree
Component_Node
Composite_ Unary_Node
Leaf_Node
Composite_Binary _Node
Composite_Negate _Node
Composite_ Add_Node
Composite_ Multiply_Node
Composite_ Subtract_Node
Composite_ Divide_Node
Bridge
Composite
Douglas C. Schmidt
Douglas C. Schmidt
ET_Interpreter
Symbol
Add
Subtract
Negate
Multiply
Divide
104
Douglas C. Schmidt
Interpreter
<< use >>
Leaf_Node
Composite_ Binary_Node
Number
Operator
Unary_Operator
Composite_ Add_Node
Composite_ Subtract_Node
Composite_ Negate_Node
Add
Subtract
Negate
Composite_ Multiply_Node
Composite_ Divide_Node
<<build>>
Multiply
Divide
Composite
Builder
105 of patterns involved in this design There are many classes, but only a handful
Douglas C. Schmidt
in-order expression
= -5*(3+4)
106
Douglas C. Schmidt
107
Douglas C. Schmidt
pre-order expression = *-5+34 post-order expression = 5-34+* level-order expression = *-+534 in-order expression = -5*(3+4)
Douglas C. Schmidt
Symbol
ET_Interpreter _Context
Operator
Unary_Operator
Multiply
Negate
Number
Add
109
Douglas C. Schmidt
Symbol
ET_Interpreter _Context
expression tree
Operator
Unary_Operator
We create the entire parse tree to enable optimizations (as a future extension)
Multiply
Negate
Number
Add
<<build>>
110
Douglas C. Schmidt
111
Douglas C. Schmidt
Symbol(Symbol *left, Symbol *right) virtual ~Symbol() virtual int precedence()=0 virtual Component_Node * build()=0
This method is used to build a component node corresponding to the expression parse tree node 112
Douglas C. Schmidt
Variability: The structure of the parse trees & expression trees can vary
depending on the format & contents of the expression input 113
Douglas C. Schmidt
Interpreter
Intent
Given a language, define a representation for its grammar, along with an interpreter that uses the representation to interpret sentences in the language Applicability When the grammar is simple & relatively stable When time/space efficiency is not a critical concern Structure
e.g., Symbol
114
Douglas C. Schmidt
Interpreter
Interpreter example in C++ The interpret() method creates a parse tree from users expression input
Expression_Tree ET_Interpreter::interpret (ET_Interpreter_Context &context, const std::string &input) { std::list<Symbol *> parse_tree; Symbols added to parse tree as ... interpreter recognizes them if(is_number(input[i])){ Handle operand number_insert(input, parse_tree); } else if(input[i] == '+'){ Handle addition operator Add *op = new Add(); op->add_precedence(accum_precedence); precedence_insert(op, parse_tree); ... } else if(input[i] == '(') { Handle parenthesized expression handle_parenthesis(context, input, accum_precedence, parse_tree); } 115 ...
Douglas C. Schmidt
Interpreter
Consequences
+ Simple grammars are easy to change & extend e.g., All rules represented by distinct classes in a consistent & orderly manner + Adding another rule adds another class Complex grammars hard to create & maintain e.g., More inter-dependent rules yield more inter-dependent classes
116 approach (e.g., parser generators) Complex grammars may require different
Douglas C. Schmidt
Interpreter
Consequences
+ Simple grammars are easy to change & extend e.g., All rules represented by distinct classes in a consistent & orderly manner + Adding another rule adds another class Complex grammars hard to create & maintain e.g., More inter-dependent rules yield more inter-dependent classes Implementation Express language rules, one per class Literal translations expressed as terminal
expressions
Douglas C. Schmidt
Interpreter
Consequences
+ Simple grammars are easy to change & extend e.g., All rules represented by distinct classes in a consistent & orderly manner + Adding another rule adds another class Complex grammars hard to create & maintain e.g., More inter-dependent rules yield more inter-dependent classes Implementation Express language rules, one per class Literal translations expressed as terminal
expressions
Douglas C. Schmidt
Builder
Intent
Separate the construction of a complex object from its representation so that the same construction process can create different representations Applicability Need to isolate knowledge of the creation of a complex object from its parts Need to allow different implementations/interfaces of an object's parts Structure
e.g., Symbol e.g., Leaf_Node, Composite_Add _Node, etc.
e.g., ET_Interpreter
119
Douglas C. Schmidt
Builder
Builder example in C++ The interpret() method builds composite expression tree from parse tree
Expression_Tree ET_Interpreter::interpret (ET_Interpreter_Context &context, const std::string &input) { ... return Expression_Tree(parse_tree.back()->build()); Invoke a recursive expression tree build, starting with the root symbol in parse tree created by the interpreter
120
Douglas C. Schmidt
Builder
Builder example in C++ The interpret() method builds composite expression tree from parse tree
Expression_Tree ET_Interpreter::interpret (ET_Interpreter_Context &context, const std::string &input) { ... return Expression_Tree(parse_tree.back()->build());
Component_Node * Multiply::build() { return new Composite_Multiply_Node (left_->build(), right_->build()); } Build equivalent component nodes Component_Node *Number::build() { return new Leaf_Node(item_); } 121
Douglas C. Schmidt
Builder
Consequences + Isolates code for construction & representation
+ Finer control over the construction process May involve a lot of classes
122
Douglas C. Schmidt
Builder
Consequences + Isolates code for construction & representation
+ Finer control over the construction process May involve a lot of classes Implementation The Builder pattern is a factory pattern with a mission A Builder pattern implementation exposes itself as a factory It goes beyond conventional factory patterns by connecting various implementations together
123
Douglas C. Schmidt
Builder
Consequences + Isolates code for construction & representation
+ Finer control over the construction process May involve a lot of classes Implementation The Builder pattern is a factory pattern with a mission A Builder pattern implementation exposes itself as a factory It goes beyond conventional factory patterns by connecting various implementations together
124
Douglas C. Schmidt
Interpreter
<< use >> ET_Interpreter_Context
Leaf_Node
Composite_ Binary_Node
Number
Operator
Unary_Operator
Composite_ Add_Node
Composite_ Subtract_Node
Composite_ Negate_Node
Add
Subtract
Negate
Composite_ Multiply_Node
Composite_ Divide_Node
<<build>>
Multiply
Divide
Composite
Builder
Douglas C. Schmidt
Douglas C. Schmidt
Douglas C. Schmidt
Visitor
ET_Visitor
Expression_Tree
Component_Node
Iterator
ET_Iterator
ET_Iterator_Impl
std::queue
Prototype
Douglas C. Schmidt
Goals
Binary Nodes
Douglas C. Schmidt
Goals
Create a framework for performing operations that affect nodes in a tree Constraints/forces Support multiple operations on the expression tree without tightly coupling operations with the tree structure i.e., dont have print() & evaluate() methods in the node classes
Unary Node
Binary Nodes
Douglas C. Schmidt
Leaf for (auto iter = expr_tree.begin(); Nodes iter != expr_tree.end(); ++iter) (*iter).accept(print_visitor);
131
Douglas C. Schmidt
In_Order_ET_Iterator_Impl Level_Order_ET_Iterator_Impl
Douglas C. Schmidt
ET_Iterator(ET_Iterator_Impl *) ET_Iterator(const ET_Iterator &) operator *() operator *() const operator++() operator++(int) operator==(const ET_Iterator &rhs) operator!=(const ET_Iterator &rhs) ...
Variability: Can be configured with specific expression tree iterator algorithms via the Abstract Factory 133 pattern
Douglas C. Schmidt
Base class of the iterator implementor hierarchy that defines the various
Douglas C. Schmidt
Iterator
Intent Access elements of an aggregate without exposing its representation Applicability Require multiple traversal algorithms over an aggregate Require a uniform traversal interface over different aggregates When aggregate classes & traversal algorithm must vary independently Structure
e.g., Expression_Tree e.g., ET_Iterator_Impl
Douglas C. Schmidt
Iterator
for (iter = exprTree.createIterator(); iter->done() == false; iter->advance()) iter->currentElement()->accept(printVisitor); delete iter; 136 The Bridge pattern enables STL iterator use in expression tree processing app
Douglas C. Schmidt
Iterator
Java iterators are closer to GoF iterators than STL iterators are, e.g.:
for (IteratorL<ExpressionTree> itr = exprTree.iterator(); itr.hasNext(); ) iter.next().accept (printVisitor); }
137 in C++/Java is to use a for loop Often, the simplest way to use an iterator
Douglas C. Schmidt
Iterator
for loop, e.g.:
138
Douglas C. Schmidt
Iterator
Iterator example in C++
Douglas C. Schmidt
Iterator
Consequences + Flexibility: Aggregate & traversal are independent
+ Multiplicity: Multiple iterators & multiple traversal algorithms Overhead: Additional communication between iterator & aggregate Particularly problematic for iterators in concurrent or distributed systems
140
Douglas C. Schmidt
Iterator
Consequences + Flexibility: Aggregate & traversal are independent
+ Multiplicity: Multiple iterators & multiple traversal algorithms Overhead: Additional communication between iterator & aggregate Particularly problematic for iterators in concurrent or distributed systems Implementation Internal vs. external iterators Violating the object structures encapsulation Robust iterators Synchronization overhead in multi-threaded programs
Douglas C. Schmidt
Iterator
Consequences + Flexibility: Aggregate & traversal are independent
+ Multiplicity: Multiple iterators & multiple traversal algorithms Overhead: Additional communication between iterator & aggregate Particularly problematic for iterators in concurrent or distributed systems Implementation Internal vs. external iterators Violating the object structures encapsulation Robust iterators Synchronization overhead in multi-threaded programs
Douglas C. Schmidt
143
Douglas C. Schmidt
Leaf Nodes
144
Douglas C. Schmidt
iter != expr_tree.end(); ++iter) (*iter).accept(print_visitor); accept() calls back on visitor, e.g.: void Leaf_Node::accept(ET_Visitor &v) { v.visit(*this); }
Leaf Nodes
Douglas C. Schmidt
Leaf_Node &node)=0 Composite_Negate_Node &node)=0 Composite_Add_Node &node)=0 Composite_Subtract_Node &node)=0 Composite_Divide_Node &node)=0 Composite_Multiply_Node &node)=0
Evaluation_Visitor
Print_Visitor
Douglas C. Schmidt
Visitor
Intent
Centralize operations on an object structure so that they can vary independently, but still behave polymorphically Applicability When classes define many unrelated operations Class relationships in structure rarely change, but operations on them change Algorithms keep state thats updated during traversal Structure
e.g., ET_Visitor e.g., Composite_Add_Node , Composite_ . Binary_Node, Composite_Unary_Node, etc. e.g., Evaluation_Visitor, Print_Visitor, etc. e.g., Component_Node
147
Douglas C. Schmidt
Visitor
Visitor implementation in C++
The Print_Visitor class prints character code or value for each node
class Print_Visitor : public ET_Visitor { public: virtual void visit(const Leaf_Node &); virtual void visit(const Add_Node &); virtual void visit(const Divide_Node &); // etc. for all relevant Component_Node subclasses };
148
Douglas C. Schmidt
Visitor
Visitor implementation in C++
The Print_Visitor class prints character code or value for each node
class Print_Visitor : public public: virtual void visit(const virtual void visit(const virtual void visit(const // etc. }; ET_Visitor { Leaf_Node &); Add_Node &); Divide_Node &);
Douglas C. Schmidt
Visitor
Visitor implementation in C++ The iterator controls the order in which accept() is called on each node in the composition accept() then visits the node to perform the desired print action
Iterator Leaf_Node(5) Composite_Negate_Node print_visitor
accept(print_visitor)
cout<< node.item();
accept(print_visitor)
cout<< -
150
Douglas C. Schmidt
Visitor
Visitor implementation in C++ The Evaluation_Visitor class evaluates nodes in an expression tree traversed using a post-order iterator e.g., 5-34+*
};
151
Douglas C. Schmidt
Visitor
Visitor implementation in C++ The Evaluation_Visitor class evaluates nodes in an expression tree traversed using a post-order iterator e.g., 5-34+*
1. S = [5] 2. S = [-5] 3. S = [-5, 3] It uses a stack to keep track of the post-order expression tree value that 4. S = [-5, 3, 4] 5. S = [-5, 7] has been processed thus far during 1526. S = [-35] the iteration traversal
Douglas C. Schmidt
Visitor
Visitor implementation in C++
The iterator controls the order in which accept() is called on each node in the composition accept() then visits the node to perform the desired evaluation action
Iterator Leaf_Node(5) Composite_Negate_Node eval_visitor
accept(eval_visitor)
stack_.push(node.item());
accept(eval_visitor)
stack_.push(-stack_.pop());
153
Douglas C. Schmidt
Visitor
Consequences
+ Flexibility: Visitor algorithm(s) & object structure are independent + Separation of concerns: Localized functionality in the visitor subclass instance Tight coupling: Circular dependency between Visitor & Element interfaces Visitor thus brittle to new ConcreteElement classes
154
Douglas C. Schmidt
Visitor
Consequences
+ Flexibility: Visitor algorithm(s) & object structure are independent + Separation of concerns: Localized functionality in the visitor subclass instance Tight coupling: Circular dependency between Visitor & Element interfaces Visitor thus brittle to new ConcreteElement classes Implementation Double dispatch General interface to elements of object structure
155
Douglas C. Schmidt
Visitor
Consequences
+ Flexibility: Visitor algorithm(s) & object structure are independent + Separation of concerns: Localized functionality in the visitor subclass instance Tight coupling: Circular dependency between Visitor & Element interfaces Visitor thus brittle to new ConcreteElement classes Implementation Double dispatch General interface to elements of object structure Known Uses ProgramNodeEnumerator in Smalltalk-80 compiler IRIS Inventor scene rendering TAO IDL compiler to handle different backends
156
Douglas C. Schmidt
157
Douglas C. Schmidt
158
Douglas C. Schmidt
159
Douglas C. Schmidt
Douglas C. Schmidt
ET_Iterator_Impl clone()
The Bridge pattern abstraction class neednt have direct knowledge of implementor subclass details
Douglas C. Schmidt
implementations Variability: Each subclass implements the clone() method to return a deep copy of itself for use by ET_Iterator::operator++(int) 162
Douglas C. Schmidt
Prototype
Intent
Specify the kinds of objects to create using a prototypical instance & create new objects by copying this prototype Applicability
When the classes to instantiate are specified at run-time Theres a need to avoid the creation of a factory hierarchy It is more convenient to copy an existing instance than to create a new one
Structure
e.g., ET_Iterator_Impl
163
Douglas C. Schmidt
Prototype
Prototype example in C++
164
Douglas C. Schmidt
Prototype
Prototype example in C++
Douglas C. Schmidt
Prototype
Consequences + Can add & remove classes at runtime by cloning them as needed + Reduced subclassing minimizes need for lexical dependencies at run-time
Every class that used as a prototype must itself be instantiated Classes that have circular references to other classes cannot really be cloned
166
Douglas C. Schmidt
Prototype
Consequences + Can add & remove classes at runtime by cloning them as needed + Reduced subclassing minimizes need for lexical dependencies at run-time
Every class that used as a prototype must itself be instantiated Classes that have circular references to other classes cannot really be cloned Implementation
Use prototype manager Shallow vs. deep copies Initializing clone internal state within a
uniform interface
167
Douglas C. Schmidt
Prototype
Consequences + Can add & remove classes at runtime by cloning them as needed + Reduced subclassing minimizes need for lexical dependencies at run-time
application of the Prototype pattern in an object-oriented language was in ThingLab related to the Prototype pattern for C++ & gives many examples & variations
Every class that used as a prototype must itself be instantiated Classes that have circular references to other classes cannot really be cloned Implementation
Use prototype manager Shallow vs. deep copies Initializing clone internal state within a
uniform interface
168
Douglas C. Schmidt
Visitor
ET_Visitor
Expression_Tree
Component_Node
Iterator
ET_Iterator
ET_Iterator_Impl
std::queue
Prototype
169 These patterns allow adding new operations without affecting tree structure
Douglas C. Schmidt
Douglas C. Schmidt
ET_Command_Impl
Expr_ Command
Eval_ Command
Quit_ Command
171
Douglas C. Schmidt
Concrete_ET_ Command_Factory_Impl
ET_Context
ET_Command
ET_Command_Impl
Command
Format_Command
Expr_Command
Eval_Command
172 These patterns decouple creation from use & provide a uniform command API
Douglas C. Schmidt
Douglas C. Schmidt
Succinct mode
Douglas C. Schmidt
175
Douglas C. Schmidt
Expr_ Command
Eval_ Command
Print_ Command
Quit_ Command
176
Douglas C. Schmidt
Format_ Command
Expr_ Command
Eval_ Command
Print_ Command
Quit_ Command
Douglas C. Schmidt
Interface
ET_Command(ET_Command_Impl *=0) ET_Command(const ET_Command &) ET_Command & operator=(const ET_Command &) ~ET_Command() These methods forward to bool execute() the implementor subclass bool unexecute()
Commonality: Provides common interface for expression tree commands Variability: Implementations of expression tree commands can vary
depending on the operations requested by user input
178
Douglas C. Schmidt
Interface
ET_Command_Impl(ET_Context &) ~ET_Command_Impl()= 0 ... The bulk of the work is virtual bool execute()=0 typically done here by virtual bool unexecute()=0
subclasses
Douglas C. Schmidt
Command
Intent Applicability
Encapsulate the request for a service as an object Want to parameterize objects with an action to perform Want to specify, queue, & execute requests at different times For multilevel undo/redo Structure
e.g., ET_Command_Impl
180
Douglas C. Schmidt
Command
Command example in C++
Douglas C. Schmidt
Command
Command example in C++
Douglas C. Schmidt
Command
Command example in C++
Douglas C. Schmidt
Command
Command example in C++
Douglas C. Schmidt
Command
Consequences + Abstracts executor of a service + Supports arbitrary-level undo-redo + Composition yields macro-commands Might result in lots of trivial command subclasses Excessive memory may be needed to support undo/redo operations
185
Douglas C. Schmidt
Command
Consequences + Abstracts executor of a service + Supports arbitrary-level undo-redo + Composition yields macro-commands Might result in lots of trivial command subclasses Excessive memory may be needed to support undo/redo operations Implementation
Copying a command before putting it on a history list Avoiding error accumulation during undo/redo Supporting transactions
186
Douglas C. Schmidt
Command
Consequences + Abstracts executor of a service + Supports arbitrary-level undo-redo + Composition yields macro-commands Might result in lots of trivial command subclasses Excessive memory may be needed to support undo/redo operations Implementation
Copying a command before putting it on a history list Avoiding error accumulation during undo/redo Supporting transactions
187
Douglas C. Schmidt
Format_Command
Print_Command
Eval_Command
Quit_Command
Expr_Command
Macro_Command
ET_Iterator
ET_Iterator_Impl
In_Order_ET_Iterator_Impl Level_Order_ET_Iterator_Impl
Post_Order_ET_Iterator_Impl
188
Pre_Order_ET_Iterator_Impl
Douglas C. Schmidt
Constraints/forces Dont recode existing clients Add new variabilities without recompiling
Format_Command
Print_Command
Eval_Command
Quit_Command
Expr_Command
Macro_Command
ET_Iterator
ET_Iterator_Impl
In_Order_ET_Iterator_Impl Level_Order_ET_Iterator_Impl
Post_Order_ET_Iterator_Impl
189
Pre_Order_ET_Iterator_Impl
Douglas C. Schmidt
190
Douglas C. Schmidt
Douglas C. Schmidt
make_format_command(const std::string &) make_expr_command(const std::string &) make_print_command(const std::string &) make_eval_command(const std::string &) make_quit_command(const std::string &) make_macro_command(const std::string &)
Commonality: Provides a common interface to create commands Variability: Implementations of expression tree command factory
methods can vary depending on the requested commands 192
Douglas C. Schmidt
ET_Command_Factory_Impl(ET_Context &context) ~ET_Command_Factory_Impl() virtual ET_Command make_command(const std::string &s)=0 ... Pure virtual methods virtual virtual virtual virtual virtual virtual ET_Command ET_Command ET_Command ET_Command ET_Command ET_Command make_format_command(const std::string &)=0 make_expr_command(const std::string &)=0 make_print_command(const std::string &)=0 make_eval_command(const std::string &)=0 make_quit_command(const std::string &)=0 make_macro_command(const std::string &)=0
Commonality: Provides a common interface to create commands Variability: Subclasses of this base class define expression tree
Douglas C. Schmidt
Factory Structure
ET_Command_Factory ET_Command
ET_Command_Factory_Impl
ET_Command_Impl
Concrete_ET_Command _Factory_Impl +make_command() #make_expr_command() #make_format_command() #make_eval_command() #make_macro_command() #make_quit_command() #make_print_command() Format_ Command Macro_ Command Print_ Command Expr_ Command Eval_ Command Quit_ Command
Douglas C. Schmidt
Factory Method
Intent concrete type to a subclass
Structure
195
Douglas C. Schmidt
Factory Method
Factory Method example in C++
196
Douglas C. Schmidt
Factory Method
Factory Method example in C++
Douglas C. Schmidt
Factory Method
Consequences
+ Flexibility: The client becomes more flexible by not specifying the class name of the concrete class & the details of its creation + Decoupling: The client only depends on the interface More classes: Construction of objects requires an additional class in some cases
198
Douglas C. Schmidt
Factory Method
Consequences
+ Flexibility: The client becomes more flexible by not specifying the class name of the concrete class & the details of its creation + Decoupling: The client only depends on the interface More classes: Construction of objects requires an additional class in some cases Implementation There are two choices The creator class is abstract & does not implement creation methods (then it must be subclassed) The creator class is concrete & provides a default implementation (then it can be subclassed) If a factory method can create different variants the method should be passed a parameter to designate the variant 199
Douglas C. Schmidt
Factory Method
Consequences
+ Flexibility: The client becomes more flexible by not specifying the class name of the concrete class & the details of its creation + Decoupling: The client only depends on the interface More classes: Construction of objects requires an additional class in some cases Implementation There are two choices
The creator class is abstract & does not implement creation methods (then it must be subclassed) The creator class is concrete & provides a default implementation (then it can be subclassed) If a factory method can create different variants the method must be provided with a parameter 200
Douglas C. Schmidt
Abstract Factory
Intent Applicability
Create families of related objects without specifying subclass names When clients cannot anticipate groups of classes to instantiate
Structure
e.g., ET_Command _Impl e.g., ET_Command _Factory_Impl e.g., Eval_Command, Print_ Command, Macro_Command, etc.
201
Douglas C. Schmidt
Abstract Factory
Abstract Factory example in C++
202
Douglas C. Schmidt
Abstract Factory
Abstract Factory example in C++
designated command based on user input virtual ET_Command make_command(const std::string &input) { auto iter = command_map_.find(command_name(input)); if (iter != command_map_.end()) { auto ptmf = iter->second; return (this->*ptmf)(command_parameter(input)); } Dispatch command 203factory method via returned via map ...
Douglas C. Schmidt
Abstract Factory
Consequences + Flexibility: Removes type (i.e., subclass) dependencies from clients + Abstraction & semantic checking: Encapsulates products composition Complexity: Tedious to extend factory interface to create new products
204
Douglas C. Schmidt
Abstract Factory
Consequences + Flexibility: Removes type (i.e., subclass) dependencies from clients + Abstraction & semantic checking: Encapsulates products composition Complexity: Tedious to extend factory interface to create new products Implementation Parameterization as a way of controlling interface size
Configuration with prototypes to determine who creates the factories Abstract factories are essentially groups of factory methods
205
Douglas C. Schmidt
Abstract Factory
Consequences + Flexibility: Removes type (i.e., subclass) dependencies from clients + Abstraction & semantic checking: Encapsulates products composition Complexity: Tedious to extend factory interface to create new products Implementation Parameterization as a way of controlling interface size
Configuration with prototypes to determine who creates the factories Abstract factories are essentially groups of factory methods
206
Douglas C. Schmidt
Concrete_ET_Command_Factory_Impl
ET_Context
ET_Command
ET_Command_Impl
Command
Format_Command
Expr_Command
Eval_Command
207 These patterns enable extensibility of operations via new factory methods
Douglas C. Schmidt
Douglas C. Schmidt
Douglas C. Schmidt
ET_Context
ET_State
Uninitialized _State
Pre_Order_ Uninitialized_State
Post_Order_ Uninitialized_State
In_Order_ Uninitialized_State
Level_Order_ Uninitialized_State
Pre_Order_ Initialized_State
Post_Order_ Initialized_State
In_Order_ Initialized_State
Level_Order_ Initialized_State
State
This pattern uses design of classes to 210 explicitly order user commands correctly
Douglas C. Schmidt
211
Douglas C. Schmidt
Douglas C. Schmidt
213
Douglas C. Schmidt
214
Douglas C. Schmidt
215
Douglas C. Schmidt
216
Douglas C. Schmidt
217
Douglas C. Schmidt
218
Douglas C. Schmidt
This Uninitialized history can be State represented as a format() state machine The state machine can be encoded using various subclasses that enforce the correct protocol for user commands
quit()
Douglas C. Schmidt
Interface
format(const std::string &new_format) make_tree(const std::string &expression) print(const std::string &format) evaluate(const std::string &format) ... Setter/getter for state() const ET_State subclasses state(ET_State *new_state) tree() tree(const Expression_Tree &new_tree)
Douglas C. Schmidt
Douglas C. Schmidt
State
Intent
Allow an object to alter its behavior when its internal state changesthe object will appear to change its class Applicability
Structure
e.g., Uninitialized_State, Pre_Order_Uninitialized_State, Pre_Order_Initialized_State, etc.
e.g., ET_Context
222
Douglas C. Schmidt
State
State example in C++
Allows ET_Context object to alter its behavior when its state changes
void ET_Context::make_tree(const std::string &expression) { state_->make_tree(*this, expression); }
class Uninitialized_State { public: virtual void make_tree(ET_Context &tc, const std::string &expr) { throw Invalid_State("make_tree called in invalid state"); } ...
223
Douglas C. Schmidt
State
State example in C++
Allows ET_Context object to alter its behavior when its state changes
void ET_Context::make_tree(const std::string &expression) { state_->make_tree(*this, expression); }
class In_Order_Uninitialized_State : public Uninitialized_State { public: Calling make_tree() in this state initializes expression tree virtual void make_tree(ET_Context &et_context, const std::string &expr) { ET_Interpreter interp; ET_Interpreter_Context interp_context; et_context.tree(interp.interpret (interp_context, expr)); et_context.state(new In_Order_Initialized_State); } ... Transition to the new state 224
Douglas C. Schmidt
State
Consequences + It localizes state-specific behavior & partitions behavior for different states + It makes state transitions explicit + State objects can be shared Can result in lots of subclasses that are hard to understand
225
Douglas C. Schmidt
State
Consequences + It localizes state-specific behavior & partitions behavior for different states + It makes state transitions explicit + State objects can be shared Can result in lots of subclasses that are hard to understand Implementation Who defines state transitions? Consider using table-based alternatives Creating & destroying state objects
226
Douglas C. Schmidt
State
Consequences + It localizes state-specific behavior & partitions behavior for different states + It makes state transitions explicit + State objects can be shared Can result in lots of subclasses that are hard to understand Implementation Who defines state transitions? Consider using table-based alternatives Creating & destroying state objects
application to TCP connection protocols are characterized by Ralph Johnson & Johnny Zweig in their article Delegation in C++. Journal of Object-Oriented Programming, 4(11):22-35, November 1991
227
Douglas C. Schmidt
ET_Context
ET_State
Uninitialized _State
Pre_Order_ Uninitialized_State
Post_Order_ Uninitialized_State
In_Order_ Uninitialized_State
Level_Order_ Uninitialized_State
Pre_Order_ Initialized_State
Post_Order_ Initialized_State
In_Order_ Initialized_State
Level_Order_ Initialized_State
State
This pattern uses design of classes to 228 explicitly order user commands correctly
Douglas C. Schmidt
Douglas C. Schmidt
ET_Event_Handler
Douglas C. Schmidt
Singleton
Options
ET_Command_Factory
ET_Event_Handler
ET_Context
Verbose_ET_ Event_Handler
Succinct_ET_ Event_Handler
ET_Command
Douglas C. Schmidt
% tree-traversal -v format [in-order] expr [expression] print [in-order|pre-order|postorder|level-order] eval [post-order] quit > format in-order > expr 1+4*3/2 > eval post-order 7 > quit
232
Douglas C. Schmidt
Dont hard-code control flow into app logic Dont hard-code event processing logic into app structure
233
Douglas C. Schmidt
234
Douglas C. Schmidt
sources of events & then demux & dispatch the events to the appropriate event handlers
ET_Event_Handler
235
Douglas C. Schmidt
236
Douglas C. Schmidt
237
Douglas C. Schmidt
Interface
238
Douglas C. Schmidt
Interface
Douglas C. Schmidt
Reactor
Intent
Need to decouple event handling from event management infrastructure When multiple sources of events must be handled in a single thread
Structure
e.g., Event_Handler
Douglas C. Schmidt
Reactor
Reactor example in C++
input events in dispatch table private: std::vector <Event_Handler *> dispatch_table_; ... 241
Douglas C. Schmidt
Reactor
Consequences + Simplify concurrency control Non-preemptive Scalability issues
242
Douglas C. Schmidt
Reactor
Consequences + Simplify concurrency control Non-preemptive Scalability issues Implementation
Decouple event detection/demuxing mechanisms from event dispatching e.g., via Bridge Handle many different types of events e.g., input/output events, signals, timers, etc.
243
Douglas C. Schmidt
Reactor
Consequences + Simplify concurrency control Non-preemptive Scalability issues Implementation
Decouple event detection/demuxing mechanisms from event dispatching e.g., via Bridge Handle many different types of events e.g., input/output events, signals, timers, etc.
244 See gee.cs.oswego.edu/dl/cpjslides/nio.pdf for Java Reactor info
Douglas C. Schmidt
Succinct mode
Douglas C. Schmidt
Succinct mode
Douglas C. Schmidt
Douglas C. Schmidt
Singleton
Intent Applicability
Ensure a class only has one instance & provide a global point of access When there must be exactly one instance of a class & it must be accessible from a well-known access point When the sole instance should be extensible by subclassing & clients should be able to use an extended instance without modifying their code Structure
If (uniqueInstance == 0) uniqueInstance = new Singleton; return uniqueInstance;
248
Douglas C. Schmidt
Singleton
Singleton example in C++
// Parse command-line arguments and sets values as follows: // 't' - Traversal strategy, i.e., 'P' for pre-order, 'O' for // post-order, 'I' for in-order, & 'L' for level-order. bool parse_args(int argc, char *argv[]); bool verbose() const; // True if program runs in verbose mode. char traversal_strategy() // Returns desired traversal strategy. ... Accessor methods to check for enabled options private: Make constructor private to prevent multiple instances Options(); static Options *instance_; ... 249 Points to the one & only instance
Douglas C. Schmidt
Singleton
Consequences + Reduces namespace pollution + Makes it easy to change your mind & allow more than one instance + Allow extension by subclassing Same drawbacks of a global if misused Implementation may be less efficient than a global Concurrency/cache pitfalls & communication overhead
Douglas C. Schmidt
Singleton
Consequences + Reduces namespace pollution + Makes it easy to change your mind & allow more than one instance + Allow extension by subclassing Same drawbacks of a global if misused Implementation may be less efficient than a global Concurrency/cache pitfalls & communication overhead Implementation Static instance operation
Douglas C. Schmidt
Singleton
Consequences + Reduces namespace pollution + Makes it easy to change your mind & allow more than one instance + Allow extension by subclassing Same drawbacks of a global if misused Implementation may be less efficient than a global Concurrency/cache pitfalls & communication overhead Implementation Static instance operation
Douglas C. Schmidt
Singleton
Consequences + Reduces namespace pollution + Makes it easy to change your mind & allow more than one instance + Allow extension by subclassing Same drawbacks of a global if misused Implementation may be less efficient than a global Concurrency/cache pitfalls & communication overhead Implementation Static instance operation
Douglas C. Schmidt
Singleton
Options
ET_Command_Factory
ET_Event_Handler
ET_Context
Verbose_ET_ Event_Handler
Succinct_ET_ Event_Handler
ET_Command
Douglas C. Schmidt
Douglas C. Schmidt
ET_Event_Handler
Douglas C. Schmidt
ET_Command_Factory
ET_Event_Handler
ET_Context
Verbose_ET_ Event_Handler
Succinct_ET_ Event_Handler
ET_Command
Douglas C. Schmidt
% tree-traversal -v format [in-order] Verbose mode expr [expression] print [in-order|pre-order|postorder|level-order] eval [post-order] quit > format in-order > expr 1+4*3/2 > eval post-order 7 > quit % tree-traversal > 1+4*3/2 7
258
Succinct mode
Douglas C. Schmidt
Simplify future
Avoid limitations of
Douglas C. Schmidt
260
Douglas C. Schmidt
261
Douglas C. Schmidt
262
Douglas C. Schmidt
263
Douglas C. Schmidt
handle_input() make_handler(bool verbose) prompt_user()=0 get_input(std::string &) make_command(const std::string &input)=0 virtual bool execute_command(ET_Command &)
Commonality: Provides a common interface for handling user input Variability: Subclasses implement various operation modes, e.g.,
verbose vs. succinct mode
264
Douglas C. Schmidt
Template Method
Intent
Provide a skeleton of an algorithm in a method, deferring some steps to subclasses Applicability Implement invariant aspects of an algorithm once & let subclasses define variant parts Localize common behavior in a class to increase code reuse Control subclass extensions Structure
e.g., ET_Event_Handler e.g., Verbose_ET_Event _Handler, Succinct_ ET_Event_Handler
265
Douglas C. Schmidt
Template Method
Template Method example in C++
266
Douglas C. Schmidt
Template Method
Template Method example in C++
Douglas C. Schmidt
Template Method
Template Method example in C++
Douglas C. Schmidt
Template Method
Consequences + Enables inversion of control (Hollywood principle: don't call us we'll call you!) + Promotes code reuse + Lets you enforce overriding rules Must subclass to specialize behavior (cf. Strategy pattern)
269
Douglas C. Schmidt
Template Method
Consequences + Enables inversion of control (Hollywood principle: don't call us we'll call you!) + Promotes code reuse + Lets you enforce overriding rules Must subclass to specialize behavior (cf. Strategy pattern) Implementation Virtual vs. non-virtual template method Few vs. many primitive operations (hook methods)
Douglas C. Schmidt
Template Method
Consequences + Enables inversion of control (Hollywood principle: don't call us we'll call you!) + Promotes code reuse + Lets you enforce overriding rules Must subclass to specialize behavior (cf. Strategy pattern) Implementation Virtual vs. non-virtual template method Few vs. many primitive operations (hook methods)
InterViews Kits ET++ WindowSystem AWT Toolkit ACE & The ACE ORB (TAO) Android Activities
Douglas C. Schmidt
272
Douglas C. Schmidt
std::string input; if(!get_input(input)) Reactor::instance() ->end_event_loop(); ET_Command cmd = make_command(input); if(!execute_command(cmd)) Reactor::instance() ->end_event_loop(); }
Strategy_ET_ Event_Handler
Douglas C. Schmidt
std::string input; if(!get_input(input)) Reactor::instance() ->end_event_loop(); ET_Command cmd = make_command(input); if(!execute_command(cmd)) Reactor::instance() ->end_event_loop(); }
Strategy_ET_ Event_Handler
ET_Command make_command(const std::string &input) { return mc_strategy_.make_command(input); } bool execute_command(const std::string &input) { return ec_strategy_.execute_command(input); }
274
Douglas C. Schmidt
performing steps in the expression tree processing algorithm Variability: Template arguments provided to Strategy_ET_Event _Handler implement various operation modes, e.g., verbose vs. succinct
275
Douglas C. Schmidt
Strategy
Intent Define a family of algorithms, encapsulate each one, & make them interchangeable to let clients & algorithms vary independently Applicability When an object should be configurable with one of many algorithms, and all algorithms can be encapsulated, and one interface covers all encapsulations Structure
276
Douglas C. Schmidt
Strategy
Strategy example in C++
Douglas C. Schmidt
Strategy
Strategy example in C++
Douglas C. Schmidt
Strategy
Strategy example in C++
Douglas C. Schmidt
Strategy
Strategy example in C++
Douglas C. Schmidt
Strategy
Consequences + Greater flexibility, reuse + Can change algorithms dynamically Strategy creation & communication overhead Inflexible Strategy interface Semantic incompatibility of multiple strategies used together
281
Douglas C. Schmidt
Strategy
Consequences + Greater flexibility, reuse + Can change algorithms dynamically Strategy creation & communication overhead Inflexible strategy interface Semantic incompatibility of multiple strategies used together Implementation Exchanging information between a strategy & its context Context is not always necessary Static binding of strategy selection via parameterized types
282
Douglas C. Schmidt
Strategy
Consequences + Greater flexibility, reuse + Can change algorithms dynamically Strategy creation & communication overhead Inflexible strategy interface Semantic incompatibility of multiple strategies used together Implementation Exchanging information between a strategy & its context Context is not always necessary Static binding of strategy selection via parameterized types
283
Douglas C. Schmidt
284
Douglas C. Schmidt
Strategy is commonly used for Black-box frameworks Template Method is commonly used for White-box frameworks
285
Douglas C. Schmidt
Reactor
Event_Handler
Options
ET_Command_Factory
ET_Event_Handler
ET_Context
Verbose_Expression_ Tree_Event_Handler
Macro_Expression_ Tree_Event_Handler
ET_Command
Douglas C. Schmidt
Summary
Pattern-oriented expression tree processing app design has many benefits: Major improvements over the original algorithmic decomposition
Expression_Tree
Component_Node
Composite_ Unary_Node
Leaf_Node
Composite_Binary _Node
Composite_Negate _Node
Douglas C. Schmidt
Summary
Pattern-oriented expression tree processing app design has many benefits: Major improvements over the original algorithmic decomposition Much more modular & extensible
Expression_Tree
Component_Node
Composite_ Unary_Node
Leaf_Node
Composite_Binary _Node
Composite_Negate _Node
Douglas C. Schmidt
Summary
Pattern-oriented expression tree processing app design has many benefits: Major improvements over the original algorithmic decomposition Much more modular & extensible Design matches the domain better
Expression_Tree
Component_Node
Composite_ Unary_Node
Leaf_Node
Composite_Binary _Node
Composite_Negate _Node
Douglas C. Schmidt
Summary
Pattern-oriented expression tree processing app design has many benefits: Major improvements over the original algorithmic decomposition Much more modular & extensible Design matches the domain better Less space overhead
1 Tree Node 0|1|2 290 Koenigs Ruminations on C++ book has another OO expression tree example
Expression_Tree
Component_Node
Composite_ Unary_Node
Leaf_Node
Composite_Binary _Node
Composite_Negate _Node
Summary
Pattern-oriented expression tree processing app design has many benefits: Major improvement over the original algorithmic decomposition Exhibits high pattern density
Iterator
Prototype
291
Summary
Pattern-oriented expression tree processing app design has many benefits: Major improvement over the original algorithmic decomposition Exhibits high pattern density Nearly all classes & objects in design play a role in one or more patterns
Prototype Iterator
292
Summary
Pattern-oriented expression tree processing app design has many benefits: Major improvement over the original algorithmic decomposition Exhibits high pattern density Nearly all classes & objects in design play a role in one or more patterns Patterns help clarify the relationships of myriad classes in the design
Prototype Iterator
293
Douglas C. Schmidt
Summary
Pattern-oriented expression tree processing app design has many benefits: Major improvement over the original algorithmic decomposition Exhibits high pattern density Same design can easily be realized in common OO programming languages
Expression_Tree expr_tree = ...; Print_Visitor print_visitor; C++11 range-based for loop for (auto &iter : expr_tree) iter.accept(print_visitor);
ExpressionTree exprTree = ...; ETVisitor printVisitor = new PrintVisitor(); Java for-each loop for (ComponentNode node : exprTree) node.accept(printVisitor);
294
Douglas C. Schmidt
Summary
Pattern-oriented expression tree processing app design has many benefits: Major improvement over the original algorithmic decomposition Exhibits high pattern density Same design can easily be realized in common OO programming languages C++ & Java solutions are nearly identical, modulo minor syntactical & semantic differences
Expression_Tree expr_tree = ...; Print_Visitor print_visitor; C++11 range-based for loop for (auto &iter : expr_tree) iter.accept(print_visitor);
ExpressionTree exprTree = ...; ETVisitor printVisitor = new PrintVisitor(); Java for-each loop for (ComponentNode node : exprTree) node.accept(printVisitor);
295 See www.vincehuston.org/dp for many examples of patterns in C++ & Java