包装类不适用于回调框架

时间:2015-01-31 16:57:54

标签: java design-patterns callback coding-style wrapper

  

包装类的缺点很少。需要注意的是,包装类不适合在回调框架中使用,其中对象将自引用传递给其他对象以进行后续调用(“回调”)。因为包装对象不知道它的包装器,它会传递对它自己的引用(this),并且回调会逃避包装器。

有人可以用一个例子来解释这意味着什么。它是用Effective Java编写的,但我并没有完全理解它。

要添加到上下文,而不是继承,我们应该支持组合而不是对Set进行子类化,我们应该使用这样的东西:

public class ForwardingSet<E> implements Set<E> {
 private final Set<E> s;
 public ForwardingSet(Set<E> s) { this.s = s; }
 public void clear() { s.clear(); }
 public boolean contains(Object o) { return s.contains(o); }
 ...
}

但是,这将如何失败,我仍然无法理解回调。在JavaScript中,我们可以使用函数回调,但如果有人可以解释,相同的概念如何应用于Java。

2 个答案:

答案 0 :(得分:8)

如果你能保证你总是在任何地方(对于未来的回调)传递被转发的对象的引用,那么一切都可以。尽管如此,你可以创建一个对象,用一些类包装它,但是该对象本身可以有一些方法在某处传递 this ,例如某些侦听器或其他地方。在这种情况下,您的包装器不知道包装对象发生了什么。 E.g:

// basic class which we will wrap
public class Model{ 
    Controller controller;

    Model(Controller controller){
        this.controller = controller; 
        controller.register(this); //Pass SELF reference
    }

    public void makeChange(){
        ... 
    }
} 

public class Controller{
    private final Model model;

    public void register(Model model){
        this.model = model;
    }

    // Here the wrapper just fails to count changes, 
    // because it does not know about the wrapped object 
    // references leaked
    public void doChanges(){
        model.makeChange(); 
    } 
}

// wrapper class
public class ModelChangesCounter{
    private final Model; 
    private int changesMade;

    ModelWrapper(Model model){
        this.model = model;
    }

    // The wrapper is intended to count changes, 
    // but those changes which are invoked from 
    // Controller are just skipped    
    public void makeChange(){
        model.makeChange(); 
        changesMade++;
    } 
}

Model的包装器只能避免来自makeChange()回调的Controller方法的调用。

答案 1 :(得分:2)

    interface SomethingWithCallback {

      void doSomething();

      void call();

    }


    class WrappedObject implements SomethingWithCallback {

      private final SomeService service;

      WrappedObject(SomeService service) {
        this.service = service;
      }

      @Override
      public void doSomething() {
        service.performAsync(this);
      }

      @Override
      public void call() {
        System.out.println("WrappedObject callback!");
      }
    }


    class Wrapper implements SomethingWithCallback {

      private final WrappedObject wrappedObject;

      Wrapper(WrappedObject wrappedObject) {
        this.wrappedObject = wrappedObject;
      }

      @Override
      public void doSomething() {
        wrappedObject.doSomething();
      }

      void doSomethingElse() {
        System.out.println("We can do everything the wrapped object can, and more!");
      }

      @Override
      public void call() {
        System.out.println("Wrapper callback!");
      }
    }

    final class SomeService {

      void performAsync(SomethingWithCallback callback) {
        new Thread(() -> {
          perform();
          callback.call();
        }).start();
      }

      void perform() {
        System.out.println("Service is being performed.");
      }
    }
    public static void main(String[] args) {
        SomeService   service       = new SomeService();
        WrappedObject wrappedObject = new WrappedObject(service);
        Wrapper       wrapper       = new Wrapper(wrappedObject);
        wrapper.doSomething();
    }   

问题在于,即使我们在包装器上调用了doSomething(),包装对象的回调也被调用了,而不是包装器的回调。这就是约书亚·布洛赫(Joshua Bloch)所说的“回调不包含包装材料”。

参考:Link