The State Design Pattern • Idea: internal state == Object • Intent: Allow an object to alter its behaviour when its internal state changes. The object will appear to change its class.
The State Design Pattern
• Idea: internal state == Object• Intent: Allow an object to alter its behaviour
when its internal state changes. The object will appear to change its class.
State Design Pattern
Open() Close() Acknowledge()
TCPConnection
Open() Close() Acknowledge()
TCPState*
Open() Close() Acknowledge()
TCPEstablished
Open() Close() Acknowledge()
TCPListen
Open() Close() Acknowledge()
TCPClosed
state->Open()
state
State Pattern• Motivation: some objects may change their behavior at
runtime…– An individual: Prince / Frog
• When kissed, turns into a prince• When cursed, turns into a frog.• If a prince, will go into duel • If a frog, will quack.
– Another individual: Dr. Jekyll / Mr. Hyde• At night: will turn into a Mr. Hyde.• At day: will turn into Dr. Jekyll• If Jekyll: cure patients• If Hyde: murder prostitutes
– A TCP connection: may be open, may be closed.– A Graphic tool: may be a select tool, but may also be a pen
Typical State BehaviorClass C {
field f; method task() {
if (f == someValue)doTaskOneWay;
else if (f == someOtherValue) doTaskInAnotherWay();
} method anotherTask() {
if (f == someValue)doAnotherTaskOneWay;
else if (f == someOtherValue) doAnotherTaskInAnotherWay();
} ….}
Many of the things you do depend
on your state of
mind
General Technique: Replacing Conditionals by Inheritance
Type Function f() { If (condition) doSomething() else doSomethingElse();}
abstract class A { abstract Type f();}
final class IfCondition { final Type f() { doSomething(); }}
final class ElseCondition { final Type f() { doSomethingElse(); }}
State Pattern
• Reaps the “if to inheritance” technique• Useful if many methods exhibit different
behavior depending on the same state variables.
Finite Automaton Example
class Automaton {State currentState = State.initial;State state = State.A;
public void op_a(final Automaton context) {state.op_a(context);
}
public void op_b(final Automaton context) {state.op_b(context);
}…
}
A B
C
aa
a
b
b
b
State as Enumerated TypeClass Automaton {
…enum State {
A {@Override public void op_a(final Automaton context)
{context.state = B;
}
@Override public void op_b(final Automaton context) {
context.state = C;}
},B {…}C {…};
public abstract void op_a(final Automaton context);public abstract void op_b(final Automaton context);
}
State Structure
Request()
Context
Handle()
State*
state->Handle()
Handle()
ConcreteStateA
state
Handle()
ConcreteStateB
Allow a object to alter its behavior when its internal state changes.
State Known Users
MousePressed() ProcessKeyboard() Initialize()
DrawingController
HandleMousePress() HandleMouseRelease() GetCurser() Activate()
Tool*currentTool
CreationTool SelectionTool TextTool
• Easy to factor out similar strategies (using inheritance)
Example: AVL Tree• AVL Tree: A balanced binary search tree.
– Main property: in all nodes• Height of left and right subtrees can differ by at most 1
– Node can be either: • Balanced: same height of left- and right- subtrees • Left tipped: left subtree is higher (by 1)• Right tipped: right subtree is higher (by 1)
– Example: tree with only one node.• Node is balanced
• The challenge– Insertion of more nodes, while maintaining the AVL
property.
An AVL Tree
left
balanced
balanced
balanced
balanced
balanced
balanced
balanced
balanced
rightleft
AVL Insertion Algorithm1. Insert: as in ordinary binary search tree.2. Balance: traverse the insertion path, bottom up:
– Examine subtree:• If height did not increase, stop traversal.
– Examine parent (height of subtree changed)• Continue:
– Balanced: tip in the appropriate direction.• Stop:
– Right inclined and arriving from left, or– Left inclined and arriving from right:
» Make parent balanced» Stop traversal
• Reorganize and stop:– Right tipping: parent is right inclined and arriving from right,– Left tipping: parent is left inclined and arriving from right:
» Make a local reorganization» Stop
AVL Reorganization
AVL Tree Implementationpublic class AVLTree<T extends Comparable<T>>
implements Checkable {
Node<T> root;
public AVLTree() {root = null;
}
public boolean isEmpty() {return root == null;
}
public void clear() {root = null;
}…
}
Inorder Traversalpublic class AVLTree … {
public void inorder(final Visitor<T> v) {inorder(v, root);
}
interface Visitor<T extends Comparable<T>> {void visit(T n);
}
private void inorder(Visitor<T> v, Node<T> n) {if (n == null)
return;inorder(v, n.left);v.visit(n.data);inorder(v, n.right);
}…}
Tree Invariantpublic class AVLTree … implements Checkable {
@Override public void invariant() {inorder(new Visitor<T>() {
T previous = null;
@Override public void visit(T current) {if (previous != null)
positive(current.compareTo(previous));previous = current;
}});if (root != null)
root.invariant();}static class Node<T extends Comparable<T>>
implements Checkable {@Override public void invariant() { … }
}}
Node Invariantpublic class AVLTree …{static class Node<T… > implements Checkable {
Node<T> left, right;T data;BalancingState b = BalancingState.BALANCED;@Override public void invariant() {
final int hl = height(left); nonnegative(hl);final int hr = height(right); nonnegative(hr);switch (hl - hr) {case 1: sure(b == BalancingState.LEFT); break;case 0: sure(b == BalancingState.BALANCED); break;case -1: sure(b == BalancingState.RIGHT); break; default: unreachable();}
if (right != null) right.invariant();if (left != null) left.invariant(); }
}}
Insertion
public class AVLTree …{private static <T…> Node<T> insert(T x, Node<T> n) {if (n == null)return new Node<T>(x);
final int comparison = x.compareTo(n.data);if (comparison == 0) // Found in tree, do nothing.return n;
if (comparison < 0)n.left = insert(x, n.left);
elsen.right = insert(x, n.right);
return n;}
}
Insertionpublic class AVLTree …{public void insert(final T x) {if (find(x) != null) // No need for insertion, nor for rebalancingreturn;
root = insert(x, root);…
}private static <T…> Node<T> insert(T x, Node<T> n) {if (n == null)return new Node<T>(x);
final int comparison = x.compareTo(n.data);if (comparison == 0) // Found in tree, do nothing.return n;
if (comparison < 0)n.left = insert(x, n.left);
elsen.right = insert(x, n.right);
return n;}
}
Rebalancingpublic class AVLTree …{public void insert(final T x) {…root = Defaults.to(balance(root, x), root);
}private static <T ….> Node<T> balance(Node<T> n, T x) {comparison = x.compareTo(n.data);if (comparison == 0) // This is the leaf where the node was inserted.return null;
if (comparison < 0) { // Node was inserted to the left of this nodeNode<T> newLeft = balance(n.left, x);if (newLeft == null) // Height of left subtree increasedreturn n.tipLeft();
n.left = newLeft;return n;
}// Node was inserted to the right of this node…}
}
Delegation to State
public class AVLTree …{
static class Node<T…> {BalancingState b = BalancingState.BALANCED;Node<T> tipLeft() {return b.tipLeft(this);
}
Node<T> tipRight() {return b.tipRight(this);
}}
}
Balancing Factor and the State Patternpublic class AVLTree …{static class Node<T… > implements Checkable {BalancingState b = BalancingState.BALANCED;enum BalancingState {
BALANCED { public <T …> Node<T> tipLeft(Node<T> me) {
me.setState(LEFT);return null; // height has increased
}public <T …> Node<T> tipRight(Node<T> me) {
me.setState(RIGHT);return null; // height has increased
}},LEFT {…},RIGHT {…};public abstract <T…> Node<T> tipLeft(Node<T> n);public abstract <T…> Node<T> tipRight(Node<T> n);
}}}
Actions in an Unbalanced Nodepublic class AVLTree …{static class Node<T… > implements Checkable {BalancingState b = BalancingState.BALANCED;enum BalancingState {
BALANCED {…},LEFT {
public <T …> Node<T> tipRight(Node<T> me) {return me.setState(BALANCED); // height did not change
}public <T …> Node<T> tipLeft(Node<T> me) {
return me.left.pivotLeft(me); // height did not change}
},RIGHT {…};public abstract <T…> Node<T> tipLeft(Node<T> n);public abstract <T …B> Node<T> tipRight(Node<T> n);
}}}
Delegating Rotation Responsibilityclass AVLTree {static class Node …{
Node<T> pivotLeft(Node<T> parent) {return b.pivotLeft(this, parent);
}enum BalancingState {
BALANCED {…},LEFT {
public <T …> Node<T> tipLeft(Node<T> me) {return me.left.pivotLeft(me); // height did not change
}Node<T> pivotLeft(Node<T> me, Node<T> parent) {
// LEFT LEFT balancing}
},RIGHT {…};<…> Node<T> pivotLeft(Node<T> me,Node<T> parent) {// Default implementation, setting the method contractnonnull(parent);nonnull(me);require(parent.left == me);return parent;
}}}}