Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Object-Oriented Patterns: A Simplified Look at State Patterns in Java

DZone's Guide to

Object-Oriented Patterns: A Simplified Look at State Patterns in Java

Want to learn a better way to keep your main class focus on its designated job in Java? Check out this post on using State design patterns to learn more.

· Java Zone ·
Free Resource

The CMS developers love. Open Source, API-first and Enterprise-grade. Try BloomReach CMS for free.

There are tons of articles and videos about using object-oriented patterns. If you cannot find a truly good example that resonates with your pre-existing understanding of Java, it becomes a little hard to understand it.  When I was biking with my 8-year-old son, I was always reminding him to gear down when he is biking uphill and to gear up when we are going flat or downhill. This gearing is a great example of how to use the State design pattern. For this tutorial, I will demonstrate the State design pattern through a biking example. I hope it will resonate with your learning style and be easily remembered for future reference.

First, I need to have a bike class. It should be pretty simple:

public class Bike {

    GearState gearState;

    public Bike(){
        gearState = new FirstGear(this);
    }

    public void gearUp(){
        gearState.gearUp();
    }

    public void gearDown(){
        gearState.gearDown();
    }
}


I kept the Bike class as simple as possible. The Bike class above is implemented using the State pattern. It could have been implemented with all of the state-related gear code inside, as long as you are able to use many "if" or "switch" conditions. The latter would be hard to maintain if you have many states. If you have a few states to maintain, the State pattern would complicate your design. 

As you see in the code above, we have GearState , which is nothing but an abstract class as you will see the full code below.  The bike could extend from GearState, but this is not a "IS A" relationship. This is why I did not extend, because, in the State Pattern, extending the State interface is not a common practice.

public abstract class GearState {
    Bike bike;
    GearState(Bike bike){
        this.bike = bike;
    }
    public abstract void gearUp();
    public abstract void gearDown();
}


There can be many gears on a bike, but, for the sake of simplicity, this bike has only three gears: FirstGear SecondGear , and ThirdGear .  Below are the implementations of the gears:

class FirstGear extends  GearState{

    FirstGear(Bike bike) {
        super(bike);
    }

    @Override
    public void gearUp() {
        System.out.println("Moving Up from FirstGear to SecondGear");
        bike.gearState =  new SecondGear(bike);
    }

    @Override
    public void gearDown() {
        System.out.println("Already in the FirstGear - cannot go lower");
    }
}


If you try to gearDown when you are in the FirstGear , you have no lower gear than the FirstGear . Therefore, it does nothing. But, when you try to gearUp , naturally, you move up to the SecondGear . The code below nicely demonstrates that. 

Let's see the other gear states. 

class SecondGear extends  GearState{

    SecondGear(Bike bike) {
        super(bike);
    }

    @Override
    public void gearUp() {
        System.out.println("Moving Up  from SecondGear to ThirdGear");
        bike.gearState =  new ThirdGear(bike);

    }

    @Override
    public void gearDown() {
        System.out.println("Moving Down from SecondGear to FirstGear");
        bike.gearState =  new FirstGear(bike);
    }


}
class ThirdGear extends GearState {

    public ThirdGear(Bike bike) {
        super(bike);
    }

    @Override
    public void gearUp() {
        System.out.println("Already in the ThirdGear - cannot go higher");
    }

    @Override
    public void gearDown() {
        System.out.println("Moving Down from ThirdGear to SecondGear");
        bike.gearState =  new SecondGear(bike);
    }
}


Now, it is time to see it running. Below is the sample main method that demonstrates different state changes. 

public class StateDemo {
    public static void main(String[] args) {
        Bike bike = new Bike();
            bike.gearDown();
            bike.gearUp();
            bike.gearUp();
            bike.gearUp();
            bike.gearUp();
            bike.gearDown();
            bike.gearDown();
            bike.gearDown();   
    }
}


When you run the code above, the following represents the console output.

Already in the FirstGear   — cannot go lower
Moving Up from FirstGear   to  SecondGear 
Moving Up from SecondGear   to ThirdGear  
Already in the ThirdGear   — cannot go higher
Already in the ThirdGear   — cannot go higher
Moving Down from ThirdGear to SecondGear  
Moving Down from SecondGear   to FirstGear  
Already in the  FirstGear  — cannot go lower

Conclusion

If you have many States to keep up with and there are some complex relations between them, the State pattern is the right solution. It will keep your main class, in this case, Bike , focused on its job. Later on, if you would like to add or remove new states, this will be less difficult. 

Hope you enjoyed this demonstration!

BloomReach CMS: the API-first CMS of the future. Open-source & enterprise-grade. - As a Java developer, you will feel at home using Maven builds and your favorite IDE (e.g. Eclipse or IntelliJ) and continuous integration server (e.g. Jenkins). Manage your Java objects using Spring Framework, write your templates in JSP or Freemarker. Try for free.

Topics:
design patterns in java ,java ,state pattern ,object-oriented

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}