Why OO+多层结构? 来自实战的深入思考...(请求置顶)
面向对象的话题本来是个老话题了。只是看到还有不少人对这个问题有所困惑,我也就不吝浅薄,谈谈自己对面向对象的理解。还请大家在读过之后,能够不计鄙人的浅薄,多提宝贵意见。不断地争论和讨论是前进的根本动力。
(更多精彩,请访问我的博客:http://blog.csdn.net/aafshzj/)
面向对象的整套方法本来可以分为面向对象分析、面向对象设计、面向对象编程等。但是在这点上,我是赞同XP的开发思想的:代码就是所有的设计。因此,我更愿意把面向对象看作一个整体:一切最终落实到体现了面向对象思想的代码。基于此种考虑,在这里我也不区分OOA/OOD/OOP,而是泛指在面向对象思想指导下的软件开发及实现全过程。需要注意的是:“体现了面向对象思想的代码”和“面向对象的代码”是不同的概念, C#,java的语言特点决定了无论有没有面向对象的灵魂,其编码的“肉体”都是面向对象的。
关于OO的应用场合,还有很多人在争论。前些天看到还有人说三层结构/多层结构不适用于网站开发。从根本上讲,这和OO是不是适用于网站开基本上是同一个问题。因为,很显然,OO和N-Tier常常是一件事情的两个不同方面,使用OO却不使用多层结构是难以想象的,使用N-Tier却不使用OO那我就更加不知其可了。总之,在我看来,这两件事情是紧密相连的。退一步讲,将二者紧密联系在一起,如果不是必然的话,至少也应被看作是开发过程中的一个Best Practice。
下面谈谈我对OO + N-Tier的几个基本观点:
1)脱离了大量缓存和基于对象的业务逻辑的OO是没有意义的。
我以前看到一些同道在进行OO开发的时候,对象常常只在数据加载和保存中的出现。对象更像是一个盒子,在需要数据时,我们建立数据库连接,获取结果集或者DataReader/ResultReader,将从数据库获得的数据“填充”到对象中,然后再将对象返回给上层应用使用;在需要保存数据时,过程基本是对称的:我们把对象中的数据,一个字段一个字段读出来,生成参数化的或者直接字符串拼接的查询语句,建立数据库连接,提交更新到数据库。如果OO的主要内容就是这些的话,OO如果不能说是完全没有意义的话,起码意义也不大。我们在付出了编写了大量对象代码并且在存储加载时多一道手续的代码和性能代价之后,得到的好处只是使上层应用操作数据的代码可读性更好并且能够进行一定的类型强制和检查。这常常是很多开发人员对OO很困惑的原因:OO看起来很美,但是做了那么多事情,难道就是为了看起来很美吗?这同时也成为一些编程老手把OO看作华而不实代名词的原因:美是美了一点,但是代码多了,性能差了,让书呆子们去用吧。之所以出现这种局面,其根本原因在于“内存对象世界”没有提供太多的附加值。
大家应该注意到“内存对象世界”这一说法。在我看来,对象至少有两个世界,一个是“持久化对象世界”,一个是“内存对象世界”,这是由当今计算机的结构特点决定的:如果数据要长期保存,数据就必须被保存到可持久化的媒介中;如果要进行运算,数据就必须被加载到可运算寻址的媒介中。前者就是DB Server等管理的硬盘、磁带机...,后者就是内存。在DB Server为中心的开发中,大家倾向于把所有逻辑直接放置在最接近“持久化对象世界”的DB Server中,并主要以存储过程的形式存在。但是这样就一定是最合理的吗?尤其是对于网站应用?如果这样确实是合理的话,OO还有什么意义呢?是不是OO真的如某些人所说,只适合于图形绘制等特定领域?要回答这些问题,我们还是要看看哪些情况下,“内存对象世界”能够相对独立于“持久化对象世界”发挥其作用,这样“内存对象世界”就具备了独立于“持久化对象世界”之外的独立意义。
网站应用的特点是:看数据的人多,创建数据的人少。众所周知,恰恰就是这一点决定了缓存对于网站系统的重要性。对于主要以静态内容为主的小型简易网站,我们在这里就没有讨论的必要了。真正有人气的网站一定是具有动态增长的准静态内容(如新闻类网站,内容不断增加,但是本身很少修改)或者大量动态内容(如交易型的电子商务网站)的。对于前一种情况,通常有两种方式来加速其访问,一种是生成静态页面,一种是在内存中缓存页面内容。生成静态页面在性能上未必总是最好的选择。只有当数据多到内存中根本缓存不下,而这些数据又都有很大可能被用户访问时,生成静态页面才是较佳选择。在数据较多,但是并发并不多时,或者并发虽多,但关注的内容并不多时(如近两日新增信息或者近几日修改信息),页面缓存就是更好的选择。原因很简单,因为页面缓存的访问速度要明显快于静态页面。当然,有时候二者可以结合起来,这里就不多讲了。对于后一种情况——真正的较大型电子商务性网站,我们面对的是另一个问题:一些信息被以网页形式缓存,但这些信息本身的数据信息(如商品信息中的剩余数量,卖家Id等)常常需要被用到进行相关处理,如果只是进行了网页缓存,而没有进行对象缓存的话,缓存的意义就不打。在这种情况下,对象缓存就成为最好的选择。其实,在前一种情况下,页面缓存也可以以对象缓存的形式单独或者分级混合并存。
回到我前面提出的观点:“脱离了大量缓存和基于对象的业务逻辑的OO是没有意义的”。其道理是显而易见的,只有使用了大量缓存和基于对象的业务逻辑,建立一个OO结构的收益才远大于我们付出的代价,OO本身所在的“内存对象世界”也才具备了脱离“持久化对象世界”而存在的根本意义。如果我们能把大量适合放在内存中的业务逻辑搬移到应用内存中(而不是DB Server)的话,那效果就更好。当然也有一些操作不适合放在应用逻辑中,如针对特别大量数据的非个性化的成批更新操作。至于什么叫大量,这取决于性能的考虑和实证。将很多业务逻辑从数据库搬移到应用内存中是可行的,尤其是对于在执行前要根据很有限的数据条目进行大量判断来决定是否实质性产生某种数据改变的逻辑更是如此。代价是速度可能较存储过程差一点点,但是却避免了因为大量不符合规则从而根本不会产生实质数据改变的无效调用而导致的应用服务器到数据服务器的网络往复,二者相比,无谓的往复往往代价要高得多。对于现实的电子商务网站,这一点是非常有价值的。
2)OO并不是低性能的代名词,设计合理的OO会带来意想不到的高性能。
OO并不是低性能的代名词。恰恰相反,很多时候我都把OO当作我提升网站性能的基本武器:通过结构合理而算法高效的对象缓存技术以及与对象结合并在同一地址空间中执行的业务逻辑,我们往往能够轻易地提升系统的性能。当然,一切的前提在于正确的设计,这不断适用于OO,也适用于世间一切,没有什么东西脱离了其实现的细节而万岁千古!
以我们最近完成的一个较大型电子商务网站为例(目前日均订单〉12000,成交订单笔数在3000左右,而且最近增长势头很好),在运行近三个月后,该网站在Alexa的速度统计已经从早先的very slow(平均一个页面6~7妙),经过slow(Alexa的数据是累计的,所以不是立即跳变),上升到average(平均一个页面2.0s)。与此同时,DB Server的CPU Usage从原来的平均80%急剧的下降到平均10%!在现有系统中,基本上除了一些大批量数据移动和数据库服务器段数据分页查询之外,所有业务逻辑均存在于我们的应用逻辑中。这大概是违法很多朋友的开发直觉的。我经常听到的是:用存储过程吧,为了性能...我不是在一切场合都反对使用存储过程。但是我们必须清楚,每一种方法的优点是什么?缺点是什么?条件是什么?什么场合适用?什么场合不会适用,什么场合二者应该在更细微的场景/场合下分别或者混合使用。这有点象物理中的定理:谁见过没有适用条件的定理?
3)如果你关注Scale out的能力,你就更应该考虑OO。
现实世界的网站应用系统,都必须考虑Scalability问题,除非业务永不增长。只要业务会增长,使用人数不断增多,服务器就一定会很快达到性能和并发极限。解决这个问题,通常只有两个办法:Scale up——买更好的服务器,而这往往因其代价过于高昂而不现实;Scale out——买更多的服务器,这往往是最终的实际选择。但是Scale out始终面临着数据集中(就算是拆分过的数据仍然各自相对集中,能做到无限随意拆分吗)的问题。如果大量的逻辑放在数据库服务器一端,我相信很快数据库服务器就会使得系统失去Scale out的能力和可能。因此,要保证Scale out的能力就必须保证数据库(当然,必要的拆分策略也很重要,但那属于另一个话题)只处理实质性的数据提交和不可避免的数据查询,对于能够避免的数据查询和非实质性数据提交都应该想办法予以避免。这其实和我们上面所说的“脱离了大量缓存和基于对象的业务逻辑的OO是没有意义的”刚好结合起来,成为一举夺得的美事。
4)OO + N-Tier的基本思想乃至软件开发方法的所有其他动力是对逻辑聚合和复用地不断追求。
大家都知道,软件开发的分析设计方法一直在不端演化,新的概念不断涌现。软件开发也从最早的直线条的机器语言,到面向过程的汇编语言,再到面向对象的对象化分析设计及编程(继承、接口、多态),以及后来的DP(设计模式)、AOP(面向方面的编程)...。一系列的演化,确实让人有眼花缭乱之惑何从入手之惑。那么在这一演化过程中,一以贯之的是什么?始终不变的又是什么呢?
...