美团90%会被问到的问题设计模式之观察者模式
一、定义与组成部分
1. 定义
观察者(Observer)模式 又名发布-订阅(Publish/Subscribe)模式。GOF 给观察者模式如下定义:
一、定义与组成部分
定义
观察者(Observer)模式 又名发布-订阅(Publish/Subscribe)模式。GOF 给观察者模式如下定义:
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
面向对象设计的一个重要原则——单一职责原则。系统的每个对象应该将重点放在问题域中的离散抽象上。因此理想的情况下,一个对象只做一件事情。这样在开发中也就带来了诸多的好处:提供了重用性和维护性,也是进行重构的良好的基础。
2. 组成部分
观察者模式由以下几部分组成:
抽象目标角色(Subject):目标角色知道它的观察者,可以有任意多个观察者观察同一个目标。并且提供注册和删除观察者对象的接口。目标角色往往由抽象类或者接口来实现。
抽象观察者角色(Observer):为那些在目标发生改变时需要获得通知的对象定义一个更新接口。抽象观察者角色主要由抽象类或者接口来实现。
具体目标角色(Concrete Subject):将有关状态存入各个Concrete Observer 对象。当它的状态发生改变时, 向它的各个观察者发出通知。
具体观察者角色(Concrete Observer):存储有关状态,这些状态应与目标的状态保持一致。实现Observer 的更新接口以使自身状态与目标的状态保持一致。在本角色内也可以维护一个指向Concrete Subject 对象的引用。
使用观察者模式的类图,这样能将关系清晰的表达出来。
在 Subject 这个抽象类中,提供了上面提到的功能,而且存在一个通知方法:notify。
还可以看到Subject 和ConcreteSubject 之间可以说是使用了模板模式(这个模式真是简单普遍到一不小心就用到了)。
这样当具体目标角色的状态发生改变,按照约定则会去调用通知方法,在这个方法中则会根据目标角色中注册的观察者名单来逐个调用相应的update 方法来调整观察者的状态。
这样观察者模式就走完了一个流程。
二、实现观察者模式
MySubject类就是我们的主对象,Observer1和Observer2是依赖于MySubject的对象,当MySubject变化时,Observer1和Observer2必然变化。
AbstractSubject类中定义着需要监控的对象列表,可以对其进行修改:增加或删除被监控对象,且当MySubject变化时,负责通知在列表内存在的对象。
Observer接口:
public interface Observer {
public void update();
}
实现类:Observer1
public class Observer1 implements Observer {
@Override
public void update() {
System.out.println("observer1 接收到了!");
}
}
实现类:Observer2
public class Observer2 implements Observer{
@Override
public void update() {
System.out.println("observer2 接收到了!");
}
}
Subject接口
public interface Subject {
/**
* 增加观察者
*
* @param observer
*/
public void add(Observer observer);
/**
* 删除观察者
* @param observer
*/
public void del(Observer observer);
/**
* 通知所有的观察者
*/
public void notifyObservers();
/**
* 自身的操作
*/
public void operation();
}
Subject实现类
public abstract class AbstractSubject implements Subject {
private Vector<Observer> vector = new Vector<Observer>();
@Override
public void add(Observer observer) {
vector.add(observer);
}
@Override
public void del(Observer observer) {
vector.remove(observer);
}
@Override
public void notifyObservers() {
Enumeration<Observer> enumo = vector.elements();
while (enumo.hasMoreElements()) {
enumo.nextElement().update();
}
}
@Override
public void operation() {
}
}
MySubject类
public class MySubject extends AbstractSubject {
@Override
public void operation() {
System.out.println("主对象的状态发生了改变!");
notifyObservers();
}
}
测试类:
public class Test {
public static void main(String[] args) {
Subject subject = new MySubject();
subject.add(new Observer1());
subject.add(new Observer2());
subject.operation();
}
}
测试结果:
三、 总结
1.观察者模式 主要解决了什么问题?
一个对象状态改变给其他对象通知的问题,而且要考虑到易用和低耦合,保证高度的协作。
2.如何解决?
使用面向对象技术,可以将这种依赖关系弱化。
3.观察者模式 何时使用?
一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知,进行广播通知。
4.观察者模式 的优缺点
【优点】
① 观察者和被观察者是抽象耦合的。
② 建立一套触发机制。
【缺点】
① 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
②如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
③ 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。
5.观察者模式 的使用场景
一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
一个对象必须通知其他对象,而并不知道这些对象是谁。
需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。
6.观察者模式 的注意事项
① JAVA 中已经有了对观察者模式的支持类。
② 避免循环引用。
③ 如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式。