我确信这是一个基本的OOP问题 - 我正在设计一个消息传递系统,其中有几种完全不同的消息格式,但我希望它们都可以放在PriorityBlockingQueue上。我的第一个想法是定义一个abstract class Message
,然后为每个消息类型定义扩展Message的子类。但这意味着,在接收端,消息处理器需要识别子类,以便知道如何处理消息的内容。我知道这样做的唯一方法是使用.instanceof()
或Class.
并且它似乎不正确。
Scott Meyers写道,
任何时候你发现自己编写的形式代码“如果对象是 T1类型,然后做一些事情,但如果它是T2类型,那么做 别的什么,“打自己。”
(他继续指出,在多态性中,你应该有相同的方法名称,每个子类都有不同的实现。我不知道如何让这个想法在我的案例中起作用 - 消息类型本身是完全不相关的。)
为了便于讨论,以下是我的消息类型:
我相信我必须知道我正在处理什么样的消息,所以我怀疑我不应该使用多态消息。我该如何正确设计?
=====编辑提出后续问题=====
我试图找到一种方法来使用多态消息,而无需在某些时候识别它的子类。建议的方法是覆盖每个子类中的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的状态更有用?
答案 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)
除了已经提出的解决方案之外,我想提一下,你可以在没有任何接口或抽象类的情况下完成它。
可以创建自己的注释,其中包含可以处理消息对象的信息。
在实施时: 您可以相应地注释您的课程。
在运行时: 当您从队列中弹出下一个元素时,您可以检查注释并调用能够处理当前实例的实现。