1 解决的问题

响应式编程问题。期望做到,一个对象做出某种反应后,监听者们能够获得该对象的反应。例如,警察关注着张三的一举一动,张三有违法行为,警察迅速行动,抓捕张三。

2 观察者模式

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

3 实现代码

// 观察者:昆虫接口
public interface Insect {
    void startWork();
    void stopWork();
}
 
// 具体观察者:蜜蜂
public class Bee implements Insect {
    private String name;
    public Bee(String name) { this.name = name; }
    @Override public void startWork() { System.out.println("蜜蜂【" + name + "】开始传粉"); }
    @Override public void stopWork() { System.out.println("蜜蜂【" + name + "】停止传粉"); }
}
 
// 具体观察者:蝴蝶
public class Butterfly implements Insect {
    private String name;
    public Butterfly(String name) { this.name = name; }
    @Override public void startWork() { System.out.println("蝴蝶【" + name + "】开始传粉"); }
    @Override public void stopWork() { System.out.println("蝴蝶【" + name + "】停止传粉"); }
}
 
// 被观察者:植物接口
public interface Plant {
    void registerInsect(Insect insect);
    void unregisterInsect(Insect insect);
    void notifyInsect(boolean isOpen);
}
 
// 具体被观察者:花类
public class Flower implements Plant {
    private final List<Insect> insects = new ArrayList<>();
    @Override public void registerInsect(Insect insect) { insects.add(insect); }
    @Override public void unregisterInsect(Insect insect) { insects.remove(insect); }
    @Override public void notifyInsect(boolean isOpen) {
        if(isOpen) {
            for(Insect insect: insects) insect.startWork();
        } else {
            for(Insect insect: insects) insect.stopWork();
        }
    }
    // 定义了一个批量解绑的方法
    public void unregisterAllInsect() {
        for(int i = 0; i < insects.size(); i++) unregisterInsect(insects.get(i));
    }
}
 
// 测试用例
public class ObserverTest {
    public static void main(String[] args) {
        Flower flower = new Flower();
        // 创建并注册观察者
        for (int i = 1; i < 4; i++) {
            flower.registerInsect(new Bee(i + ""));
            flower.registerInsect(new Butterfly(i + ""));
        }
        // 通知观察者
        flower.notifyInsect(true);
        System.out.println("=== 开花期已过 ===");
        // 通知观察者
        flower.notifyInsect(false);
        // 解绑所有观察者
        flower.unregisterAllInsect();
    }
}
 

4 观察者模式的推与拉

推方式

被观察者 → 观察者推送主题的 详细信息 (通常是被观察者的全部或部分数据),不管观察者是否需要。

拉方式

被观察者 → 观察者,只传递 少量信息,如果观察者需要更详细的信息,可主动到被观察者中获取,一般的实现方式是被观察者自身通过 update()方法传递给观察者,观察者再通过这个实例按需获取。

5 优缺点

优点

  • 开闭原则,无需修改发布者代码就可以引入新的订阅者类;
  • 运行时动态的修改对象之间的关系;
  • 将观察者与被观察者之间的抽象解耦。

缺点

  • 通知的顺序是随机的。
  • 观察者与被观察者互相依赖时,出现死循环。
  • 观察者对象多时,被观察者通知观察者的时间变长,影响程序效率。

6 应用场景

  • 对象状态改变需要修改其他对象时;
  • 存在订阅者,想订阅发布者;
  • 存在发布者,但不需要知道谁接受消息;
  • 链式触发机制:在系统中构建一个触发链,A 影响 B、B 影响 C;

7 经典应用例子

7.1 java.util.Observer & java.util.Observable

Java 的java.util包中,提供了一个 Observable 类和 Observer 接口,让我们可以更便捷地实现观察者模式。

java.util.Observer 接口

public interface Observer {
    void update(Observable o, Object arg);
}

java.util.Observable

public class Observable {
	private boolean changed = false;
	private Vector<Observer> obs;
	public Observable (){};
	protected synchronized void setChanged ( ){};
	protected synchronized void clearChanged ( ){};
	public synchronized void addObserver ( Observer o){};
	public synchronized void deleteObserver ( Observer o){};
	public synchronized void deleteObservers (){};
	public synchronized boolean hasChanged ();
	public synchronized int countObservers (){};
	public void notifyObservers(){};
	public void notifyObservers(Object arg){};
}

核心用法:被观察者实现继承 Observable,观察者实现 Observer 接口,通知变化时,调用 setChange 方法

简单的代码示例如下:

// 被观察者
import java.util.Observable;
import java.util.Observer;
 
public class CodingBoy extends Observable {
    private String title;
    private String contentUrl;
    public String getTitle() { return title; }
    public String getContentUrl() { return contentUrl; }
    public void update(String title, String url) {
        this.title = title;
        this.contentUrl = url;
        System.out.println("抠腚男孩公众号更新了文章:" + title);
        this.setChanged();  // 必不可少,通知改变
        this.notifyObservers(this); // 这里用拉方式
    }
}
 
// 观察者
public class Fan implements Observer {
    private String name;
    public Fan(String name) { this.name = name; }
    @Override
    public void update(Observable o, Object arg) {
        // 拉方式,通过实例按需获取所需信息
        CodingBoy codingBoy = (CodingBoy) arg;
        System.out.println(codingBoy.getTitle() + codingBoy.getContentUrl());
    }
}
 
// 测试用例
public class ClientTest {
    public static void main(String[] args) {
        CodingBoy codingBoy = new CodingBoy();
        // 注册观察者
        for (int i = 1; i < 4; i++) codingBoy.addObserver(new Fan(i + ""));
        codingBoy.update("this is title-arg0", "this is url-arg1");
        // 取消注册观察者
        codingBoy.deleteObservers();
    }
}

7.2 Spring Event

7.3 rabbitMq