Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何避免使用instanceof/如何在此处使用多态性_Java_Inheritance_Handler_Instanceof - Fatal编程技术网

Java 如何避免使用instanceof/如何在此处使用多态性

Java 如何避免使用instanceof/如何在此处使用多态性,java,inheritance,handler,instanceof,Java,Inheritance,Handler,Instanceof,我正在开发我的第一个网络应用程序,我发现了一些我不知道如何解决的问题。我对paquets有以下层次结构 interface Packet {} class NewClientPacket implements Packet {} class DisconnectPacket implements Packet {} class DataPacket implements Packet {} ... 现在,服务器需要处理客户机发送的任何数据包,并对每个数据包执行不同的操作。我要写的第一件事是:

我正在开发我的第一个网络应用程序,我发现了一些我不知道如何解决的问题。我对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
,当添加很多新的
数据包
子类时,伸缩性很差,而且非常冗长……)

我发现通常当我必须使用instanceof时,我可以通过使用多态性来避免它,因此我考虑将
数据包
接口更改为

interface Packet {
    void handle(PacketHandler handler);
}
然后我就可以做了

Packet packet = (Packet) myStream.readObject();
packet.handle(this);
但我不知道这是否是解决问题的好办法。你能推荐其他人吗,或者对我的评论吗?

这是一个

首先,您需要一位
访客

public class PacketVisitor {

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

}
然后,您需要将一个方法添加到
接口数据包中

interface Packet {

    void accept(PacketVisitor visitor);
}
现在,在每个
数据包中
需要实现以下方法:

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);

将发生的情况是,
PacketVisitor
的相关
visit
方法将被调用为
PacketVisitor
上的
Packet
调用
visit
的实例

I在这个上下文中,唯一知道并且应该知道其实际类型的对象是
数据包
实例。因此,您将编写一个
处理程序
,该处理程序被提供给
数据包
,并从那里进行调度。这将类似于以下内容:

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
方法。这种方法被称为。实际上,应该为方法选择不太通用的名称,以使代码更具可读性。通常,会使用特定于域的名称。

虽然访问者模式可以为您解决这个问题,但您需要注意您的依赖关系。您可能希望将所有内容拆分为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);
    }
}
就我个人而言,我不是这个模式的最大粉丝,因为它迫使你在每次添加新数据包时向访问者添加方法,但这就是你对它进行检查和编译时类型检查的方式。对于少量到中等数量的数据包,这可能是您会找到的最好的解决方案


实际上,牺牲编译时安全性并使用命名约定和反射找出合适的数据包处理程序可能更方便。

这基本上就是访问者模式,这是使用多态性解决问题的常用方法。