检查消息类型时避免使用instanceof

发布于 2024-08-02 05:34:11 字数 745 浏览 12 评论 0原文

我遇到以下情况,客户端类根据它收到的消息类型执行不同的行为。我想知道是否有更好的方法来做到这一点,因为我不喜欢 instanceof 和 if 语句。

我想做的一件事是将方法从客户端类中取出并将它们放入消息中。我会将类似 process() 的方法放入 IMessage 接口中,然后将消息特定行为放入每个具体消息类型中。这将使客户端变得简单,因为它只调用 message.process() 而不是检查类型。然而,唯一的问题是条件语句中包含的行为与 Client 类中包含的数据操作有关。因此,如果我确实在具体消息类中实现了一个处理方法,我就必须将它传递给客户端,而且我也不知道这是否真的有意义。

public class Client {
    messageReceived(IMessage message) {
        if(message instanceof concreteMessageA) {
            concreteMessageA msg = (concreteMessageA)message;
            //do concreteMessageA operations
        }
    }
        if (message instanceof concreteMessageB) {
            concreteMessageb msg = (concreteMessageB)message;
            //do concreteMessageB operations
        }
}

I have the following situation where a client class executes different behavior based on the type of message it receives. I'm wondering if there is a better way of doing this since I don't like the instanceof and the if statements.

One thing I thought of doing was pulling the methods out of the client class and putting them into the messages. I would put a method like process() in the IMessage interface and then put the message specific behavior in each of the concrete message types. This would make the client simple because it would just call message.process() rather than checking types. However, the only problem with this is that the behavior contained in the conditionals has to do with operations on data contained within the Client class. Thus, if I did implement a process method in the concrete message classes I would have to pass it the client and I don't know if this really makes sense either.

public class Client {
    messageReceived(IMessage message) {
        if(message instanceof concreteMessageA) {
            concreteMessageA msg = (concreteMessageA)message;
            //do concreteMessageA operations
        }
    }
        if (message instanceof concreteMessageB) {
            concreteMessageb msg = (concreteMessageB)message;
            //do concreteMessageB operations
        }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

稀香 2024-08-09 05:34:11

避免instanceof测试的简单方法是多态分派;例如,

public class Client {
    void messageReceived(IMessage message) {
        message.doOperations(this);
    }
}

每个消息类定义适当的 doOperations(Client client) 方法。

编辑:第二个解决方案更符合要求。

用 switch 语句替换一系列“instanceof”测试的替代方法是:

public class Client {
    void messageReceived(IMessage message) {
        switch (message.getMessageType()) {
        case TYPE_A:
           // process type A
           break;
        case TYPE_B:
           ...
        }
    }
}

每个 IMessage 类都需要定义一个 int getMessageType() 方法来返回适当的代码。在我看来,枚举与整数一样工作,而且更优雅。

The simple way to avoid instanceof testing is to dispatch polymorphicly; e.g.

public class Client {
    void messageReceived(IMessage message) {
        message.doOperations(this);
    }
}

where each message class defines an appropriate doOperations(Client client) method.

EDIT: second solution which better matches the requirements.

An alternative that replaces a sequence of 'instanceof' tests with a switch statement is:

public class Client {
    void messageReceived(IMessage message) {
        switch (message.getMessageType()) {
        case TYPE_A:
           // process type A
           break;
        case TYPE_B:
           ...
        }
    }
}

Each IMessage class needs to define an int getMessageType() method to return the appropriate code. Enums work just as well ints, and are more more elegant, IMO.

蓝天白云 2024-08-09 05:34:11

这里的一个选项是处理程序链。您有一个处理程序链,每个处理程序都可以处理一条消息(如果适用),然后使用它,这意味着它不会在链中进一步传递。首先定义 Handler 接口:

public interface Handler {
    void handle(IMessage msg);
}

然后处理程序链逻辑如下所示:

List<Handler> handlers = //...
for (Handler h : handlers) {
    if (!e.isConsumed()) h.handle(e);
}

然后每个处理程序可以决定处理/使用事件:

public class MessageAHandler implements Handler {
    public void handle(IMessage msg) {
        if (msg instanceof MessageA) {
            //process message
            //consume event 
            msg.consume();
        }
    }
}

当然,这并不能摆脱 instanceofs - 但这确实意味着您没有巨大的 if-elseif-else-if-instanceof 块,该块可能不可读

One option here is a handler chain. You have a chain of handlers, each of which can handle a message (if applicable) and then consume it, meaning it won't be passed further down the chain. First you define the Handler interface:

public interface Handler {
    void handle(IMessage msg);
}

And then the handler chain logic looks like:

List<Handler> handlers = //...
for (Handler h : handlers) {
    if (!e.isConsumed()) h.handle(e);
}

Then each handler can decide to handle / consume an event:

public class MessageAHandler implements Handler {
    public void handle(IMessage msg) {
        if (msg instanceof MessageA) {
            //process message
            //consume event 
            msg.consume();
        }
    }
}

Of course, this doesn't get rid of the instanceofs - but it does mean you don't have a huge if-elseif-else-if-instanceof block, which can be unreadable

花开雨落又逢春i 2024-08-09 05:34:11

您使用什么类型的消息系统?

许多人可以选择根据消息标题或内容向处理程序添加过滤器。如果支持这一点,您只需创建一个带有基于消息类型的过滤器的处理程序,那么您的代码就干净整洁,不需要instanceof或检查类型(因为消息系统已经为您检查了)。

我知道您可以在 JMS 或 OSGi 事件服务中执行此操作。

由于您使用的是 JMS,因此您基本上可以执行以下操作来注册侦听器。这将为每个独特的消息类型创建一个侦听器。

  String filterMsg1 = "JMSType='messageType1'";
  String filterMsg2 = "JMSType='messageType2'";

  // Create a receiver using this filter
  Receiver receiverType1 = session.createReceiver(queue, filterMsg1);
  Receiver receiverType2 = session.createReceiver(queue, filterMsg2);

  receiverType1.setMessageHandler(messageType1Handler);
  receiverType2.setMessageHandler(messageType2Handler);

现在,每个处理程序将仅接收特定的消息类型(无 instanceof 或 if-then),当然假设发送者通过调用传出消息上的 setJMSType() 设置类型。

此方法内置于消息中,但您当然也可以创建自己的标头属性并对其进行过滤。

What type of message system are you using?

Many have options to add a filter to the handlers based on message header or content. If this is supported, you simply create a handler with a filter based on message type, then your code is nice and clean without the need for instanceof or checking type (since the messaging system already checked it for you).

I know you can do this in JMS or the OSGi event service.

Since you are using JMS, you can basically do the following to register your listeners. This will create a listener for each unique message type.

  String filterMsg1 = "JMSType='messageType1'";
  String filterMsg2 = "JMSType='messageType2'";

  // Create a receiver using this filter
  Receiver receiverType1 = session.createReceiver(queue, filterMsg1);
  Receiver receiverType2 = session.createReceiver(queue, filterMsg2);

  receiverType1.setMessageHandler(messageType1Handler);
  receiverType2.setMessageHandler(messageType2Handler);

Now each handler will receive the specific message type only (no instanceof or if-then), assuming of course that the sender sets the type via calls to setJMSType() on the outgoing message.

This method is built into message, but you can of course create your own header property and filter on that instead as well.

被你宠の有点坏 2024-08-09 05:34:11
//Message.java

abstract class Message{
     public abstract void doOperations();
 }

//MessageA.java

 class MessageA extends Message{
    public void doOperations(){ 
         //do concreteMessageA operations ;
    }
 }

   //MessageB.java

class MessageB extends Message {
     public void doOperations(){ 
         //do concreteMessageB operations 
     }
}

//MessageExample.java

class MessageExample{
   public static void main(String[] args) {
        doSmth(new MessageA());
    }

    public static void doSmth(Message message) {
       message.doOperations() ;     

     }
}   
//Message.java

abstract class Message{
     public abstract void doOperations();
 }

//MessageA.java

 class MessageA extends Message{
    public void doOperations(){ 
         //do concreteMessageA operations ;
    }
 }

   //MessageB.java

class MessageB extends Message {
     public void doOperations(){ 
         //do concreteMessageB operations 
     }
}

//MessageExample.java

class MessageExample{
   public static void main(String[] args) {
        doSmth(new MessageA());
    }

    public static void doSmth(Message message) {
       message.doOperations() ;     

     }
}   
泼猴你往哪里跑 2024-08-09 05:34:11

使用双重调度的 Java 8 解决方案。没有完全摆脱 instanceof 但只需要对每条消息进行一次检查,而不是 if-elseif 链。

public interface Message extends Consumer<Consumer<Message>> {};

public interface MessageA extends Message {

   @Override
   default void accept(Consumer<Message> consumer) {
      if(consumer instanceof MessageAReceiver){
        ((MessageAReceiver)consumer).accept(this);
      } else {
        Message.super.accept(this);
      }
   }
}

public interface MessageAReceiver extends Consumer<Message>{
   void accept(MessageA message);
}

A Java 8 solution that uses double dispatch. Doesn't get rid of instanceof completely but does only require one check per message instead of an if-elseif chain.

public interface Message extends Consumer<Consumer<Message>> {};

public interface MessageA extends Message {

   @Override
   default void accept(Consumer<Message> consumer) {
      if(consumer instanceof MessageAReceiver){
        ((MessageAReceiver)consumer).accept(this);
      } else {
        Message.super.accept(this);
      }
   }
}

public interface MessageAReceiver extends Consumer<Message>{
   void accept(MessageA message);
}
天涯沦落人 2024-08-09 05:34:11

对于 JMS 2.0,您可以使用:

consumer.receiveBody(String.class)

有关更多信息,您可以参考 此处

With JMS 2.0 you can use:

consumer.receiveBody(String.class)

For more information you can refer here:

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文