论坛首页 Java企业应用论坛

元数据、开放数据模型及动态系统--形而上学篇

浏览 9459 次
该帖已经被评为精华帖
作者 正文
   发表时间:2007-01-17  

引言

在JavaEye潜水多年,这是我第一次发帖。我想在这里介绍一下我最近几年的编程实践,一套比较另类的技术。如果详细地写,恐怕篇幅太大.所以我尽量压缩篇幅,如果阅读时有跳跃的感觉,权且把它当作一个提纲而不是一篇文章。具体的细节,可以在讨论中展开。如果有人愿意尝试,我可以考虑把现有的代码修改后开源。我的奢望是,你读完以后,你会同意,或者部分同意,Java其实也可以很简洁,Java其实也可以很动态,Java其实也可以很高产。

这一套东西,我不知道怎么称呼它,所以我甚至不能给这篇文章起一个满意的名字,也不知道放在哪一个版块合适。它是数据结构,设计模式,构架(architecture),REST框架(framework),还是一种SOA技术?反正都沾边。它到底是什么,还是由你们来告诉我吧。也许它比较另类,其实也未必,最近坛子上就有许多类似想法的讨论。比如:[讨论]Java中的ActiveRecord实现(我实现了一点儿)以及 一种Java Web框架的设计和实现,大家觉得能够快速开发吗?但是我在这里介绍的,是比较成熟的技术,用它实现的系统已经运行好几年了。

不管它叫做什么,它的核心在于认识数据,尤其是元数据,通过将数值与元数据分离,构造出一个普适的,弱类型的,开放的,动态的数据结构/数据模型/数据容器,(形容词的意义会在后面明了)。在它的基础上,借助XML技术(Schema, XSLT, XPath, ...)而开发出来的一整套技术。技术的实现上,借用了SAX 2。但你的项目可以完全不用XML。

我这里说的元数据编程,和通常的元数据编程,不是一回事。通常所说的元数据编程,是将代码中人机对话(对机器的指令,把 J2EE容器看成是特殊的虚拟机)的内容分离出来另行处理的代码生成技术。我这里说的是利用元数据简化数据模型,简化编程,降低系统耦合,增强系统稳定性和动态性的技术。

感觉国内XML的应用不多,此文也可以看作是对XML应用的介绍和推动。其实在Java 6以前,Java平台就已经支持脚本语言了,我都已经用了五年了,这个脚本语言就是XSLT。XSLT就是我们项目中的DSL。网友testhubo在主题:   提问:使用策略模式的烦恼,要实现208个类 中提出的问题,如果用XSLT的话,不用那几十几百的类,只要一个XSLT文件就搞定了。单从可读性和维护性上讲,就比那几十几百个类 要强太多了。而且可以动态修改,动态部署,连J2EE容器都不必重启。

数据、元数据及注解

什么是数据(Data)?大概每一个软件人对此都是再熟悉不过了。比如下面的例子,可能是(静态类型)程序语言中最简单的数据形式了。

java 代码
 
  1. float height = 1.81;  //单位:米  

这样一条简单的语句说了些什么?它无非是引入了一个变量,变量的数值是1.81,变量的物理意义是身高,单位是米,变量的类型是float。这是人人都知道的东西,有什么可说的?别急,貌似简单的事物,未必就那么简单。

为了简化我的表述,我要引进我的数学模型。说数学模型好像挺唬人的,但它其实就是初中代数第一课介绍的数与数轴的概念。上述的语句,可以看成是一种数学描述,它定义了数轴上的一个点。对于抽象的数学问题,我们不必关心这个点(数值/value)的物理意义,但我们的程序是要解决具体问题的,因此我们要强调它的物理意义,所以数轴上标明了身高(米)。这里的要点是:height(身高)是数轴/物理量的标签,而不是数值的标签,它表明数据的物理意义。所以我们可以说它是描述数据的数据,也就是所谓的元数据。不仅物理量的名字是元数据,物理量的单位,物理量的精度,定义域的范围等等也是元数据。

我们现在来看看这个类型--float,它又是什么东西?它告诉我们,身高的定义域是实数域。从这个意义上说,它也是元数据。从人机对话的角度看,这个float也是我们写给机器的注解--annotation,它告诉机器如何处理这个变量。

这个最简单的例子告诉我们,任何有意义的数据,都可以分解为数值(value)和元数据(metadata),元数据是描写数据的数据,它提供数值的Context。以人机对话为目的元数据叫注解 (annotation)。

元数据与数据模型

我们面对的概念和事物,往往不是仅仅用几个孤立的简单变量就可以描述的,通常需要一组互相关联变量来描述。比如,要粗略地描绘一个人体的大致形态,我们可以用身高和体重来描述,比如身高1.81米和体重82.5公斤,这里的身高和体重是想关联的。相应的数学模型是一个由身高和体重构成的二维空间,上面的实例,身高1.81米和体重82.5公斤, 可以记作(1.81, 82.5)。这种有序数组的形式,假定了横轴是身高,纵轴是体重。如果我们将空间转置,横轴是体重,而纵轴是身高,它应该不会引起任何逻辑上和物理上的变化,所以数学上可以不必区分空间和它的转置空间。但是在编程上,任何不必要的假定都是硬码(hard code),而硬码就是程序的硬伤。为了避免这个硬伤,我们必须排除那个序而直接将数值和它意义绑定,记作(身高:1.81,体重:82.5),它和(体重:82.5,身高:1.81)等同。

这样的一个数学/数据模型,在编程中怎样实现?通常有两种实现方法。一种是利用通用数据结构,像散列表(Hash,Hash Table),一种是构造专用数据结构,在OO语言中就是类。两种实现方法,孰优孰劣?可能所有的人都会同意,散列表模型肯定是一个有缺陷的数据模型,那么它的缺陷到底在哪里?在现实中,尽管人们知道散列表是一个有缺陷的模型,却总有人不断地企图用散列表来做数据模型,究竟是哪一点吸引人们这样做?这些是我想要搞清楚的问题。

我们现在把Hash模型和正统的OO模型作一番简单的比较,看看它们究竟有什么异同。先作静态比较,看它们在模型不变(需求不变)的条件下是否具有相同的数据表达能力。
Hash模型:   
java 代码
 
  1. Map body = new HashMap();  
  2. body.put("height"1.82);  
  3. body.put("weight"85.2);  
   
OO模型:   
java 代码
 
  1. class Body  
  2. {  
  3.     float height;  
  4.     float weight;  
  5.     Body(float height, float weight)  
  6.     {  
  7.         this.height = height;  
  8.         this.weight = weight;  
  9.     }  
  10.     ...  
  11. }  
  12. body = new Body(1.8185.2);  

单从数值的角度看,二者的表达能力是等价的。但是我们在前面说过,编程语言中的变量,不仅仅包含了数值的信息,还包含了元数据信息。从元数据的角度来观察,我们就可以看出二者的差别:Hash模型丢失了元数据。比如在上面的Hash模型中读取身高,你在模型中找不到任何有关身高的元数据,它的元数据储存在程序员的大脑里。这是Hash模型的致命弱点。

再看动态比较,我们要观察的是在需求变化时,模型是否需要跟着变,就是观察模型对于需求扰动的稳定性。新的需求是,仅仅用身高和体重来表达人体还不够,我们还要加上性别特征。很容易看出,Hash模型是稳定的,它不需要任何新的数据结构或者修改任何数据结构就可以适应需求的变化。而OO模型必须修改,所以是不稳定的。人们不时地想用Hash模型来代替OO模型,也就是因为这个原因。

比较的结果:Hash模型对需求扰动是稳定的,柔韧的(flexible),通常我们说它是动态的,它的根源在于Hash模型是开放的,自适应的。在Hash模型中,我们可以轻易地引入新的维度来开拓领域空间。但是Hash模型是不完备的,它丢弃了元数据。OO模型是完备的,它把元数据对数据的约束“固化”在类型的定义里。但是,OO模型是封闭的,易碎的(fragile),对于需求扰动的是不稳定的。这是静态类型语言的通病。我不自禁联想到固件(firmware)。静态的类型,就像固件,也许可以叫它固码。

现在的问题是,有没有可能找到一个开放的,动态的数据模型来代替,哪怕是部分代替僵硬易碎的OO模型?在给出我的答案之前,让我们再来考察两个各具特点的数据结构:Tree和ResultSet。

在常用的数据结构中,树是另一个具有开放性质的数据结构。树形结构的一个特点就是它的嵌套性或者叫自相似性,那是一种由简单元素产生复杂结构的特性。HTML/XML就是很好的例子。在设计模式中,树的这种自相似的嵌套叫作复合模式(composite pattern)。我看中的是树的这种以自相似的嵌套来表达复杂结构和关联关系的能力。

在常用的数据结构中,ResultSet也许是唯一的包含有(分离的)元数据的数据结构。ResultSet看上去就像一个滚动的散列表+元数据表。它给我们一种启发,数值和元数据是可以适当分开的,我们可以给上述的Hash模型加上元数据的内容,克服Hash模型丢失元数据的缺点。如果我们走得更远一些,我们会发现数据和元数据不必终身绑定,在实用中元数据是可以延迟加载(lazy load)或者根本不加载的,这给了我们许多应用上的灵活性。同样在实用中,如果我们用遍历列字段(column)的方法而不是直接指名道姓的方法,我们甚至不必知道字段的名字。这是Hash模型的有利于开放性和动态性一个特点。

一个兼顾Hash, Tree,和ResultSet特点的数据结构,就是我要构造的数据结构。首先,它必须具有Hash的开放特性,只有是开放的,才可能是动态的。开放性也导致了数据容器的相似性,一个类型,以不变应万变。其次,它必须具有Tree嵌套特性。嵌套性提供了构造复合结构的能力。树的每一个节点(Node)都是一个散列表,或者滚动的散列表(像ResultSet那样),并且像ResultSet那样携带相应的元数据表。我把这个数据结构命名为数据集(DateSet)。从数学的角度看,一个散列表(名值对集合)就是线性空间中的一个矢量。一个滚动的散列表,就是一个矢量的序列。数据集就是矢量的一个实现。矢量的基本性质,并不因为空间维数的变化而变化,这就是上面说的开放性和相似性。矢量的某个分量,可以是有结构的,是一个子空间,这就是上面说的嵌套。这种与线性代数的比拟,我们在后面还会遇到。

关于数据集的细节和应用,请看《元数据、开放数据模型及动态系统--形而下学篇》。
   发表时间:2007-01-17  
commons DynaBean, DynaClass?
0 请登录后投票
   发表时间:2007-01-17  
借鉴ResultSet的特性,在数据容器中同时携带元数据,思路挺新颖的。
不过对于HashMap和POJO的比较,我觉得就是HashMap缺少了每个成员的类型,而这个类型就是你说的元数据,不知道这是不是你的意思。

听出点意思了,楼主继续啊,等你的下文呢。
0 请登录后投票
   发表时间:2007-01-17  
同感中。
我也写有一个无限级连,按名访问的HASH表类,确实感觉应用范围越来越多。
也另有一套数据类型定义。
正在设计WORKFLOW中,确实在考虑将其合并在一起提供数据模型定义,这样在运行时用户就可以自由地增加数据定义了。

0 请登录后投票
   发表时间:2007-01-17  
楼主提的 "正统的OO模型" 应该说是 静态类型 的OO模型吧. 其实基于Prototype的动态OO模型, Smalltalk系的OO语言基本都没有文中提到的大部分劣势.

Robbin以前在 http://www.iteye.com/post/139771 这篇文里提到的: "Java足够死板!" 这一特性其实也正帮助促成了它作为工业语言的地位 - 这个说法我觉得颇有感触.

In theory, practice and theory are one and the same. In practice, they're not. 
0 请登录后投票
   发表时间:2007-01-18  
引用

借鉴ResultSet的特性,在数据容器中同时携带元数据,思路挺新颖的。
不过对于HashMap和POJO的比较,我觉得就是HashMap缺少了每个成员的类型,而这个类型就是你说的元数据,不知道这是不是你的意思。

是这个意思。但是我想强调的是:仅仅从数据的角度看,带有元数据的普适容器和专有容器(类型)的表达能力是一样的,但是专有容器是固态的。另外,在分布系统中,数据在它的生命周期中,会穿梭往来于系统的各层和各节点之间,对在这个路途当中的多数驿站来说,定义在类型中的操作是没有意义的,所以类型也是没有意义的。无论你用哪一种流行的框架作的Web应用,数据从UI开始一直到数据库,它要改头换面多少次?它的本质改变了吗?我们程序员非得这样伺候它吗?
0 请登录后投票
   发表时间:2007-02-01  
引用
数据从UI开始一直到数据库,它要改头换面多少次?它的本质改变了吗?我们程序员非得这样伺候它吗?


这话让人深思啊~~~~~~~~~~
0 请登录后投票
   发表时间:2007-02-28  
看了开始的发言,感到有点意思。听新颖的。
但看了“<>无论你用哪一种流行的框架作的Web应用,数据从UI开始一直到数据库,它要改头换面多少次?它的本质改变了吗?我们程序员非得这样伺候它吗?”就完全迷糊了。
0 请登录后投票
   发表时间:2007-02-28  
pojo 写道

但是我想强调的是:仅仅从数据的角度看,带有元数据的普适容器和专有容器(类型)的表达能力是一样的,但是专有容器是固态的。另外,在分布系统中,数据在它的生命周期中,会穿梭往来于系统的各层和各节点之间,对在这个路途当中的多数驿站来说,定义在类型中的操作是没有意义的,所以类型也是没有意义的。无论你用哪一种流行的框架作的Web应用,数据从UI开始一直到数据库,它要改头换面多少次?它的本质改变了吗?我们程序员非得这样伺候它吗?


不错,同感,类型本身没有什么意义,所以我也是抛弃了各种O,转而使用基本的Container。
Ref:http://www.iteye.com/topic/51522?page=5
basicbest 写道

OO关注的是行为。往往我们觉得麻烦,或者困扰的都在PO,VO等无有效行为的对象上。这些对象只是每个层里面自己使用的中间产物,所以,我觉得VO什么的可以不用,直接用data container,在不同层进行传递数据的时候反而更加方便。
另外,我发现很多时候用Object不用data container的理由是可以取名字。有趣的现象。


似乎JavaEye可以看到几个本质上一样的实现了。
我这里综合使用其他技术后实践的结果是,效率由大约200LOC每人日上升到约550LOC每人日。

0 请登录后投票
   发表时间:2007-03-09  
今天在Google搜索时发现了这个帖子,发现我研究的东西和你讲的东西本质上有共同之处。可以参看www.xworker.org。

我在XWorker中定义的数据对象正好也是一个兼顾Hash, Tree,和ResultSet特点的数据结构,它具有你所说的那些特征。

XWorker中定义的数据对象也引入了O的概念,它可以具有行为,另外对于元数据和结构的概念我们也进行了泛化,即任何一个数据都可以非本身之外的任何数据做为自己的元数据或结构。

算法的设计是取决于数据的逻辑结构的,OO是固定结构所以算法固定,算法固定程序的功能也就固定了,而Hash里保存的内容是非固定的所以内容的结构也不固定的,所以如果能够把Hash里的内容的结构动态绑定,那么就有可能构造出动态的系统了。

然而算法总是要依赖结构的,也就是系统已有的算法(程序)必须是建立在已知的数据结构上的,通过结构编写算法是很独立的事,这时如果我们把结构和Hash关联在一起,那么我们就可以灵活的动态配置系统了。

按照这样的思路走下去一定会需要一个兼顾Hash, Tree,和ResultSet特点的数据结构,因为Hash带来动态、Tree扩展了结构复杂性、ResultSet代表结构(算法)绑定。
1 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics