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

时间:2009-08-10 03:51:53

标签: java polymorphism messages instanceof

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

我想做的一件事就是将方法拉出客户端类并将它们放入消息中。我会在IMessage接口中放入一个类似process()的方法,然后将消息特定的行为放在每个具体的消息类型中。这会使客户端变得简单,因为它只调用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
        }
}

6 个答案:

答案 0 :(得分:7)

避免实例测试的简单方法是多态分派; e.g。

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()方法以返回适当的代码。 Enum的工作方式也很好,IMO更优雅。

答案 1 :(得分:4)

此处的一个选项是处理程序链。您有一系列处理程序,每个处理程序都可以处理一条消息(如果适用),然后使用它,这意味着它不会在链中进一步传递。首先定义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();
        }
    }
}

当然,这并没有摆脱instanceof - 但它确实意味着你没有一个巨大的if-elseif-else-if-instanceof块,这可能是不可读的

答案 2 :(得分:1)

您使用的是什么类型的邮件系统?

许多人都可以根据邮件标题或内容向处理程序添加过滤器。如果支持这一点,你只需创建一个带有基于消息类型的过滤器的处理程序,那么你的代码就很好而且干净,而不需要instanceof或check类型(因为消息传递系统已经为你检查了它)。

我知道您可以在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()对传出消息设置类型。

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

答案 3 :(得分:0)

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

     }
}   

答案 4 :(得分:0)

使用双重调度的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);
}

答案 5 :(得分:0)

使用JMS 2.0,您可以使用:

consumer.receiveBody(String.class)

有关更多信息,请参见here