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

时间:2016-06-14 08:45:53

标签: java design-patterns observer-pattern

我尝试将观察者模式应用于包含一组记录的类。

在Java伪代码中,我的类是:

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();
        }
    }

    ...
}

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

当然我可以添加一些标志变量来处理对setChangednotifyObservers的调用,但我想知道&#34;最好的&#34;是什么?处理这类问题的方法。

所以我的问题是:在对观察对象进行大规模修改后,设计代码只能通知观察者对象的最佳方法是什么?

4 个答案:

答案 0 :(得分:2)

我认为你有2个一般选项

  • 让客户端代码明确禁用通知
  • 将更改封装在自己的类中并使用模板方法

让我们来看看这两个选项。我将使用一个简单的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

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

在自己的类中封装更改并使用模板方法

定义代表变更的界面

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

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

答案 1 :(得分:0)

你看,如果你所有的&#34;大&#34;操作使用您自己的低级添加/删除功能,然后您将不断通知您的观察者。

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

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
}

含义:您允许班级用户控制&#34;何时&#34;通知发生。但这基本上意味着你将责任转移到客户代码上。

显然,选项空间在这里非常有限。那么,让我问一个反问题:你是否只是假设大规模运营和通知会产生严重的性能影响;或者你是否真的进行了一些分析(或者你的设置是:&#34;我有数千个列表对象和数千名观察者会在通知时做昂贵的事情&#34;)

我的意思是:您必须在清晰,优秀的设计与潜在的性能改进之间取得平衡。含义:只有&#34;放弃&#34;当性能真正被证实受到影响时,良好的设计。

答案 2 :(得分:0)

您可以向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()的两次调用不会导致任何性能发布。

答案 3 :(得分:0)

我喜欢RenéLink的答案。但他的“客户端代码明确禁用通知”方法有两个缺点:

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

2)如果在大量更新期间需要添加/删除其他单个项目(更新一堆) - 它也无法正常工作

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