索引:
围绕EMF探索(1)之存储和查询
围绕EMF探索(2)之再探查询组件
本篇专门讲讲EMF Query这个组件的设计原理、结构和算法。
在上一篇中,已经简单介绍了EMF Query,其是为“内存中的eObjects”的检索提供一套“查询条件”,可以依据eObject的结构关系进行查询,也可以依据eObject的对象进行查询。
EMQ Query的主结构关系如下图所示:
其中咖啡色表示最基本的设计思想的基类,把握住了这几个类(接口),则就把握了整个EMF Query的设计原理。
其中蓝色的图形表示主要所需要关注的实现类和构架。
从大的方面说,整个EMF Query是由围绕查询语句的构造和执行来展开的。
(1)查询语句的结构
查询语句的结构一般如下所示:
SELECT
FROM [source]
WHERE [condition]
|
整个查询语句(SELECT)是由两个查询子句(Query Clause)组成,分别是FROM和WHERE。其中FROM表示了所需要查询的对象源(EObject Source),WHERE则表示了所需要匹配的条件。
对于EObject Source,其表示的就是一组eObject对象的集合。
(2)查询语句的执行
整个查询语句的执行规则就是,将所需要检索的对象源(EObject Source)转换成一棵对象树,通过遍历这棵对象树,依次将树中的对象与所需要检索的条件进行匹配,选择匹配成功的。
EObject eObject = null;
boolean canPrune = getFromClause().canBePruned();
WHERE whereClause = getWhereClause();
while (it.hasNext() && (isCancelled() == false)) {
eObject = (EObject) it.next();
if (whereClause.matches(eObject)) {
addEObject(eObject);
if (getResultSet().size() == maximumResultSize) {
break;
}
}
if (canPrune && whereClause.shouldPrune(eObject)) {
it.prune();
}
}
|
有关上面那张类图的结构就不再多叙述,相信一看就明白。
接下来讲讲两个比较值得关注的地方,一个是看得见的条件(Condition),还有一个是看不见的Tree Prune。
条件(Condition
)
与条件相关的类几乎占据了整个Query组件的一半,但是这些Condition是非常简单的。如果想真正理解这个条件的构造机制和原理,那么建议大家花点时间了解一下Java Funtion Programming(Java的函数式编程)。整个Condition就是围绕一个“一元谓词”而构造的,而每个Condition实际上就代表一个算子(arithmetic operator)。
其实上一篇已经提到了我曾经写过一篇有关Java函数式编程的blog,参见
http://blog.csdn.net/james999/archive/2005/01/09/246404.aspx 。
大家在使用或阅读Query源码的时候,可能会涉及到有的Condition会存在Adapter的。其实这就是利用了一个简单的Adapter模式代码,将需要匹配的EObject转换成当前Condition可以识别的值,用于计算。
对象树的裁减
前面讲过在执行检索过程中,要检索的对象源(EObject Source)转换成一棵对象树。其实是获取一个新的EMF所提供的TreeIterator对象,这是一个允许被裁减(Prune)的针对树的遍历指针。
TreeIterator的默认遍历方式是采用“树的深度遍历算法”,而裁减(Prune)则表示:当遍历到某个节点时,如果执行裁减行为,则此节点后的所有子节点将不再遍历——就像一棵树,被削了一个枝杈似的。
因为EMF的对象是支持包含关系的,而且很容易通过getContents()接口获取相关的内容对象。虽然如果不进行适当的裁减操作,完整的遍历一棵树,是比较缓慢的。
EMF Query在构造Condition的时候,提供了PruneHandler接口,用于判断当前的eObject对象是否需要进行裁减遍历行为。