【问题标题】:State pattern: why states are not Singletons?状态模式:为什么状态不是单例?
【发布时间】:2010-06-22 07:02:05
【问题描述】:

我使用状态模式来实现一个简单的有限状态机。查看Wikipedia 上给出的描述,更具体地说是建议的Java 实现,我想知道为什么实现State 接口(即各种状态)的类不是单例?

在建议的实现中,每当发生转换时都会创建一个新状态。然而,一个对象足以代表每个状态。那么,为什么每次发生转换都浪费时间创建一个新实例呢?

【问题讨论】:

  • 我在我的项目中遇到了同样的问题。我认为如果它被实现为单例,那么每个State 都应该有一个CleanUp 方法来重置信号或本地变量。稍微复杂一点。所以我宁愿每次new 一个实例。我的应用程序的目标平台是 IOS,所以性能可能是一个问题。但直到现在它都可以正常工作。

标签: java design-patterns state-pattern


【解决方案1】:

因为每个状态都可以存储实例变量?

看看您引用的维基百科示例:

class StateB implements State { 
    private int count=0; 
    public void writeName(StateContext stateContext, String name) { 
        System.out.println(name.toUpperCase()); 
        if(++count>1) { 
            stateContext.setState(new StateA()); 
        }
    }
}

你能看到它是如何存储输入次数的吗?

现在,在 FSM 中,您可能希望每个状态为 idempotent(后续调用会给出相同的反馈),但状态模式更通用。维基百科页面上描述的一种目标用途是:

一个对象的干净方式 在运行时部分更改其类型

由于大多数对象在执行操作时可能会使用它们的局部变量,因此您可能希望“更改类型”版本也使用局部变量。

【讨论】:

    【解决方案2】:

    假设你的对象有一个状态。现在,如果您需要“只需要一个完整的类似的东西”怎么办?

    【讨论】:

    • 你能解释一下“假设你的对象有一个状态”是什么意思吗?我有一个有限状态机 (FSM),它是一组状态。每个状态都不会改变。 FSM 的状态就是它的当前状态(可用状态之一)。如果 FSM 发生变化,我必须更改转换逻辑或状态集(可能通过添加/删除一些)。在这种情况下,我认为每个状态都可以是一个 Singleton,不是吗?
    • 不,因为当它是单例时,您不能克隆整个状态机对象及其状态。单例 = 每个应用程序一个。
    • 当然可以;一个浅层克隆就足够了,因为状态不是可变的,并且除了它们的身份之外不存储任何信息。当然,如果它不是纯状态机(即状态用非静态信息注释)。
    【解决方案3】:

    您可能需要一个“有状态”对象(如参考维基百科页面上的 one example 所示),此外,您可能希望在同一个 JVM 中运行多个相同类型的状态机。

    如果每个 State 都是 Singleton,这是不可能的。

    【讨论】:

      【解决方案4】:

      如果您的状态不需要特定于机器的附加状态数据,那么跨机器重用它们是非常有意义的。这并不意味着它们是单例:单例还意味着您几乎不想要的全局访问。

      这是一个简单的状态机,它重用状态,但不会使它们成为单例。

      public class SwitchState
      {
          public SwitchState(bool isOn)
          {
              mIsOn = isOn;
          }
      
          public void InitToggleState(SwitchState state)
          {
              mToggleState = toggleState;
          }
      
          public bool IsOn { get { return mIsOn; } }
          public SwitchState Toggle() { return mToggleState; }
      
          private SwitchState mToggleState;
          private bool mIsOn;
      }
      
      public class LightSwitch
      {
          public LightSwitch()
          {
              mState = sOnState;
          }
      
          public bool IsOn { get { return mState.IsOn; } }
      
          public void Toggle()
          {
              mState = mState.Toggle();
          }
      
          static LightSwitch()
          {
              sOnState = new SwitchState(true);
              sOffState = new SwitchState(false);
      
              sOnState.InitToggleState(sOffState);
              sOffState.InitToggleState(sOnState);
          }
      
          private static SwitchState sOnState;
          private static SwitchState sOffState;
      
          private SwitchState mState;
      }
      

      您可以看到,无论有多少LightSwitch 实例,整个应用程序中都只会有一个开和关状态。同时,LightSwitch 之外的任何东西都无法访问状态,因此它们不是单例。这是享元模式的经典示例。

      【讨论】:

        【解决方案5】:

        问题应该反过来问:为什么State 是单身人士?仅当您需要全局访问权限并且拥有多个实例是错误时才需要单例。

        拥有多个State 的实例当然不是错误,而且您也不需要要求全局访问权限,因此无需进行他们单身。

        【讨论】:

        • 但是,将它们设为“单例”可能会带来性能优势。如果一个状态不可变并且不存储每个实例的信息,那么它本质上只是一个枚举值,并且可以通过引用进行比较 - 这非常快 - 并且避免了实例创建/销毁(降低 GC 负载)。
        • @Eamon Nerbonne:你所描述的正是我的情况,这就是为什么我让我的州成为单身人士。但是,我知道在一般情况下这不一定是个好主意:请参阅Graphain answer above。
        猜你喜欢
        • 2011-08-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-30
        • 2011-05-11
        • 1970-01-01
        • 1970-01-01
        • 2021-02-17
        相关资源
        最近更新 更多