在 fileds 的 setter 中更新观察者是否被认为是不好的做法?

Is it considered as bad practice to update observers in fileds' setters?

假设我有一个对象 Subject,它由一个观察者列表和一个 int 字段组成:

package example.template.pattern.observer;

import java.util.ArrayList;
import java.util.List;

public class Subject {

    private List<Observer> observers = new ArrayList<Observer>();
    private int state;

    public int getState() {
        return state;
    }

    public void setState(int state) {
        this.state = state;
    }

    public void attach(Observer observer) {
        observers.add(observer);
    }

    public void notifyAllObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
}

我想在 state 的字段每次 'set' 操作后通知所有人。我知道执行此类操作的最常见代码如下:

Subject subject = new Subject();
subject.add(newObserver);
subject.setState(newState);
subject.notifyAllObservers();

但是因为每次为 state 设置新值时我都想要更新,所以我稍微更改了代码。

已将 notifyAllObservers() 访问修饰符更改为私有:

private void notifyAllObservers() { ... code }

并在 state 的 setter 中添加了一个新的额外行:

public void setState(int state) {
    this.state = state;
    notifyAllObservers();
}

上面的代码被认为是一种不好的做法吗?

为什么这样不行?

在我看来,这实际上被认为是一种很好的做法。大约一个月后,您很有可能会忘记致电 notifyAllObservers。或者更糟的是,其他人可能会使用您的代码并且不知道他们应该在设置状态后调用 notifyAllObservers。如果发生这种情况,您的代码可能无法按预期工作。这就是为什么您应该将 notifyAllObservera 放在 setState 方法中。这样你和其他人就不用担心了。

没关系,但要小心

--

如果 Observer 之一决定在收到通知后自行删除怎么办? (是的,我知道,发布的代码没有显示 detachObserver 但很少错过这个方法是个好主意)

所以,假设有一个,那么:

class SatisfiedObserver {
  Subject observed;

  public SatisfiedObserver(Subject subject) {
    this.observed=subject;
    subject.attach(this);
  }

  public void update() {
    // Doing some work
    // Well, I'm satisfied
    this.observed.detach(this);
    // Now, innocent as it looks, **THIS** will blow 
    // the for cycle in Subject.notifyAllObservers() with a
    // ConcurrentModificationException
  }
}

解决方法:

public void notifyAllObservers() {
    Observer[] obses=this.observersArray();
    for (Observer observer : obses) {
        observer.update();
    }
}

protected final Observer[] observersArray() {
  Observer[] retval=new Observer[0];
  synchronized(this.observers) {
    // we don't want other threads to screw up observers while we take a copy
    // And since we don't want that, we'll nee to synchronize the attach
    // and detach as well: its not like the observers will
    // attach/detach all the time to fear serious
    // performance impact
    retval=this.observers.toArray(retval);
  }
  return retval;
}

public void attach(Observer o) {
  if(null!=o) {
    synchronized(this.observers) {
      this.observers.add(o);
    }
  }
}
public void detach(Observer o) {
  if(null!=o) {
    synchronized(this.observers) {
      this.observers.remove(o);
    }
  }
}

其他值得考虑的事项:

  1. Observer 实例注册到多个 Subjects - 他们如何知道哪个主题改变了状态(考虑 Observer.update(Subject who)

  2. 如果 Observer 中的一个在 update 中抛出异常,你会怎么做?你会停止通知其他人并让异常冒泡吗?捕获异常,继续通知并重新抛出?(那如果很多观察者抛出怎么办?)...决定,决定...

  3. 如果没有实际状态发生变化,为什么 Observer 应该接收更新?考虑

    public void setState(int state) {
      if(this.state = state) {
        this.state = state;
        notifyAllObservers();
      }
    }