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

Phi*_*ipp 6 java api

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

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

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

每种消息类型一种方法:

public class DeviceAPI {
  public HelloResponse sendHello(HelloMessage){...}
  public InitResponse sendInit(InitMessage){...}
  ... and many more message types ....
Run Code Online (Sandbox Code Playgroud)

这很安全.(它也可能是同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 ...
Run Code Online (Sandbox Code Playgroud)

这简化了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 ...
Run Code Online (Sandbox Code Playgroud)

在我看来,这很难看.

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

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

Abh*_*kar 2

这是一种使用泛型以类型安全(且可扩展)的方式完成此操作的方法:

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> {
}
Run Code Online (Sandbox Code Playgroud)
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;
    }

}
Run Code Online (Sandbox Code Playgroud)
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());
    }
}
Run Code Online (Sandbox Code Playgroud)
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;
    }

}
Run Code Online (Sandbox Code Playgroud)
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());
    }
}
Run Code Online (Sandbox Code Playgroud)

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