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

dic*_*oce 8 oop design-patterns

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

封装对象中特定状态下对象的所有行为.将请求委托给"当前"状态对象.

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

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

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

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

我希望有更好的方法来处理这种情况.你怎么看?

Gru*_*eck 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这对于上下文对象来说应该足够了。请注意,班级colourStatePaintbrush班级都没有任何状态转换的知识。这是为了减少职责数量,并提供更灵活的设计。

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

我试图确保我不使用稻草人论证,但我将继续使用该示例。假设在不同的点你想绘制不同的图案,并且使用的颜色必须按照特定的顺序,你的客户将决定何时改变状态,如下所示:

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

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

public void paintChessboard() {
    paintbrush.setColourState(blackColourState);
    // do painting...
    // change state
    paintbrush.setColourState(whiteColourState);
    // etc...
}
Run Code Online (Sandbox Code Playgroud)

这是一个简化的例子,但它通常成立。

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

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

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


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