2

让我们定义一个简单的状态机:

public enum State {

    A, B, C, D;

    private List<State> validChange;

    static {
        A.validChange = Arrays.asList(B);
        B.validChange = Arrays.asList(C);
        C.validChange = Arrays.asList(A, D);
        D.validChange = Arrays.asList(D);
    }

    public boolean couldChange(State newState) {
        return validChange.contains(newState);
    }
}

和一个简单的状态对象

public class StateObject {

    private State currentState;

    public State getCurrentState() {
        return currentState;
    }

    public void setCurrentState(State currentState) {
        if (this.currentState != null && !this.currentState.couldChange(currentState)) {
            throw new IllegalStateException(String.format("Can not change from %s to %s", this.currentState, currentState));
        }
        this.currentState = currentState;
    }
}

正如我们在 setter 中看到的,我们检查状态更改是否有效。我的问题:

  1. 向 setter 方法添加一些逻辑是否是一个好的解决方案(我们不感兴趣它是如何工作的,只关心 setter 中的事实逻辑)?
  2. 我们什么时候应该添加逻辑,什么时候不应该?
  3. 如果不是为什么?
4

2 回答 2

2

状态模式多年来一直有其参考实现。实施无需您担心。状态不表示为枚举,而是表示为从同一基类继承的类。通过这种方式,状态之间的转换实现更加简单和清晰,因为每个状态只负责自己的转换。

我想知道为什么您决定尝试自己的方法,而不是遵循良好、可靠的做法。

回答您的问题 - 在 setter 中使用代码并没有根本性的错误。但是你实现状态模式的方式会引发不必要的问题。

于 2013-03-07T20:55:11.437 回答
0
  1. 一般来说,是的,在 setter 中有逻辑是可以的。

  2. 如果您认为SRP是一个好主意,那么您可以添加与 setter 功能相关的逻辑,这主要意味着保持对象的完整性以更改属性值。

  3. 就像我说的,这是一个坏主意的情况之一是违反 SRP。

实际上,我会说发布的代码显示了这种违规的示例,因为 setter 有两个职责:

  • 初始化State一个新StateObject实例,
  • 处理任何过渡到新的State.

如果您实现规范状态机的剩余部分,即完整的转换功能,包括转换操作,第二个责任将变得更加明显。

这种关注点分离变得重要的一个主要例子是在你只有setter注入的代码中——在这种情况下你如何区分初始化和转换?使用此代码,您不能。


此外,我相信如果你重构validChangeStateObject,情况会变得更加明显,我认为这是必要的,因为当前代码破坏了封装 -State状态机标签包含状态转换功能的“保护规范”。假设您希望在不同的状态机中拥有相同的标签 - 就目前的代码而言,您不能拥有这样的标签(甚至有一种很好的代码味道应该指出您的问题,而不是使用enum构造函数,你被迫创建一个静态初始化块)。

于 2013-03-08T21:49:36.210 回答