观察者模式

94 阅读3分钟

定义

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主体对象。这个对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

优缺点

优点:

  1. 松耦合:观察者和被观察者之间是抽象耦合的,具体观察者不需要知道被观察者的具体实现细节,只需要实现统一的接口即可。当被观察者发生改变时,无需修改各个具体的观察者类。
  2. 可扩展性:增加新的观察者非常方便,只需要继承或实现观察者接口并注册到被观察者中即可。
  3. 灵活性:在被观察者状态变化时,能够自动通知所有依赖它的观察者对象,使系统具有良好的响应性和灵活性。
  4. 广播机制:一个主题可以同时拥有多个观察者,这样,在状态变化时,主题只需触发一次更新操作,就能将信息广播给所有观察者。

缺点:

  1. 循环依赖:如果设计不当,可能会导致观察者和被观察者之间产生循环依赖,造成系统难以维护。
  2. 性能开销:如果观察者数量庞大且更新频繁,每次通知所有观察者都会有一定的性能开销。
  3. 顺序问题:由于所有的观察者都是异步执行的,无法保证通知的顺序,对于需要按照特定顺序更新的场景不适用。
  4. 内存泄漏:如果观察者和被观察者之间的生命周期管理不当,可能会导致内存泄露。例如,某个观察者不再使用但未从被观察者列表移除,就会造成内存资源无法释放。
  5. 代码复杂度增加:在某些简单场景下,为了实现观察者模式可能引入额外的复杂性,如果实际需求并不需要解耦或者事件驱动,则可能得不偿失。

结构图和示例

image.png

  • Subject类:主题或者抽象通知者,一般用一个抽象类或者一个接口实现。它把所有对观察者对象的引用保存在一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象。
  • Observer类:抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己。这个接口叫做更新接口。抽象观察者一般用一个抽象类或者接口实现。
  • ConcreteSubject类:叫做具体主题或具体通知者,将有关状态存入具体观察者对象;在具体主题的内部状态改变时,给所有登记过的观察者发出通知。
  • ConcreteObserver类:具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协调。

示例:

/**
 * 抽象通知者
 */
public interface class Subject {
    // 绑定观察者
    public void attach(Observer observer);
    // 移除观察者
    public void detach(Observer observer);
    // 通知观察者
    public void notifyObserver();
}

/**
 * 抽象观察者
 */
public interface Observer {
    public void update()
}
/**
 * 具体通知者
 */
public class ConcreteSubject implements Subject {
 
	private List<Observer> list;
	private String state;
 
	public ConcreteSubject() {
		list = new ArrayList<Observer>();
	}
 
	@Override
	public void attach(Observer o) {
		list.add(o);
	}
 
	@Override
	public void detach(Observer o) {
		if (!list.isEmpty()) {
			list.remove(o);
		}
	}
 
	@Override
	public void notifyObserver() {
		for (Observer o : list) {
			o.update(message);
		}
	}
 
	public void setState(String s) {
		this.state = s;
		System.out.println("更新状态: " + s);
		// 消息更新,通知所有观察者
		notifyObserver();
	}
 
}
/**
 * 具体观察者
 */
public class ConcreteObserver implements Observer {
 
	private String name;
 
	public ConcreteObserver(String name) {
		this.name = name;
	}
 
	@Override
	public void update() {
		System.out.println(name + " 状态更新了");
	}
}