自java,c#等语言出道以来﹐OO着实风光了一把﹐一时间"祖国江山一片O"。
不得不说﹐OO思想确实较之之前的软件设计方法给人耳目一新的感觉﹐然而要说放之四海而皆O﹐恐怕就值得商榷了。至少在以处理数据为主的业务系统(Business
Application或Oriented-Data Application)中﹐OO并不能代替数据库(这里指关系数据库)完成业务系统中最核心的业务逻辑定义和存储。
OO语言﹐较之过程化vbscript的最多只能封装一些函数﹐而在抽象﹐重用﹐变化等方面的无可奈何﹐OO以其解决这些问题的简洁和完美赢得了系统设计师的青睐﹐也就是在那时候开始﹐就忘了业务系统中另一个与过程化语言起完全不同作用的数据库。
代之而起的”数据库已死”﹐”OO与数据库存在天然阻抗”﹐”对象必将代替数据库”等言论漫天飞舞﹐一时间大有不置”数据库”于死地誓不罢休的感觉。
可笑的是﹐虽然有着这么”道貌岸然”的口号﹐但是OO们依然离不开数据库﹐为了掩饰其尴尬处境﹐只好再弄个O/R Mapping﹐并宣称﹕系统中的OO对象﹐可以通过ORM转成关系﹐然而再存到数据库﹐数据库在我的系统中只是一种持久化方式﹐让我的OO可以再次”复活”﹐除此之外﹐别无他意云云。
再看看剥离了数据库而独立存在的OO们是如何来表示”业务逻辑”的吧?
首先要进行对象设计﹐提取领域模型(各种叫法﹐随便)﹐虽然OO有其显著特征和丰富手段来进行封装﹐抽象以应对变化。但是对于OO中对象之间的关系却毫无办法﹐只能简单的通过属性来描述。没有标准的结果就是自由设计﹐并自我安慰﹕这就是OO﹐因为OO是模拟现实的呀﹐你看我要加一个属性时﹐多方便﹐几行代码搞定。看得出来﹐OO之间的关系是一种网状关系的﹐以数据结构的朮语来说﹐就是图(较之数据库关系模型的集合理论﹐图要复杂不知多少)。
然而自由就意味着要付出代价﹐因为OO中的对象是需要持久的﹐除非CPU能够直接访问硬盘﹐或者内存在掉电之后数据可以继续存在。OO还是不得不借助于数据库帮其持久(不要提对象数据库﹐除非有新理论出来﹐对象数据库应该是一种网状数据库﹐事实已证明﹐关系数据库在描述的简洁性﹐查询的方便性﹐性能的优越性等方面都优于网状数据库)﹐因此OO也必须转成关系模型存储到数据库之中﹐各种O/R
Mapping也随之而来。
何苦呢?本来就是同一个东西﹐为什么偏要转来转去﹐多麻烦。
举个最常见的例子﹕订单系统可能有这样两个对象﹕订单和客户﹐为了表示下单的人是谁﹐就在订单对象里加了一个属性﹕下单者。而某些时候可能又需要知道哪位客户下了哪些订单﹐因此又在客户类里加上了”订单列表”属性﹐表示这个客户下过的订单。
明眼人已经看出﹐OO表示对象之间关系的繁琐。同一个关系﹐需要两次或者多次描述。
而数据库的”关系模型”则简单得多﹕
实体﹕订单﹐客户
关系﹕下单(订单号﹐客户ID)
要知道下单者﹐通过”下单”关系﹐要知道客户的订单﹐也通过”下单”关系。
勿庸置疑﹐关系模型在描述实体(或对象)之间关系的简洁和有效性。
这就是伟大的数据库的关系模型﹐比之对象的”自由”的网状模型﹐其优点自不必说。
如何解决关系与对象的不匹配了﹐其实很简单﹐以关系模型理论来指导”对象”设计。
如上述例子需要设计三个对象(两个实体﹐一个关系)﹕订单和客户﹐”下单”对象(表示订单和客户的关系)﹐一个下单对象有如下属性(下单者和订单两个属性﹐其属性类型自然是两个对象了)。
/// <summary>
/// 产品
/// </summary>
class Product
{
public string
name; //产品名称
public decimal
price; //单价
}
/// <summary>
/// 订单
/// </summary>
class Order
{
public string
no; //订单号
public DateTime
date; //订单日期
public Product
product; //产品
public int
quantity; //数量
public decimal
amount{get{return
product.price * quantity;}} //金额
}
/// <summary>
/// 客户
/// </summary>
class Customer
{
public string
name; //姓名
public string
address; //地址
public string
email; //Email
}
/// <summary>
/// 下单关系
/// </summary>
class OrderCustomerRelation
{
Order
order; //订单
Customer
customer; //下单者
}
/// <summary>
/// 订单关系集合
/// </summary>
class OrderCustomerRelList
{
public static OrderCustomerRelList Instance
{
get { return new OrderCustomerRelList();
} //完成实例抓取
}
public List<OrderCustomerRelation> Lists;
}
通过”下单”这个对象来描述订单的客户。为了方便﹐你可以在订单类中增加一个”获取客户”方法﹐这个方法通过”下单”对象集合来实现。而你在客户类中增加的”订单列表”属性﹐也是通过查询”下单”对象集合来完成。
/// <summary>
/// 订单
/// </summary>
class Order
{
public string
no; //订单号
public DateTime
date; //订单日期
public Product
product; //产品
public int
quantity; //数量
public decimal amount
{ get { return
product.price * quantity; } } //金额
public Customer customer
{
get
{
OrderCustomerRelList
list = OrderCustomerRelList.Instance;
foreach
(OrderCustomerRelation rel in list.Lists)
{
if
(rel.order.Equals(this))
return customer;
}
return
null;
//更酷的查询表达式实现
//return
list.Lists.First(rel => rel.order.Equals(this)).customer;
}
}
}
/// <summary>
/// 客户
/// </summary>
class Customer
{
public string
name; //姓名
public string
address; //地址
public string
email; //Email
/// <summary>
/// 现实一些﹐应该设计一个类封装List<Order>﹐以便其Add﹐Remove方法可以实现Relation的增和删
/// 当然可以根据实际需要﹐决定在Order的Set_customer中完成下单逻辑﹐就没必要在这个类的orders中完成下单逻辑
/// </summary>
public List<Order>
orders
{
get
{
List<Order> ret = new
List<Order>();
OrderCustomerRelList
list = OrderCustomerRelList.Instance;
foreach
(OrderCustomerRelation rel in list.Lists)
{
if
(rel.customer.Equals(this))
ret.Add(rel.order);
}
return
ret;
//更酷的linq实现
//var
tmp = from rel in list.Lists where rel.customer.Equals(this) select rel.order;
//return
tmp.ToList<Order>();
}
}
}
也许这样设计你认为Client需要操作”关系”很不自然﹐其实没关系﹐做一层封装即可﹕让订单类的”客户”属性可以set,在set中再完成”关系”的建立。
/// <summary>
/// 订单
/// </summary>
class Order
{
public string
no; //订单号
public DateTime
date; //订单日期
public Product product;
//产品
public int
quantity; //数量
public decimal amount
{ get { return
product.price * quantity; } } //金额
public Customer
customer
{
get
{
OrderCustomerRelList list = OrderCustomerRelList.Instance;
foreach (OrderCustomerRelation
rel in list.Lists)
{
if (rel.order.Equals(this))
return customer;
}
return null;
//更酷的查询表达式实现
//return list.Lists.First(rel =>
rel.order.Equals(this)).customer;
}
set
{
OrderCustomerRelList
list = OrderCustomerRelList.Instance;
var
relation = list.Lists.First(rel => rel.order.Equals(this));
if
(relation != null)
relation.customer = value;
else
{
relation = new OrderCustomerRelation();
relation.customer = value;
relation.order = this;
list.Lists.Add(relation);
}
}
}
}
再进一步﹐你可以设计一个O/R Mapping框架﹐当然也可以选择现有ORM方案﹐来实现对象与实体﹐关系与关系的完全对应﹐最后完全”OO”
随着.net 3.5 sp1的发布﹐这个框架MS已帮你实现了﹐这就是ado.net entity framework(注﹕不是广告﹐我也不是MS的托﹕)
简单谈一下我对entity framework的看法﹕虽然第一眼看去﹐似乎也是个O/R
Mapping﹐但是与以往ORM不同的是﹐它提出了一种理论﹐以关系模型来设计对象﹐然后再进行Mapping﹐这比只有工具﹐而无思想的其它ORM们更进了一步。至于其e
sql,linq to entity更是让操作起这个ORM来随心所欲~
”本是同根生﹐相煎何太急”﹐OO与数据库就像人的五官一样﹐各司其职﹐各负其责﹐OO负责设计系统架构﹐业务流程﹐实现抽象﹐封装变化。而数据库则负责业务逻辑的表示﹐存储和查询。通过关系模型﹐架起OO与数据库的勾通的有效桥梁。
和为贵﹗
最后祝园子里的TX们中秋快乐﹐家好月园﹕)