目录

一、定义

二、观察者模式案例分析

三、观察者模式在JDK中的应用

四、总结


一、定义

观察者模式,也叫发布订阅模式,它是一个在项目中经常使用的模式。其定义如下:

定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新。通过观察者模式,使得某个对象与其他依赖它的对象保持状态同步。(观察者订阅被观察者的状态,当被观察者状态改变的时候会通知所有订阅的观察者的过程)。

观察者模式的通用类图如下:

我们先来了解一下观察者模式的几个角色:

  • Subject被观察者:抽象被观察者角色,也叫被订阅对象,通常是接口或者抽象类。当被观察的状态发生变化时,需要通知所有观察者对象进行在状态更新。Subject通常具有注册、移除以及通知所有观察者对象的抽象方法;
  • ConcreteSubject具体的被观察者 : 具体的被观察者角色,实现了注册、移除、通知订阅者的方法;
  • Observer观察者:抽象观察者角色,可以是接口或抽象类,当Subject的状态发生变化时,Observer对象将会进行同步更新;
  • ConcreteObserver :具体观察者角色,接收到被观察者的回调之后会处理一系列业务逻辑;

二、观察者模式案例分析

我们以红绿灯的案例来说明观察者模式的使用方法:

司机朋友在看到红灯时,必须把车辆停下来;当绿灯时,才允许启动车辆。

结合前面的类图,我们可以发现红绿灯其实就是被观察者角色,司机朋友就是观察者【司机朋友观察红绿灯的状态,一旦红绿灯发生变化,司机朋友就会触发某种动作,如停车、启动车辆等】;

相关类图如下:

 首先我们定义一下抽象的观察者角色:

/**
 * 抽象观察者角色
 */
public abstract class AbstractObserver {

    /**
     * 被观察者状态变更时执行的方法
     */
    protected abstract void update(String type);

}

然后定义两个具体的观察者角色,即两个司机朋友,代码如下:

/**
 * 具体观察者角色
 */
public class DriverA extends AbstractObserver {
    @Override
    protected void update(String type) {
        if ("1".equals(type)) {
            System.out.println("司机A看到绿灯亮,开始启动汽车...");
        } else {
            System.out.println("司机A看到红灯亮,开始停止汽车...");
        }
    }
}

/**
 * 具体观察者角色
 */
public class DriverB extends AbstractObserver {
    @Override
    protected void update(String type) {
        if ("1".equals(type)) {
            System.out.println("司机B看到绿灯亮,开始启动汽车...");
        } else {
            System.out.println("司机B看到红灯亮,开始停止汽车...");
        }
    }
}

有了观察者角色,下面定义抽象的被观察者角色:即红绿灯

/**
 * 抽象被观察者角色:红绿灯
 */
public abstract class AbstractObservable {

    /**
     * 所有的观察者集合
     */
    protected List<AbstractObserver> observerList = new ArrayList<>();

    /**
     * 添加观察者
     *
     * @param observer
     */
    public void addObserver(AbstractObserver observer) {
        this.observerList.add(observer);
    }

    /**
     * 删除观察者
     *
     * @param observer
     */
    public void deleteObserver(AbstractObserver observer) {
        this.observerList.remove(observer);
    }

    /**
     * 返回当前观察者的个数
     */
    public int getObserverSize() {
        return this.observerList.size();
    }

    /**
     * 通知所有的观察者的抽象方法
     */
    protected void notifyObserver() {
        for (AbstractObserver observer : observerList) {
            observer.update(type());
        }
    }

    /**
     * 类型
     */
    protected abstract String type();

}

可以看到,抽象被观察者内部持有一个存放具体观察者角色的容器,并且提供了添加、删除观察者的方法。

然后定义具体的被观察者角色:即红灯、绿灯:

/**
 * 具体被观察者: 绿灯
 */
public class GreenLight extends AbstractObservable {
    @Override
    protected void notifyObserver() {
        System.out.println("绿灯亮了...");
        super.notifyObserver();
    }

    @Override
    protected String type() {
        return "1";
    }
}

/**
 * 具体被观察者: 红灯
 */
public class RedLight extends AbstractObservable {

    @Override
    protected void notifyObserver() {
        System.out.println("红灯亮了...");
        super.notifyObserver();
    }

    @Override
    protected String type() {
        return "2";
    }
}

最后我们写一个场景类,看下如何使用观察者模式实现订阅发布通知:

/**
 * 场景类
 */
public class Client {
    public static void main(String[] args) {
        AbstractObservable observable = new RedLight();
        DriverA driverA = new DriverA();
        DriverB driverB = new DriverB();
        observable.addObserver(driverA);
        observable.addObserver(driverB);
        observable.notifyObserver();
        System.out.println("观察红绿灯的司机共有: " + observable.getObserverSize() + "位");
        System.out.println("===========================");
        observable = new GreenLight();
        observable.addObserver(driverA);
        observable.notifyObserver();
        System.out.println("观察红绿灯的司机共有: " + observable.getObserverSize() + "位");
    }
}

程序运行结果如下:

红灯亮了...
司机A看到红灯亮,开始停止汽车...
司机B看到红灯亮,开始停止汽车...
观察红绿灯的司机共有: 2位
===========================
绿灯亮了...
司机A看到绿灯亮,开始启动汽车...
观察红绿灯的司机共有: 1位

可以看到,当红绿灯状态发生变更时,能够触发所有观察者执行某个动作。并且使用了观察者角色,如果想动态增加订阅者就显得非常方便,只需要增加一个具体的观察者对象即可。

三、观察者模式在JDK中的应用

JDK中的Observable接口就使用到了观察者模式,一个 Observable 实例改变后,调用 Observable 的 notifyObservers() 方法的应用程序会通过调用观察者的 update() 方法来通知观察者该实例发生了改变。

【a】Observable:具体的被观察者角色,此处并没有抽象被观察者角色。

public class Observable {
    private boolean changed = false;
    //持有观察者角色的引用对象集合
    private Vector<Observer> obs;

    public Observable() {
        obs = new Vector<>();
    }
    
    //注册观察者
    public synchronized void addObserver(Observer o) {
    if (o == null)
        throw new NullPointerException();
    if (!obs.contains(o)) {
        obs.addElement(o);
    }
}

//移除观察者
public synchronized void deleteObserver(Observer o) {
    obs.removeElement(o);
}

//通知所有观察者
public void notifyObservers() {
    notifyObservers(null);
}


public void notifyObservers(Object arg) {
    Object[] arrLocal;

    synchronized (this) {
        if (!changed)
            return;
        arrLocal = obs.toArray();
        clearChanged();
    }
    //循环遍历,通知所有观察者,并且调用update()更新状态
    for (int i = arrLocal.length-1; i>=0; i--)
        ((Observer)arrLocal[i]).update(this, arg);
}

//...省略
    
}

【b】抽象观察者角色:Observer

public interface Observer {
    //更新状态(只要改变了 observable 对象就调用此方法)
    void update(Observable o, Object arg);
}

四、总结

观察者模式的优点:

  • 动态新增一个观察者非常方便,并且不需要修改被观察者代码,可维护性高,扩展性强,遵循开闭原则;
  • 观察者和被观察者之间是抽象耦合的,修改其中一方并不会影响另一方;

观察者模式的缺点:

  • 如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间;
  • 需要考虑一下开发效率和运行效率的问题,一个被观察者,多个观察者,开发和调试会比较复杂,而且在Java中消息的通知默认是顺序执行,一个观察者卡壳,会影响整体的运行效率。在这种情况下,一般考虑采用异步的方式。

观察者模式的使用场景:

  • 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象,可以使用观察者模式创建一种链式触发机制;
  • 一个对象必须通知其他对象,而并不知道这些对象是谁;
  • 一个对象的改变将导致其他一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,可以降低对象之间的耦合度;

观察者模式和责任链模式的最大区别就是观察者广播链在传播的过程中消息是随时更改的,它是由相邻的两个节点协商的消息结构;而责任链模式在消息传递的过程中基本上保持消息不可变,如果要改变,也只是在原有的消息上进行修正。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐