patterns for decoupling data structures and algorithms dung “zung” nguyen pepperdine university...
DESCRIPTION
Teaching Recursion ûTraditional Approach XOrdered Insert exampleOrdered Insert example XAlways having to check for the state of the system -- what a pain! XLook-ahead - what a mess! XSwapping -- how confusing! ûHave my goals been lost in the code specifics?TRANSCRIPT
Patterns for DecouplingData Structures and AlgorithmsDung “Zung” Nguyen
Pepperdine University/University of Houston
Stephen Wong Oberlin College
http://exciton.cs.oberlin.edu/research/sigcse99
What’s a “List”?So many authors, so many
definitions! Java 1.2 Foundation Classes (JFC) GoF
Common ground: a minimal and complete set of
methods
Teaching RecursionTraditional Approach
Ordered Insert example Always having to check for the
state of the system -- what a pain! Look-ahead - what a mess! Swapping -- how confusing!
Have my goals been lost in the code specifics?
Back To Basics...
Linear Recursive Structure
A linear recursive structure (LRS) can be empty or non-empty.
If a LRS is empty, it contains no element.
If it is not empty, it contains an element called first, and a LRS object called rest.
A LRS is a 2-state object!
State Pattern Implementation
LRStruct<<context>>
_state
NullCase
All requests delegated to the state.
NullCase
NonNullCase_first : Object
_rest
AState<<abstract state>>
Requests handled by polymorphism!
Invariants vs. Variants
Invariant Behaviors: Intrinsic to the definition of the
data structure. Complete and minimal set.
Variant Behaviors: Extrinsic algorithms operating on
the structure. Composed of invariant methods.
Basic Instinct
KISS“Keep it simple and no simpler.”
Basic behavior of a LRSExpose the components of its structure for the client to manipulate.
Invariant Behaviors
getFirst: Object // “car” insertAsFirst (dat: Object) // “cons” getRest: LRStruct // “cdr”
setFirst (dat: Object) removeFirst: Object setRest (tail: LRStruct)
Functional:
Imperative:
Big Deal!
A LRS should be able to execute any algorithm, past, present, and future, that operates on its structure, without changing any existing code.
execute(algo: IAlgo, input: Object):Object
Added Intelligence
an algorithm is an object!
What kind of object is an algorithm?
Algorithm to insert input into an Ordered LRS
LRS is in Null Case insert input as first in the LRS
LRS is in Non-Null Case If first element < input
insert input as first in the LRS Else
recurse on the rest of the LRS
base case/non-base case coding pattern!
Algorithm Interface
IAlgo+nullCa se(host: LRStruct, par am: Object) : Object+nonNullCa se(host: LRStr uct, para m: Object) : Object
«Interface»
Algo1+nullCase(host: LRStruct, param: Object) : Object+nonNullCas e(host: LRStr uct, param: Object ) : Object
«concrete algorithm»Algo2
«concrete algorithm»
+nullCase(host: LRStruct, param: Object) : Object+nonNullCas e(host: LRStr uct, param: Object ) : Object
IAlgo+nullCase(host: LRStruct, pa ram: Object ) : Object+nonNullCa se(host: LRStruct, pa ram: Object) : Object
Algo1+nullCase(host: LRStruct, param : Object) : Object+nonNullCase(host: LRStruct, param : Object) : Object
«concrete algorithm»Algo2
«concrete algorithm»
+nullCase(host: LRStruct, param : Object) : Object+nonNullCase(host: LRStruct, param : Object) : Object
IAlgo+nullCase(host: LRStruct, pa ram: Object ) : Object+nonNullCa se(host: LRStruct, pa ram: Object) : Object
Algo1+nullCase(host: LRStruct, param : Object) : Object+nonNullCase(host: LRStruct, param : Object) : Object
«concrete algorithm»Algo2
«concrete algorithm»
+nullCase(host : LRStruct, param : Object) : Object+nonNullCase(host: LRStruct, param : Object) : Object
Non-null case handler.
Null case handler.
Who determines which method is invoked?
Algorithm Interface
Non-null case handler.
Null case handler.
Who determines which method is invoked?
IAlgo+nullCa se(host: LRStruct, par am: Object) : Object+nonNullCa se(host: LRStr uct, para m: Object) : Object
«Interface»
Algo1+nullCase(host: LRStruct, param: Object) : Object+nonNullCas e(host: LRStr uct, param: Object ) : Object
«concrete algorithm»Algo2
«concrete algorithm»
+nullCase(host: LRStruct, param: Object) : Object+nonNullCas e(host: LRStr uct, param: Object ) : Object
“Visiting” the State Pattern
_stateLRStruct+e xecute(algo: IAlgo, param : Object) : Object
return _state.execute(algo, this, param);
«host»
AStateexecute(algo: IAlgo, host: LRStruct, para m: O bject) : Object
«abstract state»_state
NonNullCase«concrete state»
_rest
return _state.execute(algo, this, param);
AStateexecute(algo: IAlgo, host : LRStruct, pa ram: Obj ect) : Obj ect
NullCaseexecute(algo: IAlgo, host : LRStruct, param: Obje ct) : Obje ct
«concrete state»
return algo.nullCase(host, param);
NonNullCase«concrete state»
execute(algo: IAlgo, host : LRStruct, param: Obje ct) : Obje ct
return algo.nonNullCase(host, param);
-Object: _firs t
return algo.nullCase(host, param);
_rest
+e xecute(algo: IAlgo, par am: Obje ct) : ObjectAState
execute(algo: IAlgo, host : LRStruct, pa ram: Obj ect) : Obj ect
NullCaseexecute(algo: IAlgo, host : LRStruct, param: Obje ct) : Obje ct
«concrete state»
return algo.nullCase(host, param);
NonNullCase«concrete state»
execute(algo: IAlgo, host : LRStruct, param: Obje ct) : Obje ct
return algo.nonNullCase(host, param);
-Object: _firs t
_rest
return algo.nonNullCase(host, param);
LRStruct+execute(algo: IAlgo, param: Obj ect) : Object
return _state.execute(algo, this, param);
«host»
AStateexecute(algo: IAlgo, host: LRStruct, param: Object) : Object
«abstract state»_state
NullCase«concrete state»
NonNullCase«concrete state»
Request delegated to
Flow Control via polymorphism
Each state calls the appropriate “visiting” method
The Visitor Design Pattern
- Invariant behaviors -Variant behaviors- Consistent “hook” for visitors
- Fixed invocation interface
Hosts VisitorsA system of cooperating objects:
- Separate method for each host
- Call only the desired method in the visitor
Ordered Insertion Abstraction
Null Case Insert here!
Non-Null Case If local value < input
insert here Else
recurse on the restSeems simple enough...
The Drive Towards Abstraction
Key to robust, reusable code.Key to solving new problems.Key to putting different problems
into proper perspectives. Teach students to look for the
abstraction of the problem from the very start.
Traditional Implementation
If empty make new list
with valueElse
If value <inputput value in temp put input in valueif next node empty
• attach temp as rest
Else• attach next node as
temp’s rest• attach temp as this
node’s rest. Else
if next node empty• put input in temp• attach temp as rest
else• recurse on the rest
Hey! What happened to my simple abstraction???
Ordered Insertion Abstraction
Null Case Insert here!
Non-Null Case If local value < input
insert here Else
recurse on the rest
Let’s try this again...
nullCase(host, input) host.insertAsFirst(input)
nonNullCase(host, input) if (host.getFirst() < input)
host.insertAsFirst(input) else
(host.getRest()).execute(this, input)
State+Visitor Implementation
Such magic when the implementation matches the abstraction!
No Flow Control!The LRS host
determines what happens and when.
State+Visitor Lists
Demonstrate the power of a close match between an abstraction and its implementation.
Enable students to concentrate on the fundamentals of data and algorithm abstraction
Enable students to easily isolate the key issues in recursion.
Patterns and Pedagogy
Fundamental thinking tools of CS Abstraction Recursion
Software engineering goals Reusability Extensibility Robustness
ConclusionsThe state pattern properly implements
the abstraction of a list.The abstraction of the algorithm-list
relationship leads to the visitor pattern.
Data structures are naturally decoupled from their algorithms. This can be expressed using the state+visitor pattern.
Further Information http://exciton.cs.oberlin.edu/research/
sigcse99