简体   繁体   中英

State pattern java

I am learning design pattern in java

I was doing through some of links.I am trying to design a washing machine by state pattern

I have a query regarding the implementation of state design pattern

public interface State {

   public void openLid();
   public void closeLid();
   public void start();
   public void stop();
   public void washing();
  } 

 public class Idle implements State{
 //implementing overidden methods
 .......

 }

 public class Washing implements State {
       //implementing overidden methods
       .......
  }


 public class WashingMachine {
   State state;

   public WashingMachine(State state) {
    this.state =  new Idle();
   }

  public State getState() {
    return state;
   }

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

 }

I want to know when switching between state between idle to washing the implementation there can be two ways which is saw over net

1. WashingMachine class implements State interface and switch state from Idle to washing or vice versa based on some condition

2. Idle and Washing class has WashingMachine as member variable.

Please any one can suggest I am a bit confused about the implementation part.

Before addressing your question, I prefer reviewing the idea of the pattern and propose you some little modifications in your code.

State pattern allows an object to alter its behavior when its internal state changes.

In your case, Idle and Washing are good candidates as states and WashingMachine a good candidate to bear the state object.

However, three remarks :

1) Methods provided by the states should be some actions which implementations differ according to under which state the object is.

In your declaration :

public interface WashingMachineState {    
   public void openLid();
   public void closeLid();
   public void start();
   public void stop();
   public void washing();
  } 

washing() is not a action but a state.
It is the start() action that changes the state from idle to washing.

In the state pattern, the object with a state is named the context .
In your case, the context is WashingMachine .

2) in the state pattern, the idea is that the context wants to perform some actions which the behavior changes according to the current state.
To achieve that, the context delegates its processings to its current state instance.
It avoid having many if - else if in the context (for each processing), and it allows also to reduce the complexity of the context because when you use the state pattern, you get families of behaviors : :

  • behaviors when we are in the idle state are located in the IdleState class.

  • behaviors when we are in the washing state are located in WashingState class.

  • and so for...

To perform the actions, state instances need the context ( WashingMachine ).
To address this question, you have two ways of doing :

Either storing the WashingMachine object as a field in the state instance or passing this as an argument when the context WashingMachine object delegates to the state the processing.

I propose you to use the stateless way.
So, when the startWashing() operation is called on a WashingMachine instance, the WashingMachine instance should delegate the processing to state.startWashing() by passing itself such as state.startWashing(this) .
The state should provide as parameter a WashingMachine :

public interface WashingMachineState {    
   void openLid(WashingMachine machine);
   void closeLid(WashingMachine machine);
   void pushStartBtn(WashingMachine machine);
   void pushStopBtn(WashingMachine machine);
} 

3) Actually you defined two states : idle and washing.
These should be completed with a stopping state because some operations on the machine (opening the door, pushing the start btn for example...) have a specific behavior when the machine is in the "is stopping" state.
Note that with only two states, you may also wonder if the pattern is relevant.


Now, I can answer to your question.

I want to know when switching between state between idle to washing the implementation there can be two ways which is saw over net

1. WashingMachine class implements State interface and switch state from Idle to washing or vice versa based on some condition

2.Idle and Washing class has WashingMachine as member variable.

WashingMachine and WashingMachineState s are collaborating but different things.
So they have to not rely on the same interface.
Adding WashingMachine object as fields of state subclasses is a possibility.
As explained, you can also pass the WashingMachine as parameter of the State methods.

Note that it is not directly the WashingMachine that performs the switch from a state to another one.
This is performed by the state.
And states should invoke WashingMachine.changeState() to perform it.

The WashingMachine could be :

public class WashingMachine {

   private WashingMachineState state;

   public WashingMachine() {
     this.state =  new Idle();
   }        

   protected void changeState(WashingMachineState state) {
     this.state = state;      
   }

   public void openLid(){
     state.openLid(this);
   } 

   public void closeLid(){
     state.closeLid(this);         
   } 
   public void pushStartBtn(){
     state.pushStartBtn(this);
   } 

   public void pushStopBtn(){
     state.pushStopBtn(this);
   } 

   public State getState() {
      return state;
   }

 }

Explanations about modifications on WashingMachine :

  • changeState is more meaningful as setState when using state pattern.

  • changeState(State) could use the protected modifier to decrease the visibility of this method and of course state subclasses should be in the same package than WashingMachine . It is a implementation detail enabled in Java. With other OOP languages, you have other alternatives of course.

About switching from idle to washing, I think that it should be possible only in the IdleState state as pushStartBtn() is invoked.

Here is an example :

public class IdleState implements State {    
   public void openLid(WashingMachine machine){
       ...
   }
   public void closeLid(WashingMachine machine){
       ...
   }
   public void pushStartBtn(WashingMachine machine){
      //do processing with machine to begin effectively the washing
         ...
      machine.changeState(new WashingState());
   }

   public void pushStopBtn(WashingMachine machine){
       ...
   }
 } 

I think that better choice would be to create

public enum State{IDLE,WASHING};

and use that. This enum could be named even WashingMachineState as the states you have mentioned are specified to washing machine only - they will not be reusable, so no point of interfaces here.

If you would want to share the same states between different devices, eg. WashingMachine and DishWasher then you could use

public interface Statefull{
   public State getState();
   public void changeState(State newState);
}

And let WashingMachine and DishWasher implement Statefull interface. With Java 8's default interface implementation you could even include getter implemention in interface so no boilerplate code in implementing classes.

Okay, let's go through both of your ideas:

1.) Is the washing machine a state? No, definitely no. Then why would it need to implement the state interface?

2.) This almost works, but it makes the states not reusable (which for a small implementation like this isn't bad, but for something like a game, it's terrible). They can only be the states of a washing machine. What if I want to use these states for a dish washer?

What you should do:

Make an interface or class, something like StateManager, and make the WashingMachine implement this, then instead of using a concrete class, create a StateManager field in the states.

Example:

public abstract class StateManager {
    public State state;

    public void setState(State newState) {
        state = newState;
        newState.parent = this;
    }
    public State getState() {
        return state;
    }
}

State:

public abstract class State {
    public StateManager parent;
    // Whenever you want to set the object's state, use this
}

Example State:

public class WashingState extends State {
    // your methods here
}

Example state manager:

public class WashingMachine extends StateManager {
    // your methods here.
}

I changed everything zo classes instead of interface, because they make creating some objects easier (like if you want to make a dishwasher and a washing machine, then you can extract the washing part from them, and make a separate class for it)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM