共有状態データにアクセスする必要がある複数のリスナーの適切な結合
-
07-07-2019 - |
質問
従来のリスナーコールバックモデルの使用。さまざまなものを収集するリスナーがいくつかいます。収集された各リスナーは、内部構造のリスナー内にあります。
問題は、リスナーの一部に「もの」の一部を認識してもらいたいことです。他のリスナーで。
リスナーの登録順序を強制するため、何らかの順序でイベントを意図的に登録すると、後のリスナーは以前のリスナーがその内容を更新し、何らかの方法でそれにアクセスしてより多くのことを行うことができます。
これに対する私の最初の試みは、各リスナーが依存するリスナーへの参照を保存することです。そのため、事前に登録された依存関係を持つリスナーに依存関係のないリスナーの順序でリスナーを登録し、さまざまなメソッドでリスナー間の参照を設定します。
私はこれがどれほど悪いかを実感し始めており、以前この道を何とかしていたのではないかと思っていました。リスナーの1人が別のリスナーにアクセスする必要がある場合、より適切なパターンは何ですか?
これを説明するための擬似コードを次に示します。
interface Listener { onEvent(char e); }
class A implements Listener {
private int count;
public void onEvent(char e) { if(e == 'a') count++; }
public int getCount() { return count; }
}
class B implements Listener {
private int count;
// private A a;
// private void setA(A a) { this.a = a; }
public void onEvent(char e) { if(e == 'b') count++; }
public int getCount() { return count; }
public int getAPlusBCount() {
// We know B count, but we don't know A so how would we change this
// so B is A aware? Or not even aware, just somehow coupled? This
// is the question
// return a.getCount() + count;
}
public void doConditionalHere() {
// Do some condition in B that relies on the state of data in A
int acount = 0; // a.getCount(); ???
if(acount % 2 == 0) {
this.count--;
}
}
}
class Run {
A a = new A();
B b = new B();
List listeners = new List();
listeners.add(a);
listeners.add(b);
// The ugly way I add coupling right now is to keep a reference to A
// inside B. It's commented out because I am hoping there is a more intelligent approach
// b.setA(a);
for(char c : "ababbabab") {
for(listener : listeners) {
listener.onEvent(c);
}
}
}
解決
すべてのリスナークラスに対してonEventメソッドが起動された回数を追跡する中央オブジェクトがない理由
public interface CountObserver {
public void updateCount(String className);
public int getCount(String className);
}
public class CentralObserver implements CountObserver {
private int aCount;
private int bCount;
public void updateCount(String className) {
//There's probably a better way to do this than using
//all these if-elses, but you'll get the idea.
if (className.equals("AclassName")) {
aCount++;
}
else if (className.equals("BclassName")) {
bCount++;
}
}
public int getCount(String className) {
if (className.equals("AclassName")) {
return aCount;
}
else if (className.equals("BclassName")) {
return bCount;
}
}
class A implements Listener {
CountObserver countObserver;
public void registerObserver (CountObserver countObserver) {
this.countObserver = countObserver;
}
public void onEvent(char e) {
if(e == 'a') {
countObserver.updateCount (this.getClass.getName);
}
}
}
//Same thing for B or any other class implementing Listener. Your Listener interface should, of
//course, have a method signature for the registerObserver method which all the listener classes
//will implement.
class Run {
private A a;
private B b;
private CountObserver centralObserver;
public runProgram () {
centralObserver = new CentralObserver();
a.registerObserver(centralObserver);
b.registerObserver(centralObserver);
//run OnEvent method for A a couple of times, then for B
}
public int getAcount () {
return centralObserver.getCount(a.getClass.getName());
}
public int getBcount () {
return centralObserver.getCount(b.getClass.getName());
}
}
//To get the sum of all the counts just call getAcount + getBcount. Of course, you can always add more listeners and more getXCount methods
他のヒント
ここで多くの結合について説明しました。最善の方法は、このバックチャネルの依存関係をすべて排除することですが、失敗すると、依存関係を持つものが初期リスナーリストではなく、依存しているものをリッスンできるようになる可能性があります。または、すべての信号を受信するまで待機させることもできます。
リスナーが依存している人を識別することにより、依存関係管理を自動化できます。リスナーリストは、挿入順ではなく、依存オブジェクトが依存関係に従うように順序付けられます。リスナーインターフェイスは次のようになります。
interface Listener {
String getId();
Collection<String> getDependencies();
onEvent(char e);
}
または参照を持っているだけです:
interface Listener {
Collection<Listener> getDependencies();
onEvent(char e);
}
&quot;リスナーBがリスナーAを認識するようにこれをどのように変更しますか?または気づいていないが、どういうわけか結合しますか?&quot;
多くの場合、2つの「ピア」を結合する必要はありません。このようなオブジェクト。 2つのピアが一般的なものに依存するようにします。
より深い質問は、リスナーAまたはリスナーBが収集したすべての情報をどう処理するかです。
多くの場合、リスナーは2つのことを行います。データを収集し、アクションを実行します。多くの場合、これら2つのことは分離する必要があります。リスナーはリッスンして収集し、もう少し行う必要があります。他のオブジェクトは、リスナーによってアクティブ化できます。
あなたが持っているのは、複数のアクション(AとB)を持つ1つのリスナーだけです。リスナーは、AとBに適切なカウントを提供できます。Aに「a」カウントを提供します。Bに「a」または「b」カウントを提供します。