Skip to content

编程模式·观察者模式、事件通知、消息队列三者区别 #49

Open
@WhyNotRunning

Description

观察者模式、事件通知、消息队列三者有类似,都有回调函数注册,通知调用的设计,容易混淆。

简述和区别

观察者模式:被观察对象状态改变,所有观察它的对象得到通知。也称订阅模式,英文Observer。
被观察者不依赖观察者,通过依赖注入达到控制反转。
事件通知:事件发生后,通知所有关心这个事件的对象。
与观察者模式对比,可理解成所有对象都只依赖事件系统。一半对象观察事件系统,等待特定通知;一半对象状态变化就通过事件系统发出事件。
观察者也不依赖被观察对象,他只关心事件,不需要到被观察对象那儿注册自己。
被观察者也只是普通对象,状态改变,通过事件系统发出事件就行了。
消息队列:将消息排成队列,逐步分发通知。
与事件通知对比,可理解成事件不是立即通知,而是保存到队列里,稍后通知。
这个可以达到时间解耦的效果。Windows的消息循环就是一个应用。多线程情况下,消息队列优先于事件系统。

观察者模式

以上课铃声为例子。上课铃声响,同学们回教室。

  1. 简单写法
class 上课铃{
    function 响()
        for 学生 in 学生们 do
            学生->回教室()
        end
    end
}

这样写有问题:

上课铃主动通知学生回教室,依赖关系反了。
上课铃响,老师要来上课,这个也得上课铃通知,上课铃管的东西太多了。
2. 轮询

class 学生{
    function update()
        if 上课玲响 then
            回教室()
        end
    end
}

这样上课铃只管按时响就行了,也有问题:

学生的update会越来越复杂,学生还有很多其他事情要做呢。
update太耗时了,学生们,要精神紧张地仔细停玲声有没有响起。
3. 用观察者模式

class 上课铃: Subject{
    function 响()
        NotifyObservers()
    end
}

class 学生: Observer{
    function init()
        上课玲->AddObserver(this.回教室)
    end
    function 回教室() ... end
    function un_init()
        上课玲->RemoveObserver(this.回教室)
    end
}

这样,上课铃只要响的时候发个通知,学生们就等通知好了。老师也类似,等通知就行了。

小结
实际就是注册个回调函数,完美的将观察对象和被观察对象分离。
个人理解:依赖注入,控制反转。观察者依赖被观察者,而不是被观察者依赖观察者。

事件系统

观察者模式有两个问题:

观察者要获得被观察对象,然后才能注册。
有时只是要知道某个事件发生了而已,类似网络初始化好了的事件,并不需要获得网络管理对象。
观察者和被观察者要继承对象的,在单继承体系里,这是很昂贵的一件事。
上课铃的例子里,学生只关心铃声,不关心上课铃这个物体。
用事件模式就可以换个写法

class 事件系统{
    function register(事件类型, handle);
    function remove(事件类型, handle);
    function trigger(事件类型, 数据);
}

class 上课铃{
    function 响()
        事件系统->trigger("上课铃声")
    end
}

class 学生{
    function init()
        事件系统->register("上课铃声", this->回教室)
    end
    function 回教室() ... end
    function un_init()
       事件系统->remove("上课铃声", this.回教室)
    end
}

小结
事件通知系统用的很广泛的。很多代码会有个EventDispatcher、EventControl之类的类。
特别是UI程序,当数据发生变化时通知相关UI更新。
观察者模式可以做到,但是事件通知来实现会更加简单。

消息队列

消息队列和事件系统很像。但是消息队列不是立即通知,而是把消息先放到队列里再通知。
上课铃的例子


class 消息队列{
    function register(消息类型, handle);
    function remove(消息类型, handle);
    function sendMsg(消息);
    function process();
}

class 上课铃{
    function 响()
        消息队列->sendMsg("上课铃声")
    end
}

class 学生{
    function init()
        消息队列->register("上课铃声", this->回教室)
    end
    function 回教室() ... end
    function un_init()
        消息队列->remove("上课铃声", this.回教室)
    end
}

main{
    while(有消息) do
        消息队列->process()
    end
}

从伪代码也可以看出,消息队列和事件系统的使用基本是一样的。如果消息队列不延后处理,就是事件系统了。
消息队列可以用于多线程,接受处理消息的handle们在主线程里。发送消息的可以在其他线程里。

简单总结

需要分层解耦就用事件通知系统。
需要时间解耦就用消息队列。

链接:https://www.jianshu.com/p/e0c6a0dc84b2

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions