让我们定义一个简单的状态机:
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中看到的,我们检查状态更改是否有效。我的问题:
答案 0 :(得分:2)
状态模式有多年的参考实现。实施不受您的关注。状态不表示为枚举,而是表示从同一基类继承的类。这样,状态之间的转换实现就更简单,更清晰,因为每个状态只负责自己的转换。
我想知道为什么你决定尝试自己的方法,而不是遵循一个好的,可靠的做法。
回答你的问题 - 在setter中使用代码没有任何根本性的错误。但是,您实现状态模式的方式会产生不必要的问题。
答案 1 :(得分:0)
一般情况下,是的,在setter中设置逻辑是可以的。
如果您认为SRP是一个好主意,那么如果您添加与setter功能相关的逻辑就可以了,这主要意味着保持完整性对象改变了属性的值。
就像我说的那样,其中一个不好的想法就是违反SRP。
事实上,我会说发布的代码显示了这种违规的一个例子,因为setter有两个职责:
State
实例的StateObject
State
。如果您实现规范状态机的剩余部分,即完整转换功能,包括转换操作,第二个责任将变得更加明显。
这种关注点分离变得重要的一个主要例子是在你只有二次注入的代码中 - 在这种情况下你如何区分初始化和转换?使用此代码,您不能。
此外,我认为如果您将validChange
重构为StateObject
,我认为这是必要的,因为当前的代码打破了封装 - State
, 状态机标签,包含状态转换功能的“保护规范”。假设您希望在不同的状态机中使用相同的标签 - 就像代码当前所代表的那样,您不能拥有它(甚至有一个很好的代码味道可以指向您解决问题,而不是使用一个enum
构造函数,你被迫创建一个静态初始化块)。