250 likes | 398 Views
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. State Pattern. Motivation : some objects may change their behavior at runtime…
E N D
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 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 Behavior Many of the things you do depend on your state of mind Class 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();} …. }
General Technique: Replacing Conditionals by Inheritance abstractclass A {abstract Type f();} Type Function f() { If (condition) doSomething() else doSomethingElse();} final classIfCondition {final Type f() {doSomething(); }} final classElseCondition {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 a b B a A b b C class Automaton { State currentState = State.initial; State state = State.A; publicvoidop_a(final Automaton context) { state.op_a(context); } publicvoidop_b(final Automaton context) { state.op_b(context); } … } a
State as Enumerated Type Class Automaton { …enum State { A { @Override publicvoidop_a(final Automaton context) { context.state = B; } @Override publicvoidop_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 Allow a object to alter its behavior when its internal state changes.
State Known Users • 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 balanced balanced left balanced left right balanced balanced balanced balanced balanced
AVL Insertion Algorithm • Insert: as in ordinary binary search tree. • 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 Tree Implementation public classAVLTree<T extends Comparable<T>> implements Checkable { Node<T> root; publicAVLTree() { root = null; } publicbooleanisEmpty() { return root == null; } publicvoid clear() { root = null; } … }
Inorder Traversal public classAVLTree … { publicvoidinorder(final Visitor<T> v) { inorder(v, root); } interface Visitor<T extends Comparable<T>> { void visit(T n); } privatevoidinorder(Visitor<T> v, Node<T> n) { if (n == null) return; inorder(v, n.left); v.visit(n.data); inorder(v, n.right); }… }
Tree Invariant public classAVLTree … implements Checkable { @Override publicvoid invariant() { inorder(new Visitor<T>() { T previous = null; @Override publicvoid visit(T current) { if (previous != null) positive(current.compareTo(previous)); previous = current; } }); if (root != null) root.invariant(); } staticclass Node<T extends Comparable<T>> implements Checkable { @Override publicvoid invariant() { … } } }
Node Invariant public class AVLTree …{ staticclass Node<T… > implements Checkable { Node<T> left, right; T data; BalancingState b = BalancingState.BALANCED; @Override publicvoid 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 classAVLTree …{privatestatic <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); else n.right = insert(x, n.right); return n; } }
Insertion public classAVLTree …{ public void insert(final T x) { if (find(x) != null) // No need for insertion, nor for rebalancing return; root = insert(x, root); … }privatestatic <T…> Node<T> insert(T x, Node<T> n) { if (n == null) returnnew Node<T>(x); finalint comparison = x.compareTo(n.data); if (comparison == 0) // Found in tree, do nothing. return n; if (comparison < 0) n.left = insert(x, n.left); else n.right = insert(x, n.right); return n; } }
Rebalancing public classAVLTree …{ 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 node Node<T> newLeft = balance(n.left, x); if (newLeft == null) // Height of left subtree increased returnn.tipLeft(); n.left = newLeft; return n; } // Node was inserted to the right of this node … }}
Delegation to State public classAVLTree …{staticclass Node<T…> {BalancingState b = BalancingState.BALANCED; Node<T> tipLeft() { returnb.tipLeft(this); } Node<T> tipRight() { returnb.tipRight(this); } }}
Balancing Factor and the State Pattern public classAVLTree …{ staticclass Node<T… > implements Checkable {BalancingState b = BalancingState.BALANCED;enumBalancingState { BALANCED { public <T …> Node<T> tipLeft(Node<T> me) { me.setState(LEFT); returnnull; // height has increased } public <T …> Node<T> tipRight(Node<T> me) { me.setState(RIGHT); returnnull; // height has increased } }, LEFT {…}, RIGHT {…};publicabstract <T…> Node<T> tipLeft(Node<T> n);public abstract <T…> Node<T> tipRight(Node<T> n); }}}
Actions in an Unbalanced Node public classAVLTree …{ staticclass Node<T… > implements Checkable {BalancingState b = BalancingState.BALANCED;enumBalancingState { BALANCED {…}, LEFT {public <T …> Node<T> tipRight(Node<T> me) { returnme.setState(BALANCED); // height did not change } public <T …> Node<T> tipLeft(Node<T> me) { returnme.left.pivotLeft(me); // height did not change } }, RIGHT {…};publicabstract <T…> Node<T> tipLeft(Node<T> n);public abstract <T …B> Node<T> tipRight(Node<T> n); }}}
Delegating Rotation Responsibility classAVLTree { static class Node …{ Node<T> pivotLeft(Node<T> parent) { returnb.pivotLeft(this, parent); }enumBalancingState { BALANCED {…}, LEFT { public <T …> Node<T> tipLeft(Node<T> me) { returnme.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 contract nonnull(parent); nonnull(me); require(parent.left == me); return parent; } }}}