长期以来,通过 OOP 对象集对领域概念进行建模的目标并未得到充分实现。那么迄今为止,我们万般努力但难以解决的根本问题到底是什么?有没有更好的解决办法?在本文中我们将介绍面向组合编程(COP,Composite Oriented Programming)的概念,展示它如何规避 OOP 存在的一些问题,并重先点燃使用可重用部件组装领域建模的希望。
问题
我为何物?实际中我可以有多重身份。某些时候,我是编写软件的开发者;另些时候,我是给大家讲解某个有关 Java 话题的软件开发者。但其他时候,我可能有完全不同的身份,比如银行的客户、大学的校友。简而言之,我不同时候的身份,由当时的具体环境决定。在不同的环境中,我需要通过不同接口、以相应行为与之交互。在所有这些环境中,我其实就是具有不同接口同一个对象。在我编写软件的时候银行不会出现另一个我。
解决方案
如果在软件中用 OOP 为我建模,一些人会将我设计为一个 Developer 类。但这样一个类显然不能在不同时候表达我的不同身份(比如一个大学校友,因为 Developer 类不包括社交概念)。因此对我建模的结果,可能会是几个不同的类,或者在一个类中实现所有行为。在本文中,我们提出另一种方案:利用 Mixin(混入)概念完成这个实现。
组合
首先,Mixin 被实现为一个普通 Java 类,它通常实现一个特定接口,而这个接口将是 Composite 所要暴露接口的一部分。接着,我们用如下方法声明一个 Composite:创建一个 Java 接口,用注解声明它要用到的 Mixin,并且使用“extends”关键字指明需暴露哪些领域接口。通过这一方法,我们就可以载一个集中的地点明确定义 Composite 的结构和行为。
使用 COP 时,虽然把横切关注点保留在独立的实现类中是一个不错的主意,但是它们的装配或组成方式应该还是集中用 Java 接口来说明。为了避免出现重复的说明,我们可以通过“extends”关键字重用被扩展接口中的声明。这样,如果修改了被扩展接口,从其扩展而来的 Composite 接口声明就会自动改变,不需我们逐个做人工修改。
利用这种办法,我们相信可以做到两全其美:存在于各个单独实现类中的关注点实现了分离,每个类仅需关注特定的任务;对于 Composite 最终应该是什么样的描述则是集中和确定的,这样,Composite 的开发者就可以全权负责定义中应该包含的内容。
代码示例
Composite 在实际中如何具体应用呢?让我们看一个例子。假设我是一个 Composite,那么可如下描述我:
@Mixins({DeveloperMixin.class, SpeakerMixin.class, AlumniMixin.class}) public interface HumanComposite extends Developer, Speaker, Alumni, Composite {}
被扩展接口中包含了实际要被调用的方法,由 Qi4j 运行时环境去构造一个 Composite 实例,这一实例可以将“来自客户端的调用”路由给特定的 Mixin 实例。但从客户端的角度看,这个 Composite 实例完全是一个普通 Java 对象(尽管与普通 OOP 方法实现的领域对象比较起来,它有更多的接口)。像 Developer 这样的领域接口是普通接口,和 Qi4j 无任何特殊关系,其实现本身也是实现了该接口的简单 Java 类。但是上面所说的领域对象的身份是由 Composite 实例而非某个 Mixin 实例定义的,这样就解决了身份问题:对“我”这个对象的引用,可在系统范围里传递,并被传递给在特定上下文环境中很有用的接口。如果引入更多领域或上下文环境时,也可通过扩展这个 Composite 来处理。
如需想创建另一个也使用 Alumni 接口的 Composite 及其实现,我们可以让此接口也扩展 Alumni,并声明使用相同的 Mixin。因此,多重继承和复用基础类的常见问题也解决了。
LMM 结构
软件通常是在纸上分模块、分层进行设计的。我们对类似如下的设计图已经非常熟悉了:
这个图包含有多个模块,不同模块构成了不同层,而各层又叠放在一起。我们可将这种设计方法简称为 LMM(Layered Modules Metaphor,分层模块表示法)。LMM 图可用来传达一个整体应用的总括,不让我们陷入太多的细节当中。严格按照 LMM 的要求设计系统,可减少系统缺陷、降低长期维护成本,这种系统对未来变更的反映也可更为灵活。绝大多数项目都使用 LMM 来描述应用程序的构成方式,许多项目设法遵循 LMM,但只有很少的项目是按此执行的。我想我们都已经看到过很多惨痛教训,比如在基础结构层的类中直接使用 Web 层的类。
Qi4j 支持的结构
Qi4j 目前已经能为 LMM 提供明确的支持,这有助于规范团队中开发人员的行为。Qi4j 应用结构是一个小规则集:
-
结构
-
所有结构在应用启动时静态声明
-
所有 Composite 实例都有所属模块
-
所有服务都有所属模块
-
所有模块都有所属层
-
所有层都有上下层次关系(但不循环)
-
所有层一起构成了应用
-
访问
-
模块能访问同层的所有其他模块
-
层能访问它的直接下级层(不能跃层访问)
-
可见性
-
Composite 实例缺省只在所属模块内可见
-
Composite 实例也能设置为在层内或层间可见
看起来比较复杂,其实不然。本质上,Composite 实例在创建它们的模块中都是私有的,除非显式声明为对模块外或层外公开。这和在普通 Java 中使用“public”和“pirvate”修饰词限定类的可见性是类似的。
Qi4j 目前尚不提供其他可供选择的结构,但其包含了构造常用应用程序结构的简单方法(包括一个层中只包含一个模块的情况)。
结构的使用
领域代码无需了解应用程序结构,但可以用 @Structure 注解的形式出现。如下例所示:
CompositeBuilderFactory 将在创建时被注入 Mixin,而且它仅允许代码实例化结构中可见的 Composite。
结构发挥作用的另一个常见例子发生在查找服务时。若在相同模块中有且仅有一个要求类型的服务,那么就无需引入额外装配(Assembly)。服务的使用变得十分简单。
例如,如果服务 GenericInventory 被声明在 Bread 模块中,那么每个 inventory 服务实例都将受其邻近各自客户端的程度约束。
应用结构的生成
Qi4j 应用需通过应用程序代码实现自举,最简单的启动方法大致如下:
另外,还可利用 SingletonAssembler 编写上述功能:
SingletonAssembler 是一个用于创建单层单模块 Qi4j 应用程序的工具类。
newApplication() 方法也可接收 Assembly[][][] 类型的参数,由此可创建“千层饼式”分层结构的应用环境(除了第一个和最后一个层,其他都有相邻的上、下层)。例如:
上面代码实现的结构如下图示:
最后,如果应用程序结构十分复杂,还可将 ApplicationAssembly 实例作为参数传递给 newApplication() 方法。 ApplicationAssembly 用类似迭代的形式创建全部 LayerAssembly,再为每个 LayerAssembly 创建 ModuleAssembly。举例如下:
运行上面代码可得到如下结构:
结构化的好处
通过代码显式实现应用程序结构有两个显著的好处:
- 就近访问。
- 架构强制执行。
这意味着,越近的 Composite 优先级越高,越易访问;外部不能访问模块或层内私有的 Composite。因为服务被实现为 Composite,其解析方法更为含蓄,需要的装配配置也少得多。
Qi4j 结构概念的另一个有趣之处在于每个应用都有一个静态结构组合,它可以通过工具来抽取并展示,而不必单独维护。这使得架构师、设计师或团队负责人可轻松跟踪开发人员对架构的遵循情况,很容易找到越轨者。
总结
本文通过实现于 Java 平台的 Qi4j,简要讨论了 COP 的可行性。我们看到以传统 OOP 观念实现对象的 Composite,是如何更好分离关注点(concerns),从而提升代码的质量和复用性的。此外,我们也讨论了显式建模应用结构的思路,这一结构通常只在纸面上而不是在代码中定义。通过显式建模应用结构,我们可以更容易落实架构执行并消除服务间的相互依赖。这将帮助我们创建更大规模的系统,而且随着引入越来越多的组件及服务,我们的系统不至被其自身的“重量”压垮。
最后要强调的一点是,COP 和 Qi4j 中的绝大多数理念并非前无古人。我们现在做的,恰恰是在前人编程实践和各种框架中寻找各种优秀的思想和模式,并提炼出那些我们认为在编写软件及保持容易理解且易于维护方面都能给开发者提供帮助的内容。无论是开发软件还是我们的日常生活中,将古老的东西运用于新的环境都是非常重要的。
作者简介
Rickard Öberg 曾参与过多个 J2EE 开源项目的开发工作,如 JBoss、XDoclet 和 WebWork。他也是 SiteVision CMS/portal 平台(以 AOP 为基础)的首席架构师。现在服务于 Jayway,主要关注方向是在新一轮以互联网为中心的应用中广泛采用的面向领域软件开发技术。
Jayway 简介
Jayway 是瑞典一家拥有 90 位认证 Java 专家的一流 Java 公司。我们的服务范围包括:内部开发、专业咨询以及 Java 平台相关指导与培训。我们对开源软件有坚定信念,并正为大量开源项目积极工作。我们珍视知识的交流与共享。Jayway 的网站是 www.jayway.com 。
阅读英文原文: Composite Oriented Programming with Qi4j 。
译者简介:罗小平,上海某大型公司互联网中心技术总监, CSDN 大版主,网络 ID 为 lxpbuaa(桂枝香在故国晚秋),曾著有《Delphi 精要》一书。个人博客为 http://blog.csdn.net/lxpbuaa ,他的 Email 和 MSN 为 lxpbuaa AT 263.net 。
给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家加入到 InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。
评论