[转载]AAF灵便应用框架简介系列(6):休息一下,泛谈面向对象 Why OO+多层结构?
面向对象的话题本来是个老话题了。只是看到还有不少人对这个问题有所困惑,我也就不吝浅薄,谈谈自己对面向对象的理解。还请大家在读过之后,能够不计鄙人的浅薄,多提宝贵意见。不断地争论和讨论是前进的根本动力。
面向对象的整套方法本来可以分为面向对象分析、面向对象设计、面向对象编程等。但是在这点上,我是赞同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(面向方面的编程)…。一系列的演化,确实让人有眼花缭乱之惑何从入手 之惑。那么在这一演化过程中,一以贯之的是什么?始终不变的又是什么呢?
在回答这个问题前,我们先来看一看MVC。MVC是现在大家都很熟悉的设计模式。那么到底什么是MVC呢?下面是Wikipedia关于MVC的解 释:Model-View-Controller (MVC) is a software architecture that separates an application’s data model, user interface, and control logic into three distinct components so that modifications to the view component can be made with minimal impact to the data model component. 翻译过来就是:MVC是一种将应用的数据模型、用户界面(视图)以及控制逻辑分解到不同组件中的软件构架。这种分解可以使得视图组件的细修改对数据模型只 会产生很小的影响或者根本不产生影响,
在思考问题的时候,我喜欢跳出问题来看问题,在跨越时间甚至跨越领域的大跨度类比中不断完成思想的升级及重构。回到前面的问题。在软件开发分析设计 方法上,一以贯之的其实就是一种类似于MVC的思想:对问题域不断进行重构,使得一种数据(不要狭隘的看待数据,信息、规则、动作,一切都可以被看作是数 据)、该数据的管控逻辑、以及该数据的使用者( 不一定是UI,UI只是特例)能够被区分开来。从这个角度来看待软件开发领域的一切,你就会发现,一切都在改变,一切也都没有改变。我们只是为我们一以贯 之的方法找到了的应用发领域。如此而已。再简化一点,实质上,一切软件设计方法的突破都是在于指明新的逻辑聚合方向,建立了新的逻辑聚合方法。OO如 此,AOP如此,设计模式如此,一切都是如此。
5)只有一些很特定的情况不适用OO + N-Tier。
当然不是所有场合都必须或者适用OO + Np-Tier,因为二者的引入无疑会在某些方面增加成本,因此我们必须考虑我们付出的时候会得到什么,我们得到的时候又会失去什么?权衡——这实际上也是我们做一切事情的最基本思路,而不只是针对软件开发。
大家可以自己权衡何时使用OO + N-Tier。下面是我认为不适合的一些场合:
a)太简单的应用,写起来没几句代码,使用OO + N-Tier根本不值得
b)已经有一套很接近目标系统的原型系统,暂时没有必要使用OO + N-Tier,因为成本不合算
c)开发人员完全不知道该怎么使用OO + N-Tier,教育成本不低,暂时不建议使用
d)实时、密集信息处理,其处理过程非常简单,连判断都很少,使用OO + N-Tier根本没必要
…