我正在使用一个名为Processing的框架,它基本上是一个Java applet。它具有执行键事件的能力,因为Applet可以。您还可以将自己的各种回调转换为父级。我现在没有这样做,也许这就是解决方案。目前,我正在寻找更多的POJO解决方案。所以我写了一些例子来说明我的问题。
请忽略在命令行(控制台)上使用键事件。当然这将是一个非常干净的解决方案,但它不可能在命令行上,我的实际应用程序不是命令行应用程序。事实上,关键事件对我来说是一个很好的解决方案,但我试图理解事件和轮询,而不仅仅是键盘特定的问题。
这两个例子都翻转了一个布尔值。当布尔值翻转时,我想要触发一次。我可以将布尔值包装在一个Object中,所以如果Object发生了变化,我也可以触发一个事件。我只是不想不必要地使用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));
}
}
答案 0 :(得分:7)
如果您的实现使用轮询,则会误解观察者模式。在观察者模式中,更改主体状态的方法将导致订阅观察者在完成之前得到通知。
显然,这意味着主题必须支持观察者模式,不能在观察者模式中使用任何普通的旧java对象作为主语。
答案 1 :(得分:7)
是的,事件处理是您问题的关键。您显示的C#片段还使用事件处理来告诉侦听器属性已更改。 Java也有这些PropertyChangeEvent,用于大多数Beans。 (例如: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);
答案 2 :(得分:1)
事实上,额外的线程是不必要的。您应该ObserverStillPolling
延长Observable
并在input.equals("a")
时通知观察员。
是的,这意味着POJO本身应该扩展Observable
并通知观察者本身。
尽管如此,Observer/Observable
实际上设计得很差。我建议你自己创建interface PropertyChangeListener
并让你的POJO实现它。然后让POJO在构造期间向观察者注册,或者通过检查instanceof PropertyChangeListener
(如果适用)来动态地执行。
答案 3 :(得分:0)
也许解决方案是wait()
/ notify()
或Java的并发实用程序之一。这些结构避免了“忙等待”轮询,这似乎是问题的关键点。
这些用于多个线程。一个线程传递事件,其他线程响应它。这是一个例子:
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;
}
}