DESIGN PATTERNS SAGA #15: REAL WORLD SITUATIONS WITH STATE

When it’s necessary to maintain State from an object we can create a big code full of ifs controlling the State from the class. Certainly, this is not the right approach to solve this problem. Repetition of code is what we must avoid, we must follow the DRY (Don’t repeat yourself) principle to keep the code flexible and powerful.

state_diagram.PNG

Get the Design_Patterns_Saga_GitHub_Source_Code

Let’s first see a bad example of a “State” implementation.

1 – State Bad Example: There are two constants, OPEN and CLOSED. Whenever the openWindow or closeWindow methods are invoked all the rules will be manipulated inside them. As you can see in the code below, there is repetition.

public class AutomaticWindow {

	final static int CLOSED = 0;
	final static int OPEN = 1;

	int state = CLOSED;

	public AutomaticWindow() {
		super();
	}

	public void openWindow() {
		if (state == OPEN) {
			System.out.println("Window is already open");
		} else if (state == CLOSED) {
			System.out.println("Opening window.");
			state = OPEN;
		}
	}

	public void closeWindow() {
		if (state == OPEN) {
			System.out.println("Closing window.");
			state = CLOSED;
		} else if (state == CLOSED) {
			System.out.println("Window is already closed.");
		}
	}

	public String toString() {
		if (state == OPEN) {
			return "Window is open";
		} else {
			return "Window is closed";
		}
	}
}

Let’s now see how to use the State pattern for real! You will see the difference of a much cleaner and flexible code.

1 – State: It is the generic abstract class that handles the request from any class that must keep the State.

public abstract class State {

	public abstract void handleRequest();

}

2 – State concrete classes: They are basically the classes that extend the State class. They are responsible for changing the State to the next one in the sequence.

public class GreenTrafficLightState extends State {

	private TrafficLight trafficLight;

	public GreenTrafficLightState(TrafficLight trafficLight) {
		this.trafficLight = trafficLight;
	}

	@Override
	public void handleRequest() {
		System.out.println("Turning traffic light to yellow.");
		trafficLight.setState(trafficLight.getYellowLightState());
	}

	public String toString() {
		return "Traffic light is green.";
	}
}

public class RedTrafficLightState extends State {

	private TrafficLight trafficLight;

	public RedTrafficLightState(TrafficLight trafficLight) {
		this.trafficLight = trafficLight;
	}

	@Override
	public void handleRequest() {
		System.out.println("Turning traffic light to green...");
		trafficLight.setState(trafficLight.getGreenLightState());
	}

	public String toString() {
		return "Traffic light is on red.";
	}
}

public class YellowTrafficLightState extends State {

	private TrafficLight trafficLight;

	public YellowTrafficLightState(TrafficLight trafficLight) {
		this.trafficLight = trafficLight;
	}

	@Override
	public void handleRequest() {
		System.out.println("Turning traffic light to red.");
		trafficLight.setState(trafficLight.getRedLightState());
	}

	public String toString() {
		return "Traffic light is yellow.";
	}
}

3 – Orchestrator: This class is responsible for orchestrating the changes of the traffic lights’ State. In the constructor, we initialize all the States and we pass the same instance in the constructor to each one of the States classes. In the changeState method, we just delegate the call to the State instance variable to change the State.

public class TrafficLight {

	State red;
	State yellow;
	State green;

	State state;

	public TrafficLight() {
		red = new RedTrafficLightState(this);
		yellow = new YellowTrafficLightState(this);
		green = new GreenTrafficLightState(this);

		state = red;
	}

	public void changeState() {
		state.handleRequest();
	}

	public String toString() {
		return state.toString();
	}

	public State getGreenLightState() {
		return green;
	}

	public State getYellowLightState() {
		return yellow;
	}

	public State getRedLightState() {
		return red;
	}

	public void setState(State state) {
		this.state = state;
	}
}

4- Unit Tests: Now let’s see if the states are changing as expected. We are expecting that traffic lights change from red to green, yellow to red and so forth. In the window case, we want it to open and close.

public class StateTest {

	@Test
	public void stateTest() {
		TrafficLight trafficLight = new TrafficLight();

		trafficLight.changeState();

		trafficLight.changeState();

		trafficLight.changeState();

		trafficLight.changeState();

		Assert.assertEquals(trafficLight.state.getClass(),
				GreenTrafficLightState.class);
	}

	@Test
	public void badExampleStateTest() {
		AutomaticWindow automaticWindow = new AutomaticWindow();

		automaticWindow.openWindow();

		automaticWindow.closeWindow();

		Assert.assertEquals(automaticWindow.toString(),
				"Window is closed");
	}

}

Summary of actions:

  1. Created the State abstract generic class.
  2. Created the State classes that extend the State class.
  3. Implemented the handleRequest method from the State class.
  4. Created the TrafficLight class to manipulate the States.
  5. Created the changeState method in the TrafficLight class.

To practice the State Pattern you can create another State class with another scenario, for example changing any light state or anything else. Keep in mind that practicing the Patterns is crucial to fully master them.  Be sure to use TDD (Test Driven Development).

DESIGN PATTERNS SAGA #15: REAL WORLD SITUATIONS WITH STATE

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s