在 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);
}
}
}
其他值得考虑的事项:
Observer
实例注册到多个 Subject
s - 他们如何知道哪个主题改变了状态(考虑 Observer.update(Subject who)
)
如果 Observer
中的一个在 update
中抛出异常,你会怎么做?你会停止通知其他人并让异常冒泡吗?捕获异常,继续通知并重新抛出?(那如果很多观察者抛出怎么办?)...决定,决定...
如果没有实际状态发生变化,为什么 Observer
应该接收更新?考虑
public void setState(int state) {
if(this.state = state) {
this.state = state;
notifyAllObservers();
}
}
假设我有一个对象 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);
}
}
}
其他值得考虑的事项:
Observer
实例注册到多个Subject
s - 他们如何知道哪个主题改变了状态(考虑Observer.update(Subject who)
)如果
Observer
中的一个在update
中抛出异常,你会怎么做?你会停止通知其他人并让异常冒泡吗?捕获异常,继续通知并重新抛出?(那如果很多观察者抛出怎么办?)...决定,决定...如果没有实际状态发生变化,为什么
Observer
应该接收更新?考虑
public void setState(int state) { if(this.state = state) { this.state = state; notifyAllObservers(); } }