基于BREW的松耦合设计初探

 

基于BREW的松耦合设计初探

毛晓冬 2007-10-19

 

一、    概述:

在进行模块/系统设计时,我们通常应该遵循“低耦合、高内聚”,“针对接口编程,不针对实现编程”等原则,这使得我们的设计可以被重用同时易于扩展和维护,可以抵抗“需求”的不断变化。BREW中,大量使用应用和扩展来实现各种功能,如果处理不当,会使得大量的这些对象耦合起来,整个系统将很难抵抗“变化”。本文就BREW系统中如何进行松耦合的设计进行初步的探究。

 

二、    Browser需求:

本文以Browser这样一个实际的模块作为例子来讲述各种方案的演变。我们假设Browser模块对外提供的通用的功能为:能够以默认方式启动(进Browser的主菜单界面),能够启动并直接加载主页,能够启动并直接加载客户指定的URL,能够启动并直接进入互动信箱,能够清空互动信箱,能够恢复出厂设置。

 

三、    Browser App处理一切的方案

我们的第一个方案是最“传统”的,一切都由Browser App来处理。方案框图如下:

 

 

 

 

 

在这种方案下,你可以看到一切都是“外界”直接和Browser App交互。在BREW中,最典型的实现是,其他应用都通过事件来得到Browser的服务,如上,App1通过事件的方式请求Browser启动,加载URL或者进入互动信箱。App2也利用事件的方式请求Browser模块清空互动信箱和恢复出厂设置,这里的双向箭头代表的是,可能App2需要得到一个操作的结果,那么Browser App还需要通过事件告知这个“结果”。

一切都是直接交互。

一切都是针对实现。

App1,App2Browser紧紧的耦合在一起。相互之间必须知道对方的存在(CLSID)。

现在,我们来看看当需求变化后发生了什么。

假设某一天,市场部认为现在的Browser界面太难看,可操作性太差,所以“枪毙”了现有的这个应用提供商,他们找了另外一个有丰富经验的Browser开发商来替换这个应用。

针对上面的框图,也就是“变化”发生了。我们可以看到,当变化发生时,虽然变化的仅仅是Browser,但是我们的App1,App2不得不将原先与Old Browser的交互转而延伸到与NewBrowser的交互。

牵一发而动全身!

为什么会这样??

因为我们针对了实现编程。

我们没有将变化点封装起来。

 

四、    分离出Browser Extension的方案

对于上面的方案,有一点需要注意的是,清空互动信箱和恢复出厂设置可能仅仅是一种数据处理,或者说是逻辑处理,完全没有必要启动应用。启动应用会带来额外的开销。此时,我们可以将职责分离,将Browser模块的逻辑/数据操作单独封装成一个对象(对应BREW中就是Extension),Browser App本身采用组合的方式引用该对象来完成具体的逻辑/数据处理,而自身仅仅完成高层的UI处理。同时,Browser模块也利用该封装的对象对外提供一致的逻辑/数据处理。使得客户如果仅仅需要请求逻辑/数据服务时,无须将Browser App牵扯进来,去除不必要的开销。

 

更改后的方案如下面框图所示。

 

 

 

 

不过该方案的目的在于分离职责,将逻辑与UI分离,使得各自的变化不影响对方。其本质是使得Browser模块中的逻辑和UI解耦。

但是,该方案仍然没有解决客户和Browser模块间的耦合。因为客户仍然面对直接和“实现(CLSID)”的交互。所以一旦“需求”变化后,客户不得不再次面临“灾难”。

 

五、    使用MIME Type降低耦合的方案:

现在是时候该让客户(App1,App2)和实现(Browser模块)进行解耦了。基本的原则,仍然是必须面向接口编程,而不是面向实现。封装各自的变化,并在运行时才绑定双方,而不是编译时!对于BREWMIME Type Handler是一个现成的很好的可被利用的机制。利用该机制,可以轻松的实现上面的“奢求”。我们先看一下方案框图吧:

 

 

 

 

利用MIME Type Handler机制,可以将一个对象(实现)与一个MIME Type绑定(注册)。而客户对于具体对象(实现)的请求,现在改为向MIME Type Manager的请求,委托MIME Type Manager来与真正的对象(实现)交互,或者委托MIME Type Manager返回一个运行时的具体(实现)对象给客户的抽象接口。 BREWMIME Type机制,就好像是一堵墙,隔离了客户和具体的实现对象,只要事先遵循一组事先约定的规约(接口,这里就是MIME Type),双方就可以单独的变化,而不影响另一方。甚至双方根本不知道对方的存在。

好了,说了这么多的大道理,我们来看看针对Browser,具体如何实现吧。

 

1.BrowserApp的对外服务抽象成一个MIME Type,假设为WapService,并且约定下面的规约:

WapService://DefaultLaunch                 默认方式启动

WapService://MailLaunch                   启动并进入互动信箱

WapService://HomePageLaunch              启动并加载主页

WapService://URLLaunch?URLValue          启动并加载客户指定的URL

2.BrowserAppMIF中注册该MIME Type,即 WapService

3.BrowserApp在自己的实现中履行该规约,即,在HandleEvent中实现上述请求。

4.Browser Extension继承至一个抽象接口,IBrowserBase,并实现所有的方法。

5.Browser Extension注册自己的MIME Type,假设为WapServiceExtension

6.Browser模块无须将任何信息暴露给客户。对于上面提到的规约和IBrowserBase.h,那甚至应该是客户提供的!

7.客户需要请求Browser App提供服务时,只需要调用ISHELL_SendURL/PostURL并按照规约传入不同的请求MIME Type String即可。注意,这里客户对“实现”一无所知,这种绑定是在运行期,且委托给了BREW内核。CLSID这个该死的家伙现在已经不需要了!

8.客户需要请求Browser Extension提供服务时,只需要调用ISHELL_GetHandler并按照规约传入WapServiceExtension,即可获得具体ExtensionCLSID并进行创建接口。需要注意的是,这里获得的CLSID是在运行时动态获得的(BREW内核返回的),并非在编译期绑定。所以具体对象的实例化是在运行时才确定的,唯一只有抽象接口才是在编译期决定的,但是这又何妨,这本身就是一种规约。

9.客户获得实例化的Extension对象后,就可以按照规约(IBrowserBase抽象接口定义的抽象方法)来使用它了。

 

现在,我们来看看当“需求”变化时的情况:

很不幸,现有的Browser提供商终究还是被“枪毙”了,新的Browser供应商来到了。在最早的时刻,我们就把我们的规约扔给了他们,包括IBrowserBase.h以及其他的MIME Type规约。(他们为我们提供服务,所以得听我们的,规约由我们来定J

OK,新的供应商很有经验,没几天就按规约完成了一切。此时,我们需要集成它们的模块,我们需要作什么??

我们不需要修改任何客户代码!

我们仅仅需要重新编译,连接生成我们的Image文件(如果以静态方式集成的话)。

我们甚至连Image文件都不需要重新生成(如果直接加载动态应用的话)。

这一切是如何发生的那??

请重新看看上面框图中的那堵墙,那些规约。

 

别高兴的太早,供应商也从我们这里学到了。现在它们需要我们的服务了,它们也扔了一份规约给我们。

该我们行动了:-)

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值