State Design Pattern
Intent
Alows and object to alter its behaviour when its internal state changes. The object appears to change its type.
Applicability
Use the State Pattern in either of the following cases:
- An object’s behaviour dependent on its state and it must change its behaviour at runtime depending on that state.
- Objects have large multi-part conditional statements that depend on the object’s state. The State pattern puts each branch of the conditional in a seperate class.
UML diagram
Consequences
State pattern has following consequences:
- As all the state specific behaviour is lives in a State subclass:
- It localizes state specific behaviour.
- New states and transitions can be added easily by defining new subclass.
-
As state transitions are atomic (happen by rebinding one variable, not several) from the Context perspective, State objects protect the context from inconsistent internal state.
- State objects can be shared : If state objects have no instance variables and the state they represent is encoded in entirely in their type, then context can share a State object.
Implementaion
Implimentation related issue to consider:
- Who defines the state transitions ? : State pattern does not specify which participant defines the criteria for state transitions. If the criteria are fixed then they can be implemented entirely in the Context.
- Creating and destroying state objects :
- if states are not known at runtime, and context change state infrequently then create states only when they are needed.
- If state change occurs rapidly, and you want to avoid destroying states, because they may be needed again shortly, then create states ahead of time and never destroy them.
Example Code
Speed.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.designpatterns.behavioural.state;
public enum Speed {
OFF(0),
LOW(1),
MEDIUM(2),
HIGH(3);
private final int speed;
Speed(int speed) {
this.speed = speed;
}
public int getSpeedValue() {
return speed;
}
}
State.java
1
2
3
4
5
6
package com.designpatterns.behavioural.state;
public interface State {
public Speed increaseSpeed();
public Speed decreaseSpeed();
}
HighSpeedState
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.designpatterns.behavioural.state;
public class HighSpeedState implements State {
@Override
public Speed increaseSpeed() {
return Speed.OFF;
}
@Override
public Speed decreaseSpeed() {
return Speed.MEDIUM;
}
}
MediumSpeedState.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.designpatterns.behavioural.state;
public class MediumSpeedState implements State {
@Override
public Speed increaseSpeed() {
return Speed.HIGH;
}
@Override
public Speed decreaseSpeed() {
return Speed.LOW;
}
}
LowSpeedState
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.designpatterns.behavioural.state;
public class LowSpeedState implements State {
@Override
public Speed increaseSpeed() {
return Speed.MEDIUM;
}
@Override
public Speed decreaseSpeed() {
return Speed.OFF;
}
}
OffState.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.designpatterns.behavioural.state;
public class OffState implements State {
@Override
public Speed increaseSpeed() {
return Speed.LOW;
}
@Override
public Speed decreaseSpeed() {
return Speed.HIGH;
}
}
Regulator.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.designpatterns.behavioural.state;
/*
* This is Context class and responsible for State creation and destruction.
*/
public class Regulator {
private State [] states = {new OffState(), new LowSpeedState(),new MediumSpeedState(), new HighSpeedState()};
Speed currentSpeed;
public Regulator() {
currentSpeed = Speed.OFF;
}
public void increaseSpeed() {
currentSpeed = states[currentSpeed.getSpeedValue()].increaseSpeed();
System.out.println("Current Speed :" + currentSpeed);
}
public void decreaseSpeed() {
currentSpeed = states[currentSpeed.getSpeedValue()].decreaseSpeed();
System.out.println("Current Speed :" + currentSpeed);
}
}
StateDemo.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.designpatterns.behavioural.state;
public class StateDemo {
public static void main(String[] args) {
Regulator regulator = new Regulator();
regulator.increaseSpeed(); //LOW
regulator.increaseSpeed(); //MEDIUM
regulator.decreaseSpeed(); //LOW
regulator.increaseSpeed(); //MEDIUM
regulator.increaseSpeed(); //HIGH
regulator.decreaseSpeed(); //MEDIUM
regulator.increaseSpeed(); //HIGH
regulator.increaseSpeed(); //OFF
}
}
Output
Current Speed :LOW
Current Speed :MEDIUM
Current Speed :LOW
Current Speed :MEDIUM
Current Speed :HIGH
Current Speed :MEDIUM
Current Speed :HIGH
Current Speed :OFF