如何在Java中设计类型安全的消息API?

时间:2013-12-11 09:46:37

标签: java api

我有一个Java客户端,希望通过串行通信的消息与设备通信。客户端应该能够使用干净的API,抽象出串行通信的丑陋细节。客户端可以通过该API发送许多类型的消息并获得响应。我正在寻找建议哪种方式最适合实施此API。

为简单起见,我们只提供两种消息类型:HelloMessage触发HelloResponseInitMessage触发InitResponse(实际上还有更多)

设计API(即设备的Java抽象)我可以:

每种消息类型一种方法:

public class DeviceAPI {
  public HelloResponse sendHello(HelloMessage){...}
  public InitResponse sendInit(InitMessage){...}
  ... and many more message types ....

这很安全。 (它也可能是同一send()方法的多次重载,但这个方法大致相同。但它非常明确,而且不够灵活 - 我们无法在不修改API的情况下添加消息。

我也可以使用一个send方法,它接受所有消息类型:

class HelloMessage implements Message
class HelloResponse implements Response   
...
public class DeviceAPI {
  public Response send(Message msg){
    if(msg instanceof HelloMessage){
       // do the sending, get the response
       return theHelloResponse
    } else if(msg instanceof ...

这简化了API(只有一种方法),并允许在以后添加其他消息类型而无需更改API。同时,它要求客户端检查响应类型并将其转换为正确的类型。

客户代码:

DeviceAPI api = new DeviceAPI();
HelloMessage msg = new HelloMessage();
Response rsp = api.send(msg);
if(rsp instanceOf HelloResponse){
    HelloResponse hrsp = (HelloResponse)rsp;
    ... do stuff ...

在我看来这很难看。

你推荐什么?还有其他方法可以提供更清晰的结果吗?

欢迎参考!别人怎么解决这个问题?

4 个答案:

答案 0 :(得分:2)

我现在已经有了一个完全正常的例子:

定义消息类型:

public interface MessageType {
    public static class INIT implements MessageType { }
    public static class HELLO implements MessageType { }
}

基础MessageResponse类:

public class Message<T extends MessageType> {

}

public class Response<T extends MessageType> {

}

创建自定义初始化消息和响应:

public class InitMessage extends Message<MessageType.INIT> {
    public InitMessage() {
        super();
    }

    public String getInit() {
        return "init";
    }
}

public class InitResponse extends Response<MessageType.INIT> {
    public InitResponse() {
        super();
    }

    public String getInit() {
        return "init";
    }
}

创建自定义问候消息和响应:

public class HelloMessage extends Message<MessageType.HELLO> {
    public HelloMessage() {
        super();
    }

    public String getHello() {
        return "hello";
    }
}

public class HelloResponse extends Response<MessageType.HELLO> {
    public HelloResponse() {
        super();
    }

    public String getHello() {
        return "hello";
    }
}

DeviceAPI

public class DeviceAPI {
    public <T extends MessageType, R extends Response<T>, M extends Message<T>> R send(M message) {
        if (message instanceof InitMessage) {
            InitMessage initMessage = (InitMessage)message;
            System.out.println("api: " + initMessage.getInit());
            return (R)(new InitResponse());
        }
        else if (message instanceof HelloMessage) {
            HelloMessage helloMessage = (HelloMessage)message;
            System.out.println("api: " + helloMessage.getHello());
            return (R)(new HelloResponse());
        }
        else {
            throw new IllegalArgumentException();
        }
    }
}

请注意,它确实需要instanceof - 树,但您需要它来处理它是什么类型的消息。

一个有效的例子:

public static void main(String[] args) {
    DeviceAPI api = new DeviceAPI();

    InitMessage initMsg = new InitMessage();
    InitResponse initResponse = api.send(initMsg);
    System.out.println("client: " + initResponse.getInit());

    HelloMessage helloMsg = new HelloMessage();
    HelloResponse helloResponse = api.send(helloMsg);
    System.out.println("client: " + helloResponse.getHello());
}

输出:

api: init
client: init
api: hello
client: hello

更新:添加了有关如何从客户端要发送的消息中获取输入的示例。

答案 1 :(得分:1)

您可以拥有一个消息处理程序系统,您的DeviceAPI可以选择哪个处理程序适合传入消息;并将其委托给适当的消息处理程序:

class DeviceAPI {

private List<Handler> msgHandlers = new ArrayList<Handler>();

public DeviceAPI(){
    msgHandlers.add(new HelloHandler());
            //Your other message handlers can be added
}

public Response send(Message msg) throws Exception{
    for (Handler handler : msgHandlers) {
        if (handler.canHandle(msg)){
            return handler.handle(msg);
        }
    }
    throw new Exception("No message handler defined for " + msg);
}
}

HelloHandler看起来像:

 interface Handler<T extends Message, U extends Response> {
    boolean canHandle(Message message);
    U handle(T message);
 }


class HelloHandler implements Handler<HelloMessage, HelloResponse> {

@Override
public boolean canHandle(Message message) {
    return message instanceof HelloMessage;
}

@Override
public HelloResponse handle(HelloMessage message) {
    //Process your message
    return null;
}
}

同样适用于您的其他消息。我相信你可以让它更优雅,但这个想法仍然保持不变 - 不要用ifs的怪物方法;而是使用多态。

答案 2 :(得分:1)

以下是使用泛型以类型安全(和可扩展)方式执行此操作的方法:

public interface MessageType {    
    public static final class HELLO implements MessageType {};
}

public interface Message<T extends MessageType> {
    Class<T> getTypeClass();
}

public interface Response<T extends MessageType> {
}

public class HelloMessage implements Message<MessageType.HELLO> {

    private final String name;

    public HelloMessage(final String name) {
        this.name = name;
    }

    @Override
    public Class<MessageType.HELLO> getTypeClass() {
        return MessageType.HELLO.class;
    }

    public String getName() {
        return name;
    }

}

public class HelloResponse implements Response<MessageType.HELLO> {

    private final String name;

    public HelloResponse(final String name) {
        this.name = name;
    }

    public String getGreeting() {
        return "hello " + name;
    }

}

public interface MessageHandler<T extends MessageType, M extends Message<T>, R extends Response<T>> {
    R handle(M message);
}

public class HelloMessageHandler 
    implements MessageHandler<MessageType.HELLO, HelloMessage, HelloResponse> {
    @Override
    public HelloResponse handle(final HelloMessage message) {
        return new HelloResponse(message.getName());
    }
}

import java.util.HashMap;
import java.util.Map;

public class Device {

    @SuppressWarnings("rawtypes")
    private final Map<Class<? extends MessageType>, MessageHandler> handlers =
        new HashMap<Class<? extends MessageType>, MessageHandler>();

    public <T extends MessageType, M extends Message<T>, R extends Response<T>> 
        void registerHandler(
            final Class<T> messageTypeCls, final MessageHandler<T, M, R> handler) {
        handlers.put(messageTypeCls, handler);
    }

    @SuppressWarnings("unchecked")
    private <T extends MessageType, M extends Message<T>, R extends Response<T>> 
        MessageHandler<T, M, R> getHandler(final Class<T> messageTypeCls) {
        return handlers.get(messageTypeCls);
    }


    public <T extends MessageType, M extends Message<T>, R extends Response<T>> 
        R send(final M message) {
        MessageHandler<T, M, R> handler = getHandler(message.getTypeClass());
        R resposnse = handler.handle(message);
        return resposnse;
    }

}

public class Main {
    public static void main(final String[] args) {
        Device device = new Device();
        HelloMessageHandler helloMessageHandler = new HelloMessageHandler();
        device.registerHandler(MessageType.HELLO.class, helloMessageHandler);

        HelloMessage helloMessage = new HelloMessage("abhinav");
        HelloResponse helloResponse = device.send(helloMessage);
        System.out.println(helloResponse.getGreeting());
    }
}

要添加对新消息类型的支持,请实现MessageType接口以创建新消息类型,为新Message实现ResponseMessageHandler接口1}}类并通过调用MessageType来注册新消息类型的处理程序。

答案 3 :(得分:0)

我认为这根本不丑:

if(rsp instanceOf HelloResponse){
    HelloResponse hrsp = (HelloResponse)rsp;
    ...
else if ...

只要你没有100个不同的回答。您可以将多种响应封装在一起,具体取决于它们具有的数据。例如:

class GenericResponse{
   private String message;
   private int responseType;
   ...
}

我开发了一些多人游戏,这是一个很好的方法。 如果你有太多不同类型的消息,你可以使用通用的java类型,比如上面的skiwi示例。

希望有所帮助