40

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

 4 years ago
source link: https://www.tuicool.com/articles/hit/aAfiIjF
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK