从Swift语言看ORM的定位错误

Swift提供了数据结构struct和类Class两种, 数据结构和类一样支持行为,包括方法和初始化,数据结构和类的重要区别是:数据结构按复制方式传递,当你将一个数据结构传递给另外一个变量时,实际是复制了一份,但是类生成的对象进行传递时是按引用传递,传递的是那个对象的地址(当然地址值也是复制)。

Swift Struct代码如下:


struct Card {
 var rank: Rank
 var suit: Suit
 func simpleDescription() -> String {
  return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
 }
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()

Swift语言显式地区分了数据结构struct和类Class,而在我们传统语言中,包括Java C# Pthyon Ruby等基本都只有类Class一种对象模型。这就产生了问题。

这个问题我在从数据结构+算法分析ORM的末日已经提到,数据表是一种数据结构,ORM框架是将数据结构转为对象,其实这个对象是贫血对象,也就是只有属性或setter/getter而没有其他业务行为的对象,如:


class Order{
String orderId;
Double money;
..
}

我们使用ORM,是将数据表order中的记录拷贝到这个贫血对象Order中。实际上这个失血对象Order也是一种数据结构,只不过因为语言本身不支持数据结构,所以,我们借用类型Class的对象模型来作为数据结构对象,而且是对Class进行肢解的一种黑客方式(去除了其鲜明特征:行为)。

现在有了Swift的struct,我们实际应该是将数据表order的数值复制到struct中,而且struct是按值传递的,如果你修改了原来的struct,其他复制的不会被改变。数据表实际是一个远程的struct。

从以上分析看出,目前ORM框架包括JPA Hibernate Django Rails的ActiveRecord实际都定位错误了,将类型Class对象模型作为数据结构使用。

以前我们指出ORM末日主要是从抽象泄漏和性能角度提出问题,但是这些问题一般只是在项目复杂时才显露出来,小项目反而体现ORM的快速的特点,但是我们从Swift这个新语言上看到,ORM方向搞错了,应该是structRM才对啊。

ORM在未来面临土崩瓦解,这个词语会成为历史名词。

[该贴被banq于2014-06-07 09:05修改过]

C# 支持struct啊。。。而且我一直都是把ORM映射到结构体上的

只有把数据实体(结构体)当成领域对象用的时候才会出现贫血、充血的问题,其实ORM并不需要负责这些。将数据实体(结构体)转换成领域对象可以用automapper之类的内存映射框架。

贫血模型有一个对象行为setter/getter,c已经将其简化,Java中将这两个方法和属性Attribute合称为property,我的观点是,这个 Property其实应该是属于数据结构Strut的,而不是对象,面向对象的基本原则是封装性,不要暴露细节,setter/getter方法很容易将内部属性字段暴露给外界,外界一旦在某个地方修改了这个属性字段,也就是可变的状态,意味着对象自己已经失去对其内部细节的封装和控制了。

DDD强调聚合跟实体通过行为守护自己的可变状态,因此是反对直接将状态通过setter/getter直接暴露的,在这个意义上看,语言的Class类型没有必要缺省提供setter/getter的简写,这点上C和Swift还需要改进,而数据结构可以有 setter/getter简写。

这就更容易将设计意图贯彻到语言设计上。

我再想罗嗦几句,语言是一个中介接口,一面与操作系统 CPU底层打交道,一面要方便实现抽象软件设计,两个方面都要兼顾。这与云计算Paas设计一样,云计算也是一个中介接口,一方面与底层有关,一方面也要方便业务设计实现。如果我们只注重底层,只认为运维是云计算的核心,就失偏颇了,大概也是身在庐山不识真面貌,另外一个极端是Ruby,忽视了底层性能,只注重OOP。

下图横坐标是开发者开发效率,符合当前设计思想的开发方式应是最有效率的;纵坐标是性能,要达到两者平衡应该是最好。


[该贴被banq于2014-06-08 16:05修改过]

有两块映射需求:一个是界面UI(Form),一个是持久化,只需要数据及映射规则,不需要行为。MVC和ORM是解决这两大命题的途径。那么这些问题,是否在语言设计之初就提供更合理的语法支持呢?祈求设计一种语言,把这些值不可变和对象可变状态的问题都在语言级别上解决掉吧。

额外说一下,造成函数、逻辑语言效率地下的原因是冯诺依曼体系。也就是在冯诺依曼体系下,指令系列语言都占有大量优势。

数据结构与对象问题,一直都是不可变与可变问题。正因为承认了可变性,才有对象等思想诞生。而且哪怕C系列语言中,数据结构也没有定位为不可变数据,变量就是承认了变,只要变量存在就无法摆脱复杂性。你让他不变,只是看起来像个样,实际上完全是两回事。优化手段,投射到硬件上指令序列,完全不一样。

所以在冯诺依曼体系下,就乖乖接受指令式的统治吧。以我现在的思想和知识,我认为冯诺依曼体系虽是成功,但是是一条歪路——单纯逻辑投射到逻辑门电路上是异常的直接和简单,但引入指令却使一切变得复杂。

其实觉得,提供get/set没有什么问题,特别是C中的自动属性已经做得很好了,聚合根内部状态虽然都要通过产生相应事件来转换,但是外部也需要获知当前状态,设置一个private set即可。

而C#中提供的结构也是能非常好满足数据载体的要求,从语言角度上说,可惜在CLR上结构传递时由于是值复制,性能比较低下,官方并不建议频繁使用。