Welcome 微信登录

首页 / 软件开发 / JAVA / 用Qi4j进行面向组合编程

用Qi4j进行面向组合编程2011-07-08 infoq 长期以来,通过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层的类。