12

基本的にJavaアプレットであるProcessingというフレームワークを使用しています。アプレットができるので、重要なイベントを実行する機能があります。ある種の独自のコールバックを親にロールバックすることもできます。私は今それをしていません。おそらくそれが解決策です。今のところ、より POJO ソリューションを探しています。そこで、私の質問を説明するためにいくつかの例を書きました。

コマンド ライン (コンソール) でのキー イベントの使用は無視してください。確かにこれは非常にクリーンなソリューションですが、コマンド ラインでは不可能であり、実際のアプリはコマンド ライン アプリではありません。実際、キーイベントは私にとって良い解決策ですが、キーボード固有の問題だけでなく、イベントとポーリングを理解しようとしています。

これらの例はどちらもブール値を反転します。ブール値が反転したら、一度何かを発射したい。ブール値をオブジェクトにラップして、オブジェクトが変更された場合にイベントを発生させることもできます。不必要に if() ステートメントでポーリングしたくないだけです。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

/*
 * Example of checking a variable for changes.
 * Uses dumb if() and polls continuously.
 */
public class NotAvoidingPolling {

    public static void main(String[] args) {

        boolean typedA = false;
        String input = "";

        System.out.println("Type 'a' please.");

        while (true) {
            InputStreamReader isr = new InputStreamReader(System.in);
            BufferedReader br = new BufferedReader(isr);

            try {
                input = br.readLine();
            } catch (IOException ioException) {
                System.out.println("IO Error.");
                System.exit(1);
            }

            // contrived state change logic
            if (input.equals("a")) {
                typedA = true;
            } else {
                typedA = false;
            }

            // problem: this is polling.
            if (typedA) System.out.println("Typed 'a'.");
        }
    }
}

これを実行すると、次のように出力されます。

Type 'a' please. 
a
Typed 'a'.

一部のフォーラムでは、オブザーバーの使用が提案されました。これにより、監視対象のクラスからイベント ハンドラーが分離されますが、まだ if() が永久ループにあります。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Observable;
import java.util.Observer;

/* 
 * Example of checking a variable for changes.
 * This uses an observer to decouple the handler feedback 
 * out of the main() but still is polling.
 */
public class ObserverStillPolling {

    boolean typedA = false;

    public static void main(String[] args) {

        // this
        ObserverStillPolling o = new ObserverStillPolling();

        final MyEvent myEvent = new MyEvent(o);
        final MyHandler myHandler = new MyHandler();
        myEvent.addObserver(myHandler); // subscribe

        // watch for event forever
        Thread thread = new Thread(myEvent);
        thread.start();

        System.out.println("Type 'a' please.");

        String input = "";

        while (true) {
            InputStreamReader isr = new InputStreamReader(System.in);
            BufferedReader br = new BufferedReader(isr);

            try {
                input = br.readLine();
            } catch (IOException ioException) {
                System.out.println("IO Error.");
                System.exit(1);
            }

            // contrived state change logic
            // but it's decoupled now because there's no handler here.
            if (input.equals("a")) {
                o.typedA = true;
            }
        }
    }
}

class MyEvent extends Observable implements Runnable {
    // boolean typedA;
    ObserverStillPolling o;

    public MyEvent(ObserverStillPolling o) {
        this.o = o;
    }

    public void run() {

        // watch the main forever
        while (true) {

            // event fire
            if (this.o.typedA) {
                setChanged();

                // in reality, you'd pass something more useful
                notifyObservers("You just typed 'a'.");

                // reset
                this.o.typedA = false;
            }

        }
    }
}

class MyHandler implements Observer {
    public void update(Observable obj, Object arg) {

        // handle event
        if (arg instanceof String) {
            System.out.println("We received:" + (String) arg);
        }
    }
}

これを実行すると、次のように出力されます。

Type 'a' please.
a
We received:You just typed 'a'.

if() が CPU で NOOP だったとしても問題ありません。しかし、実際にはすべてのパスを比較しています。実際の CPU 負荷が表示されます。これは投票と同じくらい悪いことです。スリープでスロットルを戻すか、最後の更新からの経過時間を比較することはできますが、これはイベント駆動型ではありません。ポーリングが少ないだけです。では、どうすればこれをよりスマートに行うことができますか? ポーリングせずに POJO の変更を監視するにはどうすればよいですか?

C# には、プロパティと呼ばれる興味深いものがあるようです。私は C# の専門家ではないので、これは私が思うほど魔法のようなものではないかもしれません。

private void SendPropertyChanging(string property)
{
  if (this.PropertyChanging != null) {
    this.PropertyChanging(this, new PropertyChangingEventArgs(property));
  }
}
4

4 に答える 4

7

はい、イベント処理が問題の鍵です。表示する C# スニペットでは、イベント処理を使用して、プロパティが変更されたことをリスナーに伝えます。Java には、ほとんどの Bean で使用されるこれらの PropertyChangeEvent もあります。(例: Swing コンポーネントはそれらを使用します)。

ただし、これを手動で行うこともできます: (ただし、これは POJO ではなく、Java Bean に近いものです)

EventBean.java :

import java.util.LinkedList;
import java.util.List;

public class EventBean {

    private final List<Listener> listeners = new LinkedList<Listener>();

    protected final <T> void firePropertyChanged(final String property,
            final T oldValue, final T newValue) {
        assert(property != null);
        if((oldValue != null && oldValue.equals(newValue))
                || (oldValue == null && newValue == null))
            return;
        for(final Listener listener : this.listeners) {
            try {
                if(listener.getProperties().contains(property))
                    listener.propertyChanged(property, oldValue, newValue);
            } catch(Exception ex) {
                // log these, to help debugging
                ex.printStackTrace();
            }
        }
    }

    public final boolean addListener(final Listener x) {
        if(x == null) return false;
        return this.listeners.add(x);
    }
    public final boolean removeListener(final Listener x) {
        return this.listeners.remove(x);
    }

}

Listener.java :

import java.util.Collections;
import java.util.Set;
import java.util.TreeSet;

// Must be in same package as EventBean!
public abstract class Listener {

    private final Set<String> properties;

    public Listener(String... properties) {
        Collections.addAll(this.properties = new TreeSet<String>(), properties);
    }

    protected final Set<String> getProperties() {
        return this.properties;
    }

    public abstract <T> void propertyChanged(final String property,
            final T oldValue, final T newValue);
}

そして、次のようにクラスを実装します。

public class MyBean extends EventBean {

    private boolean typedA;

    public void setTypedA(final boolean newValue) {
        // you can do validation on the newValue here
        final boolean oldValue = typedA;
        super.firePropertyChanged("typedA", oldValue, newValue);
        this.typedA = newValue;
    }
    public boolean getTypedA() { return this.typedA; }

}

次のように使用できます。

MyBean x = new MyBean();
x.addListener(new Listener("typedA") {
    public <T> void propertyChanged(final String p,
                 final T oldValue, final T newValue) {
        System.out.println(p + " changed: " + oldValue + " to " + newValue);
        // TODO
    }
});

x.setTypedA(true);
x.setTypedA(false);
x.setTypedA(true);
x.setTypedA(true);
于 2010-05-12T22:20:14.957 に答える
7

オブザーバー パターンの実装でポーリングを使用している場合、オブザーバー パターンを誤解しています。オブザーバー パターンでは、サブジェクトの状態を変更するメソッドにより、サブスクライブされたオブザーバーが完了する前に通知されます。

明らかに、これはサブジェクトがオブザーバー パターンをサポートする必要があることを意味します。オブザーバー パターンのサブジェクトとしてプレーンな古い Java オブジェクトを使用することはできません。

また、オブザーバー パターンのウィキペディアの例も参照してください。

于 2010-05-12T21:52:45.197 に答える
1

実際、余分なスレッドは不要です。のときにオブザーバーにObserverStillPolling拡張して通知する必要があります。Observableinput.equals("a")

はい、これは、POJO 自体が拡張Observableされ、オブザーバー自体に通知される必要があることを意味します。


とはいえ、Observer/Observable実際には設計が不十分です。interface PropertyChangeListener独自に開発し、POJO に実装させることをお勧めします。次に、構築中に POJO 自体をオブザーバーに登録するか、instanceof PropertyChangeListener該当する場合はどこかをチェックして動的に登録します。

于 2010-05-12T22:04:57.390 に答える
0

おそらく、解決策はwait()/notify()または Java の同時実行ユーティリティの 1 つです。これらの構造は、問題のポイントであると思われる「ビジー待機」ポーリングを回避します。

これらは複数のスレッドで使用されます。1 つのスレッドがイベントを配信し、他のスレッドがそれに応答します。次に例を示します。

class SyncQueue {

  private final Object lock = new Object();

  private Object o;

  /* One thread sends "event" ... */
  void send(Object o)
    throws InterruptedException
  {
    if (o == null)
      throw new IllegalArgumentException();
    synchronized (lock) {
      while (this.o != null)
        lock.wait();
      this.o = o;
      lock.notifyAll();
    }
  }

  /* Another blocks (without burning CPU) until event is received. */
  Object recv()
    throws InterruptedException
  {
    Object o;
    synchronized (lock) {
      while (this.o == null)
        lock.wait();
      o = this.o;
      this.o = null;
      lock.notifyAll();
    }
    return o;
  }

}
于 2010-05-12T21:57:13.523 に答える