替代instanceOf的方法(状态模式)

4

我遇到了使用状态模式的问题,我不知道如何检查一个 State 是否是某个实例,但不能使用 instanceOf(因为这被认为是一种不好的做法)。

TCPConnection 持有一个 TCPState 对象。假设我想获取所有具有状态 TCPEstablishedTCPConnections,我该怎么做?

enter image description here

一种方法是:

public List<TCPConnection> getAllEstablished() {
  List<TCPConnection> list = new ArrayList<TCPConnection>();

  for(TCPConnection tcp : allConnections) {
      if(tcp.getState().instanceOf(TCPEstablished)) {
          list.add(tcp);
      }
  }

  return list;
}

但是这种方法使用了instanceOf,我不想使用它。有更好的方法吗?或者我的使用instanceOf是有效的吗?


1
对于状态管理,我会使用 enum 而不是类。 - Luiggi Mendoza
1
在您的情况下使用 instanceof 运算符没有问题。但是,使用方式在语法上是不正确的。instanceof 不是一个方法,而是一个运算符。 - Rohit Jain
1
为什么instanceOf不是一个好的实践方法? - EDToaster
1
@JClassic:这不是不良行为,而是代码异味。如果您需要知道变量是哪个类,则可能耦合过紧,需要重新考虑如何使用该变量。这并非总是适用的(序列化是一个明显的例子),但在使用该运算符时要记住这一点。 - Guvante
1
顺便提一下,tcp.getState().instanceOf(TCPEstablished) 违反了迪米特法则 - proskor
显示剩余3条评论
2个回答

5
是的,使用 instanceOf 被认为是一种坏味道。此外,检查状态对象的类型与状态模式本身的思想相反(在子类型中封装依赖于状态的行为,使这些检查变得不必要)。
然而,你可以通过向 TCPState 添加另一个操作来技术上消除 instanceOf 的使用,例如 bool isEstablished(),并实现它使其只在 TCPEstablished 上返回 true
interface TCPState {
    ...
    boolean isEstablished();
}

class TCPEstablished implements TCPState {
    ...
    boolean isEstablished() {
        return true;
    }
}

class TCPClosed implements TCPState {
    ...
    boolean isEstablished() {
        return false;
    }
}

将操作添加到 TCPConnection

class TCPConnection {
    ...
    boolean isEstablished() {
        return this.getState().isEstablished();
    }
}

那么您的操作getAllEstablished将如下所示:

List<TCPConnection> getAllEstablished() {
    List<TCPConnection> list = new ArrayList<TCPConnection>();

    for(TCPConnection tcp : allConnections) {
        if(tcp.isEstablished()) {
            list.add(tcp);
        }
    }

    return list;
}

instanceOf已经消失了。但这值得吗?


3
你可以将状态转换为枚举类型。
public enum TCPState{
  ESTABLISHED,
  LISTEN,
  CLOSED;

  ..methods go here

}

由于枚举值是完整的对象,它们可以重写方法来执行特定状态行为。然后,您可以使用equals()或甚至==检查来检查状态。

for(TCPConnection tcp : allConnections) {
  if(tcp.getState()==TCPState.Established)) {
      list.add(tcp);
  }
}

编辑

以下是如何使每个枚举值对于一个方法具有不同的实现。

假设 TCPState 实现了一个接口或拥有一个抽象方法 foo()

public enum TCPState{
  ESTABLISHED,
  LISTEN,
  CLOSED;

 public abstract void foo();

}

你可以按照以下方式为每个值实现不同的方法:
 public enum TCPState{
  ESTABLISHED{
     @Override
     public void foo(){
         System.out.println("established");
     }
  },
  LISTEN{
     @Override
     public void foo(){
         System.out.println("listening");
     }
  },
  CLOSED{
     @Override
     public void foo(){
         System.out.println("closed");
     }
  }
  ;

 public abstract void foo();

}

或者,您可以在TCPSTate中制作基础实现,而不是声明abstract,然后仅在需要执行不同操作的值中使用覆盖。


此外,“状态”不需要知道如何打开或关闭连接。 - Luiggi Mendoza
你如何确保每个状态下三种方法都有三种不同的实现? - Stanko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接