Monday 30 June 2008

State design pattern in Java

State design pattern is extremely useful when you need a state class. Typical example is a class making a connection to a server (check POP3 client tutorial for example). The class then has two states – connected and disconnected (which is the default state). In the class you would need to check for the state at many places. Let’s imagine that your class doesn’t have just two states but four or ten. You would find then many constructs similar to the following in your class:

public class Door {

private DoorStates state;

private void someMethod() {
// ....
switch (state) {
case CLOSED: // .....
case CLOSING: // .....
case OPENED: // .....
case OPENING: // .....
}
// ...
}

}


The main reasons for State design pattern are extensibility and simplification of the code.

Now I would like to tell you how to implement State designer pattern. The first thing you need to do is to figure out states which your class (let’s call it main class) can have. Let’s have a class handling a connection to a server. The class can certainly have states connected and disconnected. Each state is implemented using its own class (let’s call it state class) and all state classes have one identical ancestor (either interface or abstract class). In state class would be methods that behave differently in each state (the methods that would contain switch (state) { … } ). At first let’s see the main class:

public class Connection {

// state class instance reference
private ConnectionState state;

// list of all possible states
protected final ConnectionState CONNECTED = new ConnectionStateConnected(this);
protected final ConnectionState DISCONNECTED = new ConnectionStateDisconnected(this);

public Connection() {
// default state is disconnected
state = DISCONNECTED;
}

public void connect() {
state.connect();
}

public void disconnect() {
state.disconnect();
}

public boolean isConnected() {
return state.isConnected();
}

// called by a state class to set new state to this connection
protected void setState(ConnectionState state) {
this.state = state;
}

}


The first in the class is the state attribute that holds the actual state of the main class. Next attributes CONNECTED and DISCONNECTED are all possible states that the class can have. Please notice that they are protected. That is because of the state classes which need access them as you will see later. All state methods just delegate the call to the state class.

Now let’s see the ancestor of the all state classes:

public abstract class ConnectionState {

// childs need to have access to the connection instance too
protected final Connection connection;

public ConnectionState(Connection connection) {
this.connection = connection;
}

public abstract void connect();

public abstract void disconnect();

public abstract boolean isConnected();

}


Each state class has to implement the abstract class above. The only interesting thing is the connection attribute. A state class usually need somehow change state of the main class thus it has a main class reference so that the setState(…) method can be called in a state class.

I said that the connection class would have two states – connected and disconnected. We would then have two implementations of the abstract state class named ConnectionStateConnected and ConnectionStateDisconnected.

public class ConnectionStateConnected extends ConnectionState {

public ConnectionStateConnected(Connection connection) {
super(connection);
}

public void connect() {
throw new IllegalStateException("Already connected");
}

public void disconnect() {
// disconnect somehow

// finally set disconnected state of the connection instance
connection.setState(connection.DISCONNECTED);
}

public boolean isConnected() {
return true;
}

}


Please notice how the state class changes the state of the main class in disconnect() method.

public class ConnectionStateDisconnected extends ConnectionState {

public ConnectionStateDisconnected(Connection connection) {
super(connection);
}

public void connect() {
// connect somehow ...

// finally set connected state of the connection instance
connection.setState(connection.CONNECTED);
}

public void disconnect() {
throw new IllegalStateException("Already disconnected");
}

public boolean isConnected() {
return false;
}

}


You have to carefully figure out what to do if a state is called but it shouldn’t be. Example can be calling disconnect() method when there is no connection. You can either throw IllegalStateException or just log warning message wherever.

As you can notice … adding a new state is very simple task. You just add new child of the abstract state class, add new attribute representing the new state to the main class and that’s all.

You can download source codes here: http://rapidshare.com/files/126155409/stateDesignPattern.zip