1

この一般的なシナリオのデザインパターンを選択したいと思います。

メッセージを受信するモジュール(MessageListener)があります。受信する各メッセージは実際にはオブジェクトです(MyMessage1、MyMessage2、..)

My_Message1、My_Message2はMy_Message_Abstractを拡張します。

これで、MessageListener Object(onMessage(..))によって取得されるメッセージが、メッセージインスタンスに依存する異なる「コマンド」を実行したい場合は、次のようになります。

onMessage(My_Message_Abstract msg)
{
   if (msg instance of My_Message1)
   {
      doSomething()..
   }
   else if(msg instance of My_Message2)
   {
     doSomethingElse()..
    }
}

私はこのボイラーをif/thenコードで取り除き、将来のメンテナンス/動的/プラグアビリティ/きちんとした方法でそれを改善したいと思っています。

そこで、コマンドデザインパターンを採用しました。そして、私はそのようなものを持つことができることを知りました:

メッセージリスナーでマップを作成するには:

Map<Integer, MessageCommand> messageCommandsMap = new HashMap<Integer, MessageCommand>();

..

sessionTargetMap.put(MSG_1_TYPE, new Message1Command());
sessionTargetMap.put(MSG_2_TYPE, new Message2Command());

(Message1Command,Message2Command implements from Command interface)

onMessage(My_Message_Abstract msg)
{
messageCommandsMap.get(msg.getType).executeCommand(msg);
}

私はこのようにMessageListeenrのハッシュマップのアイデアが気に入らなかったので、すべてのコマンドをそのオブジェクト(MessageListener)に結合しています。

このスレッドで提供される解決策として: Javaのifステートメントの長いリスト

どうすればこれを改善できますか?Mybeこのアイデアには他のパターンを使用する必要がありますか?

ありがとう、

4

6 に答える 6

3

私が採用しているアプローチは、Java関数の仮想的な性質を利用できるようにする共通のインターフェースでこれらのクラスを装飾することです。たとえば、私はこれを行います:

public interface CoolInterface  
{
      void doSomething();  
}  

My_Message_Abstract implements CoolInterface  
{  
      public abstract void doSomething();
}  
Message1Command extends My_Message_Abstract
{  
       public void doSomething(){ System.out.println("First");
}    
Message2Command extends My_Message_Abstract
{  
       public void doSomething(){ System.out.println("");
}   

これで、コードは次のようになります。

onMessage(My_Message_Abstract msg)
{
  msg.doSomething();
}

委任したい場合は、これを実行してください。

 My_Message_Abstract implements CoolInterface  
    {  
          public void doSomething()
          {
             System.out.println("Default");
          }  
    }    

   Message1Command extends My_Message_Abstract
{  
       public void doSomething(){ System.out.println("First");
}    
Message2Command extends My_Message_Abstract
{  
       // no need to override the method just invoke doSomething as normal
}    
于 2013-03-27T13:31:49.587 に答える
1

enum可能なことのリストが必要な場合は、常にを使用するのが好きです。

public class Test {
  // A type of message.
  class MyMessage1 {
  };
  // A whole set of message types.

  interface MyMessage2 {
  };

  // The Dos - To demonstrate we just print something.
  enum Do {
    Something(MyMessage1.class) {
      @Override
      void doIt() {
        System.out.println("Something");
      }
    },
    SomethngElse(MyMessage2.class) {
      @Override
      void doIt() {
        System.out.println("Something else");
      }
    },
    Everything(Object.class) {
      @Override
      void doIt() {
        System.out.println("Everything");
      }
    };
    // Which classes this one applies to - could use an array of Class here just as easily.
    final Set<Class> applies = new HashSet<Class>();
    // You can add multiples on construction.
    Do(Class... applies) {
      this.applies.addAll(Arrays.asList(applies));
    }

    // Perform all that are relevant to this message type.
    static void doIt(Class messageClass) {
      for (Do d : Do.values()) {
        // If it is assignable
        boolean doIt = false;
        for (Class c : d.applies) {
          if (c.isAssignableFrom(messageClass)) {
            doIt = true;
          }
        }
        if (doIt) {
          // Execute the function.
          d.doIt();
        }
      }
    }

    // What to do.
    abstract void doIt();
  }

  public void test() {
    System.out.println("Hello");
    // Test with a concrete message.
    onMessage(new MyMessage1());
    // And an implementation of an interface.
    onMessage(new MyMessage2() {
    });
  }

  private void onMessage(Object message) {
    // Do something depending on the class of the message.
    Do.doIt(message.getClass());
  }

  public static void main(String args[]) {
    new Test().test();
  }
}

このパターンを使用するEverythingと、すべてのメッセージに対してトリガーされるを設定することもできます。

これを拡張して、これSomethingが適用可能なクラスの配列を使用するのは簡単です。

最初の一致だけを適用する場合は、適切な場所でループから抜け出します。

私のポイントは、このメカニズムを使用すると、コードを積極的にハックすることなく、ほぼすべての戦略を実装できるということです。

于 2013-03-27T14:02:15.167 に答える
0

特定のメッセージタイプに使用されるMessageCommandを基本的に認識しているMessageCommandFactoryを使用できます。工場出荷時では、コマンドクラスを識別するためだけにマップまたはif/elseを使用できます。

これで、メッセージリスナーはファクトリにタイプに基づいて適切なメッセージコマンドを与えるように要求し、各メッセージコマンドはコマンド(doSomethingElse)メソッドを効果的に処理するロジックを保持します。

コードがどのように見えるかの抽象的なアイデア:

   class MessageCommandFactory {
       Command get(MessageType messageType) {
         if(messageType == MSG_1_TYPE) {
           return new Message1Command();
         } else ...
       } 

    class MessageListener {
    MessageCommandFactory messageCommandFactor;
        onMessage(My_Absctract_Message message) {
          Command command =  messageCommandFactory.get(message.getType());
          command.execute();
      }
   }
于 2013-03-27T13:34:40.197 に答える
0

したがって、実際にMessageListenerは2つのステップを実行します。

  1. 受信したメッセージを把握する
  2. 適切なコマンドを実行します

@ Woot4Mooのようにメッセージ自体に「実行中」を委任するか、次のようMessageListenerにメッセージに対して特定のことを行うサブクラスを作成できます。

public class Message1Listener implements MessageListener {
  onMessage(SpecificMessage1 msg) {
    /* do something with msg */
  }
}

public class MessageBus {
  Map<Class<? extends Message>, MessageListener> listeners = new HashMap<>();
  void register(MessageListener listener, Class<? extends Message> msgType) {
    listeners.put(msgType, listener);
  }

  void onMessage(Message msg) {
    listeners.get(msg.getClass()).onMessage(msg);
  }
}

あなたMessageListenerMessageBusここになり、登録されたハンドラーにプロキシします。これは非常に基本的な設定であり、必要に応じて拡張できます(つまり、MessageListenerメッセージタイプごとにリストがあります)。または、コンテキストにすでに存在している場合もあります。

于 2013-03-27T13:43:11.043 に答える
0

同様の状況で、私はあなたがすでに持っているものから始めましたが、メソッドを追加しました:

addMessageListener(int messageType, MessageCommand messageCommand)

さらに、他のクラスには、クラスを「見つける」方法が必要です。ご使用の環境で機能する場合は、上記のメソッドをpublic staticにするのが最も簡単ですが、必要に応じて、他の検出方法を使用することもできます。

このメソッドは、すでに持っているマップに追加するだけです。

2つのコマンドが同じメッセージを聞きたい場合は、マップの「値」側をリストにすることで、それを微調整するのは簡単です。

また、事前に「事前登録」されたコマンドを使用してマップを開始したり、特定のタイプの構成から事前登録情報を読み取ったりすることもできます。

例1: NewMessageHandlerという新しいクラスがあるとします。このクラスは、完全に新しいメッセージタイプを処理する必要があります。ある適切な時点で、NewMessageHandlerがそれ自体を初期化するときに、それ自体をリスナーとして登録することもできます。ディスパッチャクラスを「検索」する必要があり(できればそれはシングルトンです)、次にaddMessageListener()を呼び出すことができます。

例2: Dispatcherクラスは、メッセージを処理するメッセージタイプとクラスのペアを定義する構成ファイルを読み取ることができます。

public class Dispatcher 
{
  public static Dispatcher getInstance()
  {
     //return the instance. Could accept parms for more complex needs
  }

  public void addMessageListener(int messageType, MessageCommand messageCommand)
  {
    //Add to the internal map
  }

  private void init()
  {
     //Optionally, read config file or System properties, and call addMessageListener()
  }

  private void dispatchMessage(Message msg)
  {
    //Look up map and dispatch to the registered instance
    //Call the handleMessage() method on the appropriate listener      
  }

}

インターフェイス

public interface MessageCommand 
{
   public void handleMessage(Message msg);
}

そしてもう1つ...

public class NewMessageHandler implements MessageCommand 
{

   private void init()
   {
      Dispatcher.addMessageListener(666, this)
   }

  public void handleMessage(Message msg)
  {

  }
}
于 2013-03-27T13:43:56.107 に答える
0

ここでの基本的な問題は、メッセージがデータであり、そのデータの属性に基づいてディスパッチする必要があることです。したがって、データオブジェクトを変更して別のハンドラーにディスパッチするか、リスナーでディスパッチテーブルを使用する必要があります。いずれにせよ、ディスパッチを処理するためのコードが必要です。

IMO、メッセージはディスパッチを実装するのに間違った場所です。それはデータであり、行動はリスナーの責任です。また、リスナーが2つある場合は、異なるディスパッチテーブルを使用することをお勧めします。

私自身の実装では、おそらく最大5つまたは6つの可能な区別にif-elseチェーンを使用します。その上に、ファンクターマップ。これらは簡単で、何が起こっているのかは明らかです。

ただし、簡単なアプローチが本当に気に入らない場合は、リフレクションを使用するアプローチを次に示します。

public class Dispatcher {

    public static class Foo {}
    public static class Bar {}


    public void dispatch(Object obj) throws Exception {
        try {
            Method handler = this.getClass().getDeclaredMethod("handler", obj.getClass());
            handler.invoke(this, obj);
        }
        catch (Exception e) {
            System.out.println("couldn't determine handler for " + obj.getClass().getName());
        }
    }

    private void handler(Foo foo) {
        System.out.println("handler(Foo)");
    }

    private void handler(Bar bar) {
        System.out.println("handler(Bar)");
    }


    public static void main(String[] argv) throws Exception {

        Dispatcher dispatcher = new Dispatcher();

        dispatcher.dispatch(new Foo());
        dispatcher.dispatch(new Bar());
        dispatcher.dispatch("something else");

    }
}
于 2013-03-27T14:32:08.770 に答える