就像我在应用架构设计总结这篇BLOG中提到的那样,面向对象的系统设计就好象一群人翻越一座陡峭的悬崖,只要任何一个人没有翻越过去,那么就无法设计出真正的面向对象系统。
归结一下,在你打算进行面向对象的系统之前,你一定要考虑是否已经解决或能解决以下的问题:
一、对象的持久化
对象的持久化是最容易被想到的问题,同时也是最难解决的问题。由于关系型数据库模型和面向对象模型存在一些比较大的差异,如何将你的对象保存和快速的查询出来很是头痛。虽然采用面向对象数据库虽然可以最方便的解决这个问题,但是你会面临更多其它的问题,比如备份,报表和实施维护人员的培训等等。虽然有像NHibernate这样的ORM框架来帮助你做这个工作,不过你需要去多学习一门资料不全的新技术。另外,这些开源产品中有不少的BUG,你必须有做好调试别人代码的准备,如果你无法完全掌握这些代码的话,你会死得很惨。
如果你采用了面向对象来设计领域模型,又没有找到将对象直接关系化到你的数据库中的办法的话,最好不要采用面向对象的设计。否则你会被手工的ORM折磨疯的。比较好的选择是用强类型的DataSet,配合VS.NET的数据向导工具,非常快。(很有道理)
二、界面显示
由于界面无法直接的将复杂的对象显示出来,你在界面层又必须再做一次对象的平面化操作,将对象变成各种列表和文本框控件可以直接绑定的一维或二维数据结构。这方面目前还没有什么好的自动化方案,你必须手工做。所以我们的策略就是尽量减少这个转换步骤,就是在服务端就把面向对象转换成平面数据,然后在客户端直接绑定到控件上。在数据传输对象的选择上,可以用自定义对象,也可以用DataTable做传输对象,两者区别不是很大,各有各的好处。但是考虑到DataTable可以有一些绑定方面的优势和VS.Net的调试方面的支持,我们选择了DataTable。
如果你的对象是手工持久化的话,你做到这一步会感到很沮丧。因为平面的数据被你费了老大劲从数据库中转为对象,还没有怎么体会到面向对象的好处,又要再将对象还原成平面数据交给界面去显示,你可能会觉得非常的不值得。不如直接就把平面数据读出来交给界面去显示,既简单又方便。如果你已经产生了这个念头的话,恭喜你,你已经离全面放弃面向对象系统设计,完全退回到面向过程不远了。(再次暴露了ORM存在的一些问题,从数据库取回的DataSet转成Object集合后,到UI又得进行转化后与控件绑定,特别是CS应用)
三、远程调用
如果前面你还能坚持下来,到了这一步,很少人再能坚持了。面向对象对远程访问的问题考虑不足,使用面向对象开发系统的一个最大的优点是建模能力强,业务模型很清晰。比如你要找某个用户的公司名称,你可以简单的用User.Company.Name来得到该用户所属公司的名称。假如你用了多层结构,那么问题来了,你的对象是建在n层上的,而你需要在n+1层里对对象进行操作。而且n层和n+1层是远程调用。那么,如果你传个User过去,很明显用User.Company.Name是获取不到公司信息的。所以,如果你打算在n+1层做业务逻辑的话,你必须把整个对象树传递过去,这样做明显代价又太高。很多面向对象专业人士就提出了什么VO,BO,DTO,PO等等五花八门的贫血对象来企图解决这个问题,这样做的结果不仅是把问题给搞复杂化,更糟糕的是,这样一来,你当初所憧憬的面向对象系统其实已经是一个变相的Action Script系统了。
所以最好的方式是你就在领域模型层把业务处理完,中间层不要采用什么分布式系统设计。Martin Fowler说过:“分布式系统设计的第一原则是,不要使用分布式系统”。不知道有多少人能理解这句话的含义。
AF在解决这个问题上也提供了很大的帮助,我们利用了AF创造的统一编程模型,将逻辑尽量都放到领域层里。从而可以让更多的操作直接利用到领域模型的完全面向对象的业务逻辑处理的好处。
四、开发人员的面向对象思维
这一点最重要,如果开发人员无法认识到面向对象的好处,并且在思维模式上有很顽固的面向过程套路的话。在面对上述问题的时候会抱怨连连,不会尽全力的配合。如果整个的意见无法达成一致,那什么东西都做不出来,因为团结才是最重要的。所以你要么尽力说服你的团员,让他们真正的明白并理解为什么要这么做,要么你就趁早放弃。
最后一条建议:
要么就完全用面向对象的概念去设计,要么就完全用面向过程。如果当初是决定要走面向对象的道路,后来又在关键性问题上向面向过程做妥协。那么就干脆完全用面向过程的思维去解决问题比较好。否则面对一个四不象的怪物,你会发现维护起来更加麻烦。
翻越面向对象这座大山非常艰辛,但是你如果真正能翻越过去,你会发现面向对象的世界真的是一个很美妙的境界。
系统分层经验谈
为了将业务规则从界面和数据库中剥离出来,通常的做法是抽象出一个业务逻辑层出来,专门负责对业务逻辑进行处理。一般多采用三层结构,既表现层,业务层和数据层。
当开发人员在以前的两层结构中痛苦煎熬了很长一段时间,突然看到了三层结构的解决方案的时候,一般会有终于找到了救世主的感觉。但是这种感觉往往会导致掉到另外一个同样恐怖的陷阱“过度设计”中。在我以前曾经供职的一家公司,以前都是把SQL语句直接写在ASPX页面的,后来在读到了一些关于多层结构方面的资料之后,一下子又把整个系统分成了:表现层(ASPX)、接口外观层(IF),业务外观层(BF),数据访问外观层(DAF),数据访问层(DA)和数据访问组件(SQLHelper)。但是我并没有吸取教训,导致后来也犯了同样的错误。
犯错误的原因有很多,不过主要是因为没有一个比较明确的如何分层的指导性原则。假如说我们分层的原则是为了抽象逻辑,分三层的原因是要让业务逻辑和界面及数据库解除耦合,那么如果按照这个分层原则,我把逻辑重新归类更加细的分为四层、五层、六层行不行呢?如果不行,那是什么原因不行呢?在没有正确的原则指导下,分层技巧很容易被滥用,导致分出许多没有必要的层出来。无端的增加了开发和维护成本,以及更重要的是增加了重构的代价,降低了团队的敏捷能力。
面向对象架构设计大师Martin Fowler在介绍如何设计分布式系统的时候曾说过:分布式系统的设计原则的第一条是,不要使用分布式。他的意思当然不是说要绝对禁止使用分布式设计,而是劝导人们尽量把问题简单化。能不分布式设计的,就不要分布式设计。
我套用他的这句话提出我对分层的感受就是:多层结构系统的设计原则第一条是,不要使用多层结构。(增加相关分层一定要有明确的作用和存在的意义)
当然我的意思也并不是说层数越少就越好,而是希望你能清醒的认识到增加层数会增加结构的复杂性,不要轻易的作出分层的决定,一定要到感觉必须要增加一层才能解决问题的时候,再来决定增加一层。
过多的层次除了会给系统带来不必要的复杂性外,还会影响你的系统结构设计。如果你打算采用面向对象的领域模型来设计系统的话,在业务系统内的分层会给面向对象系统的设计带来很多麻烦,会很容易走回到事务脚本的老路上去。关于这一点,我在面向对象系统设计经验谈这篇BLOG里详细的谈到过,这里就不再赘述。
下图是我们最终定型的应用系统结构层次:
总结一下:
建立一个完全面向对象建模的领域模型层,让这个层尽量处理多的业务逻辑。其它层尽可能的薄一点,把业务逻辑都转移到领域模型层中。
UI尽可能和领域模型贴近一点,中间不要经过太多中转,物理边界也尽可能的少。
业务对象只能有一套,也就是领域模型。只要出了领域模型层,外面全部是零散数据,没有对象的概念。
只有在领域模型层才可以处理对象。
如果一定要分布式。全部用简单数据类型通过接口访问领域模型。
这个分层结构其实是经历了多次精简完成的,所有的感触都归结为一句话:不要过度设计,简单就是美。
本文为人月神话博客原创文章,转载请务必注明出处。