java design patterns: the state pattern

30
1 State Pattern Antony Quinn

Upload: antony-quinn

Post on 05-Dec-2014

597 views

Category:

Education


1 download

DESCRIPTION

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

TRANSCRIPT

Page 1: Java Design Patterns: The State Pattern

1

State Pattern

Antony Quinn

Page 2: Java Design Patterns: The State Pattern

2

Structure

Intent

Example

UML structure

Benefits and drawbacks

Exercise

Page 3: Java Design Patterns: The State Pattern

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

Page 4: Java Design Patterns: The State Pattern

4

Example: Cell cycle

Our system has 5 states:

Start

Interphase

Mitosis

Cytokinesis

End

It has 2 events:

advance

grow

Page 5: Java Design Patterns: The State Pattern

5

Sample Code

Page 6: Java Design Patterns: The State Pattern

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()

Page 7: Java Design Patterns: The State Pattern

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

}

}

Page 8: Java Design Patterns: The State Pattern

8

class InterphaseState implements CellState {

public void grow(Cell cell) {

cell.makeProtein();

}

public void advance(Cell cell) {

cell.replicateDNA();

cell.setState(new MitosisState());

}

}

Page 9: Java Design Patterns: The State Pattern

9

class MitosisState implements CellState {

public void grow(Cell cell) {

throw new IllegalStateException();

}

public void advance(Cell cell) {

cell.divideNucleus();

cell.setState(new CytokinesisState());

}

}

Page 10: Java Design Patterns: The State Pattern

10

class CytokinesisState implements CellState {

public void grow(Cell cell) {

throw new IllegalStateException();

}

public void advance(Cell cell) {

cell.divideCytoplasm();

cell.setState(new EndState());

}

}

Page 11: Java Design Patterns: The State Pattern

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

}

}

Page 12: Java Design Patterns: The State Pattern

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

}

}

Page 13: Java Design Patterns: The State Pattern

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

Page 14: Java Design Patterns: The State Pattern

14

Structure

Page 15: Java Design Patterns: The State Pattern

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

Page 16: Java Design Patterns: The State Pattern

16

Known Uses Java

JTable selection

Java Media Framework (JMF)

EBI

UniProt automated annotation (Ernst,

Dani and Michael)

Page 17: Java Design Patterns: The State Pattern

17

Question

At what level of complexity would you

refactor code to use the State Pattern?

Page 18: Java Design Patterns: The State Pattern

18

Exercise

Design a Frog class that

contains the state

machine on the left.

Page 19: Java Design Patterns: The State Pattern

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.

Page 20: Java Design Patterns: The State Pattern

20

Solution 1

Page 21: Java Design Patterns: The State Pattern

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

Page 22: Java Design Patterns: The State Pattern

22

Solution 2

Page 23: Java Design Patterns: The State Pattern

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;

}

Page 24: Java Design Patterns: The State Pattern

24

abstract class FrogState {

public FrogState develop() {

throw new IllegalStateException();

}

public FrogState eat() {

throw new IllegalStateException();

}

public FrogState die() {

return new EndState();

}

}

Page 25: Java Design Patterns: The State Pattern

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;

}

}

Page 26: Java Design Patterns: The State Pattern

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.");

}

}

Page 27: Java Design Patterns: The State Pattern

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

}

}

Page 28: Java Design Patterns: The State Pattern

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

}

Page 29: Java Design Patterns: The State Pattern

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

Page 30: Java Design Patterns: The State Pattern

30

Any questions?