模式介绍
观察者模式也称为订阅者模式,实际上我觉得订阅者更容易理解。这种设计模式在生活中很常见。比如订阅期刊杂志、定牛奶等等。我们使用的软件中也很常见。比如说微博,你关注了某位明星,其实你就是他的观察者。每当你关注的明星发了新的动态,你就会接收到通知。观察者模式基于发布订阅的方式。订阅者订阅目标对象,目标对象维护订阅者的集合。一旦目标对象状态变化,需要通知所有订阅者,从而触发订阅者的某个行为。
示例(上课点名)
上学的时候经常逃课,如果老师点名的话学生(目标对象)立马喊未来上课同学(观察者)赶过来
目标对象TeacherRollCallSubject
public class TeacherRollCallSubject {
private List<Observer> observers = new ArrayList<>();
/**
* 添加观察者
* @param observer
*/
public void addObserver(Observer observer){
observers.add(observer);
}
/**
* 移除观察者
* @param observer
*/
public void removeObserver(Observer observer){
observers.remove(observer);
}
/**
* 通知观察者
*/
public void notifyObservers (){
observers.forEach(Observer::update);
}
}
观察者接口Observer
观察者只需要实现一个方法,供通知者做通知的时候调用。
public interface Observer {
void update();
}
观察者一FirstStudentObserver
第一个观察者接到通知后,会马上去教室。
public class FirstStudentObserver implements Observer {
@Override
public void update() {
System.out.println("收到点名通知");
System.out.println("去教室报道");
}
}
观察者二SecStudentObserver
第二个观察者接到通知后,通知自己的好朋友帮自己答到
public class SecStudentObserver implements Observer {
@Override
public void update() {
System.out.println("收到点名通知");
System.out.println("让同学帮忙报道");
}
}
客户端Client
客户端代码中,分别声明两个不同的观察者,然后让这两个观察者都观察老师点名了目标对象。最后出发目标对象的通知方法。
public class Client {
public static void main(String[] args) {
Observer first = new FirstStudentObserver();
Observer second = new SecStudentObserver();
TeacherRollCallSubject teacher = new TeacherRollCallSubject();
teacher.addObserver(first);
teacher.addObserver(second);
teacher.notifyObservers();
}
}
结果
收到点名通知
去教室报道
收到点名通知
让同学帮忙报道
优缺点
优点
目标对象状态的变化,不需要观察者真的一直观察。当存在大量观察者时,如果所有的观察者都去轮询状态,那么系统资源的消耗极大。而观察者模式避免了这种情况;
观察者模式支持广播,状态变化时,目标对象的所有观察者都会得到通知;
符合开闭原则,目标对象依赖的是观察者的接口,可以很方便的对观察者进行扩展,而不需要修改已有观察者。反过来观察者也是依赖的目标接口。
缺点
- 观察者模式中,观察者接口限定了方法签名。有一定的局限性。
场景
- 抽象模型可以分为两个部分,一部分行为取决于另外一部分状态的变化。并且你想让这两部分各自独立。每部分都可以独自使用和复用;
- 一个对象的变化,需要通知其他对象,并且有多少对象需要通知并不清楚
- 你想让通知与被通知双方松耦合。
小结
观察者模式最大的优点就是把目标和观察者解耦,观察者根据目标对象状态的变化作出响应。而目标者可以把自己状态变化广播给所有注册的观察者。实际使用中有推/拉两种模型。
- 在推模型中,目标对象会把状态改变相关的所有信息推送出去,信息的量有可能会很大。
- 在拉模型中,目标对象只推送出最核心的信息,比如变化的数据 id。 观察者收到消息后再决定如何处理,比如查询与变化相关的自己感兴趣的数据。推模型,目标对象需要知道所有观察者对数据的需求。而拉模型效率会比较差,观察者收到消息后,还需要自己再去获取改变的内容。关于推拉模型总结如下: