0

I use state pattern in my application because my state-entities has API the behaviour of which changes due to current state.

In my case that state-dependent API consists only from one method - changeState. This method change state and also execute some state-dependent logic.

But there is the circumstance that made me to ask this question: State dependent logic depends not only on current state, but also it depends on newState (see code below). So I have to use instanceof.

Question: what is the true way to get rid of instanceof?

public interface IStateEntity {
  void changeState(IState newState);
}

public interface IState {
  void doStateDependentLogic(IState newState);
}

public class StateEntityImpl {

  private IState state;

  @Override
  public void changeState(IState newState) {
    state.doStateDependentLogic(newState);
    state = newState;  
  }
}

public class StateImplA { //each state implementation is a singleton

  @Override
  public void doStateDependentLogic(IState newState) {
      // HARDSHIP #1: state dependent logic depends not only on current state, but also it depends on newState. So I have to use `instanceof`
     if (newState instanceof StateImplA) {
       //do one logic
     } else if (newState instanceof StateImplB) {
       //do another logic
     }
  }
}

EDIT (why not enum):

If enum replace a getClass().getSimpleName(): because it's functional programming paradigm. When I make my architecture in such way that it relies on className, that the first sign that something goes wrong.It seems for me that change instanceof to enum doersn't mean to change architecture, but only to change one bad for worse.

If make each IState implementation as enum: each IState implementation is Hibernate entity.

jaco0646
  • 15,303
  • 7
  • 59
  • 83
VB_
  • 45,112
  • 42
  • 145
  • 293
  • 1
    Another approach would be to create an `enum`, enumerating all the possible states and "tag" each `IState` instance with it. If you took that approach, you could even `switch` over it (e.g. `switch(newState.state)` – jedwards Jan 18 '14 at 19:27
  • 1
    If your states are singletons then use an `enum` for your states. You can then use a `switch` in your logic. – Boris the Spider Jan 18 '14 at 19:28
  • @jedwards do you mean that each state should hold an appropriate final enum value? But my state is already a singleton, it mean that `simpleClassName` already accomplish that logic. – VB_ Jan 18 '14 at 19:31
  • @BoristheSpider jedwards more details please – VB_ Jan 18 '14 at 19:32
  • @jedwards see EDIT section of my question – VB_ Jan 18 '14 at 19:37

2 Answers2

2

The accepted way in Java of creating a singleton is with an enum. This provides thread safe initialisation as well as JVM guaranteed singleton status. There are many ways for creating a second instance of a class that is not an enum.

The additional benefit of using an enum is that you are allowed to case switch on the enum in your logic:

public enum State {

    STATE_A {
        @Override
        public void doStateDependentLogic(final State newState) {
            switch (newState) {
                case STATE_B:
                    //do stuff
                //case SOME_OTHER_STATE
            }
        }
    },
    STATE_B {
        @Override
        public void doStateDependentLogic(final State newState) {
        }
    };

    public abstract void doStateDependentLogic(State newState);
}

I don't know where you got the idea that an enum is a functional programming paradigm. This is not the case. enum in Java is designed to do exactly what you want.

Boris the Spider
  • 59,842
  • 6
  • 106
  • 166
  • sorry but I don't point in my question that each state is Hibernate entity. So I can't use `enum` – VB_ Jan 18 '14 at 19:41
  • In that case you will have to use an `enum` as the hibernate qualifier for persistence and add a method to the `interface` that returns that `enum`. – Boris the Spider Jan 18 '14 at 19:43
1

Your problem is not really the state pattern. Your problem lies after the state pattern has done it's job which is to route a method call to a certain implementation that represents a state.

The instanceof problem with subclasses is an independent problem. The solution here is to avoid instanceof like in other code too, preferably with with interaction between the state and it's next state via methods. switch over enum types looks nicer already but is not much better since you still depend on the concrete type of the other object. Could be considered okay since all of them are defined in the same class so the dependency is just on yourself.

What you should end up with in the best case looks something like

  @Override
  public void doStateDependentLogic(IState newState) {
     if (newState.isBeautiful()) {
         System.out.println(newState.getYourPartOfTheInterAction(this));
     } else {
         somethingElse();
     }
  }

i.e. method calls on abstractions without being concerned about the implementation details.

zapl
  • 63,179
  • 10
  • 123
  • 154
  • yes, but if I should choose between three or four newState variants. Than `isBeautiful` turns not so attractive. – VB_ Jan 18 '14 at 21:02
  • 1
    @VolodymyrBakhmatiuk That's just an example. There could be a method return a number, an enum, even a [strategy](http://en.wikipedia.org/wiki/Strategy_pattern) object, anything that provides you more choices than a single boolean. Or add more booleans, you get 2^n ways of determining what you need to do with just them :) – zapl Jan 18 '14 at 21:06
  • 1
    The general idea is that in any interaction you pass your knowledge to the other class (e.g. `willYouMaryMe(this.gender)`) and the other object replies you with it's own knowledge about itself (e.g. `return gender == Gener.Female || this.age > 40`) so that the knowledge about yourself does not need to be hardcoded into the other object. – zapl Jan 18 '14 at 21:12
  • in all cases, in `isBeautiful` method I haven't got any extra logic except cheking if instance is instanceof specific class. Seems that I'll just move instanceof to another method – VB_ Jan 18 '14 at 21:13
  • @VolodymyrBakhmatiuk one thing that came to my mind: Maybe your states are wrong. If `StateA` behaves different depending on the next State you could potentially solve that problem by splitting that state into e.g. `StateAfollowedByB` and `StateAfollowedByC` and thereby remove the need to have a decision based on the next state. – zapl Jan 18 '14 at 22:30