草庐IT

java - 在没有轮询的情况下观察变量的变化

coder 2023-09-01 原文

我正在使用一个名为 Processing 的框架,它基本上是一个 Java applet。它具有执行关键事件的能力,因为 Applet 可以。您还可以将自己的各种回调滚动到父级中。我现在不这样做,也许这就是解决方案。现在,我正在寻找更 POJO 的解决方案。所以我写了一些例子来说明我的问题。

请忽略在命令行(控制台)上使用按键事件。当然,这将是一个非常干净的解决方案,但在命令行上是不可能的,而且我的实际应用程序不是命令行应用程序。事实上,按键事件对我来说是一个很好的解决方案,但我试图了解事件和轮询,而不仅仅是键盘特定问题。

这两个例子都翻转了一个 boolean 值。当 boolean 值翻转时,我想触发一次。我可以将 boolean 值包装在一个对象中,这样如果对象发生变化,我也可以触发一个事件。我只是不想不必要地使用 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'.

在一些论坛上,人们建议使用 Observer。尽管这将事件处理程序与被观察的类分离,但我仍然有一个永远循环的 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 负载。这和投票一样糟糕。我也许可以通过 sleep 来限制它,或者比较自上次更新以来耗时,但这不是事件驱动的。这只是较少的轮询。那么我怎样才能更聪明地做到这一点呢?如何在不进行轮询的情况下观察 POJO 的变化?

在 C# 中似乎有一些有趣的东西叫做属性。我不是 C# 专家,所以也许这并不像我想的那么神奇。

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

最佳答案

是的,事件处理是您问题的关键。您显示的 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);

关于java - 在没有轮询的情况下观察变量的变化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2822901/

有关java - 在没有轮询的情况下观察变量的变化的更多相关文章

  1. ruby - 难道Lua没有和Ruby的method_missing相媲美的东西吗? - 2

    我好像记得Lua有类似Ruby的method_missing的东西。还是我记错了? 最佳答案 表的metatable的__index和__newindex可以用于与Ruby的method_missing相同的效果。 关于ruby-难道Lua没有和Ruby的method_missing相媲美的东西吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/7732154/

  2. ruby-on-rails - rails 目前在重启后没有安装 - 2

    我有一个奇怪的问题:我在rvm上安装了ruby​​onrails。一切正常,我可以创建项目。但是在我输入“railsnew”时重新启动后,我有“程序'rails'当前未安装。”。SystemUbuntu12.04ruby-v"1.9.3p194"gemlistactionmailer(3.2.5)actionpack(3.2.5)activemodel(3.2.5)activerecord(3.2.5)activeresource(3.2.5)activesupport(3.2.5)arel(3.0.2)builder(3.0.0)bundler(1.1.4)coffee-rails(

  3. ruby - 默认情况下使选项为 false - 2

    这是在Ruby中设置默认值的常用方法:classQuietByDefaultdefinitialize(opts={})@verbose=opts[:verbose]endend这是一个容易落入的陷阱:classVerboseNoMatterWhatdefinitialize(opts={})@verbose=opts[:verbose]||trueendend正确的做法是:classVerboseByDefaultdefinitialize(opts={})@verbose=opts.include?(:verbose)?opts[:verbose]:trueendend编写Verb

  4. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

    我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

  5. ruby - 通过 ruby​​ 进程共享变量 - 2

    我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是

  6. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  7. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  8. ruby-on-rails - 如何在我的 Rails 应用程序 View 中打印 ruby​​ 变量的内容? - 2

    我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby​​中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R

  9. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  10. 没有类的 Ruby 方法? - 2

    大家好!我想知道Ruby中未使用语法ClassName.method_name调用的方法是如何工作的。我头脑中的一些是puts、print、gets、chomp。可以在不使用点运算符的情况下调用这些方法。为什么是这样?他们来自哪里?我怎样才能看到这些方法的完整列表? 最佳答案 Kernel中的所有方法都可用于Object类的所有对象或从Object派生的任何类。您可以使用Kernel.instance_methods列出它们。 关于没有类的Ruby方法?,我们在StackOverflow

随机推荐