构建面向对象的应用软件系统框架 第3章

 

第二部分 应用服务层的设计


第3章          数据和对象

31数据的形态

在应用软件系统中,首先要处理的对一个对象就是数据。应用软件系统,主要目标就是采集数据、处理数据、分析数据、察看数据。对于软件,诚如有一句名言所说:“软件,就是数据结构加算法”。

在软件中,数据有多种表现形态。

首先,在程序中,数据总是以某种数据结构的方式被表示出来,这种表示,通常被编译成二进制文件存在于硬盘上,并且在运行时刻在内存中被实例化。

这种数据结构有多种表达的方式,简单的情况下,他可能只是一个数字,或者一个字符串,用某个变量来描述。例如,为了表述某种商品的价格,可能使用如下的申明来表述这个数据:

 

double price = 100 ;

 

现实中要处理的数据总是比较复杂的,为了描述一个完整的信息,通常要组合多项简单的数据,例如,为了描述某种商品的信息,通常需要描述他的名称、价格、重量等。在传统的C语言中,可以使用结构来描述:

 

struct product

{

    char* name;

    double price;

    double weight;

}

 

在面向对象的语言里,类似的数据结构,可以使用类来表述。上面的代码可以用Java语言表述如下:

 

public class Product

{

    public String name;

    public double price;

    public double weight;

}

 

可以看出来,实际上两者的差别是非常小的。

对于更加复杂的数据结构,一个类可能引用到其它的类,例如,上面的Product,可能有一个Size属性,而这个Size属性,也有heightwidth构成,那么,整体的数据结构就可以描述如下:

 

public class Product

{

    public String name;

    public double price;

    public double weight;

    public Size size;

}

public class Size

{

    public int height;

    public int width;

}

 

数据的另外一种表现形态,就是永久化保存的形态。上面描述的数据的形态,是一种“瞬时”的数据,只有在程序运行的时候才存在于内存中,一旦程序结束,或者数据处理结束,数据就从内存中清楚。在很多情况下,需要把处理的数据保存到磁盘上,这时候,数据就进入永久化保存状态。

可以有多种保存数据的格式。可以把数据保存为普通文本文件存放在磁盘上,或者,也可把数据保存在XML文件中。在JavaC#中,也都提供了这样一种能力,就是可以把对象序列化后保存到磁盘上,然后,在需要的时候,可以反序列化成对象。

虽然有多种持久化保存数据的方案,然而其中使用关系型数据库来保存数据,无疑是最常用的办法和最可靠的办法。这就引伸出一个在面向对象的系统设计中的常见问题:对象/关系型映射(O/R Mapping)

在考虑O/R Mapping的时候,有两个概念是经常会接触的,那就是VOPO

所谓的VO,就是Value Object,这种对象,只包含了对象的数据,而没有状态,或者说,处于瞬时状态。VO可以用来在层之间传递数据

所谓PO,就是Persistent Object,就是持久化保存的对象,这种对象,一般是有状态的。O/R Mapping框架需要根据PO的状态,来执行相应的同数据库的交互。关于PO的状态,我们在后面再讨论

 

32对象/关系型映射

对象关系型映射,最核心的要完成两个功能:对象和关系型之间的映射规则,以及两者之间的相互转换。

除了这两个基本的功能,一般的O/R Mapping产品还会加上一些额外的特性和功能,以加强产品的功能,为软件开发提供更多的方便和提高性能。一些常见的功能,例如缓存。

现在有一些典型的O/R Mapping框架可以参考和使用,比较著名的有EJB中的Entity BeanJDOHibernate等,这些方案都是基于Java的。在Microsoft.Net平台下,相对来说可供选择的方案比较少,其中有一个开放源代码的方案Websharp,可以从 www.websharp.org 下载。

我们在实际开发中,可以选择使用已有的解决方案和产品,也可以自己设计自己的O/R Mapping框架。当然,无论采用何种方式,都需要我们对O/R Mapping的基本原理和方法有一个基本的了解。

在支持OO的语言中,继承是语言的基本特征。O/R Mapping的框架,也需要对继承做出相应的支持。一般说来,有三种继承模式:ONE_INHERITANCE_TREE_ONE_TABLEONE_INHERITANCE_PATH_ONE_TABLEONE_CLASS_ONE_TABLE

Ø       ONE_INHERITANCE_TREE_ONE_TABLE

一个继承树映射到一个表,即将具有相同父类的所有类都映射到一个数据库表中,这些类属性映射的集合组成这个表的列,在这种模式下,只需要对最底层的类进行映射。

如下面一个类结构:

 

 

在这个类结构中,父类Parent有两个子类Child1Child2,父类有属性Property1Property2Child1新增了一个属性Property3,而Child2新增了另外一个属性Property4,当采用ONE_INHERITANCE_TREE_ONE_TABLE映射模式的时候,数据库中只有一张表,结构如下:

 

 

其中,Column1Column2字段分别对应Parent类的Property1Property2属性,Column3字段对应Child1Property3属性,而Column4字段对应Child2Property4属性。Column3对于Child2Column4对于Child1是没有意义的。

采用这种映射模式,优点是比较简单,缺点是数据的冗余比较大。这个表要容纳所有的子类的字段,很多字段只是对某个类有意义,而对于其他类则没有意义,是纯粹多余的,并且,在继承树中新增一个继承节点的时候,往往导致表的字段的重新设计,这是一件非常麻烦的事情。

 

Ø        ONE_INHERITANCE_PATH_ONE_TABLE

一个继承路径映射到一个表,即将一个类映射到一个数据库表中,这个类的所有属性(包括从父类继承来的)映射的集合组成这个表的列。

在这种模式下,对于上面的类结构,数据库的结构就是:

 

 

其中,表Child1Child2分别对应于类Child1Child2

这种模式是非常常用的,也没有数据冗余,缺点是在搜索的时候比较麻烦。例如,当我要搜索符合某个条件的Parent对象的时候,需要同时搜索Child1Child2表,并且,当在继承树中新增一个继承节点的时候,需要新增一个表,搜索的范围也必须扩大,原来的程序可能不得不重新设计。

 

Ø        ONE_CLASS_ONE_TABLE

一个类映射到一个表,即将每个类映射到对应的一个表,这个类的属性(不包括从父类继承来的非主键属性)映射组成这个表的列,在这种模式下,具有继承关系的类被映射到不同的表中,表之间通过主键关联。

在这种模式下,对于上面的类结构,数据库的结构就是:

 

 

Parent作为Child1Child2的父表,父子表之间通过主键Colimn1关联。

这种模式是非常容易理解的,因为和类图非常相像,缺点是在查询的时候,由于设计到表的关联,SQL语句会比较复杂,效率也会比较低。这个情况当继承数的深度增加的时候,会体现的比较明显。

如果一个类没有子类和父类,那么采用三种模式中的哪一种都是一样的效果。

 

 

33对象的状态

为了很好的控制对象,以及在同后台存储交互时的行为,通常O/R Mapping框架需要维护PO对象的状态。在不同的框架中,对对象状态的定义不尽相同,不过,也都有一些共同点,某些方面可能只是名称的不同。通常的O/R Mapping框架都需要以各种方式来直接或间接的处理PO的这些状态。下面列出的一些状态是一些基本的,比较共通的一些状态

Ø         Transient

Ø         Persistent-new

Ø         Persistent-dirty

Ø         Persistent-clean

Ø         Persistent-deleted

Transient

在这种状态下,对象处于一种“瞬时”的状态,还没有同任何数据库的数据相关联,对象也不受O/R Mapping框架的运行时控制,对象的表现就是一个普通的类的实例。

比较典型的,在JDO里面,对于Transient的状态的说明如下:

JDO instances created by using a developer-written or compiler-generated constructor that do not involve the persistence environment behave exactly like instances of the unenhanced class.

There is no JDO identity associated with a transient instance.

There is no intermediation to support fetching or storing values for fields. There is no support for demarcation of transaction boundaries. Indeed, there is no transactional behavior of these instances, unless they are referenced by transactional instances at commit time.

When a persistent instance is committed to the datastore, instances referenced by persistent fields of the flushed instance become persistent. This behavior propagates to all instances in the closure of instances through persistent fields. This behavior is called persistence by reachability.

No methods of transient instances throw exceptions except those defined by the class developer.

A transient instance transitions to persistent-new if it is the parameter of makePersistent, or if it is referenced by a persistent field of a persistent instance when that instance is committed or made persistent.

意思是说,

 Persistent-new

这种状态,表示一个新的可持久化对象,该对象还没有被保存到存储介质中。在这种状态下,当事务结束被提交的时候,框架会执行一个插入的操作,将对象保存到存储设备中。

 

Persistent-dirty

这种状态,表示一个对象是可持久化的对象,已经对应于数据库中的某表记录,但是,在程序中,该对象已经被编辑过,与数据库中的数据并不同步。在这种情况下,当事务被被提交的时候,框架会执行一个更新的操作,将对象和数据库同步。

 

Persistent-clean

这种状态,表示一个对象是可持久化的对象,并且与数据库中的数据是同步的。

 

Persistent-deleted

这种状态,表示一个对象是可持久化的对象,并且该对象已经被删除。在这种情况下,当事务被被提交的时候,框架会执行一个删除的操作,将数据从数据库中删除。

posted on 2005-11-29 14:25  孙策  阅读(1037)  评论(2编辑  收藏  举报