如何安全地装饰现有的回调?

问题描述:

假设我使用以下回调API:

Suppose I'm working with the following callback API:

/**
 * Registers a new action which will be run at some later time on
 * some other thread, clearing any previously set callback action.
 *
 * @param callback an action to be run later.
 * @returns the previously registered action.
 */
public Runnable register(Runnable callback);

我想注册我自己的操作,但是我想保留任何设置的行为。换句话说,我想我的动作看起来像:

I'd like to register my own action, but I want to preserve any set behavior. In other words I'd like my action to look something like:

new Runnable() {
  public void run() {
    // do my work

    originalCallback.run();
  }
}

最简单的方法是提供 originalCallback $ c> to my Runnable

What's the cleanest way to provide originalCallback to my Runnable?

当调用回调或涉及一些复杂的锁定时, originalCallback 不可用。

The naive solutions that come to mind risk introducing a window of time where originalCallback isn't available when the callback is called, or that involve some intricate locking.

经过一番挖掘,我找到了Guava的 SettableFuture 和Java 8的 CompletableFuture 。我将把 BlockingSupplier 留给后代,但是这些 Future 的实现将更加标准化,

After some more digging I found Guava's SettableFuture and Java 8's CompletableFuture. I'll leave my BlockingSupplier up for posterity, but either of these Future implementations would be more standard, and work just the same.

您基本上需要一个拥有阻止 get )方法。类似的东西:

You basically need a holder class with a blocking get() method. Something like this:

public class BlockingSupplier<E> implements Supplier<E> {
  private final CountDownLatch latch = new CountDownLatch(1);
  private volatile E value;

  public synchronized void set(E value) {
    checkState(latch.getCount() > 0, "Cannot call set more than once.");
    this.value = value;
    latch.countDown();
  }

  @Override
  public E get() {
    latch.await(); // will block until set() is called
    return value;
  }
}

然后你可以这样使用:

BlockingSupplier<Runnable> supplier = new BlockingSupplier<>();
// Pass the BlockingSupplier to our callback
DecoratorCallback myAction = new DecoratorCallback(supplier);
// Register the callback, and set the BlockingSupplier to the old callback
supplier.set(register(myAction));

其中 DecoratorCallback run()如下所示:

public void run() {
  // do my work

  // This will block until supplier.set() returns
  originalCallbackSupplier.get().run();
}

由于durron597提到有更好的方法来设计回调API, API的问题,这似乎是合理的。

As durron597 mentions there are better ways to design a callback API, but given the API in the question, this seems reasonable.