稀有猿诉

十年磨一剑,历炼出锋芒,说话千百句,不如码二行。

Understanding the Observer Pattern

观察者模式是用于解耦的,把不同功能的类,而又对某一共同事件或者消息感兴趣解耦开来,使双方互不知道对方。常规的实现是通过接口的方式来把需要关注的消息封装起来,双方各自实现接口即可。

理解观察者模式

什么是观察者模式

观察者模式Observer pattern用于解耦消息发布或者状态发布的,对象之间有消息依赖的一种设计模式。消息发布者,或者说被关注者称之为主体(Subject),它会不定时的更新消息和状态,希望接收到消息和状态变化的称为观察者(Observer)。用接口来隔离主题对象(被关注者)和观察者,观察者被动接收来自主题的变化,然后更新自己的状态。

通常也被称作为发布-订阅者模式因为它与生活中的报纸杂志订阅非常的类似,用户(Subscriber)向发行商(Publisher)订阅,当有新的期刊来了时,发行商会邮寄给用户。发行商称之为Subject或者Publisher,用户称之为Observer或者Subscriber,添加订阅称作Subscription,邮寄新期刊称之为notify。

Subject持有一个Observer的列表,提供三个接口:添加订阅(attach或者addObserver),取消订阅detach或者removeObserver)和通知更新(notify),Observer则有一个更新(update)。

订阅 关系建立后,当有新的数据或者状态需要更新时,Subject就会调用notify接口来实现状态的发布。

观察者模式的示例

对于大部分编程语言来说都提供了观察者模式的接口,比如Java中就可以直接用java.util.Observable和java.util.Observer来实现。

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
40
41
42
43
44
45
46
47
48
49
50
51
import java.util.Observable;
import java.util.Observer;
import java.util.Random;

public class TestDriver {
    public static void main(String[] args) {
        final MusicTeacher teacher = new MusicTeacher();
        final Student tommy = new Student("Tommy", findViewById(R.id.tommy));
        final Student jimmy = new Student("Jimmy", findViewById(R.id.jimmy));
        final Student george = new Student("George", findViewById(R.id.george));
        teacher.addObserver(tommy);
        teacher.addObserver(jimmy);
        teacher.addObserver(george);

        teacher.singWithMe();
        teacher.singWithMe();
        teacher.singWithMe();
    }
}

class MusicTeacher extends Observable {
    private static final String[] songs = {
            "\tTwinkle twinkle little star,\n\tHow I wonder what you are.",
            "\tJohnny Johnny?\n\tYes papa.\n\tEating sugar?\n\tNo papa.",
            "\tHumpty dumpty sat on wall,\n\tHumpty dumpty had a great fall.",
            "\tOne two three four five,\n\tOnce I caught a fish alive."
    };
    private Random random;

    public MusicTeacher() {
        random = new Random(System.currentTimeMillis());
    }

    public void singWithMe() {
        setChanged();
        notifyObservers(songs[random.nextInt(songs.length)]);
    }
}

class Student implements Observer {
    private final String name;

    public Student(String name) {
        this.name = name;
    }

    @Override
    public void update(Observable observable, Object o) {
        System.out.println(name + ":\n" + o.toString());
    }
}

常规的实现是这样的,在代码层面有直接的依赖,也就是说Observable与Observer相互知道对方的存在,且代码上面有直接的编译依赖关系。并且一般也都是同步的,状态变化 后Subject会直接发出通知,以更新Observer。

什么时候用观察者模式

观察者模式的特点是『一对多』,主体只有一个,向多个观察者发布状态更新,这是它最主要的特点。另外就是,它最主要的作用是解耦 这种『一对多』状态更新关系。所以,当需要解决这种『一对多』状态更新的问题时就可以使用观察者模式。

厘清问题,找到主体Subject,再找到Observer,然后分别实现对应的接口即可。

发布-订阅模式

随着软件越来越复杂,比如组件的出现,多中间件的出现,远程(服务器客户端),并发和多线程多进程的出现,使得观察者模式也有了新的样式,比如Subject和Observer可以不会有直接依赖关系,或者都依赖于一个中间组件,比如一些Event-Bus系统,以及消息的更新与通知都是异步的。 这时就是出现了发布-订阅者模式(Publish-Subscrib pattern)。 向外发布消息的叫Publisher,它与Observable类似,但最重要的区别在于,Publisher并不知道Subscriber的存在,它是直接像一个第三方的消息队列,或者叫做消息平台,发布消息。而Subscriber,也不知道Publisher的存在,是直接向消息队列或者消息平台订阅。

它的特点是:

  • 组件间,甚至是不同的应用之间,不同的端之间的消息发布模型。
  • 都是异步的,就说是发布者是往消息队列发消息,然后就算发布完成了。订阅者是从消息队列拿消息。Publisher与Subscriber之间并无同步关系。一个消息发布出去,接收时间不确定。
  • 对应该关系自由,可以多对多,也可以多对一或者一对多。
  • 支持并发。

安卓里面非常著名的EventBus就是这一模式的经典实现。以及Linux世界里的dbus也是这种。

关于观察者模式与发布者模式区别可以看这篇文章

生产者消费者问题

再有一个比较类似的就是生产者和消费者问题(Producer consumer problem)主要是涉及多线程和同步问题。

参考资料

Comments