java design patterns: the state pattern

Post on 05-Dec-2014

597 Views

Category:

Education

1 Downloads

Preview:

Click to see full reader

DESCRIPTION

A short course in the State design pattern using cells, tadpoles and frogs as examples. Code examples are written in Java.

TRANSCRIPT

1

State Pattern

Antony Quinn

2

Structure

Intent

Example

UML structure

Benefits and drawbacks

Exercise

3

Intent

Allow an object to alter its behaviour

when its internal state changes. The

object will appear to change its class.

Also known as

Objects for states

4

Example: Cell cycle

Our system has 5 states:

Start

Interphase

Mitosis

Cytokinesis

End

It has 2 events:

advance

grow

5

Sample Code

6

public class Cell {

private CellState state = new StartState();

public void grow() {

state.grow(this);

}

public void advance() {

state.advance(this);

}

// Package-private

void setState(CellState newState) {

if (state != newState) {

System.out.println(toString(state) + " -> " + toString(newState));

state = newState;

}

}

// Plus: makeProtein(), replicateDNA(), divideNucleus(), divideCytoplasm()

7

interface CellState {

/** @throws IllegalStateException*/

public void grow(Cell cell);

/** @throws IllegalStateException*/

public void advance(Cell cell);

}

class StartState implements CellState {

public void grow(Cell cell) {

throw new IllegalStateException();

}

public void advance(Cell cell) {

cell.setState(new InterphaseState());

}

}

8

class InterphaseState implements CellState {

public void grow(Cell cell) {

cell.makeProtein();

}

public void advance(Cell cell) {

cell.replicateDNA();

cell.setState(new MitosisState());

}

}

9

class MitosisState implements CellState {

public void grow(Cell cell) {

throw new IllegalStateException();

}

public void advance(Cell cell) {

cell.divideNucleus();

cell.setState(new CytokinesisState());

}

}

10

class CytokinesisState implements CellState {

public void grow(Cell cell) {

throw new IllegalStateException();

}

public void advance(Cell cell) {

cell.divideCytoplasm();

cell.setState(new EndState());

}

}

11

class EndState implements CellState {

public void grow(Cell cell) {

throw new IllegalStateException("Dead cells can't grow");

}

public void advance(Cell cell) {

throw new IllegalStateException("Dead cells can't advance");

}

}

12

public class TestCell {

public static void main(String[] args) {

Cell cell = new Cell();

cell.advance(); // Interphase

cell.grow();

cell.grow();

cell.advance(); // Mitosis

cell.advance(); // Cytokinesis

cell.advance(); // End

cell.grow(); // error

}

}

13

Applicability Use the State pattern when

An object's behaviour depends on its

state and must change its behaviour at

run-time depending on that state

Operations have large, multipart

conditional statements that depend on

the object's state, typically switch or

if-else-if constructs

14

Structure

15

Consequences Benefits

Localises state-specific behaviour and

partitions behaviour for different states

Makes state transitions explicit

State objects can be shared

Drawbacks

Lots of classes

16

Known Uses Java

JTable selection

Java Media Framework (JMF)

EBI

UniProt automated annotation (Ernst,

Dani and Michael)

17

Question

At what level of complexity would you

refactor code to use the State Pattern?

18

Exercise

Design a Frog class that

contains the state

machine on the left.

19

Solution

Use an abstract class for common

functionality (but in general we “favor

composition over inheritance” - see the

Strategy pattern)

Start state is transitional, so we can skip it.

20

Solution 1

21

Alternative solution

Could instead let the state's methods return the

new state

Advantages:

No dependency between FrogState and Frog

(looser coupling)

setState is private in Frog

22

Solution 2

23

public class Frog {

private FrogState state = new EmbryoState();

public void develop() {

setState(state.develop());

}

public void eat() {

setState(state.eat());

}

public void die() {

setState(state.die());

}

private void setState(FrogState newState) {

if (state != newState) {

System.out.println(state + " -> " + newState);

state = newState;

}

24

abstract class FrogState {

public FrogState develop() {

throw new IllegalStateException();

}

public FrogState eat() {

throw new IllegalStateException();

}

public FrogState die() {

return new EndState();

}

}

25

class EmbryoState extends FrogState {

public FrogState develop() {

return new TadpoleState();

}

}

class TadpoleState extends FrogState {

public FrogState develop() {

return new AdultState();

}

public FrogState eat() {

System.out.println("Eating algae.");

return this;

}

}

26

class AdultState extends FrogState {

public FrogState eat() {

System.out.println("Eating flies.");

return this;

}

}

class EndState extends FrogState {

public FrogState die() {

throw new IllegalStateException("Dead frogs can't die.");

}

}

27

public class TestFrog {

public static void main(String[] args) {

Frog frog = new Frog(); // Embryo

frog.develop(); // Tadpole

frog.eat();

frog.develop(); // Adult

frog.eat();

frog.eat();

frog.die(); // Dead frog

frog.die(); // Error

}

}

28

import junit.framework.TestCase;

public class EmbryoStateTest extends TestCase {

public void testDevelop() {

FrogState state = new EmbryoState();

FrogState nextState = state.develop();

assertEquals(TadpoleState.class, nextState.getClass());

}

public void testEat() {

FrogState state = new EmbryoState();

try {

FrogState nextState = state.eat();

fail("Embryos can't eat.");

}

catch (Exception e) {

assertEquals(IllegalStateException.class, e.getClass());

}

29

import junit.framework.TestCase;

public class TadpoleStateTest extends TestCase {

public void testEat() {

FrogState state = new TadpoleState();

FrogState nextState = state.eat();

assertEquals(TadpoleState.class, nextState.getClass());

}

public void testDevelop() {

FrogState state = new TadpoleState();

FrogState nextState = state.develop();

assertEquals(AdultState.class, nextState.getClass());

}

public void testDie() {

FrogState state = new TadpoleState();

FrogState nextState = state.die();

assertEquals(EndState.class, nextState.getClass());

30

Any questions?

top related