2017-02-04 1 views
2

私のクラスはもともと複数の別々のaddAListener()addBListener()removeAListenerなどから始まりました。これは1つのクラスでそれほど悪くはありませんでしたが、クラスが別のクラスによって内部的に使用され、拡張リスナーが伝播すると、instanceofのチェーンを短絡する良い方法はありますか?

簡単な解決策は、単一のインターフェイスを使用してinstanceofでリスナーを整理することです:

public interface Listener { 
} 

public class ListenerA extends Listener { 
} 

public class ListenerB extends Listener { 
} 

public class ListenerC extends Listener { 
} 

List<ListenerA> ofListenersA = new List<>(); 
List<ListenerB> ofListenersB = new List<>(); 
List<ListenerC> ofListenersC = new List<>(); 

void addListener(Listener listener) { 
    if (listener instanceof ListenerA) { 
     ofListenersA.add(listener); 

     return; 
    } 

    if (listener instanceof ListenerB) { 
     ofListenersB.add(listener); 

     return; 
    } 

    if (listener instanceof ListenerB) { 
     ofListenersB.add(listener); 

     return; 
    } 
} 

void removeListener(Listener listener) { 
    if (listener instanceof ListenerA) { 
     ofListenersA.remove(listener); 

     return; 
    } 

    if (listener instanceof ListenerB) { 
     ofListenersB.remove(listener); 

     return; 
    } 

    if (listener instanceof ListenerB) { 
     ofListenersB.remove(listener); 

     return; 
    } 
} 

しかし、今、私はあなたがいないswitchクラスでできる限り独占的に各instanceofを評価する必要があります。

私は多くの種類のリスナーをチェックする必要がないため、これは最適化を求める試みではありません。むしろ、オブジェクト指向設計に関してこれが悪いアプローチであるかどうかについての質問です。インターフェース内の列挙を使用して

更新

短絡アプローチ:

enum ListenerType { 
    ListenerTypeA, 
    ListenerTypeB, 
    ListenerTypeC 
} 

public interface Listener { 
    ListenerType getType(); 
} 

public class ListenerA extends Listener { 
    ListenerType getType() { 
     return ListenerType.ListenerTypeA; 
    } 
} 

public class ListenerB extends Listener { 
    ListenerType getType() { 
     return ListenerType.ListenerTypeB; 
    } 
} 

public class ListenerC extends Listener { 
    ListenerType getType() { 
     return ListenerType.ListenerTypeC; 
    } 
} 

List<ListenerA> ofListenersA = new List<>(); 
List<ListenerB> ofListenersB = new List<>(); 
List<ListenerC> ofListenersC = new List<>(); 

void addListener(Listener listener) { 
    switch (listener) { 
     case ListenerTypeA: { 
      ofListenersA.add(listener); 

      return; 
     } 
     case ListenerTypeB: { 
      ofListenersB.add(listener); 

      return; 
     } 
     case ListenerTypeC: { 
      ofListenersC.add(listener); 

      return; 
     } 
    } 
} 

void removeListener(Listener listener) { 
    switch (listener) { 
     case ListenerTypeA: { 
      ofListenersA.remove(listener); 

      return; 
     } 
     case ListenerTypeB: { 
      ofListenersB.remove(listener); 

      return; 
     } 
     case ListenerTypeC: { 
      ofListenersC.remove(listener); 

      return; 
     } 
    } 
} 
+0

もしあなたが 'instanceof'を使っているのであれば、あなたのデザインはおそらく間違っています。なぜこれらのリスナーを別々に追跡したいのですが、それらを別々にする必要がある場合、なぜ単一の 'addListener'メソッドですか? – chrylis

+0

このクラスのオブジェクトをプライベートメンバーとして使用する他のオブジェクトがあるためです。だから私は上からリスナーを提供する場合は、そのクラスの各リスナーを再度追加/削除する必要があります。私が別々のリスナーとしてそれらをすべて持っているのは、それぞれのイベントごとに個別にループするからです。 – Zhro

+0

私は多くのopensourceプロジェクトを見てきましたが、一般的なリスナーのために単一のメソッドを使用する人は覚えていません。各リスナーに対してメソッドを作成し、直感的な名前を追加したいとします。 – Enzokie

答えて

0

単一interfaceを有し、enumを定義します。 interfaceabstract methodには、パラメータとしてenumのインスタンスを設定できます。

リスナーのリストは1つだけ必要です。

これで、アクションまたはイベントタイプごとにメソッドを呼び出すことができます。実装中は、enum instance typeに従って処理することができます。

他の方法は、single interfacedifferent methodsにすることです。発生したイベントごとにメソッドを呼び出し、実装中はメソッドごとに処理できます。

イベントタイプ(変数enum、stringまたはintegerの変数で、列挙型の変数)を定義する変数を持つ単一メソッドの方法の利点は、より多くのアクションが追加された場合、それらをインターフェイスに追加する必要はない多くの変更が必要になります。列挙型で追加されたアクションを追加するだけで済みます(文字列または整数の定数がある場合は、新しいアクションに対して追加の定数を定義することができます)。これで、新しく定義されたアクションタイプで同じメソッドを呼び出すことができます。

Fatインターフェイスを避けるように注意する必要があります。あまりにも薄い(メソッドごとに1つのインターフェイス)と脂肪インターフェイスのバランスを取る必要があります。論理的に同じタスクに属するメソッドをインタフェースでグループ化することができます。あまりにも散在したコードも悪く、すべての(関連して無関係の)apiを単一のインターフェースに入れることも悪いです。

0

あなたのコードベースを知らなくてもinstanceOfを使用するよりも、より良い方法は、常に(私の経験で)があり、最も適切であるものを言うのは難しいですが、状況に応じて、次のタイプの比較のために良いです:

  • マップでは、マップのキーをスイッチしたいタイプのキーにして、それが一致したときに返されるものを指定する限り、タイプベースのswitch文とほぼ同じ方法で操作できます。

  • Command Patternここでは、リスナークラス階層を抽象的な/具体的なコマンドに置き換えます。次に、実際の動作をレシーバにカプセル化することができます。

  • Visitor Patternすべてのリスナーに一度に通知する場合は、訪問者パターンが適用可能な場合もあります。タイプ比較は、訪問先の要素としてリスナー階層を通じて再度実行されます。

私はこれが何かの役に立つと思っています。

1

興味のあるタイプのリスナーを指定するタイプを追加することをお勧めします。また、キーを他のものに変更することもできます(たとえば、hashCodeとequalsを持つ通常のクラス)。

enum ListenerType { 
    TYPE_A, TYPE_B, TYPE_C 
} 

interface Listener { 

} 

Map<ListenerType, Set<Listener>> listeners = new ConcurrentHashMap<>(); 

public void addListener(ListenerType type, Listener listener) { 
    listeners.computeIfAbsent(type, k -> Collections.newSetFromMap(new ConcurrentHashMap<>())).add(listener); 
} 

public void removeListener(ListenerType type, Listener listener) { 
    listeners.computeIfPresent(type, (k, v) -> v.remove(listener) && v.isEmpty() ? null : v); 
} 

public Set<Listener> getListeners(ListenerType type) { 
    return listeners.getOrDefault(type, Collections.emptySet()); 
} 
0

このようなシナリオでは、ビジターパターンを使用しています。 リスナーの新しい実装が追加された場合に、ビジターパターンでコードを維持することも簡単です。

関連する問題