我愿意为这种常见情况选择设计模式:
我有一个接收消息的模块(MessageListener)。 它收到的Eeach消息实际上是一个对象(MyMessage1,MyMessage2,..)
My_Message1,My_Message2扩展了My_Message_Abstract。
现在,当MessageListener对象(onMessage(..))检索到一条消息时 我想做一个不同的“命令”取决于消息实例..这样的事情:
onMessage(My_Message_Abstract msg)
{
if (msg instance of My_Message1)
{
doSomething()..
}
else if(msg instance of My_Message2)
{
doSomethingElse()..
}
}
我希望摆脱这个锅炉,如果/然后代码,并有更好的未来 - 维护/动态/插件/整洁的方式。
所以我采用了Command设计模式。我发现我可以有类似的东西:
在MessageListener中有一张地图:
Map<Integer, MessageCommand> messageCommandsMap = new HashMap<Integer, MessageCommand>();
..
sessionTargetMap.put(MSG_1_TYPE, new Message1Command());
sessionTargetMap.put(MSG_2_TYPE, new Message2Command());
(Message1Command,Message2Command implements from Command interface)
onMessage(My_Message_Abstract msg)
{
messageCommandsMap.get(msg.getType).executeCommand(msg);
}
我不喜欢MessageListeenr中的hashmap想法,这样我将所有命令耦合到该对象(MessageListener)。
正如此主题提供的解决方案: Long list of if statements in Java
任何想法我怎么能改善这个? Mybe我应该用其他模式来实现这个想法吗?
感谢,
答案 0 :(得分:3)
我采用的方法是使用通用接口来装饰这些类,这使我能够利用Java函数的虚拟特性。例如,我会这样做:
public interface CoolInterface
{
void doSomething();
}
My_Message_Abstract implements CoolInterface
{
public abstract void doSomething();
}
Message1Command extends My_Message_Abstract
{
public void doSomething(){ System.out.println("First");
}
Message2Command extends My_Message_Abstract
{
public void doSomething(){ System.out.println("");
}
您的代码现在变为:
onMessage(My_Message_Abstract msg)
{
msg.doSomething();
}
如果您想委派,请执行以下操作:
My_Message_Abstract implements CoolInterface
{
public void doSomething()
{
System.out.println("Default");
}
}
Message1Command extends My_Message_Abstract
{
public void doSomething(){ System.out.println("First");
}
Message2Command extends My_Message_Abstract
{
// no need to override the method just invoke doSomething as normal
}
答案 1 :(得分:1)
当您想要可能的事情列表时,我总是喜欢使用enum
:
public class Test {
// A type of message.
class MyMessage1 {
};
// A whole set of message types.
interface MyMessage2 {
};
// The Dos - To demonstrate we just print something.
enum Do {
Something(MyMessage1.class) {
@Override
void doIt() {
System.out.println("Something");
}
},
SomethngElse(MyMessage2.class) {
@Override
void doIt() {
System.out.println("Something else");
}
},
Everything(Object.class) {
@Override
void doIt() {
System.out.println("Everything");
}
};
// Which classes this one applies to - could use an array of Class here just as easily.
final Set<Class> applies = new HashSet<Class>();
// You can add multiples on construction.
Do(Class... applies) {
this.applies.addAll(Arrays.asList(applies));
}
// Perform all that are relevant to this message type.
static void doIt(Class messageClass) {
for (Do d : Do.values()) {
// If it is assignable
boolean doIt = false;
for (Class c : d.applies) {
if (c.isAssignableFrom(messageClass)) {
doIt = true;
}
}
if (doIt) {
// Execute the function.
d.doIt();
}
}
}
// What to do.
abstract void doIt();
}
public void test() {
System.out.println("Hello");
// Test with a concrete message.
onMessage(new MyMessage1());
// And an implementation of an interface.
onMessage(new MyMessage2() {
});
}
private void onMessage(Object message) {
// Do something depending on the class of the message.
Do.doIt(message.getClass());
}
public static void main(String args[]) {
new Test().test();
}
}
使用此模式,您甚至可以为所有消息触发Everything
。
扩展它很容易使用这个Something
适用的类数组。
如果您只想要应用第一个匹配项,请在适当的位置突破循环。
我的观点是 - 使用这种机制 - 你可以实现几乎任何你喜欢的策略,而不必积极地破解代码。
答案 2 :(得分:0)
你可以拥有一个MessageCommand Factory,它基本上知道哪个MessageCommand用于给定的消息类型。在工厂中,您可以使用map或if / else来识别命令类。
现在,您的消息侦听器要求工厂根据类型提供相应的Message Command,然后每个Message Command都保存逻辑以有效地处理命令(doSomethingElse)方法。
代码如何显示的抽象概念:
class MessageCommandFactory {
Command get(MessageType messageType) {
if(messageType == MSG_1_TYPE) {
return new Message1Command();
} else ...
}
class MessageListener {
MessageCommandFactory messageCommandFactor;
onMessage(My_Absctract_Message message) {
Command command = messageCommandFactory.get(message.getType());
command.execute();
}
}
答案 3 :(得分:0)
实际上,您的MessageListener
执行了两个步骤:
您可以将“running”委托给@Woot4Moo的消息本身,或者将MessageListener
子类委托为消息执行特定的操作,如下所示:
public class Message1Listener implements MessageListener {
onMessage(SpecificMessage1 msg) {
/* do something with msg */
}
}
public class MessageBus {
Map<Class<? extends Message>, MessageListener> listeners = new HashMap<>();
void register(MessageListener listener, Class<? extends Message> msgType) {
listeners.put(msgType, listener);
}
void onMessage(Message msg) {
listeners.get(msg.getClass()).onMessage(msg);
}
}
您的MessageListener
已成为MessageBus
,代理已注册的处理程序。
这是一个非常基本设置,您可以根据需要进行扩展(即每个消息类型都有MessageListener
列表),甚至可能已存在于您的上下文中。
答案 4 :(得分:0)
在类似的情况下,我已经开始使用你已有的东西,但添加了一个方法:
addMessageListener(int messageType, MessageCommand messageCommand)
此外,其他课程需要一种“定位”你的课程的方法。最简单的方法是将上述方法用于公共静态,如果它适用于您的环境......但是可以酌情使用其他发现方式。
该方法只是添加到您已有的地图中。
如果两个命令希望收听同一条消息,那么通过将地图的“值”一边变成一个List
就可以轻松调整它此外,您始终可以使用已经“预先注册”的某些预先确定的命令启动地图,或者从某种类型的配置中读取预注册信息......等等。
示例1:假设您有一个名为NewMessageHandler的新类。该类需要处理全新的消息类型。在某些适当的时候,当NewMessageHandler初始化自己时,if也可以将自己注册为监听器。它必须“找到”调度程序类(希望这是一个单例),然后它可以调用addMessageListener()。
示例2: Dispatcher类可以读取配置文件,该文件定义将处理消息的消息类型和类对。
public class Dispatcher
{
public static Dispatcher getInstance()
{
//return the instance. Could accept parms for more complex needs
}
public void addMessageListener(int messageType, MessageCommand messageCommand)
{
//Add to the internal map
}
private void init()
{
//Optionally, read config file or System properties, and call addMessageListener()
}
private void dispatchMessage(Message msg)
{
//Look up map and dispatch to the registered instance
//Call the handleMessage() method on the appropriate listener
}
}
界面
public interface MessageCommand
{
public void handleMessage(Message msg);
}
和另一个......
public class NewMessageHandler implements MessageCommand
{
private void init()
{
Dispatcher.addMessageListener(666, this)
}
public void handleMessage(Message msg)
{
}
}
答案 5 :(得分:0)
这里的基本问题是消息是数据,您需要根据该数据的属性进行调度。因此,您需要更改数据对象以分派给不同的处理程序,或者您需要在侦听器中使用调度表。无论哪种方式,您都需要代码来处理调度。
IMO,消息是实现调度的错误的位置。它是数据,行为是听众的责任。如果你有两个听众,你会想要使用不同的发送表。
对于我自己的实现,我可能会使用if-else链来进行多达五到六次可能的干扰。在上面,一个算子地图。这些更容易,而且很明显发生了什么。
但是,如果你真的不喜欢简单的方法,那么这里就是使用反射的方法:
public class Dispatcher {
public static class Foo {}
public static class Bar {}
public void dispatch(Object obj) throws Exception {
try {
Method handler = this.getClass().getDeclaredMethod("handler", obj.getClass());
handler.invoke(this, obj);
}
catch (Exception e) {
System.out.println("couldn't determine handler for " + obj.getClass().getName());
}
}
private void handler(Foo foo) {
System.out.println("handler(Foo)");
}
private void handler(Bar bar) {
System.out.println("handler(Bar)");
}
public static void main(String[] argv) throws Exception {
Dispatcher dispatcher = new Dispatcher();
dispatcher.dispatch(new Foo());
dispatcher.dispatch(new Bar());
dispatcher.dispatch("something else");
}
}