信息唯一性原则

本人偶然间想到,Google了半天也没有找到相似的东西。在此抛砖引玉,望各位不吝赐教。

信息唯一性原则是解耦合与促进一致性的数据结构设计方法。一个信息只出现一次,其他地方只是引用。

比如客户买书《xxx》这一业务,需要进行两次相关操作,即书店将《xxx》这种书的数量减1,客户账户中《xxx》这种书的数量加1。需要注意,这里是《xxx》,是指一个种类,后面建模时提到的《xxx》是具体的卖出的那一本。

这个过程缺一不可,如果只让书店减1,忘记了客户加1,则客户买不到书;反之,则是卖出的数量会超过书店的存货。

在数据库领域,采用ACID方式来解决,就是事务。将书店减1和客户加1看作一个整体,其中一个操作失败,就算这两个操作整体失败。这样可以保证一致性。

你会发现,这一业务中,数据需要从两处地方配合起来说明买卖的数量。这就产生了一个耦合。但通常我们认为这种耦合是天经地义的。

不过,耦合就意味着大量验证,保持信息唯一性原则或许可以减轻验证的负担。

经过分析,我们发现,这里的加1减1实际上是买卖的那本《xxx》书的数量属性。上面在处理业务时将卖出的那本《xxx》书忽略掉了,只将他其中的一个属性保存在了变量中。

在整个买卖过程中,《xxx》书的交换,其本质是卖出那本《xxx》书的所有人发生了变化。从书店变为了客户。

因此,在信息唯一性原则下,首先要建立卖出《xxx》书的实体,然后定义其数量和所有人。买卖的过程,就是所有人属性发生变化了的过程。

这样,书店的《xxx》这种书的总数,和客户的《xxx》这种书的总数,就从直接影响业务的数据,变成了一个随时可以重算的统计结果。他们共同受所有《xxx》书实体的所有人属性和数量属性的影响。

这样,原来需要ACID事务支持的买卖业务,就变成了一个普通的业务。

同样的道理,客户付钱也是一样。付款可以将人民币创建一个实体,其数量就是《xxx》本书的卖出价格(可能会涉及打折等因素,与标价不一致,是每一本卖出《xxx》书的特有属性)。

书的交换和钱的交换是同时发生的,但在信息唯一性原则下并不需要ACID支持。如果书的所有人发生了变化,而钱忘记了。则在统计的过程中,应付账款与已付账款就有一个差额,卖家很容易判断和发现。此时客户可以再次付款,直到付款成功。反之,钱付了,而书忘了变换所有人,则也有差额,客户的付款就变为了预付款。客户可以用这个款项再去买书,直到买到为止。

信息唯一性原则的本质在于,实事求是的将客观世界发生的事情告诉计算机,记录下每一个与业务相关的现象。这些现象作为计算的基础。如果计算机忘了处理一些现象,那么说明其认识的世界是残缺的,但残缺的世界也是可解释和理解的。人类能够修复其认识上的缺陷,从而保证其正确性。

很不错。

那么是否需要一个《xxx》书实体买卖历史记录?因为书实体总是反映的是最近一次买卖行为发生后的结果,而且这个信息只能出现一次。


[该贴被banq于2010-05-11 15:20修改过]

2010年05月11日 15:20 "banq"的内容
那么是否需要一个《xxx》书实体买卖历史记录?因为书实体总是反映的是最近一次买卖行为发生后的结果,而且这个信息只能出现一次。 ...

信息唯一性原则的实质是告诉计算机事实和细节,可以说是与现实世界的对应。
如果要管理的对象中有一本书,那么就实例化这本书,再将这本书的相关属性持久化。包括序列号、数量、所有人、价格等。不用专门定义一个交易历史记录,这本书本身就是拥有历史的。
如果将来客户退还这本书,我们还能够通过序列号或者所有人找到系统里的书实体。
计算机里有一个现实世界,与要管理的现实世界的事物一一对应。
我觉得有些时候,我们遇到的设计难题,往往是告诉了计算机不真实的世界造成的。我们告诉计算机的,是经过我们加工过的世界,经过我们数学抽象后的世界。比如将一本有各种属性的书,简化为了1。而1要还原为1本书,则需要我们在程序中二次定义。
这往往是造成困扰的根源,因为不管是别人还是自己,超过一定时间,可能就不能准确的将抽象的内容还原为实际的内容了。
[该贴被redorange于2010-05-11 17:03修改过]

2010年05月11日 16:39 "redorange"的内容
我们告诉计算机的,是经过我们加工过的世界,经过我们数学抽象后的世界。 ...

这个很认同,楼主这种原则其实就OO思维,软件和需求讲究的是一对一对应,这种对应是自然的对应,而不是经过抽象变形的对应,所以,抽象的方式是很讲究的。

我认为抽象方式有象数两种,或者说有数学模型抽象和类似绘画写生的直观式哲学式抽象。很显然,如果客观事物本身是物理等自然科学领域,天生拥有数学模型在其中,用前者是没有问题,但是实际上这只是一个方面,我们现在用软件已经不只是解决自然科学计算,而是和人有关的一切都力争用软件解决,而自然科学不可能解决人周围一切事物,暂时没有发达到那个程度,因此,更多时候我们需要后者,也就是“象”之抽象,从事物特征来抽象它,而非精确的数学模型。见我这个贴象数之认识

使用数学方式抽象的一个问题是:数学太细节,属于点,而客观事物属于面,一个面要很多点组成,最后,很难有主要和次要之分,要么就再抽象,浓缩为一个主要的,比如楼主的举例,把书浓缩为数字1,问题又来了,细节被完全隐藏,反而不够全面,不够丰富。而且这种用数学来抽象过程需要数学经验和背景,是有门槛的,而现在数学并没有解决世界一切,所以,数学也有不够用的时候。这两个问题足以影响客观事物和计算机世界模型之间对应。

所以,使用主要和次要以及主要和全面结合的 有继承和实现层次的哲学直观方式来描述客观事物,无疑能做到拷贝不走样。

楼主的信息唯一性原则和DDD的实体唯一标识特性有些相似,只有一个信息,改变的是引用,这个概念又和DDD的值对象有些类似。另外也和对象职责中的“信息拥有者”模型相似:
当一个对象是信息拥有者,它的职责是知道这些信息,不应该期望和其他对象协作获得信息。

有时信息拥有者有职责将他们数据持久化。
信息是否被缓存,如果修改如何更新,这是如何协作的?

[该贴被banq于2010-05-11 17:13修改过]
[该贴被banq于2010-05-11 17:14修改过]
[该贴被banq于2010-05-11 17:17修改过]

可能有点语境上的差异,我正在弄的一个小玩意儿深得banq的影响。
理论上讲,这个小玩意儿有点像JavaScript的对象持久层。数据库中的对象可以像前台JS对象那样随意增加删除属性。
这样做的结果是:
1、JS对象可以持久化,随时存取,编程者不用再去设计数据库。
2、ASP、PHP、JSP等后台语言的使用机会将大为减少,因为后台实际上就只是一个JS对象持久化工具。

2010年05月11日 16:39 "redorange"的内容
我觉得有些时候,我们遇到的设计难题,往往是告诉了计算机不真实的世界造成的。 ...
我们看错了世界,却反过来说世界欺骗了我们。(泰戈尔)O(∩_∩)O~ 艺术的本质果然是出奇的统一

关键是你这个想法怎么实现啊?

一本<<xxx>>的书, 一条记录, N本N条<<xxx>>的记录?
不然你那<<xxx>>的书就只能卖给一个所有者了.
那你的唯一性也太唯一了吧.
可能我理解错了. 望解答.
[该贴被cray于2010-05-27 16:57修改过]

2010年05月27日 16:54 "cray"的内容
一本>的书, 一条记录, N本N条>的记录?
不然你那>的书就只能卖给一个所有者了. ...

你的理解很正确。
比如书店有5本《xxx》,卖给甲1本,卖给乙2本,还剩2本。
这样就从原来的一个对象的记录,变为了三个对象的记录。
当乙退还1本时,原来的数量2,就变为了数量1。
如果书店对退回书籍有特殊处理方式的话,退回的那本就保持为独立对象。此时就变为了四个对象的记录。
最后书店店长认为退回的书可以视同为未卖出的书,就与书店剩下的2本合并为一个对象,数量为3。这时就变为了三个对象的记录。

注意,这里是三个对象的记录,不是三条记录。一个对象也许需要很多条记录来描述。

这实际上告诉计算机的是这个世界的变化过程,是一系列的未经特别处理的现象。至于从这些现象中得出什么结论,比如卖出几本,剩余几本等,则是根据现象计算的结果。

其实简单点说, 你只是在处理一个Transaction History的问题,

我只需要在卖东西的时候记录在一个叫Sell Transaction的表就行了,
卖1本<<xxx>>给甲时, 创建一条记录在表里面, quantity为-1,
卖3本<<xxx>>给乙时, 同样有一条记录, quantity为-3,
但乙退回一本的时候, 创建记录, quantity为+1

计算结果: -1 + -3 + 1 = -3 卖出去了3本. 而且你还可以加一个remark的字段, 说明书为什么退还的, 记录原因.

那样你一本的所有销售跟退还情况都可以track到, 你也不需要改书里面的总量,

要查剩下多少书, 就用(书总量+transaction表里面这个书的quantity(因为quantity是负数)), 这样做时候会更好?

这样处理就不用把对象弄得那么复杂.

个人见解而已. 希望多多指教. banq大哥也说说嘛.

2010年05月27日 19:50 "cray"的内容
其实简单点说, 你只是在处理一个Transaction History的问题,

我只需要在卖东西的时候记录在一个叫Sell Transaction的表就行了, ...

你对买书退书环节的业务理解的很正确,也能从数据表设计中来解决这个问题。
这个设计体现了以下几个特点:
1、设计者先要充分理解这个业务的数据关系,然后通过数据表的设计来让计算机处理。
2、设计者的理解仅仅是当时当地的业务,就事论事的。这对计算机来说相当直接,这很好,很清晰。但没有就将来可能的变化留下设计余地。
3、解决问题是通过一种类似于数学参数和公式计算的方法,这也是计算机的强项,也没有问题。但除了这个问题外,这个解决问题的方法只能用在有限的领域。

实际上,我们这里所谈的并不是就某一个具体业务而言的数据表设计和计算方法,而是一种让计算机描述世界、理解世界的方法,或者叫针对客观世界的数学建模。
诚然,这种方法看起来很冗余、浪费,但这是以客观世界为依据,可以获得最大的灵活性与简便性。对于大型的程序或应用是很有必要的。
因此,这个方法是有适用范围的。如果解决相对固定、简单的问题,就不必要这么“麻烦”了。
但是,核心的问题在于,我们在编程序的时候,总是想解决所有的问题,这样程序才有优势。原来的小程序会慢慢变为大程序。当你的程序有可能变大时,最好在一开始就采用OO方法。否则,维护起来会吃亏。
当然,如果老板、领导、客户愿意吃亏,或者放任你吃亏,这就是迫害与反迫害的问题了。

业务跟实体分开, 这样叫不OO

这样的设计会怎么难维护 , 举个例子? 这样设计会冗余、浪费, 这个就更不解了.
假如要生成report, 比你的"唯一性"容易吧.
假如你处理有个10000数量的东西, 依照你的做法应该怎么做呢?

我觉得你考虑的方面太过于偏执于数学领域了. 一套好的系统, 需要有一套好的业务流程. 而且, 我没觉得我的设计不OO.

你的对象处理的时候, 还要处理业务, 看看这个人之前有没有买过, 买过就在之前的记录数量+1, 没买过就创建多一条记录, 这样叫做OO???
[该贴被cray于2010-05-28 09:17修改过]

这是你的系统没有有效建模,买书的责任没有对象承担,导致被打散在整个系统里。

买书是一个动作或者说是一个过程,应该统一在一个地方,而你所说的,“+1”与“-1”分散在世界各地。

如果这个系统只有一个类处理买卖书,那么+1与-1只有一个地方出现,怎么可能被忘。

虽然觉得OO的设计比较好,但是不知怎么实现才最好,关键是数据库方面如何实现对象的存储,希望banq大哥指点迷津!

其实这个唯一性,跟一致性是同理的。数据库的一致性通常是通过事务、存储过程、触发器等。其实准确说是逻辑一致性,也就是说必须符合该逻辑映射的事实。

数据上的一致性体现在+1-1,其实这是“描述”了拥有者转移的事实,或者说这是“事实的另外一种表现”。这方式相当于旁打侧敲,这种不直接的方式带来了麻烦(是指描述逻辑时带来的麻烦)。可以说这是数据过程性与事实逻辑性的差别。