应用于大规模记录更新的可观察模式

Observable pattern applied in massive record update

我正在尝试将 Observer 模式应用于包含一组记录的 class。

在Java伪代码中我的class是:

public class MyClass<E> extends Observable
{
    private ArrayList<E> items = new ArrayList<E>();

    public MyClass()
    {
    }

    public void add(E e)
    {
        this.items.add(e);

        setChanged();
        notifyObservers();
    }

    public void remove(E e)
    {
        if(this.items.remove(e))
        {
            setChanged();
            notifyObservers();
        }
    }

    ...
}

我的问题是:在某些情况下,在程序执行期间,我必须在结构中进行大量的记录删除和插入操作,并且出于性能目的,我希望在整个操作结束时只通知观察者对象一次.

当然我可以添加一些标志变量来处理对setChangednotifyObservers的调用,但我想知道"best"处理这种问题的方法是什么。

所以我的问题是:设计代码以在对观察对象进行大量修改后仅通知一次观察对象的最佳方法是什么?

你看,如果你所有的 "big" 操作都使用你自己的低级 add/remove 函数,那么你将不断通知你的观察者。

这可以通过提供其他方法来改变,例如:

public void doSomeMassiveOperation(args) {
   do massive things directly on your list
   notify listeners

另一种更加骇人听闻、错误且不被使用的方法是以这种方式扩展您的界面:

public void disableNotifications() {
   this.notificationsEnabled = false;
}

public void enableNotificationsAndNotify() {
   this.notificationsEnabled = true;
   notify ...
}

public void add(E e) {
   add ...
   if (notificationsEnabled) {
     notify
}

含义:您允许 class 的用户控制 "when" 通知的发生。但这基本上意味着您将该责任转移到您的客户端代码。

显然选项 space 在这里非常有限。那么让我问一个反问:你只是假设大量操作和通知会对性能产生严重影响吗?或者你真的做了一些分析(或者你的设置是这样的:"I many thousand list objects and many thousand observers that will do expensive things upon notifications")?

我的意思是:您必须在清晰、良好的设计与潜在的性能改进之间取得平衡。意思是:只有 "give up" 在性能 确实证明 会受到影响的情况下,才会有良好的设计。

您可以向 MyClass 添加方法:

public class MyClass<E> extends Observable {
    public void addAll(Collection<? extends E> e) {
        this.items.addAll(e);

        setChanged();
        notifyObservers();
    }

    public void removeAll(E e) {
        if(this.items.removeAll(e)) {
            setChanged();
            notifyObservers();
        }
    }

    //UPDATE: 
    //you can define general purpose method

    public void modifyItems(Runnable modifier) {
        disableNotification();
        modifier.run();
        enableNotification();
        notifyObservers();
    }

    private void disableNotification() { ... }
    private void enableNotification() { ... }
}

...
myClass.modifyItems(
    () - {
        myClass.add(item);
        myClass.remove(item);
        ...
);

方法 notifyObservers() 的两次调用不会导致任何性能问题。

我认为您有 2 个常规选项

  • 让客户端代码明确禁用通知
  • 封装自己的变化class并使用模板方法

让我们看看这两个选项。我将使用简单的 Observer 作为示例。

public class StdoutObserver implements Observer {
  @Override
  public void update(Observable o, Object arg) {
    System.out.println("Observable changed: " + o);
  }
}

客户端代码明确禁用通知

public class MyClass<E> extends Observable {
  private ArrayList<E> items = new ArrayList<E>();

  private boolean changing;

  public void setIsChanging(boolean changing){
    this.changing = changing;
    if(!changing && hasChanged()){
      notifyObservers();
    }
  }

  public void add(E e) {
    this.items.add(e);
    notifyChanged();
  }

  public void remove(E e) {
    if (this.items.remove(e)) {
      notifyChanged();
    }
  }

  private void notifyChanged(){
    setChanged();
    if(!changing){
      notifyObservers();
    }
  }
}

客户端代码如下所示

MyClass<String> stringMyClass = new MyClass<String>();
stringMyClass.addObserver(new StdoutObserver());

System.out.println("setting changing: true");
stringMyClass.setIsChanging(true);

stringMyClass.add("C");
stringMyClass.add("D");
stringMyClass.add("E");

System.out.println("setting changing: false");
stringMyClass.setIsChanging(false);

输出将是

setting changing: true
setting changing: false
Observable changed: MyClass@229ed927

此解决方案的问题是客户端代码可能忘记启用通知(或告诉可观察者更改已完成)。在这种情况下,通知可能会永远关闭。

封装自己的变化class并使用模板方法

定义一个接口来表示更改

public interface ObservableChange<T extends Observable> {
    public void doChange(T observable); 
}

您的 Observable 然后将使用模板方法来确保 changing 状态在更改完成后被重置。

public class MyClass<E> extends Observable {

  private ArrayList<E> items = new ArrayList<E>();

  private boolean changing;

  public void update(ObservableChange<MyClass2<E>> observableChange) {
    setIsChanging(true);
    try {
      observableChange.doChange(this);
    } finally {
      setIsChanging(false);
    }

  }

  private void setIsChanging(boolean changing) {
    this.changing = changing;
    if (!changing && hasChanged()) {
      notifyObservers();
    }
  }

  public void add(E e) {
    this.items.add(e);
    notifyChanged();
  }

  public void remove(E e) {
    if (this.items.remove(e)) {
      notifyChanged();
    }
  }

  private void notifyChanged() {
    setChanged();
    if (!changing) {
      notifyObservers();
    }
  }
}

客户端代码将如下所示;

MyClass<String> stringMyClass = new MyClass<String>();
stringMyClass.addObserver(new StdoutObserver());


stringMyClass.update(new ObservableChange<MyClass<String>>() {
  @Override
  public void doChange(MyClass<String> observable) {
    observable.add("A");
    observable.add("B");
    observable.add("C");
    observable.add("D");
  }
});

输出将是

Observable changed: MyClass2@33837697

使用模板方法可确保仅在更改完成后才禁用通知,因为观察者对此进行控制。

我喜欢 René Link 的回答。但是他的 "Client code explicitly disables notifications" 方法有两个缺点:

1) stringMyClass.setIsChanging(false) 由于某些原因(异常、错误代码等)可以避免调用

2) 如果在大规模束更新期间需要 added/deleted 一些其他单个项目(束更新之外) - 它也不会工作

Romeo Sheshi 评论了简单有效的解决方案:使用 deleteBulk()addBulk() 方法和 notifyChanged() 方法。