【设计模式】观察者模式

267 阅读3分钟

模式介绍

观察者模式也称为订阅者模式,实际上我觉得订阅者更容易理解。这种设计模式在生活中很常见。比如订阅期刊杂志、定牛奶等等。我们使用的软件中也很常见。比如说微博,你关注了某位明星,其实你就是他的观察者。每当你关注的明星发了新的动态,你就会接收到通知。观察者模式基于发布订阅的方式。订阅者订阅目标对象,目标对象维护订阅者的集合。一旦目标对象状态变化,需要通知所有订阅者,从而触发订阅者的某个行为。

示例(上课点名)

上学的时候经常逃课,如果老师点名的话学生(目标对象)立马喊未来上课同学(观察者)赶过来

目标对象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();
    }
}

结果

收到点名通知
去教室报道
收到点名通知
让同学帮忙报道

优缺点

优点

  1. 目标对象状态的变化,不需要观察者真的一直观察。当存在大量观察者时,如果所有的观察者都去轮询状态,那么系统资源的消耗极大。而观察者模式避免了这种情况;

  2. 观察者模式支持广播,状态变化时,目标对象的所有观察者都会得到通知;

  3. 符合开闭原则,目标对象依赖的是观察者的接口,可以很方便的对观察者进行扩展,而不需要修改已有观察者。反过来观察者也是依赖的目标接口。

缺点

  1. 观察者模式中,观察者接口限定了方法签名。有一定的局限性。

场景

  1. 抽象模型可以分为两个部分,一部分行为取决于另外一部分状态的变化。并且你想让这两部分各自独立。每部分都可以独自使用和复用;
  2. 一个对象的变化,需要通知其他对象,并且有多少对象需要通知并不清楚
  3. 你想让通知与被通知双方松耦合。

小结

观察者模式最大的优点就是把目标和观察者解耦,观察者根据目标对象状态的变化作出响应。而目标者可以把自己状态变化广播给所有注册的观察者。实际使用中有推/拉两种模型。

  • 在推模型中,目标对象会把状态改变相关的所有信息推送出去,信息的量有可能会很大。
  • 在拉模型中,目标对象只推送出最核心的信息,比如变化的数据 id。 观察者收到消息后再决定如何处理,比如查询与变化相关的自己感兴趣的数据。推模型,目标对象需要知道所有观察者对数据的需求。而拉模型效率会比较差,观察者收到消息后,还需要自己再去获取改变的内容。关于推拉模型总结如下: