2013-12-11 2 views
6

私は、シリアル通信でメッセージを使ってデバイスと通信したいJavaクライアントを持っています。クライアントは、シリアル通信の醜い詳細を抽象化して、きれいなAPIを使用できるはずです。クライアントは、そのAPIを介して多くのタイプのメッセージを送信し、応答を得ることができます。私は、このAPIを実装するのが最善の方法であるというアドバイスを探しています。簡単にするためにJavaでタイプセーフメッセージAPIを設計するにはどうすればよいですか?

、我々は2つだけのメッセージタイプを持っていると言う:HelloMessage APIの設計InitResponse(現実には、多くの人がもっとあります)

をトリガーHelloResponseInitMessageをトリガする(つまり、の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を変更せずに、後から追加することを可能にする:

Iはまた、すべてのメッセージタイプを取る単一の送信方法を有することができます。同時に、クライアントは応答タイプをチェックし、それを正しいタイプにキャストする必要があります。

クライアントコード:

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

これは私の意見では醜いです。

お勧めですか?よりクリーンな結果をもたらす他のアプローチはありますか?

参考までにお待ちください。どのように他の人がこれを解決しましたか

+0

列挙型のパラメータ化された型は可能性がありますか?または、これは不可能です(まったくありません)。 – skiwi

+0

「レスポンス」のタイプをチェックし、HelloMessageをキャストします。それはグロテスクに見える。とにかく、実装クラスの "instanceOf"と "casting"操作を隠すことができます。 – arjacsoh

+0

タイプをチェックする必要はないとは思わないが、これはどうやって実装するのだろう。あなたがこれを望まないなら、再考するべき正しい場所はあなたのモデルだと思います。これらの応答が異なるオブジェクトであることを必要とするのは何ですか?例えばジェネリックではありませんか? –

答えて

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インタフェースを実装し、新しいMessageTypeクラスのMessageResponseMessageHandlerインターフェースを実装し、ハンドラを登録しますDevice.registerHandlerを呼び出してください。

+0

非常にきれいなソリューション、ありがとう! (Skiwiのものに非常に似ています)。ちょっとした微調整の後、私はMessageからgetTypeClass()メソッドを削除することもできました。あなたは、同じ情報にmessage.getClass()。getGenericInterfaces()[0] .getActualTypeArguments()(どこかでParameterizedTypeへのキャストが必要です) – Philipp

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

} 

カスタム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 InitMessage extends Message<MessageType.INIT> { 
    public InitMessage() { 
     super(); 
    } 

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

​​

をカスタムhelloメッセージと応答を作成します。

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 

UPDATE:クライアントが送信したいメッセージから入力を取得する方法について例を追加しました。

+0

より良い解決策に取り組んでいます。 – skiwi

+0

ありがとう、それは面白いです。応答が異なる情報を運んでいるとします。どうすればこの情報にアクセスできますか? – Philipp

+0

@Philippこれで、あなたは 'Message 'の代わりに 'InitMessage'のようなものを使用できるようにしたいと考えています。 – skiwi

0

私は、これがすべてで醜いだとは思わない:

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

限り、あなたは100の異なる応答のように持っていないので。それぞれのデータに応じて、多くの種類の応答を1つにまとめることができます。たとえば、

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

私はいくつかのマルチプレイヤーゲームを開発しました。これは良い方法です。 メッセージのタイプが多すぎる場合は、上記のskiwiの例のような汎用のJavaタイプを使用できます。

は、それはあなたがメッセージハンドラのシステムを持つことができ、そして、あなたのDeviceAPIは、着信メッセージに適しているハンドラ選択することができ

+0

私はinstanceOfはしごがコードのにおいであることを教えられました。だから私はより多くのOOのアプローチを探しています。たぶんビジターパターン? – Philipp

1

役に立てば幸い。そして、適切なメッセージハンドラにそれを委任する:あなたの他のメッセージを

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

同上:

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は次のようになります。私はあなたがそれをよりエレガントにすることができると確信していますが、アイデアはまだ変わりません。代わりに多型を使用してください。ここで

+0

私はアイデアが好きです:それはコールバックアプローチのほうがはるかです。 – Philipp

関連する問題