我应该如何在Java中实现Message类的子类型?

时间:2011-07-23 04:46:17

标签: java polymorphism subtyping

我确信这是一个基本的OOP问题 - 我正在设计一个消息传递系统,其中有几种完全不同的消息格式,但我希望它们都可以放在PriorityBlockingQueue上。我的第一个想法是定义一个abstract class Message,然后为每个消息类型定义扩展Message的子类。但这意味着,在接收端,消息处理器需要识别子类,以便知道如何处理消息的内容。我知道这样做的唯一方法是使用.instanceof()Class.并且它似乎不正确。

Scott Meyers写道,

  

任何时候你发现自己编写的形式代码“如果对象是   T1类型,然后做一些事情,但如果它是T2类型,那么做   别的什么,“打自己。”

(他继续指出,在多态性中,你应该有相同的方法名称,每个子类都有不同的实现。我不知道如何让这个想法在我的案例中起作用 - 消息类型本身是完全不相关的。)

为了便于讨论,以下是我的消息类型:

  • ConsoleMessage,标识ConsoleObject和ObjectState。
  • CardReaderRequestMessage,不包含任何东西,只是简单地请求“下一张牌”
  • CardReaderMessage,包含字节[80]卡片图像和最后一张卡片指示符
  • CardPunchMessage,包含一个字节[80]卡片图片
  • CardPunchResponseMessage,不包含任何内容,但表示卡片图像已复制到打孔缓冲区

我相信我必须知道我正在处理什么样的消息,所以我怀疑我不应该使用多态消息。我该如何正确设计?

=====编辑提出后续问题=====

我试图找到一种方法来使用多态消息,而无需在某些时候识别它的子类。建议的方法是覆盖每个子类中的process()方法。这是我的(简化的)抽象消息和两个子类:

public abstract class Message {

    public abstract void process() {
        // subclasses of Message implement this
    }

    public static class ConsoleMessage extends Message {
        private int obj;
        private int state;
        public ConsoleMessage(int x, int y) {
            obj = x;
            state = y;
        }
        @Override
        public void process() {
            // do something with obj and state?
        }

    public static class CardReaderMessage extends Message {
        private byte[] card;
        private boolean lastCardIndicator;
        public CardReaderMessage(byte[] c, boolean lc) {
            card = c;
            lastCardIndicator = lc;
        }
        @Override
        public void process() {
            // do something with card and lastCardIndicator
        }
}

每个线程都有一个队列用于所有“入站”消息。假设我的线程需要等待来自控制台的消息“恢复”,但同时应该接收并处理其他消息类型:

waitForResumeMessage() {
    while (true) { // the following will block until a msg arrives
        Message msg = inboundMessageQueue.receiveMessage();
        msg.process();    

但现在呢? process()的一些实现已经在某处移动了一些数据,但最终我需要能够编写:

        if // msg was ConsoleMessage "resume" command
            return;  // .. from waitForResumeMessage()
    } // else iterate until another message
}

这基本上意味着找出'msg'所属的类。

我接近这一切都错了吗?我意识到“等待”在“事件驱动”模型中并不合适,但这是一个长期运行的后台工作者。也许使用process()的想法对于改变引导事件驱动线程的FSM的状态更有用?

4 个答案:

答案 0 :(得分:2)

你不是疯了。那个论点:

  

任何时候你发现你自己写的形式的代码“如果对象是T1类型,那么做一些事情,但如果它是T2类型,那么做点其他事情,”自拍吧。

只是故事的一个方面。这个故事的另一面是,保持关注点分离就像(如果不是更多)一样重要。一个常见的例子是,您不应该仅仅因为可以多态处理它而将表示代码添加到模型中。不幸的是,Java语言在同时完成两个理想方面没有多大帮助。

有许多方法可以保持“并行但解耦”的代码。 Visitor pattern是一个,大块开关或if-instance-of块。另一个选项是具体类的具体类Map

答案 1 :(得分:1)

你的想法很好,你应该从一个抽象类或一个名为Message的接口开始,这个接口应该是这样的:

     public interface Message {
     void process();
     //some other methods
}

public class MessageType1  implements Message {
    @Override
    public void process() {
      //My special way to process this message
}
}

这样接收端只需要收到一条消息,即实现你的消息接口,它并不关心它所关心的特定消息的类型是它响应进程,所以你可以实现如何在那里处理每个特定的消息。

答案 2 :(得分:1)

你想要的是模板方法。这意味着您在Message方法中定义接收端希望使用相同的接口调用,但是基于实际对象的类型,实际上将调用任何类型的消息中的重写方法。

答案 3 :(得分:1)

除了已经提出的解决方案之外,我想提一下,你可以在没有任何接口或抽象类的情况下完成它。

可以创建自己的注释,其中包含可以处理消息对象的信息。

在实施时: 您可以相应地注释您的课程。

在运行时: 当您从队列中弹出下一个元素时,您可以检查注释并调用能够处理当前实例的实现。