不变性是统领业务分析和高性能架构重要法门,通过业务上不变性分析设计,可以实现代码运行的并发高性能和高扩展性。
不可变性是一种抽象,它并不在自然界中存在,世界是可变的,持续不断变化。所以数据结构是可变的,他们是描述真实世界某段时间内的状态。而状态经常会被修改,如下面情况:
1.状态被并发线程同时修改
2.多个用户对一个共享对象(状态)进行冲突性修改。
3.用户提供了无效数据,应该被回滚。
在自然可变的模型下,会在上面情况下发生不一致性或数据破碎crushing,显然可变性很容易导致错误。我们需要的是一种新视野,新角度,一种事务transaction视野,当你从事务性程序中看世界时,一切都是不可变的。这和蒯因与引用透明 中功能重复执行不可变原理非常类似,体现逻辑的线性特征。
不可变性说得这么玄,是不是和平常编程没有太大关系呢?
完全错误。举个例子,当你要对一个变量进行修改时,这个变量处于一个对象A内,你是直接对这个对象(A类的实例)内这个变量进行修改?还是重新创建一个A类新的对象实例,新的变量在新的对象实例中,然后替换掉原来的对象呢?
很多程序员对这么一个权衡问题没有过多考虑,基本是随意选择其中方案,甚至根据工作量多少为抉择,这些不经意行为都为整个系统的性能留下祸根。
这个问题其实涉及到了不可变性的问题,如果A类被设计为不可变了,那么就要采取第二个的方案,创建后替换,这个答案带来两个问题:
1.程序员如何知道A类是一个不可变的类?
2.不可变类为什么能够获得性能提高?
关键问题是第一个,我认为我们可以通过OOA/OOD等建模方式明确哪个类为不可变,很显然DDD领域驱动设计方法中,有值对象为不可变的概念,如果领域专家指定来自需求中哪个类是值对象,那么我们程序员就容易在编程实践中采取构建替换的办法,从而又能照顾到了计算机底层的性能。
我2006年在jivejdon按照DDD设计开发时,探索性地提出状态可以表达设计为值对象概念,见实战DDD(Domain-Driven Design领域驱动设计:Evans DDD),虽然此后一直有反复和怀疑,但是最近看了如何打败CAP定理?一文以后,比较肯定了自己当初想法,心中一块石头落地,这是一种探索的乐趣。
下面谈谈我如何在JiveJdon 4.5版本中如何把值对象状态不可变性贯彻落实完整的情况,在之前版本因为心中不确定,所以对状态修改时,没有遵循上面提到的第二步法则,而是直接对对象字段修改,就好象直接去修改数据表字段那样鲁莽。
这种破坏性修改的恶果也让我吃尽苦头,就是数据出现严重的不一致性,如帖子更新状态,经常是增加新帖后,在论坛列表的“最近更新”栏目出现的不是最后增加的那个新帖,有时正确有时错误,头疼不已,当时是非常怀念基于数据库的编程模式,因为这个功能只要通过一条SQL查询就能完成,轻松而简单,而JiveJdon是基于内存中的对象,这个功能是对对象的状态进行修改完成的,方式不同,思路就不一样了,后来虽然费了九牛二虎之力勉强正确,但是不够优雅和轻量,一直是自己不敢面对的痛。
现在理顺了值对象状态不可变性这样一个思路,我几乎三下五除二把JiveJdon这个功能就重构了,而且一次性调试通过。下面谈谈这个重构过程。先展示一下JiveJdon的设计类图:
将ForumThreadState这个经常变换的状态和实体聚合边界内其他变化是不一致的,惨遭DDD一书中的订单价格变化案例设计为单独的ForumThreadState,下面问题是,ForumThreadState变化是直接修改其中字段,还是重新构建一个ForumThreadState替换原来的,不幸的是我和大多数一样,起初没有太多留意走的是前一种办法,绕了不少弯路。,不变性是统领业务分析和高性能架构重要法门,通过业务上不变性分析设计,可以实现代码运行的并发高性能和高扩展性。
将ForumThreadState这个经常变换的状态和实体聚合边界内其他变化是不一致的,惨遭DDD一书中的订单价格变化案例设计为单独的ForumThreadState,下面问题是,ForumThreadState变化是直接修改其中字段,还是重新构建一个ForumThreadState替换原来的,不幸的是我和大多数一样,起初没有太多留意走的是前一种办法,绕了不少弯路。