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

如何在Java中设计类型安全的消息API?,java,api,Java,Api,我有一个Java客户端,它希望通过串行通信通过消息与设备通信。客户端应该能够使用干净的API,抽象出串行通信的丑陋细节。客户端可以通过该API发送多种类型的消息并获得响应。我正在寻找实现这个API的最佳方法 为简单起见,假设我们只有两种消息类型:hellomemessage触发helloreresponse和InitMessage触发InitResponse(实际上,还有更多) 设计API(即设备的Java抽象)我可以: 每个消息类型一个方法: public class DeviceAPI {

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

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

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

每个消息类型一个方法:

public class DeviceAPI {
  public HelloResponse sendHello(HelloMessage){...}
  public InitResponse sendInit(InitMessage){...}
  ... and many more message types ....
这是很好的类型安全。(它也可能多次使用相同的
send()
method,重载,但大致相同)。但是它非常明确,也不是很灵活——我们不能在不修改API的情况下添加消息

我还可以有一个单一的发送方法,它接受所有消息类型:

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 ...
我觉得这很难看

你推荐什么?是否有其他方法可以提供更干净的结果


欢迎推荐!其他人是如何解决这一问题的?

我现在有了一个完全可行的示例,说明您想要什么:

要定义消息的类型,请执行以下操作:

public interface MessageType {
    public static class INIT implements MessageType { }
    public static class HELLO implements MessageType { }
}
基本
消息
响应
类:

public class Message<T extends MessageType> {

}
请注意,它确实需要一个
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

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

我认为这一点都不难看:

if(rsp instanceOf HelloResponse){
    HelloResponse hrsp = (HelloResponse)rsp;
    ...
else if ...
只要你没有100个不同的回答。您可以将多种响应封装在一个响应中,具体取决于它们所拥有的数据。例如:

class GenericResponse{
   private String message;
   private int responseType;
   ...
}
我开发了一些多人游戏,这是一个很好的方法。 如果有太多不同类型的消息,可以使用通用java类型,如上面的skiwi示例


希望它有帮助

您可以拥有一个消息处理程序系统,并且您的设备API可以选择适合传入消息的处理程序;并将其委托给相应的消息处理程序:

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);
}
}
类设备API{
私有列表msgHandlers=newArrayList();
公共设备API(){
添加(新的HelloHandler());
//可以添加其他消息处理程序
}
公共响应发送(消息消息消息)引发异常{
for(处理程序:MsgHandler){
if(handler.canHandle(msg)){
返回handler.handle(msg);
}
}
抛出新异常(“没有为“+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;
}
}
接口处理程序{
布尔canHandle(消息);
U句柄(T消息);
}
类HelloHandler实现处理程序{
@凌驾
公共布尔canHandle(消息){
返回HelloMessage的消息实例;
}
@凌驾
公共Hello响应句柄(HelloMessage消息){
//处理你的信息
返回null;
}
}

其他消息也一样。我相信你可以使它更优雅,但想法仍然是一样的-没有一个怪物的方法与ifs;请改用多态性。

以下是一种使用泛型以类型安全(且可扩展)的方式实现多态性的方法:

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> {
}


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

是否可以使用带有枚举的参数化类型?或者这根本不可能。你检查“响应”的类型,然后抛出HelloMessage。这似乎很奇怪。无论如何,您可以在实现类中隐藏“instanceOf”和“casting”操作。我认为检查类型一点也不难看,这可能也是我实现它的方式。如果你不想这样,我认为重新考虑的正确地点是你的模型;是什么使这些响应成为不同的对象?例如,它们不能是泛型吗?@Marktieleman这些信息有很多相似之处,但也有很多不同之处。假设Hello响应包含设备的序列号。我希望HelloResponse对象具有getSerialNumber()方法。我怎样才能用泛型实现这一点?@arjacsoh抱歉,这是一个打字错误。谢谢你发现了。谢谢,这很有趣。假设响应包含不同的信息,我如何访问它?@Philipp目前正在处理这个问题,您希望能够使用类似
InitMessage
的东西,而不是
Message
。@Philipp我现在有了最后一个工作示例。我希望这正是您想要的。@Philipp并提供了一个如何从消息中读取输入值的工作示例。这是一种聪明的方法。所有的演员都被隐藏了!谢谢你开发它。尽管如此,我相信Abhinav Sarkar的解决方案甚至更为优越,因为它用处理程序的映射来代替梯形图的实例。我被教导梯形图的实例是代码气味。这就是为什么我在寻找一种更加面向对象的方法。也许是访问者模式?我喜欢这个想法:这更像是一种回调方法。非常干净的解决方案,谢谢!(与斯奇维非常相似)。经过一些调整,我还可以从消息中删除getTypeClass()方法。您可以通过message.getClass().getGenericInterfaces()[0].getActualTypeArguments()访问相同的信息(需要对参数进行一些转换)
api: init
client: init
api: hello
client: hello
if(rsp instanceOf HelloResponse){
    HelloResponse hrsp = (HelloResponse)rsp;
    ...
else if ...
class GenericResponse{
   private String message;
   private int responseType;
   ...
}
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);
}
}
 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;
}
}
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());
    }
}