3

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

正如斯科特迈耶斯所写,

任何时候你发现自己在编写“如果对象是 T1 类型,则做某事,但如果它是 T2 类型,则做其他事情”形式的代码,给自己一记耳光。

(他继续指出,在多态中,你应该有相同的方法名称,每个子类有不同的实现。我不明白如何让这个想法在我的情况下发挥作用——消息类型本身是完全不相关的.)

为了讨论,这里是我的消息类型:

  • ConsoleMessage,标识一个 ConsoleObject 和一个 ObjectState。
  • CardReaderRequestMessage,不包含任何内容,只是请求“下一张卡片”
  • CardReaderMessage,包含一个 byte[80] 的卡片图像和一个 Last Card 指示符
  • CardPunchMessage,包含一个byte[80]的卡片图片
  • CardPunchResponseMessage,不包含任何内容,但表示卡片图像已复制到打孔缓冲区

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

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

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

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
}

这基本上意味着找出“味精”属于哪个类。

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

4

4 回答 4

2

你没疯。那个论点:

任何时候你发现自己在编写“如果对象是 T1 类型,则做某事,但如果它是 T2 类型,则做其他事情”形式的代码,给自己一记耳光。

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

有很多方法可以保持“并行但解耦”的代码。访问者模式是一个,大开关或 if-instance-of 块也是如此。另一种选择是Map具体类到具体类。

于 2011-07-23T04:52:27.803 回答
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
}
}

这样,接收端只需要接收 IS A Message 即实现您的 Message 接口的东西,它并不真正关心什么类型的特定消息,它只关心它响应进程,所以你可以实现每个特定的应该在那里处理消息。

于 2011-07-23T04:51:17.157 回答
1

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

于 2011-07-23T04:52:19.970 回答
1

除了已经提出的解决方案之外,我想提一下,您可以在没有任何接口或抽象类的情况下做到这一点。

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

在实施时:您相应地注释您的类。

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

于 2011-07-23T08:38:23.060 回答