「设计模式」观察者模式

147 阅读4分钟

一、概述

观察者模式(Observer Pattern)又称为发布/订阅(Publish/Subscribe)模式,在对象之间定义了一对多的依赖,这样一来,当一个对象改变状态,依赖它的对象会收到通知并自动更新。

观察者模式的4种角色:

  • 抽象被观察者(Subject)角色:也就是一个抽象主题,它把所有对观察者对象的引用保存在一个集合中,每个主题都可以有任意数量的观察者。抽象主题提供一个接口,可以增加和删除观察者角色。一般用一个抽象类和接口来实现。
  • 抽象观察者(Observer)角色:为所有的具体观察者定义一个接口,在得到主题通知时更新自己。
  • 具体被观察者(ConcreteSubject)角色:也就是一个具体的主题,在集体主题的内部状态改变时,所有登记过的观察者发出通知。
  • 具体观察者(ConcreteObserver)角色:实现抽象观察者角色所需要的更新接口,一边使本身的状态与制图的状态相协调。

二、优缺点

优点:

  1. 观察者和被观察者是抽象耦合的。
  2. 建立一套触发机制。

缺点:

  1. 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间。
  2. 如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。
  3. 观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。

三、实现方式

观察者模式比较简单,下面手动简单实现和基于jdk内置观察者实现观察者模式。

手动实现观察者模式

被观察者抽象类

public interface Subject {

    /**
     * 存储观察者
      */
    List<Observer> observers = new ArrayList<>();

    /**
     * 增加观察者
     */
    public void addObserver(Observer obs);

    /**
     * 删除观察者
     */
    public void removeObserver(Observer obs);

    /**
     * 通知所有观察者
     */
    public void notifyAllObservers();
}

被观察者具体类

public class ConcreteSubject implements Subject{

    @Override
    public void addObserver(Observer obs) {
        observers.add(obs);
    }

    @Override
    public void removeObserver(Observer obs) {
        observers.remove(obs);

    }

    @Override
    public void notifyAllObservers() {
        for(Observer observer : observers){
            observer.update();
        }
    }
}

观察者抽象类

public interface Observer {

    void update();

}

观察者具体类

public class ObserverA implements Observer{
    @Override
    public void update() {
        System.out.println("观察者A已收到通知");
    }
}

public class ObserverB implements Observer{
    @Override
    public void update() {
        System.out.println("观察者B已收到通知");
    }
}

客户端

public class Client {
    public static void main(String[] args) {

        //目标对象
        Subject subject = new ConcreteSubject();

        //创建2个观察者
        Observer observerA = new ObserverA();
        Observer observerB = new ObserverB();

        //将2个观察者注册到订阅队伍
        System.out.println("注册观察者A和观察者B");
        subject.addObserver(observerA);
        subject.addObserver(observerB);

        //通知所有观察者
        System.out.println("通知所有观察者");
        subject.notifyAllObservers();

        System.out.println("---------------------");
        //移除观察者A
        System.out.println("移除观察者A");
        subject.removeObserver(observerA);
        //通知所有观察者
        System.out.println("通知所有观察者");
        subject.notifyAllObservers();
    }
}

输出结果

注册观察者A和观察者B
通知所有观察者
观察者A已收到通知
观察者B已收到通知
---------------------
移除观察者A
通知所有观察者
观察者B已收到通知

Jdk内置观察者模式实现

在 java.util 包中包含有基本的Observer接口和Observable抽象类,里面内置了注册、删除、通知观察者等功能。

被观察者具体类

public class ConcreteSubject  extends Observable {

    public void notifyAllObservers() {
        //必须设置已更改标志,才会通知给所有观察者
        /**
         * protected synchronized void setChanged() {
         *         changed = true;
         *     }
         */
        setChanged();
        notifyObservers();
    }
}

观察者具体类

public class ObserverA implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("观察者A已收到通知");
    }
}

public class ObserverB implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("观察者B已收到通知");
    }
}

客户端

public class Client {
    public static void main(String[] args) {
        //目标对象
        ConcreteSubject subject = new ConcreteSubject();

        //创建2个观察者
        Observer observerA = new ObserverA();
        Observer observerB = new ObserverB();

        //将2个观察者注册到订阅队伍
        System.out.println("注册观察者A和观察者B");
        subject.addObserver(observerA);
        subject.addObserver(observerB);

        //通知所有观察者
        System.out.println("通知所有观察者");
        subject.notifyAllObservers();

        System.out.println("---------------------");
        //移除观察者A
        System.out.println("移除观察者A");
        subject.notifyObservers(observerA);
        //通知所有观察者
        System.out.println("通知所有观察者");
        subject.notifyAllObservers();
    }
}

结果输出

注册观察者A和观察者B
通知所有观察者
观察者B已收到通知
观察者A已收到通知
---------------------
移除观察者A
通知所有观察者
观察者B已收到通知
观察者A已收到通知

四、常见应用场景

  • 一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这些方面封装在独立的对象中使它们可以各自独立地改变和复用。
  • 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度。
  • 一个对象必须通知其他对象,而并不知道这些对象是谁。
  • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可以使用观察者模式创建一种链式触发机制。