Java Design Patterns: The State Pattern

1,408 views

Published on

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

Published in: Education, Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,408
On SlideShare
0
From Embeds
0
Number of Embeds
14
Actions
Shares
0
Downloads
40
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Java Design Patterns: The State Pattern

  1. 1. 1 State Pattern Antony Quinn
  2. 2. 2 Structure  Intent  Example  UML structure  Benefits and drawbacks  Exercise
  3. 3. 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. 4. 4 Example: Cell cycle  Our system has 5 states: Start Interphase Mitosis Cytokinesis End  It has 2 events: advance grow
  5. 5. 5 Sample Code
  6. 6. 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; } }
  7. 7. 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. 8. 8 class InterphaseState implements CellState { public void grow(Cell cell) { cell.makeProtein(); } public void advance(Cell cell) { cell.replicateDNA(); cell.setState(new MitosisState()); } }
  9. 9. 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. 10. 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. 11. 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. 12. 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. 13. 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. 14. 14 Structure
  15. 15. 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. 16. 16 Known Uses  Java JTable selection Java Media Framework (JMF)  EBI UniProt automated annotation (Ernst, Dani and Michael)
  17. 17. 17 Question  At what level of complexity would you refactor code to use the State Pattern?
  18. 18. 18 Exercise Design a Frog class that contains the state machine on the left.
  19. 19. 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. 20. 20 Solution 1
  21. 21. 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. 22. 22 Solution 2
  23. 23. 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. 24. 24 abstract class FrogState { public FrogState develop() { throw new IllegalStateException(); } public FrogState eat() { throw new IllegalStateException(); } public FrogState die() { return new EndState(); } }
  25. 25. 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. 26. 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. 27. 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. 28. 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. 29. 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();
  30. 30. 30 Any questions?

×