sigcse99stat - Rice University

Download Report

Transcript sigcse99stat - Rice University

Patterns for Decoupling
Data Structures
and Algorithms
Dung “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 Recursion
 Traditional 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>>
All requests
delegated to
the state.
_state
NullCase
AState
<<abstract state>>
_rest
NonNullCase
NullCase
_first : Object
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 LRS
Expose the components of its
structure for the client to
manipulate.
Invariant Behaviors
 Functional:
 getFirst: Object // “car”
 insertAsFirst (dat: Object) // “cons”
 getRest: LRStruct // “cdr”
 Imperative:
 setFirst (dat: Object)
 removeFirst: Object
 setRest (tail: LRStruct)
Big Deal!
Added Intelligence
 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
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
IAlgo
«Interface»
+nullCase(host:
+nullCase(host:
LRStruct,
LRStruct,
pa ram:
pa ram:
Object)
Object)
: Object
: Object
Null case handler. +nonNullCa
IAlgo
+nonNullCa
se(host:
se(host:
LRStruct,
LRStruct,
pa ram:
pa
ram:
Object)
Object)
: Object
: Object
+nullCa se(host: LRStruct, par am: Object) : Object
+nonNullCa se(host: LRStr uct, para m: Object) : Object
Non-null case handler.
«concrete
«concrete
algorithm»
algorithm»
Algo1
Algo1
«concrete
«concrete
algorithm»
algorithm»
Algo2
Algo2
nullCase(host:
+nullCase(host:
LRStruct,
LRStruct,
param
param
: Object)
: Object)
: Object
: Object
«concrete
algorithm»
nonNullCase(host:
+nonNullCase(host:
LRStruct,
LRStruct,
param
param
: Object)
: Object)
: Object
: Object
+nullCase(host:
+nullCase(host:
LRStruct,
LRStruct,
param
param
: Object)
: Object)
: Object
: Object
«concrete
algorithm»
+nonNullCase(host:
+nonNullCase(host:
LRStruct,
LRStruct,
param
param
: Object)
: Object)
: Object
: Object
Algo1
Algo2
+nullCase(host: LRStruct, param:
Object)
: Object
+nullCase(host:
param: Object) : O
Who
determines
which
methodLRStruct,
is invoked?
+nonNullCas e(host: LRStr uct, param: Object) : Object
+nonNullCas e(host: LRStr uct, param: Objec
Algorithm Interface
Null case handler.
«Interface»
IAlgo
+nullCa se(host: LRStruct, par am: Object) : Object
+nonNullCa se(host: LRStr uct, para m: Object) : Object
Non-null case handler.
«concrete algorithm»
«concrete algorithm»
Algo1
Algo2
+nullCase(host: LRStruct, param:
: Object
+nullCase(host:
param: Object)
WhoObject)
determines
which
method isLRStruct,
invoked?
+nonNullCas e(host: LRStr uct, param: Object) : Object
+nonNullCas e(host: LRStr uct, param: Obje
“Visiting” the State Pattern
returnreturn
_state.execute(algo,
this,
param);
this,
param);
return_state.execute(algo,
_state.execute(algo,
this,
param);
Request delegated to
AState
AState
execute(algo:
IAlgo,
host:
LRStruct,
pa
ram:
ObjObj
ect)ect)
: Obj
ect ect
execute(algo:
IAlgo,
host:
LRStruct,
pa ram:
: Obj
«abstract
state»
«abstract
state»
_state
_state
_state
AState
AState
+e xecute(algo: IAlgo,«host»
par
am: Obje ct) : Object
«host»
LRStruct
LRStruct
+execute(algo:
IAlgo, param:
ect) : Object
+e_rest
xecute(algo: IAlgo,
param :Obj
Object)
: Object
execute(algo:IAlgo,
IAlgo, host: LRStruct,
Object)
: Object
execute(algo:
LRStruct,param:
para m:
Object)
: Object
_rest
_rest
Flow Control via polymorphism
«concrete
state»
«concrete
state»
NonNullCase
NonNullCase
«concretestate»
state»
-Object:
-Object:
_first_first
«concrete
execute(algo:
IAlgo,
host:
LRStruct,
param:
: Obje
execute(algo:
IAlgo,
host:
LRStruct,
param:
ObjeObje
ct) :ct)
Obje
ct ct
NonNullCase
NonNullCase
return algo.nonNullCase(host, param);
return
algo.nonNullCase(host,
param);
return
algo.nonNullCase(host,
param);
«concrete
state»
«concrete
state»
NullCase
NullCase
«concrete
state»
execute(algo:
IAlgo,
host:
LRStruct,
param:
: Obje
execute(algo:
IAlgo,
host:
LRStruct,
param:
ObjeObje
ct) :ct)
Obje
ct ct
NullCase
return algo.nullCase(host, param);
return
algo.nullCase(host,
param);
return
algo.nullCase(host,
param);
Each state calls the appropriate “visiting” method
The Visitor Design Pattern
A system of cooperating objects:
Hosts
Visitors
- Invariant behaviors
- Consistent “hook”
for visitors
-Variant behaviors
- Fixed invocation
interface
- Call only the desired
method in the visitor
- Separate method
for each host
Ordered Insertion Abstraction
 Null Case
 Insert here!
 Non-Null Case
 If local value < input
insert here
 Else
recurse on the rest
Seems 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
 Else
• attach next node
as temp’s rest
• attach temp as this
node’s rest.
 make new list with
value
 Else
 If value <input
 put value in temp
 put input in value
 if next node empty
• attach temp as 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
Let’s try this again...
 Null Case
 Insert here!
 Non-Null Case
 If local value < input
insert here
 Else
recurse on the rest
State+Visitor Implementation
 nullCase(host, input)
 host.insertAsFirst(input)
 nonNullCase(host, input)
 if (host.getFirst() < input)
No Flow Control!
The LRS host
determines what
happens and when.
host.insertAsFirst(input)
 else
(host.getRest()).execute(this, input)
Such magic when the implementation
matches the abstraction!
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
Conclusions
 The 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