我有一个Java客户端,它想通过串行通信的消息与设备进行通信。客户端应该能够使用干净的API,抽象出串行通信的丑陋细节。客户端可以通过该 API 发送多种类型的消息并获得响应。我正在寻找哪种方式最适合实现此 API 的建议。
为简单起见,假设我们只有两种消息类型:触发HelloResponse
的HelloMessage
和触发InitResponse
的InitMessage
(实际上,还有更多(
设计 API(即设备的 Java 抽象(我可以有:
每种消息类型一种方法:
public class DeviceAPI {
public HelloResponse sendHello(HelloMessage){...}
public InitResponse sendInit(InitMessage){...}
... and many more message types ....
这是很好的类型安全。(它也可能是很多次相同的send()
方法,重载,但这大致相同(。但它非常明确,而且不是很灵活 - 我们无法在不修改 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 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
接口以创建新的消息类型,为新的MessageType
类实现Message
、Response
和MessageHandler
接口,并通过调用Device.registerHandler
注册新消息类型的处理程序。
我现在有一个完整的工作示例,说明您想要什么:
要定义消息类型:
public interface MessageType {
public static class INIT implements MessageType { }
public static class HELLO implements MessageType { }
}
基Message
和Response
类:
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
更新:添加了有关如何从客户端要发送的消息中获取输入的示例。
你可以有一个消息处理程序系统,你的 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 的怪物方法;而是使用多态性。
我认为这一点也不丑陋:
if(rsp instanceOf HelloResponse){
HelloResponse hrsp = (HelloResponse)rsp;
...
else if ...
只要你没有100个不同的回答。您可以将多种响应封装在一个响应中,具体取决于它们拥有的数据。例如:
class GenericResponse{
private String message;
private int responseType;
...
}
我开发了一些多人游戏,这是一个很好的方法。如果你有太多不同类型的消息,你可以使用通用的java类型,就像上面的skiwi的例子一样。
希望对您有所帮助