开发者

What is the best way, using the "State" design pattern, to change states?

开发者 https://www.devze.com 2022-12-17 09:25 出处:网络
My current understanding of the State design pattern is basically this: Encapsulate all of the behavior of an object in a particular state in an object. Delegate requests to the \"Current\" state ob

My current understanding of the State design pattern is basically this:

Encapsulate all of the behavior of an object in a particular state in an object. Delegate requests to the "Current" state object.

My question is: What is the best way to handle state transitions? In my case, it is likely that "Current" state object will be the one who decides to what other state we need to transition to. I've thought of 2 ways of implementing this:

  1. The state objects methods can return some particular value that means "I'm requesting a state transition". The main object can then query the current state for what new state we should transition to, call ChangeState(), and then route the original request to the new state.

  2. The state object itself can c开发者_JAVA百科all ChangeState() on the parent, and then itself pass the request that caused the state change on to the new object.

Scenario 2 has the advantage that the main object needs only ever delegate requests to the "Current" state (it internally will handle any necessary state transitions). It is also perhaps a little less obvious.

I'm hoping there are known better ways of handling this scenario. What do you think?


I prefere that state method returns new state object (it reduces coupling and more appropriate to S.O.L.I.D. principles).

Here example (this idea uses in real project):

class ExternalContext {
    //...
}

class Entity
{
    public Entity(ExternalContext context)
    {
        //Creating current state with factory method
        state = EntityState.Create(context);
    }

    public void ChangeEntity(ExternalContext context)
    {
        state = state.Change(context);
    }

    private EntityState state;
}

abstract class EntityState
{
    public abstract EntityState Change(ExternalContext externalContext);
    public static EntityState Create(ExternalContext externalContext);
}
class EntityState1 : EntityState {
    public override EntityState Change(ExternalContext externalContext) {
        //..
    }
}


I think you are perhaps limiting yourself to thinking only in terms of the objects implementing the state pattern (Context object and State objects). This is not the case, and there is other objects involved (Clients). It is possible, that the client, which holds a reference to the context object, should have the responsibility of transitioning state.

Consider this made up example:

// Paintbrush is the context object
class Paintbrush {
    // The State object, ColourState would be the abstraction
    private ColourState colourState; 

    // ... other class stuff

    public paint() {
        // Delegation to the state object
        this.colourState.paintInYourSpecificColour(); 
    }

    public void setColourState(ColourState newState) {
        this.colourState = newState;
    }
}

This should be enough implementation for the context object. Note that neither colourState nor the Paintbrush class have any knowledge of state transitions. This is to reduce the number of responsibilities, as well as to provide a more flexible design.

Basically, changes of state could be the responsibility for the calling class. How this is actually achieved in code is implementation detail, but the important thing to note is that neither the context object nor the state object have a responsibility for transitioning state.

I'm trying to make sure I don't use a Strawman argument, but I'll continue to run with the example. Say at different points you wanted to paint different patterns, and the colours used must be in a specific order, your client would decide when to change state, like this:

public void paintRainbow() {
    paintbrush.setColourState(new RedColourState());
    // do painting...
    // Change state to next colour
    paintbrush.setColourState(new OrangeColourState());
    // Chane state again, and so on...
}

You could have the order of colours specified by the state or the context object, ie. have a subclass of Paintbrush called RainbowPaintbrush, and it would pick the next colour. Or your state objects could pick the next state, in which case you would have to have a RedRainbowColourState, which knew the next state was OrangeRainbowColourState and so on. But the problem with both these examples is that you have to go in and modify (by extension) both the context and the state objects to achieve a different set of transitions. However, if neither know the transitions, and that is the responsibility of the calling class, this can be done without changing the state or context object. Ie.

public void paintChessboard() {
    paintbrush.setColourState(blackColourState);
    // do painting...
    // change state
    paintbrush.setColourState(whiteColourState);
    // etc...
}

This is a simplified example, but it generally holds.

A quick read of Wikipedia's example of the State pattern shows that individual states have knowledge of the next state, so I don't think it's invalid do so. I guess overall it's a tradeoff of where you want the control to be, and how that will fit into your problem. This is how using the concrete state objects to transition would fit into my lame example:

public class RedRainbowColourState implements ColourState {
    public void doPaint(Paintbrush paintbrush) {
        // do painting
        ColourState nextStateInRainbow = new OrangePaintbrushColourState();
        paintbrush.setColourState(nextStateInRainbow);
    }  

But note the explosion of state classes that will be required to transition through all the states using this way. However, an advantage here is that the client can be relieved of responsibility and knowledge of how to create the individual states. In your situation, that may be better way to transition.


To summarise, you can let the individual states perform the transition, or even the context object. Another choice is to let the client handle state transitions.

0

精彩评论

暂无评论...
验证码 换一张
取 消