【问题标题】:State pattern implementation hardship状态模式执行困难
【发布时间】:2014-01-18 19:19:55
【问题描述】:

我在我的应用程序中使用state pattern,因为我的state-entities 具有API,其行为会因当前状态而改变。

在我的情况下,依赖于状态的 API 仅由一种方法组成 - changeState。这个方法改变状态,也执行一些状态相关的逻辑。

但是有一种情况让我提出了这个问题:状态依赖逻辑不仅依赖于当前状态,还依赖于 newState(见下面的代码)。所以我必须使用instanceof

问题: 真正摆脱instanceof的方法是什么?

public interface IStateEntity {
  void changeState(IState newState);
}

public interface IState {
  void doStateDependentLogic(IState newState);
}

public class StateEntityImpl {

  private IState state;

  @Override
  public void changeState(IState newState) {
    state.doStateDependentLogic(newState);
    state = newState;  
  }
}

public class StateImplA { //each state implementation is a singleton

  @Override
  public void doStateDependentLogic(IState newState) {
      // HARDSHIP #1: state dependent logic depends not only on current state, but also it depends on newState. So I have to use `instanceof`
     if (newState instanceof StateImplA) {
       //do one logic
     } else if (newState instanceof StateImplB) {
       //do another logic
     }
  }
}

编辑(为什么不枚举):

如果枚举替换 getClass().getSimpleName(): 因为它是函数式编程范式。当我以依赖类名的方式创建架构时,这是出现问题的第一个迹象。在我看来,将 instanceof 更改为 enum 并不意味着要更改架构,而只是为了更改一个坏的更糟。

如果将每个 IState 实现设为枚举:每个 IState 实现都是 Hibernate 实体。

【问题讨论】:

  • 另一种方法是创建一个enum,枚举所有可能的状态并用它“标记”每个IState 实例。如果你采用这种方法,你甚至可以switch 覆盖它(例如switch(newState.state)
  • 如果你的州是单身,那么你的州使用enum。然后,您可以在逻辑中使用 switch
  • @jedwards 你的意思是每个州都应该持有一个适当的最终枚举值吗?但是我的状态已经是单例了,这意味着simpleClassName已经完成了这个逻辑。
  • @BoristheSpider jedwards 请提供更多详细信息
  • @jedwards 请参阅我的问题的编辑部分

标签: java design-patterns architecture state-pattern


【解决方案1】:

Java 中公认的创建单例的方式是使用enum。这提供了线程安全初始化以及 JVM 保证的单例状态。有很多方法可以创建不是 enumclass 的第二个实例。

使用enum 的额外好处是您可以在逻辑中打开enum 的大小写:

public enum State {

    STATE_A {
        @Override
        public void doStateDependentLogic(final State newState) {
            switch (newState) {
                case STATE_B:
                    //do stuff
                //case SOME_OTHER_STATE
            }
        }
    },
    STATE_B {
        @Override
        public void doStateDependentLogic(final State newState) {
        }
    };

    public abstract void doStateDependentLogic(State newState);
}

我不知道你从哪里得到enum 是一种函数式编程范例的想法。不是这种情况。 Java 中的enum 旨在满足您的需求。

【讨论】:

  • 抱歉,但我并没有在我的问题中指出每个状态都是休眠实体。所以我不能用enum
  • 在这种情况下,您将不得不使用enum 作为持久性的休眠限定符,并向interface 添加一个返回enum 的方法。
【解决方案2】:

您的问题实际上并不是状态模式。您的问题出在状态模式完成其工作之后,即将方法调用路由到表示状态的特定实现。

instanceof 子类问题是一个独立问题。这里的解决方案是避免instanceof 就像在其他代码中一样,最好通过方法在状态和下一个状态之间进行交互。 switch over enum types 看起来已经更好了,但并没有好多少,因为您仍然依赖于另一个对象的具体类型。可以认为没问题,因为它们都定义在同一个类中,所以依赖关系只在你自己身上。

在最好的情况下你应该得到的结果看起来像

  @Override
  public void doStateDependentLogic(IState newState) {
     if (newState.isBeautiful()) {
         System.out.println(newState.getYourPartOfTheInterAction(this));
     } else {
         somethingElse();
     }
  }

即方法调用抽象而不关心实现细节。

【讨论】:

  • 是的,但是如果我应该在三个或四个 newState 变体之间进行选择。比isBeautiful 变得不那么有吸引力了。
  • @VolodymyrBakhmatiuk 这只是一个例子。可能有一个方法返回一个数字、一个枚举,甚至是一个 strategy 对象,任何比单个布尔值提供更多选择的方法。或者添加更多布尔值,你有 2^n 种方法来确定你需要用它们做什么:)
  • 一般的想法是,在任何交互中,你将你的知识传递给另一个类(例如willYouMaryMe(this.gender)),而另一个对象用它自己的关于它自己的知识来回复你(例如return gender == Gener.Female || this.age > 40),这样关于你自己的知识不需要硬编码到另一个对象中。
  • 在所有情况下,在isBeautiful 方法中,除了检查实例是否为特定类的实例外,我没有任何额外的逻辑。看来我只是将 instanceof 移到另一个方法
  • @VolodymyrBakhmatiuk 我想到了一件事:也许你的状态是错误的。如果StateA 的行为因下一个状态而异,您可以通过将该状态拆分为例如StateAfollowedByBStateAfollowedByC,从而无需根据下一个状态做出决定。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-28
  • 1970-01-01
  • 2012-10-18
相关资源
最近更新 更多