项目地址:http://github.com/honovation/veil
设计理念
做为框架:改不改得动是检验一切架构的唯一标准
我们认为框架不仅仅是为了节省开发人员的时间,能够让你五分钟写一个博客系统。更重要的是给应用程序的逻辑提供一个组织的方式。因为我们知道,软件开发就是管理复杂度的艺术。管理好软件业务逻辑的复杂度,就需要代码有一个良好的组织。这么多年来,人们总结出来的金玉良言就是“高内聚,低耦合”的模块化设计方式。Veil框架仅仅是我们对于践行模块化设计的一个努力和尝试。
模块化设计并不是一个新概念。甚至C的module也可以称自己为模块化。Veil的特点在于彻底的模块化,比如有的时候我们会把东西放到不同的地方
- 安装程序如果是集中管理的,那么每个模块的一部分实现还是要外泄到安装程序里
- 运行脚本如果时集中管理的,那么集中的运行脚本还需要知道每个模块是如何运行的
- 代码如果是分层管理的,每个模块实现的功能需要跨越多个层次,被割裂在几个分开的目录内
- 页面和代码如果不是搁一块的,那么模块还是要分为代码和页面两块
- javascript和css如果是单独存放的,那么模块的改动还要不停地在相关的javascript和页面之间跳来跳去
而有的时候我们又不把东西分开来
- 使用相同表的方法和实现哪怕是为了完成不同功能,我们也会把这些代码放到同一个“Model”,不管它已经有多大了
- 同一层的代码可以肆无忌惮的彼此引用,哪怕有的时cms有的是交易代码,风马牛不相及
- 调用web service等外部服务的代码散落到代码的各个角落,可以随意引用
所以Veil从某种意义上来说是对传统”秩序“的反思。我们把很多应该放在一起的东西出于种种困难没有能够放在一起,并且已经视之为理所当然。而有些完全不该放在一起的东西,打着”分层“,”OO“等旗号被堆砌到了一起,使得代码庞杂而难以管理。Veil无视我们已经习惯的秩序,单纯地从”高内聚,低耦合“的角度出发,来评判什么应该被放在一起,而什么又不应该放在一起。
最终的目标不是为了OO,不是为了达成什么架构上的理想,甚至实现“高内聚,低耦合”也不能称之为目标。最终一个框架,以及其背后的架构设计思想的检验标准只能是响应变化的能力。也就是说,同样堆砌功能,用架构A和架构B当然都可以实现。而实现的质量除了静态的检验(功能性需求和非功能性需求是否满足),更重要的时动态的检验,就是“动”的能力。当我们需要对功能进行增删,把模块的职责互相移动的时候,代码能不能改,怕不怕改乱来,才是真正体现功力的地方。没有任何设计能够在一开时预见所有的需有,好的架构就是要让设计需要改动的时候,改得动不怕改。而在我们看来,要实现这一目标就是一个很简单的原则,就是要分而治之,同时保证被分治的部分之间保持“高内聚,低耦合”的关系。在Veil的设计过程中,就是秉着这样的目标,把模块化追求到极致。至于是否能够帮到你,是否真的能够达到前面所说的帮助你提高响应需求变化的能力,还有待实际使用中的检验。
做为库:出来混迟早是要还的
框架与应用的关系是,框架提供骨骼(所谓架构),应用提供肉(所谓业务逻辑)。但是不管是什么框架,最终还是会提供一些库来给以用使用,作者总是会期待能够把“公共”的“重复性“的劳动帮助应用完成了。而这种简便性,往往成了一个框架宣传时候的所谓亮点。但是我们知道物质是守恒的,代码的复杂度也是守恒的。实现同样的功能,不是应用程序多写一点代码,就是使用的库的多实现一些功能。从长远的角度来看,出来混迟早是要还的。所有库帮应用实现的功能,最终仍然是应用的一部分,从依赖的角度来看,你使用的语言的运行时(比如python的标准库),再下面的c运行时环境,操作系统的api,硬件驱动,乃至cpu指令都是你应用的一部分。一旦一个功能出错了,它可能是cpu出错了,也可能是你代码写错了,也可能是从你的代码到cpu中的每一个环节。
既然认识到库也是应用的一部分,那么我们就需要思考什么时候需要用库,我们需要什么样的库。Veil自身需要提供什么样的库api给应用使用。我们认为影响库的设计与使用取舍有三条:
- 问题域本身的复杂度。 如果问题本身过于复杂,无法让库的用户安心使用库提供的api,那么这样的场合可能就不适合封装成库。比如说SQL就一个很复杂的问题域,经过ORM的封装,它事实上重新发明了一种查询语言。带来的结果是,做为ORM的用户,不但要了解ORM封装之后提供的新的查询语言,还要知道SQL(事关执行效率),而且还要知道ORM封装后的查询语言与SQL之间的翻译关系(要不然我怎么让在性能出问题的时候,让ORM翻译出想要的SQL呢)。
- 库自身api设计是否良好,有没有泄漏内部实现。 如果问题本身不复杂,比如计算一个字符串的md5值之类的。那么就只需要api设计良好,一般用户都不用去关心起内部实现了。
- 库的代码质量。 但是有的时候我们还是被迫去了解一些库的内部实现。比如曾经用过的一版.net的hibernate,其处理并发情况下的lazy load list的时候,从连接池有取到不同连接的问题。在彻底检查了自己的代码之后悲惨地发现是库的代码有bug。
对于以上三条,无论是哪条有问题,都会让我们进入一个还债的状态,以十倍地时间去了解我们不甚了解而又往往过于复杂的代码。因为库面对的用户是千千万万的,解决的问题也是形形色色的。比如说linux的进程调度有林林总总的实现,满足不同计算场合的要求。如果哪天我们被迫需要知道我们的进程是如何被linux调度的,那需要耗费的时间可想而知。即便是库解决的是一个明确而又小的问题,设计有良好的api,而且自身代码也不出什么问题,我们仍然会有可能遇到,问题可能稍微发生了一些变化,库不完全能解决我们的问题,而又不失完全不能解决的时候。遇到这样的场合,不少人可能都会选择与修改使用的库,要么fork,要么提交回去。
总之,就是那句话,出来混迟早是要还的。Veil的原则也很直白,就是简单粗暴。如果要封装,也尽量处理明确而又小的问题,api不泄漏实现,同时要保证质量。不要在应用的代码和最终实现之间插入太多的magic。