约定优于配置——软件开发的简约原则
by 杨玉廷
at 2012-10-02 10:35:13
original http://hp.dewen.org/?p=1700
工作快满1年了。在刚从CUHK毕业的时候,我还满怀悲壮地表示在中文大学饱受摧残,各种 assignments projects 所写的代码已经超过了整个在武大的四年。而现在,wc -l 显示的数字已经可以让我十分蛋定,这一年的代码量已经超过了过去所有学生时代的总和。回过头来看一年前写的代码,真是惨不忍睹,恨不得把当时的自己拉出去暴打一顿。确实走了很多弯路,但我也因此收获颇丰。
感触之一就是怎样编写漂亮整洁的代码。我曾经说过,我是个完美主义者,写博客的时候我要检查以防出现错别字,写代码的时候我会特别注意变量命名是否规范,甚至代码缩进是否对齐。这大概已经属于强迫症的治疗范围了。在现实项目开发中,整洁漂亮 意味着用最少的代码,实现完整的业务功能,同时代码是易于理解的。然而随着时间流逝代码不断被修改,系统设计的整体结构则逐渐衰弱。编码从严谨的工程堕落为随性地胡乱砍劈。不重构,软件就会慢慢腐烂。这一切是如何发生的呢?
归根到底,还是源于软件开发人员惰性。懒惰是天生的,例如 sofish 举例过的一段代码——
if(condition_a) { var str = condition_a.trim().replace(/^{(\w+)}$/, function(match){ // ... }); } elseif(condition_b) { var str = condition_b.trim().replace(/^{(\w+)}$/, function(match){ // ... }); } elseif(condition_c) { var str = condition_a.trim().replace(/^{(\w+)}$/, function(match){ // ... }); } else { var condition = defaultValue; str = condition.trim().replace(/^{(\w+)}$/, function(match){ // ... }); } |
如果又有新的 condition_d,懒人复制粘贴一下就搞定。你可以同样改成 switch,但本质上都是在写重复的代码。DRY 原则,即 Don’t Repeat Yourself,是编写高质量代码毫无疑问最重要的原则,重复是所有邪恶的来源。上面的代码可以稍作修改
function dry(condition) { condition = condition || defaultValue; condition.trim(); return condition.replace(/^{(\w+)}$/, function(match){ // … }); } if(condition_a) { dry(condition_a); } elseif(condition_b) { dry(condition_b); } elseif(condition_c) { dry(condition_c); } else { dry(); } |
将重复的逻辑提取出来,抽象为一个独立的函数,看上去就干净漂亮很多。所以抽象是一个很重要的能力。说一个差劲的程序员懒惰,并不是指他不情愿编码,事实上,他正不遗余力的敲打键盘;懒惰体现在不愿意思考,不想碰架构优化现有的系统。
为什么需要架构?
架构是当事情变得复杂之后必须考虑的问题。如果你的桌上有 2 本书,那么你怎么摆放其实没关系,你随手翻翻也能很快找到你想要的段落。如果你有几百本书,那么你就需要一个书架了。如果是一个图书馆,那么就更需要一个合理的规划,将书本分门别类,编号,建立索引,方便查找。软件开发是一样的道理,你得知道系统将要到一个什么样的规模,提前做好各方面的约定。例如选用一套开发框架,指定团队的代码规范。
约定优于配置
这是 maven 最核心的设计理念,很多语言框架都将其思想发扬光大。遵循约定虽然损失了一定的灵活性,不能随意安排目录结构,不能随意进行函数命名,但是却能减少配置。更重要的是,遵循约定可以帮助开发人员遵守构建标准。
例如 Yii 的典型项目结构是:
projectName/ index.php Web 应用入口脚本文件 assets/ 包含公开的资源文件 css/ 包含 CSS 文件 images/ 包含图片文件 themes/ 包含不同风格的主题 protected/ 受保护的应用文件,主要代码在这里面 components/ 包含可重用的用户组件,自定义类等等 Controller.php 所有控制器类的基础类 Identity.php 用来登录认证的 'Identity' 类 config/ 包含配置文件 main.php Web 应用配置 controllers/ 包含控制器的类文件,MVC 中的 C SiteController.php 默认控制器的类文件 data/ 包含示例数据库 schema.mysql.sql 示例 MySQL 数据库 extensions/ 包含第三方扩展 messages/ 包含翻译过的文本,适用于多国语言支持 models/ 包含模型的类文件,MVC 中的 M LoginForm.php 'login' 动作的表单模型 tests/ 包含测试脚本 views/ 包含控制器的视图和布局文件,MVC 中的 V layouts/ 包含布局视图文件 main.php 所有视图的默认布局 column1.php 使用单列页面使用的布局 column2.php 使用双列的页面使用的布局 site/ 包含 'site' 控制器的视图文件 index.php 'index' 动作的视图 login.php 'login' 动作的视图
又例如 Yii 中对于 action 通配符的规范,XyzController.php 中若有一个名为 actionEdit() 的方法,那么体现在网页 URL 上,就是是除去 action 前缀的动作函数名。即 http://www.mydomain.com/xyz/edit
更多的规范还包括变量、函数和类使用驼峰命名风格,即每个单词的首字母大写并连在一起,中间无空格。变量名和函数名第一个单词全部小写,而类的首字母需要大写,例如:$basePath, runFunction(), LinkPager。对 private 变量和函数来说,以下划线作为前缀,例如 $_actionList
当然还有数据库的规范——
- 数据库表名和列名都使用小写命名。
- 名字中的单词应使用下划线分割 (例如 product_order)。
- 对于表名,你既可以使用单数也可以使用复数。但不要同时使用两者。为简单起见,推荐使用单数名字。
如果风格统一,那么代码将是有规可循的。我们可以根据命名了解结构,根据结构理解构建的代码原理。团队的合作将是无缝的,多人 debug 也将没有障碍。
我之前做的一个项目,就是因为缺乏遵循规范,而花费了大量时间和精力去做适配不一致的接口参数,各种变量名称的转换占了整个项目代码的很大部分。亲自动手写好一个站点并自己进行代码维护,理解其中的弊端,经历无数次的捶胸顿足痛定思痛,才知道最终如何改进才算架构“合理”。
关于重构
进度预估实际上是比较困难的一件事情。每当 Leader 问我这个模块要几天搞定时,我都支支吾吾。因为有时需要对旧代码进行改造,或者说重构,而这些工作对于功能上没有任何产出效益。在版本发布的重压下,快速迭代只是我个人的一个期望而已。
软件开发已经进化为软件设计。在设计当中应当注重简约的原则,少就是多。按照一定的设计模式来构建,总能达到事倍功半的效果。例如使用继承、多态等面向对象手段,还有广泛采用的工厂模式等等。又例如每个函数都简单到仅仅 3 到 4 行长,函数的缩进层级不多于三层,每个函数只做一件事,可复用,同时充分解耦。
好的代码需要打磨。一个优秀的程序员,总能保证每一次 commit 仓库里的代码比上一次更好。
关于技术布道
Fenng 曾经谈过关于 技术布道 的一些经验,其实就是“用影响影响影响”。那些大牛们孜孜不倦地对外传递大公司的动态、新的技术趋势以及他们自己对技术的思考感悟,用自己的影响力去影响一部分具备影响力的人,再促使这部分人自发的去影响更大的目标群体。一些人之所以更牛是因为他们专注于自己做的事情,认为它是重要的,满怀热情,所以他有动力去优化自己的工作。相比之下更多的人只不过是在应付。
我没有那些令人景仰的牛人的魅力,我所能做的,只是把自己所知道的总结出来,教给和自己合作的开发者,让他们也能在技术上有所提升,这样合作起来也会更轻松。我想这是件令人愉快的事情。
参考链接:
MVC架构导致的词汇表分裂
每一件意义重大的小事——读《代码整洁之道》
延伸阅读:
本文作者:尘埃落定 | 原文地址:约定优于配置——软件开发的简约原则