【发布时间】:2016-01-26 08:44:27
【问题描述】:
我正在使用状态模式,但我发现的示例是出于教育目的,我想知道在这种模式中在状态之间共享对象的最佳实践是什么,即布尔值、列表并且还为一个状态对象更改自动机对象中的状态引用。
我将建立一个简单的模型作为示例。
public abstract class State {
Automata automata;
public State( Automata automata){
this.automata = automata;
}
public abstract void action();
}
public class State1 extends State {
public State1(Automata automata){
super(automata)
}
@Override
public void action() {
// GET : Use obj1
// POST :Set in automata state to State2
}
}
public class State2 extends State {
public State2(Automata automata){
super(automata)
}
@Override
public void action() {
// GET :Use obj1
// POST :Set in automata state to State1
}
}
public class Automata {
private State state1 = new State1(this);
private State state2 = new State2(this);
private State state = state1;
public void takeAction() {
state.action()
}
}
我尝试了以下解决方案:
- 对于 GET 在 Automata 中存储
obj1并使用 getter 和 setter。 POST 在 Automata 中存储状态并使用 getter 和 setter。这种方法会通过使用 getter 使代码变得不必要地长,并且随着obj1和状态的不断变化的列表变得难以维护。 - 使用私有内部类。将
State, State1和State2声明为私有内部类并直接访问obj1和状态。仅仅因为文件的长度,很难维护和更新。无法与其他Automata班级共享。 - 公开字段。我不想公开所有这些字段。
- 使用单例/静态类方法共享
obj1's
我不太了解包私有访问。
在我的设计中,我将此模式与模板方法模式结合起来作为辅助信息。
我知道不存在一刀切的方法,但是使用这种模式的常见最佳实践是什么?
【问题讨论】:
-
在状态之间共享对象是什么意思?状态应该是互斥的,它们的生命周期不应该重叠。您可以做的一件事是在从状态 A 转换到状态 B 时传输一个对象,但仅此而已。
-
任何需要考虑过去事件/动作历史的设计,所有状态都应该能够读取和写入历史。我并不是说你的方法是错误的,只是如果总历史由 10 个对象组成,并且任何两个状态 si 和 sj 将具有固定数量的对象的交集(即 s1 使用 obj1 和 obj2 s2 使用 obj2 和 obj3 ... .s10 使用 obj10 和 obj1),您需要传递一长串参数,这会使代码不可读。此外,它不能解决 s1 想要转换到状态 s2 以将自动机的状态更新为正确的参考
-
另外,正如@rinde 正确指出的那样,最好的选择不是让状态改变它的主机,而是让它返回一个新的状态。然后宿主通过自身变异转换到这个新状态。
-
TBH,没有朋友声明和内部类(为什么不呢?),
package protected字段似乎是将共享数据公开给相关状态的最短方法。也许将状态机放在专用包中。 -
@MarkusKull 这与全局变量无关。它是关于每个对象保护和隐藏自己的内部状态——基本 OO 原则之一。状态模式是一个很好的机会,可以为此添加一个功能性的扭曲并从不变性中受益,因为原则上您只需将
State替换为另一个,您不会在State内改变事物。在多个 State 对象共享可变字段的情况下,用糟糕的 OO 来污染这一点会使 IMO 完全失败。
标签: java design-patterns state-pattern