使用“状态”设计模式改变状态的最佳方法是什么?

时间:2010-01-20 22:09:50

标签: design-patterns oop

我目前对州设计模式的理解基本上是这样的:

封装对象中特定状态的对象的所有行为。将请求委托给“当前”状态对象。

我的问题是:处理状态转换的最佳方法是什么?在我的情况下,很可能“当前”状态对象将决定我们需要转换到的其他状态。我想到了实现这个的两种方法:

  1. 状态对象方法可以返回一些特定值,表示“我正在请求状态转换”。然后,主对象可以查询我们应该转换到的新状态的当前状态,调用ChangeState(),然后将原始请求路由到新状态。

  2. 状态对象本身可以在父对象上调用ChangeState(),然后自己将导致状态更改的请求传递给新对象。

  3. 场景2的优点是主对象只需要将请求委托给“当前”状态(它在内部将处理任何必要的状态转换)。它也许不太明显。

    我希望有更好的方法来处理这种情况。你觉得怎么样?

2 个答案:

答案 0 :(得分:3)

我首选state方法返回新的状态对象(它减少了耦合,更适合S.O.L.I.D.原则)。

这里的例子(这个想法在实际项目中使用):

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) {
        //..
    }
}

答案 1 :(得分:2)

我认为你可能只限于实现状态模式的对象(Context对象和State对象)。情况并非如此,并且还涉及其他对象(客户端)。持有对上下文对象的引用的客户端应该负责转换状态。

考虑这个例子:

// 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;
    }
}

这应该是上下文对象的足够实现。请注意,colourStatePaintbrush类都不知道状态转换。这是为了减少责任的数量,以及提供更灵活的设计。

基本上,状态的变化可能是调用类的责任。如何在代码中实现这一点是实现细节,但重要的是要注意上下文对象和状态对象都不负责转换状态。

我正在努力确保我不使用Strawman论证,但我将继续使用该示例。在不同的点上说你想画出不同的图案,并且使用的颜色必须按照特定的顺序,你的客户会决定何时改变状态,如下所示:

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

可以具有状态或上下文对象指定的颜色顺序,即。有一个Paintbrush的子类叫RainbowPaintbrush,它会选择下一个颜色。或者你的状态对象可以选择下一个状态,在这种情况下你必须有一个RedRainbowColourState,它知道下一个状态是OrangeRainbowColourState,依此类推。但是这两个示例的问题在于您必须进入并修改(通过扩展)上下文和状态对象以实现不同的转换集。但是,如果既不知道转换,也不知道调用类的责任,则可以在不更改状态或上下文对象的情况下完成。即

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

这是一个简化的例子,但它通常都有。

快速阅读维基百科的example of the State pattern表明,各个州都知道下一个州,所以我认为这样做无效。我想总的来说,这是你想要控制的位置以及它如何适合你的问题的权衡。这就是如何使用具体的状态对象进行转换将适合我的蹩脚示例:

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

但请注意,使用这种方式转换所有状态所需的状态类的爆炸性增长。然而,这里的一个优点是客户可以免除责任和如何创建个别状态的知识。在您的情况下,这可能是更好的过渡方式。


总而言之,您可以让各个州执行转换,甚至是上下文对象。另一个选择是让客户端处理状态转换。