22

我手头有一个问题,我不知道要使用哪种设计模式。问题是这样的:

我必须构建一个具有“N”个状态的系统,并且我的系统必须根据某些条件从任何状态转换到任何其他状态。例如:在条件 1 下,从状态 1 移动到 3,在条件 2 下从状态 1 移动到 4。

甚至从一种状态到另一种状态的转换也可以在 2 个或更多不同的条件下完成。

例如,从状态 1 到状态 3 的转换可以在以下情况下完成:
条件 1:“星期天”
条件 2:“下雨”
条件 3:“下雨和星期天”
在每个条件下,状态 3 的处理可以是不同的。

我希望我能够清楚地理解这个问题。请帮忙。

非常感谢

4

6 回答 6

41

它显然是一个有限状态机的例子,但最好结合条件而不是为每个组合创建一个新条件。我不喜欢 Wikipedia 上状态模式的 Java 示例,因为状态知道其他状态,这在很多情况下都没有意义。跟踪状态、适用条件状态的转换表有助于解决该问题。

面向对象有限状态机的两分钱。您可以在 OO 方面进行一些改进,但它可以让想法得到传达。

class Transition {
    State from;
    Set<Condition> conditions;
    State to;
}

class State {
    String state;
}

class Condition {
    String condition;
}

可以使用上述类型构建状态机。没有错误检查,但是如果在某些情况下找不到下一个状态,您可以抛出异常或其他东西。

class StateMachine {
    List<Transition> transitions;
    State current;

    StateMachine(State start, List<Transition> transitions) {
        this.current = start;
        this.transitions = transitions;
    }

    void apply(Set<Condition> conditions) {
        current = getNextState(conditions);
    }

    State getNextState(Set<Condition> conditions) {
        for(Transition transition : transitions) {
            boolean currentStateMatches = transition.from.equals(current);
            boolean conditionsMatch = transition.conditions.equals(conditions);
            if(currentStateMatches && conditionsMatch) {
                return transition.to;
            }
        }
        return null;
    }
}

和一个测试运行:

编辑:根据您的评论,有更多的过渡和新的状态:

State one = new State("one");
State two = new State("two");
State three = new State("three");

Condition sunday = new Condition("Sunday");
Condition raining = new Condition("Raining");
Condition notSunday = new Condition("Not Sunday");
Condition notRaining = new Condition("Not Raining");

List<Transition> transitions = new ArrayList<Transition>();
transitions.add(one, new Set(sunday), three);
transitions.add(one, new Set(sunday), two); // <<--- Invalid, cant go to two and three
transitions.add(one, new Set(raining), three);
transitions.add(one, new Set(sunday, raining), three);
transitions.add(one, new Set(notSunday, notRaining), three);

StateMachine machine = new StateMachine(one, transitions);
System.out.print(machine.current); // "one"
machine.apply(new Set(sunday, raining));
System.out.print(machine.current); // "three

在一个相当大的项目中使用状态机时,我有过一段痛苦的经历。问题在于复合状态。就像您提到的复合条件(周日和下雨)一样,技术上可能存在复合状态,可以进一步分解为单位状态。在您的情况下可能会或可能不会出现这种情况,但仍然值得一提。如果是这种情况,最好修改经典的有限状态机并使用一组状态而不是单个状态来表示 from 和 to 状态。如果你的 N 很大,这将有助于保持理智水平不变。想想 hotmail 文件夹与 gmail 标签。然后转换表将显示为

Transition(Set<State> from, Set<Condition> conditions, Set<State> to)
于 2010-01-14T13:02:13.317 回答
14

这听起来像是有限状态机的典型用途

简而言之,状态机描述了您的系统可以处于的各种状态,以及在哪些条件下它可以从一个状态转到另一个状态。状态机的描述与您的英文描述完全相同。并且可以用状态图形式化描述

在代码中,您可以制作这样的状态机:

 enum State { Init, ShowMenu, ShowMsg, DisplayVideo, Exit };
 State state = State.Init;

 while (state != State.Exit)
 {
      switch(state)
      {
           case State.Init:
                init();
                state = State.ShowMenu;
                break;
           case State.ShowMenu:
                if(lastMenuItemSelected==1) state = State.ShowMsg;
                if(lastMenuItemSelected==2) state = State.DisplayVideo;
                break;
           case State.ShowMsg:
                ....
                break;
           ....
 }

我不确定我是否得到了正确的 Java 语法...我更喜欢 C#

于 2010-01-13T15:24:44.620 回答
9

状态模式不会起作用吗?

于 2010-01-13T15:23:50.050 回答
2

正如其他人所说,状态机可以在带有开关的过程代码中建模,也可以在带有状态模式的 OO 代码中建模,这可能是您所追求的。

然而,第三种方法是将其实际编码为图形,将状态作为节点,将条件作为有向边。然后可以使用访问者模式将图表应用于不同的用途。这将特别适合用户定义状态和/或转换的设计,但可能比其他答案中描述的硬编码状态机更占用内存。

于 2010-01-14T12:56:16.353 回答
1

关于您的示例,我首先注意到的是 State 3 = (State 1 == true && State 2 == true)。随着涉及更多可能的状态,这不会很好地扩展。如果你只考虑是下雨还是星期天,你可以有这样的枚举,有 4 种可能的类型:

enum State { CLEAR_OTHER_DAY, RAINING_OTHER_DAY, CLEAR_SUNDAY, RAINING_SUNDAY }

这将允许您在编写代码时在 switch 块中清晰地陈述条件。但是,如果您还必须考虑外面是否温暖,则必须在该枚举中再添加 4 个值以捕获所有可能的条件。在您的项目后期,您的代码可能需要捕获比您当前设想的更多的条件。

至于设计模式,状态模式及其在 Wikipedia 上的 Java 示例看起来是一个不错的起点。

Wikipedia 的示例有一个StateContext类,该类带有一个名为的方法,该方法setState采用名称。我考虑过建议您在此处添加状态确定逻辑,但这会使您的StateContext类需要过于接近其他类的实现细节。最好将一种用于确定系统状态的方法放在一个类中,该类很容易知道从一个状态到另一个状态的条件。这样,如果您的项目将来需要更改并且您有更多状态要跟踪或不同的条件,您只需将逻辑维护在一个地方。

于 2010-01-13T16:04:05.803 回答
1

状态设计模式适用于状态变化的概念。整个流程生命周期可以分为多个阶段。随着每个阶段的完成,流程从一个状态退出并进入另一个状态。

例如,在 JSF 框架中,整个 Web 请求响应生命周期分为六个阶段:

在每个阶段完成后,进程从一个状态退出并进入另一个状态。例如,在 RestoreValuePhase 之后,我们可以说 ViewRestored 为退出状态,RequestApply 为进入状态。

因此,要实现状态设计模式,需要以这样的方式划分整个过程,以便可以在多个阶段进行处理,每个阶段退出定义一个状态变化。

现在让我们用下面的代码来理解这一点。

任何项目生命周期都可以分为多个阶段,例如

requirementAssessment
Design
Development
QualityAssessment
Deploy
Closure

所以这些是以下示例中使用的阶段

规则:

  1. 我们需要定义一个类,我们可以在其中存储进程的当前状态。下面代码中的 NextPhase 类就是这样做的。

  2. 我们需要定义一个接口,我们可以在其中提供将在每个阶段实现的联系方法。在下面的代码中 ProjectPhase 就是这样做的。

在此处了解有关状态设计模式的更多信息——状态设计模式

http://efectivejava.blogspot.in/2013/09/java-state-design-patten-oops-state.html?utm_source=BP_recent

于 2013-09-26T17:24:10.307 回答