【问题标题】:State pattern java状态模式java
【发布时间】:2016-10-29 10:31:34
【问题描述】:

我正在学习java中的设计模式

我正在通过一些链接进行操作。我正在尝试按状态模式设计洗衣机

我对状态设计模式的实现有疑问

public interface State {

   public void openLid();
   public void closeLid();
   public void start();
   public void stop();
   public void washing();
  } 

 public class Idle implements State{
 //implementing overidden methods
 .......

 }

 public class Washing implements State {
       //implementing overidden methods
       .......
  }


 public class WashingMachine {
   State state;

   public WashingMachine(State state) {
    this.state =  new Idle();
   }

  public State getState() {
    return state;
   }

   public void setState(State state) {
    this.state = state;
   }

 }

我想知道当在空闲状态和清洗实现之间切换时,有两种方法可以通过网络看到

1.WashingMachine类实现State接口,并根据Idle切换状态到washing或反之一些条件

2.IdleWashing 类以 WashingMachine 作为成员变量。

请任何人建议我对实现部分有点困惑。

【问题讨论】:

  • 你的想法实际上都不行

标签: java design-patterns state-pattern


【解决方案1】:

在解决您的问题之前,我更喜欢回顾一下该模式的想法,并建议您对您的代码进行一些小的修改。

状态模式允许对象在其内部状态发生变化时改变其行为。

在您的情况下,Idle 和 Washing 是作为状态的良好候选者,而 WashingMachine 是承载状态对象的良好候选者。

但是,三个备注:

1) 状态提供的方法应该是一些动作,根据对象所处的状态,实现不同。

在您的声明中:

public interface WashingMachineState {    
   public void openLid();
   public void closeLid();
   public void start();
   public void stop();
   public void washing();
  } 

washing() 不是动作而是状态。
正是 start() 操作将状态从空闲更改为正在清洗。

在状态模式中,具有状态的对象被命名为上下文
在您的情况下,上下文是WashingMachine

2) 在状态模式中,想法是上下文想要执行一些行为根据当前状态改变的动作。
为此,上下文将其处理委托给其当前状态实例。
它避免在上下文中(对于每个处理)有许多 if - else if,并且它还允许降低上下文的复杂性,因为当您使用状态模式时,您会得到一系列行为: :

  • 我们处于空闲状态时的行为位于IdleState 类中。

  • 我们处于洗涤状态时的行为位于WashingState类中。

  • 等等……

要执行这些操作,状态实例需要上下文 (WashingMachine)。
要解决这个问题,您有两种方法:

要么将WashingMachine 对象作为字段存储在状态实例中,要么在上下文WashingMachine 对象将处理委托给状态时将其作为参数传递。

我建议你使用无状态方式。
因此,当在WashingMachine 实例上调用startWashing() 操作时,WashingMachine 实例应通过传递自身(例如state.startWashing(this))将处理委托给state.startWashing()
该州应提供WashingMachine作为参数:

public interface WashingMachineState {    
   void openLid(WashingMachine machine);
   void closeLid(WashingMachine machine);
   void pushStartBtn(WashingMachine machine);
   void pushStopBtn(WashingMachine machine);
} 

3) 实际上你定义了两种状态:空闲和洗涤。
这些应该以stopping 状态完成,因为机器上的某些操作(例如打开门、按下启动按钮...)在机器处于“停止”状态时具有特定行为。
请注意,只有两种状态,您可能还想知道模式是否相关。


现在,我可以回答你的问题了。

我想知道什么时候在空闲状态和洗涤状态之间切换 实现可以有两种方式,可以通过网络看到

1.WashingMachine类实现State接口,根据某些条件将状态从空闲切换到洗涤,反之亦然

2.Idle 和 Washing 类有WashingMachine 作为成员变量。

WashingMachineWashingMachineStates 是合作的,但不同的东西。
所以他们不必依赖同一个接口。
添加WashingMachine 对象作为状态子类的字段是可能的。
如前所述,您还可以将 WashingMachine 作为 State 方法的参数传递。

请注意,不是直接由WashingMachine 执行从一个状态到另一个状态的切换。
这是由国家执行的。
并且各州应该调用WashingMachine.changeState() 来执行它。

WashingMachine 可以是:

public class WashingMachine {

   private WashingMachineState state;

   public WashingMachine() {
     this.state =  new Idle();
   }        

   protected void changeState(WashingMachineState state) {
     this.state = state;      
   }

   public void openLid(){
     state.openLid(this);
   } 

   public void closeLid(){
     state.closeLid(this);         
   } 
   public void pushStartBtn(){
     state.pushStartBtn(this);
   } 

   public void pushStopBtn(){
     state.pushStopBtn(this);
   } 

   public State getState() {
      return state;
   }

 }

WashingMachine 修改说明:

  • changeState 在使用状态模式时比setState 更有意义。

  • changeState(State) 可以使用protected 修饰符来降低此方法的可见性,当然状态子类应该与WashingMachine 在同一个包中。它是在 Java 中启用的实现细节。对于其他 OOP 语言,您当然还有其他选择。

关于从空闲切换到洗涤,我认为应该只有在IdleState状态下才可以调用pushStartBtn()

这是一个例子:

public class IdleState implements State {    
   public void openLid(WashingMachine machine){
       ...
   }
   public void closeLid(WashingMachine machine){
       ...
   }
   public void pushStartBtn(WashingMachine machine){
      //do processing with machine to begin effectively the washing
         ...
      machine.changeState(new WashingState());
   }

   public void pushStopBtn(WashingMachine machine){
       ...
   }
 } 

【讨论】:

  • @coder25 我改变了第三点。停止状态似乎比开门状态更相关。开门状态不是一级状态,更多的是子状态。没有太大兴趣代表它。
  • 关于更改状态 (machine.changeState(new WashingState())) - 是否可以让上下文类中的所有状态都带有 getter 并让每个状态都像 machine.changeState(machine.getWashingState()) 一样保持状态对象单例?跨度>
  • @ user7 如果状态对象是无状态的(总结:状态类中没有实例字段),这是可能的,甚至是一个好主意。在这种情况下,我可能会使用枚举而不是 getter 来表示状态:public enum WashingMachineState { WASHING, IDLE, STOPPING},因为它提供了开箱即用的单例,同时是一种更直接的方式。
  • 但是(作为一个人为的例子)如果它有一些依赖注入(可能是一些策略)作为实例变量,你会怎么做呢?
  • 我不明白。您在那里混合的东西可能太多了 :) 如果您有一个有趣的问题,请不要犹豫,提出一个问题,我相信有人会解决它 :)
【解决方案2】:

我认为更好的选择是创建

public enum State{IDLE,WASHING};

并使用它。这个enum 甚至可以命名为WashingMachineState,因为你提到的状态只指定给洗衣机——它们不能重复使用,所以这里没有接口。

如果您想在不同设备之间共享相同的状态,例如。 WashingMachineDishWasher 那么你可以使用

public interface Statefull{
   public State getState();
   public void changeState(State newState);
}

并让WashingMachineDishWasher 实现Statefull 接口。 使用 Java 8 的默认接口实现,您甚至可以在接口中包含 getter 实现,因此在实现类时不需要样板代码。

【讨论】:

【解决方案3】:

好的,让我们来看看你的两个想法:

1.) 洗衣机是状态吗?不,绝对不。那为什么还要实现状态接口呢?

2.) 这几乎可行,但它使状态不可重用(这对于像这样的小型实现来说还不错,但对于像游戏这样的东西来说,这很糟糕)。它们只能是洗衣机的状态。如果我想将这些状态用于洗碗机怎么办?

你应该做什么:

创建一个接口或类,例如 StateManager,并让 WashingMachine 实现它,然后不使用具体类,而是在状态中创建一个 StateManager 字段。

例子:

public abstract class StateManager {
    public State state;

    public void setState(State newState) {
        state = newState;
        newState.parent = this;
    }
    public State getState() {
        return state;
    }
}

状态:

public abstract class State {
    public StateManager parent;
    // Whenever you want to set the object's state, use this
}

示例状态:

public class WashingState extends State {
    // your methods here
}

示例状态管理器:

public class WashingMachine extends StateManager {
    // your methods here.
}

我更改了所有 zo 类而不是接口,因为它们使创建一些对象更容易(例如,如果您想制作洗碗机和洗衣机,那么您可以从中提取洗涤部分,并为它创建一个单独的类)

【讨论】:

  • 你能举例说明一下吗
  • 我回家后发布一些东西
  • 能否为您的设计推荐发表一些东西
  • 完成,你去
猜你喜欢
  • 2012-10-22
  • 2021-09-13
  • 1970-01-01
  • 1970-01-01
  • 2017-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多