模式9:Published Interface

 由  Gou Rui 发布

模式描述

让参与者都清楚的知道你的模块所发布的接口。


具体描述

众所周知(也是老生常谈):一个模块必须封装它的实现细节。所以理想情况下一个模块会暴露其API来作为其它模块与其交互的方式,而且这些API应该易于理解。简单的说,一个模块的API就是它发布的接口。如果还要一个更加准确的解释,可以参考如下文字:

发布的接口就是模块导出包(Exported Packages)里面的公共类的公共方法,这些方法能够被其他模块调用。

不幸的是,Java并不允许你定义一个模块的导出包,所以在标准Java里面想使用这个发布接口的模式式是不可能的(但是在OSGi这样的框架里面却是轻松加愉快)。由于这个缺陷,你得到的接口就有可能还包含的有其他不属于接口的公共方法,这样描述可能会有点难以理解,我们看看下图描述的场景:

http://assets.osgi.com.cn/article/7289326/PublishedInterface.jpg

在上图中,service仅仅定义了Method1和Method2这两个方法,但是ServiceImpl在提供接口实现的时候多实现了一个公共的Method3方法。这时候,如果Client端通过接受类型为Service的实例的引用来调用Service的话,就无法使用ServiceImpl所提供的Method3(或者说client不知道Method3的存在);或者换一个角度来想,其实Method3根本就不应该是公有的,因为它根本不属于Service的接口定义。有人说,那我为Service增加一个Method3的接口定义不就行了。但是为了迎合接口的实现而改变接口的定义,这就本末倒置了。

此外,由于图中的Service.jar对Client.jar是完全可见的,实际上我们在实例化Service对象的时候,依然需要调用ServiceImpl的构造函数,这样也没做到屏蔽实现细节的要求。


实现变种

在标准的Java中,有两种方式来限制一个模块中方法的访问:

  • 将方法设置为私有的(private)
  • 将类设置为私有的

这两种方法虽然成功的阻止了外部模块的访问,但是却也阻止了同一个模块中的其他类的访问。一个模块内之间考虑到其高内聚的特性,相互访问的可能性是很高的,于是上述两种方法的结果就是因噎废食。

怎么办?如果你仅仅想依靠Java的原生支持,那基本不可能给你一个满意的解决方案。你只有使用一些基于Java平台的模块化框架,才能比较满意的解决这个问题。

OSGi

不得不说OSGi框架就是你一个很好的选择(虽然像在打广告,不过我们可没收人家一分钱)。对于我们提到的上述问题,OSGi使得我们可以将封装粒度限制在模块级,任何在模块内的代码,只有被定义为外部可访问了以后,其他模块才能访问;而模块内的代码,则是没有限制的。本社区还提供了其他对OSGi更为详细的介绍文章,欢迎感兴趣的读者来阅读。下面是一些推荐:


结果

总的来说,发布接口最大的一个好处就是能让你的模块能更容易的被其他开发者使用。即使你的模块设计的再好,功能再有用,一个杂乱且难用的API会把别的开发者吓跑的。

不过使用接口也有一定的局限性:虽然你可以防止别人调用你不想提供的方法,但是在Java原生支持下这是不可能的;如果内部的代码想要通过接口来使用功能,必须得保证接口的实现是固定不变的,如果模块工作的时候老是在用不同的实现来提供接口,会使得External Configuration模式的实现变得困难。


总结

有了OSGi,你可以通过OSGi的发布服务(service)来轻松达到本设计模式的要求。而如果没有OSGi这种模块化系统的帮助,使用这个模式一定会给你带来淡淡的忧伤。

查看评论