利用观察者模式来对程序模块进行解耦

这段时间在对我们项目的代码进行重构,发现我们以往开发软件的时候耦合度非常高, 最严重的是很难对软件进行扩展和删减,现在对软件进行扩展的功能成本非常大

比如我们有一个模块a,当到打一定的时间,或者说条件后,需要调用它:

function()

{

    function_a1()

    function_a2()

    function_a3()

}

 

这个时候软件还好,但是随着时间的增加,你需要加入b,c,d,e的功能,代码将会非常长,而且如果有时候我们需要阉割一些功能,或者说,我们需要增加一些功能,但是这个功能只是在某个客户所需求的,于是我们开始利用宏定义或者if语句配合配置文件来,达到所谓的扩展和删减功能,这样久了后,软件的主干部分是这样的:

function()

{

    read_config()

 

    if(need_a)

    {

        function_a1()

        function_a2()

        function_a3()

    }

 

    if(need_b)

    {

        function_b1()

        function_b2()

        .....

    }

 

    ......

}

 

 这样扩展下去后,你将会有两个问题:

配置文件膨胀

代码文件膨胀

 

在一个项目里面,不可能只有function这么简单的函数会用到其他的模块,可能你app init的时候,app destroy的时候,都需要因引用到其他模块,于是,这个问题会非常严重,而且在对一些客户进行定制开发的时候,他们也都需要在适当的时候调用对应的代码,于是不管在app init,app destroy或者其他地方,你总能看到一大堆的if语句,有时候我们要修改一个客户的功能,我们要先从几百个文件里面查找代码,然后在一个一个文件去修改

 

现在我们的一个配置文件有40+个配置项目,经常有时候出问题都不知道是哪个模块出问题,也不知道配置文件要怎么搞,一个cpp里面有6000+行代码,几乎include了整个系统的头文件,而且这个cpp里面实际上有3000+行都是针对单一客户的,也就是说,我们发一个版本给一个客户,实际上他也要了其他针对几十个客户的定制的模块

 

代码膨胀和配置文件膨胀是我们在维护上遇见的最大的问题

 

这样在团队开发的过程中,我们经常会遇见一个问题就是两个人都需要同时修改到function,如果一不小心,容易引起冲突,然后代码被覆盖掉,小乌龟在合并上做的不是很好,经常会提示冲突,需要手动解决,smartsvn在自动合并上就做得很好

 

耦合度太高也是我们团代配合时候经常会出现的问题

 

总之,这种开发模式下,我们的软件已经越来越复杂,维护越来越困难,从去年开始我开始思考如何做到更好的模块化,今年开始着手

 

就是引入了一个事件机制,当系统达到一定的条件后,将会去通知你,当然是前提你要告诉他,这个就是观察者模式,也可以说是sub/pub模式

 

比如像上面的function函数,我们模块a和模块b需要他调用,那么我们可以定义个虚基类,然后像function的管理类注册,到时间自动调用

 

如下:

class IModuleEvent

{

public:

    virtual void OnEvent() = 0;

};

 

class EvtMgr

{

    void RegisterEvt(IModuleEvent *evt)

    {

        m_EvtList.push_back(evt);

    }

}

 

function()

{

    for_each(EvtMgr.m_EvtList)

}

 

上面的是程序的核心部分,下面则是模块a

ModuleA:public IModuleEvent

{

    ModuleA()

    {

         EvtMgr.RegisterEvt(this);

    }

};

 

这样

  1. function不知道module a的存在,module a随时可以向EvtMgr注册事件,也可以随之滚蛋走人,就一句代码的事情,很容易扩展和删减

  2. module a的代码就肯定在模块,不会像之前那样散落在几十个地方,也不会造成代码冲突

基本就这样了,在对一个新的客户进行软件定制的时候,我开始对使用这套系统,虽然还没全部完成,但是到目前为止,我们已经可以很好的对新项目进行扩展和卸载,新的客户软件就是一个lib库,如果需要,那么我们让程序link我们的lib库就可以了

 

顺便说下,因为是使用lib库的原因,再者程序的核心部分并不知道有什么模块加入到这个软件里面,编译器不会主动去链接模块的lib,所以需要修改vs的强制符号引用,强制链接,后续可以通过改成dll来达到动态加载的目的

 

后续:

上面的程序使用了继承的方式,是不是觉得很麻烦,特别是在一个系统中,有几十个事件很正常,那岂不是要连续定义十多个class或者接口

其实现在已经有function和bind来取代虚函数了,推荐

http://blog.csdn.net/solstice/article/details/3066268

vs2010也自带了这个

 

再后续:

后续引入boost的signals2的库

 

posted @ 2015-01-18 22:40  linyilong  阅读(2105)  评论(0编辑  收藏  举报