如何避免在这里使用instanceof /如何使用多态

时间:2014-05-05 08:40:11

标签: java inheritance handler instanceof

我正在开发我的第一个网络应用程序,我发现了一些麻烦,我不知道如何解决。我有以下hierarchie for paquets

interface Packet {}
class NewClientPacket implements Packet {}
class DisconnectPacket implements Packet {}
class DataPacket implements Packet {}
...

现在,服务器需要处理可能由客户端发送的任何数据包,并为每个数据包执行不同的操作。我要写的第一件事就是:

Packet packet = (Packet) myStream.readObject();
if (packet instanceof NewClientPacket) { 
    ...
} else if (packet instanceof DisconnectPacket { 
    ...
} else if (packet instanceof DataPacket) {
    ...
} 
...

但我完全不喜欢这个(它使用instanceof,在添加大量新的Packet子类时扩展性很差,并且它非常详细...)

我发现通常当我必须使用instanceof时,我可以通过使用多态来避免它,所以我想将Packet接口更改为

interface Packet {
    void handle(PacketHandler handler);
}

然后我就可以了

Packet packet = (Packet) myStream.readObject();
packet.handle(this);

但我不知道这是否是解决问题的好方法。你能建议别人,还是对我的评论?

3 个答案:

答案 0 :(得分:2)

在这个上下文中,唯一且应该知道其实际类型的对象是Packet实例。因此,您将写一个HandlerPacket并从那里发送。{1}}。这看起来如下所示:

interface Handler {
  void handle(NewClientPacket packet);
  void handle(DisconnectPacket packet);
  void handle(DataPacket packet);
}

interface Packet {
  void dispatch(Handler handler)
}

class NewClientPacket implements Packet {
  @Override
  public void dispatch(Handler handler) { 
    handler.handle(this);
  }
}

class DisconnectPacket implements Packet {
  @Override
  public void dispatch(Handler handler) { 
    handler.handle(this);
  }
}

class DataPacket implements Packet {
  @Override
  public void dispatch(Handler handler) { 
    handler.handle(this);
  }
}

所有实现都将按类型调用正确的handle方法。这种方法被命名为Visitor Pattern。实际上,您应该为方法选择较少的通用名称,以使代码更易读。通常,使用特定于域的名称。

答案 1 :(得分:1)

这是Visitor Pattern

首先,您需要Visitor

public class PacketVisitor {

    void visit(NewClientPacket packet);
    void visit(DisconnectPacket packet);
    void visit(DataPacket packet);

}

然后您需要为interface Packet添加方法:

interface Packet {

    void accept(PacketVisitor visitor);
}

现在每个Packet你需要实现这个方法:

public class NewClientPacket implements Packet {

    @Override
    public void accept(PacketVisitor visitor) {
        visitor.visit(this);
    }

}

最后在您的服务代码中:

final PacketVisitor visitor = new PacketVisitor() {
   //implementation...
}
final Packet packet = (Packet) myStream.readObject();
packet.accept(visitor);

将会发生visit的相关PacketVisitor方法作为Packetvisit调用PacketVisitor的实例。

答案 2 :(得分:0)

虽然访问者模式可以为您解决此问题,但您需要观察您的依赖关系。您可能希望将所有内容拆分为3个单独的包:

  • 仅限客户
  • 仅限服务器
  • 共享课程

客户端/服务器将依赖于共享类,但这三者之间不存在其他依赖关系。

所有数据包显然都会进入共享类,但它们各自的客户端/服务器端处理程序将分别对客户端/服务器是私有的。因此数据包不能对客户端或服务器类有任何依赖性;这使得数据包无法提供处理程序/调度(在这种情况下数据包不可能知道它的处理程序)。

要实现访问者模式,您将在共享类中拥有一个抽象访问者,并且客户端/服务器实际实现它。这样,数据包可以依赖于抽象访问者:

abstract class Dispatcher {
    public abstract void handleX(X x);

    public abstract void handleX(Y y);
}

class X extends Packet {
    public void dispatch(Dispatcher d) {
         d.handleX(this);
    }
}

就我个人而言,我不是这种模式的最大粉丝,因为它会强制您在添加新数据包时向访问者添加方法,但这就是您使用它的方式很好并且编译时间类型已检查。对于小到中等数量的数据包,这可能是您找到的最好的解决方案。

在实践中,仅仅牺牲编译时安全性并使用命名约定和反射来找出正确的数据包处理程序可能更方便。