生活中,许多事物不是单独存在的,其中一个事物发生变化可能会导致一个或多个其他事物的行为也发生变化。例如:公众号的博主与用户之间(每当推送一篇文章,我们就能被动的接收到一篇文章,前提是你关注了它)。在软件设计中也是一样,例如:MVC 模式中的模型与视图的关系。此类场景使用观察者模式来实现的话,就非常方便。

# 一、观察者模块的定义与优缺点

观察者模式(Observer Pattern): 定义对象间的一种一对多依赖关系,使得每当一个对象状态发生改变时,其相关依赖对象皆得到通知并被自动更新。观察者模式又叫做发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。观察者模式是一种对象行为型模式。

观察者模式的优点如下: ①、降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。②、目标与观察者之间建立了一套触发机制。

观察者模式的缺点如下: ①、目标与观察者之间的依赖关系并没有完全解除。②、当观察者对象很多时,通知的发布会花费很长时间,影响程序的效率。③、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。④、如果在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能导致系统崩溃。

# 二、观察者模式的结构与类图

实现观察者模式时,需要注意具体目标对象和具体观察者对象之间不能直接调用,否则会使两者之间紧密耦合起来,这违反了面向对象的设计原则。

观察者模式主要包含以下角色:
【1】抽象主题角色(Subject): 也叫抽象目标类,它提供了一个用于保存观察者对象的聚集类和增加、删除观察者对象的方法,以及通知所有观察者的抽象方法。
【2】具体主题角色(Concrete Subject): 也叫具体目标类,它实现了抽象目标中的通知方法,当具体主题的内部状态发生改变时,通知所有注册过的观察者对象。
【3】抽象观察者角色(Observer): 它是一个抽象类或接口,它包含了一个更新自己的抽象方法,当接到具体主题的更改通知时被调用。
【4】具体观察者角色(Concrete Observer): 实现抽象观察者中定义的抽象方法,以便在得到目标的更改通知时更新自身的状态。

职责链模式

# 三、观察者模式案例分析

【1】抽象主题角色(Subject): 包含对观察者对象的增加、删除和通知等方法的抽象接口。

//抽象主题类
public interface ISubject {
    //添加观察者对象
    public void addObserver(IObserver o);
    //删除观察者对象
    public void removeObserver(IObserver o);
    //通知方法
    public void notifyObserver();
}
1
2
3
4
5
6
7
8
9

【2】具体主题角色(Concrete Subject): 需要创建一个集合,存放观察者对象。并实现 ISubject 接口,并实现增删和通知方法。同时添加修改数据的方法 setDate。

public class SubjectImpl implements ISubject{
    private String name;//博客名
    private String content;//博客内容
    private String type;//博客类型
 
    public void setDate(String name,String content,String type) {
        this.name=name;
        this.content=content;
        this.type = type;
    }
 
    //定义一个集合存放 观察者对象
    List<IObserver> observers;
 
    //构造器
    public SubjectImpl() {
        observers = new ArrayList<IObserver>();
    }
    //添加
    @Override
    public void addObserver(IObserver o) {
        observers.add(o);
    }
    //删除
    @Override
    public void removeObserver(IObserver o) {
        observers.remove(o);
    }
 
    @Override
    public void notifyObserver() {
        for (IObserver iObserver : observers) {
            if(iObserver != null) {
                iObserver.update(name,content,type);
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

【3】抽象观察者角色(Observer):抽取观察者们的共同方法 update 获取目标对象传递过来的信息。

//抽象观察者
public interface IObserver {
    //接受主题类的信息
    public void update(String name,String content,String type);
}
1
2
3
4
5

【4】具体观察者角色(Concrete Observer):实现抽象的观察者角色,并获取目标对象传递的值,进行逻辑处理。

public class ObserverImpl_A implements IObserver{
 
    @Override
    public void update(String name, String content, String type) {
        //对接受到的信息进行逻辑处理
        System.out.println("===观察者A====");
        System.out.println("***博客名称 : " + name + "***");
        System.out.println("***博客内容: " + content + "***");
        System.out.println("***博客类型: " + type + "***");
    }
}
1
2
3
4
5
6
7
8
9
10
11

【5】客户端:创建目标对象和观察者对象,并将观察者对象添加到目标对象的观察者集合中,并进行通知。

public class Client {
 
    public static void main(String[] args) {
        //目标对象 博客博主
        SubjectImpl subjectImpl = new SubjectImpl();
        //创建观察者对象 用户A
        ObserverImpl_A observerImpl_A = new ObserverImpl_A();
        //将观察对象 关注 表表对象
        subjectImpl.addObserver(observerImpl_A);
        //博客开始更新博客
        subjectImpl.setDate("Java设计模式", "访问者模式", "技术类");
        //通知观察者  关注的用户门
        subjectImpl.notifyObserver();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 四、观察者模式扩展优点

【1】只需要新增观察者对象类。

package obsever;
 
public class ObserverImpl_B implements IObserver{
 
    @Override
    public void update(String name, String content, String type) {
        //对接受到的信息进行逻辑处理
        System.out.println("===观察者B====");
        System.out.println("***博客名称 : " + name + "***");
        System.out.println("***博客内容: " + content + "***");
        System.out.println("***博客类型: " + type + "***");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

【2】客户端调用时,将其注册到目标对象中即可。符合 OCP 原则。

ObserverImpl_B observerImpl_B = new ObserverImpl_B();
//将观察对象 关注 表表对象
subjectImpl.addObserver(observerImpl_B);a
1
2
3

# 五、观察者模式应用源码分析

【1】查看一下 Observable 的源码,发现 Observable 是一个具体的主题类,此处与我们的例子不同之处是没有实现接口,我们细想也会发现,其实主题类也无需实现接口。

public class Observable {
    private Vector<Observer> obs;
 
    /** 创建集合 存放观察者对象 Observer */
 
    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);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

【2】抽象观察者接口 Observer。

public interface Observer {
    void update(Observable o, Object arg);
}
1
2
3
(adsbygoogle = window.adsbygoogle || []).push({});