我尝试将观察者模式应用于包含一组记录的类。
在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>
当然我可以添加一些标志变量来处理对setChanged
和notifyObservers
的调用,但我想知道&#34;最好的&#34;是什么?处理这类问题的方法。
所以我的问题是:在对观察对象进行大规模修改后,设计代码只能通知观察者对象的最佳方法是什么?
答案 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()
方法。