Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/396.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 状态模式在状态对象高效和可扩展实现之间共享公共知识_Java_Design Patterns_State Pattern - Fatal编程技术网

Java 状态模式在状态对象高效和可扩展实现之间共享公共知识

Java 状态模式在状态对象高效和可扩展实现之间共享公共知识,java,design-patterns,state-pattern,Java,Design Patterns,State Pattern,我正在使用状态模式,但我发现的示例是出于教育目的,我想知道在这种模式中,在状态之间共享对象的最佳实践是什么,即布尔值、列表,以及一个状态对象在自动机对象中更改状态引用的最佳实践 我将建立一个简单的模型作为示例 public abstract class State { Automata automata; public State( Automata automata){ this.automata = automata; } public

我正在使用状态模式,但我发现的示例是出于教育目的,我想知道在这种模式中,在状态之间共享对象的最佳实践是什么,即布尔值、列表,以及一个状态对象在自动机对象中更改状态引用的最佳实践

我将建立一个简单的模型作为示例

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()
     }
}
我尝试了以下解决方案:

  • 对于在自动机中获取存储
    obj1
    ,请使用getter和setter。对于自动机中的后期存储状态,请使用getter和setter。通过使用getter,这种方法将使代码变得不必要的长,并且随着
    obj1
    和状态列表的变化而变得难以维护
  • 使用私有内部类。将
    State、State1
    State2
    声明为私有内部类,并直接访问
    obj1
    和states。由于文件的长度,很难维护和更新。无法与另一个
    自动机
    类共享
  • 将字段公开。我不想公开所有这些字段
  • 使用单例/静态类方法共享
    obj1
  • 我不太喜欢包私有访问

    在我的设计中,我将此模式与模板方法模式结合起来作为辅助信息


    我知道不存在“一刀切”的方法,但使用此模式的常见最佳做法是什么?

    我会这样做:

    public class Automata {
      List<String> someList;
      boolean someBoolean;
      private State currentState;
    
      public void performAction() {
        currentState = currentState.action(this);
      }
    
      interface State {
        State action(Automata context);
      }
    
      enum States implements State {
        IDLE {
          @Override
          public State action(Automata context) {
            if (context.someBoolean) {
              return WORKING;
            }
            return this;
          }
        },
        WORKING {
          @Override
          public State action(Automata context) {
            if (context.someList.size() > 7) {
              return IDLE;
            }
            return this;
          }
        }
      }
    }
    
    公共类自动机{
    列出一些清单;
    布尔;
    私人国家;
    公开无效执行(){
    currentState=currentState.action(此);
    }
    界面状态{
    状态动作(自动机上下文);
    }
    枚举状态实现状态{
    闲散{
    @凌驾
    公共状态操作(自动机上下文){
    if(context.someBoolean){
    返回工作;
    }
    归还这个;
    }
    },
    工作{
    @凌驾
    公共状态操作(自动机上下文){
    if(context.someList.size()>7){
    返回空闲状态;
    }
    归还这个;
    }
    }
    }
    }
    

    状态设置现在在
    自动机的
    performAction()
    中完成,无需在每个状态中进行设置。我使用enum作为状态的实现,因为当您希望将状态作为纯函数实现时,它们非常有用。但是,如果您的状态本身不是无状态的,您可能希望将状态实现为静态内部类。

    继@rinde post之后,我很好奇这是否是一个可接受的解决方案

    public abstract class State {
    
        public State(){
        }
    
        public abstract void action(sharedState);
    
        public class SharedState{
             Obj1 obj1;
             State state1;
             State state2;
             State curState;
    
             //Getters and setters
        }
    }
    
    public class State1 extends State {
    
        @Override
        public void action(SharedState sharedState) {
            // GET  : Use obj1 
            sharedState.obj1.doSomething()
            // POST :Set in automata state to State2 
            sharedState.curState = sharedState.state2;
        }
    } 
    
    //..same for State2
    
    public class Automata {
    
       State.SharedState sharedState;
    
       public Automata(){
            sharedState.setState1(new State1());
            sharedState.setState2(new State2());
       }
    
    
       public void takeAction() {
           sharedState.getCurrentState().action(sharedState);
       }
    }
    

    在州之间共享对象是什么意思?国家应该是相互排斥的,它们的生命周期不应该重叠。您可以做的一件事是在从状态A转换到状态B时传输对象,但仅此而已。任何需要说明过去事件/动作历史的设计,所有状态都应该能够读取和写入历史。我并不是说你的方法是错误的,只是如果整个历史由10个对象组成,任何两个状态si和sj都会有一个固定数量对象的交集(即s1使用obj1,obj2 s2使用obj2和obj3…s10使用obj10和obj1),你需要传递一长串参数,这会使代码无法读取。此外,它不能解决s1需要转换到状态s2以将自动机的状态更新为正确的重构的问题。正如@rinde正确指出的那样,最好的选择不是让状态改变它的宿主,而是让它返回一个新的状态。然后主机通过改变自身来转换到这个新状态。TBH,没有友元声明,也没有内部类(为什么不?),
    包保护的
    字段似乎是将共享数据仅公开给相关状态的最短方法。也许把状态机放在一个专用的包中。@MarkusKull这不是关于全局变量的。它是关于每个对象保护和隐藏自己的内部状态——这是OO的基本原则之一。状态模式是一个很好的机会,可以为它添加一个函数式的扭曲,并从不变性中获益,因为原则上,您只使用另一个状态替换
    状态,而不会在
    状态中变异事物。在我看来,用多个状态对象共享可变字段的糟糕OO来污染这一点完全是徒劳的。我喜欢您编写的代码,但如果状态移到类自动机之外,它基本上会碰到我列表中的1。如果州需要一个变量,而您使用的是内部类,那么它就是我列表中的选项2。但是,正如您所指出的,通过构造函数在操作中传递上下文是一个更好的选择。上下文保持对共享状态的引用是一个好主意还是可能的,共享状态作为状态中的一个单例内部类实现,从而访问所有字段?只要将状态保持在与自动机相同的包中,它就可以正常工作。但是,如果将自动机定义为一个接口,其中包含一些用于读取某些变量的特定方法和一些用于执行操作的方法,则可能会更简洁。然后您可以让ConcreateAutomata和SpecialConcreteAutomata都使用相同的状态,但可能具有不同的行为(并且状态可以放在任何包中)。因此,如果
    SharedState
    (或
    AutomataBaseClass
    )和
    State,State1,State2
    在同一个包中,您使用package private声明字段,然后您可以具有与我的回答中相同的行为,但将类
    SharedState
    从类
    状态
    移动到包中?可能是相同的行为,但您的问题是关于状态机的设计,在我看来,这是相当不同的。我不知道你为什么认为你需要
    SharedState
    ?这对我来说似乎是多余的。另外,我认为最好只让
    自动机
    I