2009-08-10 5 views
9

私は、クライアントクラスが受信するメッセージのタイプに基づいて異なる動作を実行する次のような状況があります。私はinstanceofとif文が好きではないので、これを行うより良い方法があるのだろうかと思います。メッセージタイプのチェック時にinstanceofを避ける

私が考えていたことの1つは、クライアントクラスからメソッドを引き出してメッセージに入れることでした。私はIMessageインターフェースにprocess()のようなメソッドを置き、具体的なメッセージタイプのそれぞれにメッセージ固有の振る舞いを入れます。これは、タイプをチェックするのではなく、単にmessage.process()を呼び出すだけなので、クライアントを単純にします。ただし、これに伴う唯一の問題は、条件に含まれる動作がClientクラスに含まれるデータに対する操作と関係していることです。したがって、具体的なメッセージクラスでプロセスメソッドを実装した場合、クライアントに渡す必要があり、これが本当に意味をなさないかどうかはわかりません。

public class Client { 
    messageReceived(IMessage message) { 
     if(message instanceof concreteMessageA) { 
      concreteMessageA msg = (concreteMessageA)message; 
      //do concreteMessageA operations 
     } 
    } 
     if (message instanceof concreteMessageB) { 
      concreteMessageb msg = (concreteMessageB)message; 
      //do concreteMessageB operations 
     } 
} 
+0

JMSを使用しているという事実に基づいて、IMessageとは何ですか? JMSにはあらかじめ定義されたメッセージタイプがあります。したがって、JMSを抽象化しようとしているのでなければ、それを変換する仲介者がいない限り、独自のメッセージタイプを定義する必要はありません。 – Robin

答えて

7

インスタンスのテストを避ける簡単な方法は、ポリモーフィックにディスパッチすることです。例えば

public class Client { 
    void messageReceived(IMessage message) { 
     message.doOperations(this); 
    } 
} 

ここで、各メッセージクラスは適切なdoOperations(Client client)メソッドを定義します。

EDIT:要件によく一致する第2の解決策。

switch文で「instanceofは」一連のテストを置き換える代替は次のとおりです。

public class Client { 
    void messageReceived(IMessage message) { 
     switch (message.getMessageType()) { 
     case TYPE_A: 
      // process type A 
      break; 
     case TYPE_B: 
      ... 
     } 
    } 
} 

各IMessageがクラスには、適切なコードを返すようにint getMessageType()メソッドを定義する必要があります。列挙型はintと同様に機能し、より洗練されたIMOです。

+1

メッセージサブタイプでのみ利用可能なプロパティにアクセスするために異なる*プロセス*ブロックが必要な場合は、2番目のアプローチではキャストが必要な可能性があります –

+0

メッセージの処理方法を定義するクライアントの考えが嫌いです。 doOperationsを変更する必要がある場合はどうなりますか?この更新されたコードをどのようにすべてのクライアントに提供しますか?この場合、訪問者のパターンが良いと思います。クライアントの訪問者がメッセージで受け入れられ、欲しいものをやり遂げるために回り込むことができます。 – pjp

+0

@pgp:erm ...あなたはこの問題を誤解していると思います。問題が引き起こされる方法、メッセージ>>を処理するロジックは<< Clientクラスに特有です。 –

4

1つのオプションは、ハンドラチェーンです。ハンドラのチェーンがあり、それぞれがメッセージを処理できます(該当する場合)。を消費します。つまり、チェーンの下を通過しません。

public interface Handler { 
    void handle(IMessage msg); 
} 

そしてハンドラチェーンロジックは次のようになります:

List<Handler> handlers = //... 
for (Handler h : handlers) { 
    if (!e.isConsumed()) h.handle(e); 
} 

その後、各ハンドラがイベントを消費する/処理するために決定することができます:の

public class MessageAHandler implements Handler { 
    public void handle(IMessage msg) { 
     if (msg instanceof MessageA) { 
      //process message 
      //consume event 
      msg.consume(); 
     } 
    } 
} 

まずはHandlerインタフェースを定義しますもちろん、これはinstanceofを取り除いていませんが、それはあなたが巨大なif-elseif-else-if-instanceofブロックを持っていないことを意味します、それは読めないかもしれません

+0

可読性は意見/趣味の問題です。メッセージタイプごとに新しいハンドラクラスを作成すると、ロジックを見つけるために別の場所(ClientクラスとIMessageクラスを除く)をもう一度見なければなりません。私はそれを「大きな可読性の勝利」と呼んでいません。 –

+1

どちらもありません!それはあなたが大きな 'if-else-instanceof'ブロックを嫌うかどうかによって決まります。 OPのように聞こえるので、私はそれを提案しました。ハンドラチェーン*はif-elseよりも柔軟性があるので便利です。ハンドラはイベントを消費する必要はないので、複数のハンドラで処理することができます –

1

使用しているメッセージシステムのタイプは何ですか?

多くの場合、メッセージヘッダーまたはコンテンツに基づいてハンドラにフィルタを追加するオプションがあります。これがサポートされている場合は、単にメッセージタイプに基づいたフィルタを持つハンドラを作成するだけで、メッセージングシステムがすでにそれをチェックしているので、あなたのコードはinstanceofやチェックタイプを必要とせずに素敵できれいです。

私はあなたがJMSまたはOSGiイベントサービスでこれを行うことができることを知っています。

JMSを使用しているため、基本的に次の操作を行い、リスナーを登録できます。これにより、固有のメッセージタイプごとにリスナーが作成されます。

String filterMsg1 = "JMSType='messageType1'"; 
    String filterMsg2 = "JMSType='messageType2'"; 

    // Create a receiver using this filter 
    Receiver receiverType1 = session.createReceiver(queue, filterMsg1); 
    Receiver receiverType2 = session.createReceiver(queue, filterMsg2); 

    receiverType1.setMessageHandler(messageType1Handler); 
    receiverType2.setMessageHandler(messageType2Handler); 

今各ハンドラは、発信メッセージで)送信者がsetJMSType(への呼び出しを介してタイプを設定することをもちろん仮定して、特定のメッセージ・タイプのみ(なしのinstanceofまたはIF-THEN)を受信します。

このメソッドはメッセージに組み込まれていますが、独自のヘッダープロパティを作成して、その代わりにフィルタすることもできます。

+0

私はJMSを使用していますので、あなたの提案を調べなければなりません。 – volker238

+0

@ volker238 - これはセレクタと呼ばれ、メッセージヘッダー情報に基づいてフィルタリングするために使用できます。 – Robin

+0

@ volker238 - JMSを使用しているという事実に基づいてコードスニペットを追加しました。ヘッダープロパティーを設定できると仮定すると、セレクターは、JMS実装がすべてのルーティングを行うことができるので、絶対的な方法です。あなた自身をロールバックする醜い操作コードの必要はありません。 – Robin

0
//Message.java 

abstract class Message{ 
    public abstract void doOperations(); 
} 

//MessageA.java 

class MessageA extends Message{ 
    public void doOperations(){ 
     //do concreteMessageA operations ; 
    } 
} 

    //MessageB.java 

class MessageB extends Message { 
    public void doOperations(){ 
     //do concreteMessageB operations 
    } 
} 

//MessageExample.java 

class MessageExample{ 
    public static void main(String[] args) { 
     doSmth(new MessageA()); 
    } 

    public static void doSmth(Message message) { 
     message.doOperations() ;  

    } 
} 
0

ダブルディスパッチを使用するJava 8ソリューション。 instanceofを完全に取り除くことはできませんが、if-elseifチェーンの代わりにメッセージごとに1つのチェックしか必要としません。

public interface Message extends Consumer<Consumer<Message>> {}; 

public interface MessageA extends Message { 

    @Override 
    default void accept(Consumer<Message> consumer) { 
     if(consumer instanceof MessageAReceiver){ 
     ((MessageAReceiver)consumer).accept(this); 
     } else { 
     Message.super.accept(this); 
     } 
    } 
} 

public interface MessageAReceiver extends Consumer<Message>{ 
    void accept(MessageA message); 
} 
関連する問題