The State Design Pattern Idea: internal state == Object Intent: Allow an object to alter its behaviour when its internal state changes. The object will.

Post on 01-Jan-2016

219 Views

Category:

Documents

1 Downloads

Preview:

Click to see full reader

Transcript

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 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;

}}}}

top related