不久前写过一篇 《给我一把榔头,满世界都是钉子》,从算法和数据结构的角度谈了谈对于问题和问题解决的工具这两方面我的看法;而最近看到了这样的代码,一个表格,单数行和双数行的样式不同,于是有程序员这样写道:
var trs = $("#spreadSheet tr");
for(var i=0; i<trs.size(); i++){
if(i%2)
$(trs.get(i)).children("td").css("color", "RED");
else
$(trs.get(i)).children("td").css("color", "GREEN");
}
从功能实现上看,这是一点都没有问题的,JQuery 这把 “榔头”,确实是把这个 “钉子” 给砸进墙里面去了。但问题在于,这是个图书钉,使用了一个建筑工地上用的超大号榔锤。样式的问题,当然优先考虑 CSS 去解决:
#spreadSheet tr:nth-child(odd) td {
color : GREEN;
}
#spreadSheet tr:nth-child(even) td {
color : RED;
}
这其实是一个很常见的问题,不同的榔头,应该用来解决不同的问题。类似这样的问题有很多,再比如下面这几组列表的混用:
w3cshool 中文网站上面有 HTML 标签的详尽列表,虽说基础,但是全部掌握这些语义并在合适的时机使用也并不容易。
如果说,榔头选得不好,依然把钉子砸进去了,只是这个过程有些别扭,那已经是很不错的一个结果了。更糟的可能是,砸完钉子以后无尽的后遗症。我记得刚工作的时候,第一个项目是中国移动的彩铃项目,这类项目都是面向电信运营商的,业务逻辑可谓相当复杂,整个系统大概超过了六十万行代码,但是业务逻辑居然是写在存储过程里的。开山鼻祖的程序员第一次拿存储过程这个榔头砸问题的时候,他大概也没有想到,这会成为名副其实的 “maintenance burden”,后续无尽痛苦的源泉。
关于编程范型
接着我想谈一谈设计模式和编程范型。抽象地说,它们是两种不同角度的对榔头的分类方式。更多的人非常熟悉设计模式(Design Pattern)的含义,大多数设计模式和语言类型、语言本身无关,掌握得好的程序员可以写出简洁、解耦、易维护的代码;半吊子程序员也可以写出概念堆叠、过度设计的代码。
但是编程范型(Programming Paradigm)则往往和语言本身的特性强相关,一种特定的语言,只适用于一种或几种编程范型。简单地说,它类似于一种编程风格,换一种说法,它是问题解决方案落到代码上的表现形式。但是,能否恰当地使用编程范型,决定了能否写出清晰、高效的代码。
我曾经在 《编程的未来》里面提到过编程范型的进化:
很多时候程序员会觉得,算法还是不容易转变成代码,即便是简单的算法,思路简单的纸上实现,变成代码却比较冗长。我觉得大部分情况下这不是你编码技巧的问题,而是编程语言的问题——换句话说,如果你使用一种合适范型的编程语言,兴许就可以轻松解决这个问题——即便这样的语言并不一定好找,并不一定容易设计。
也使用了 Prolog 作为例子。在维基百科的链接上,可以找得到很多编程范型的归类,最常见的几个说出来也会觉得耳熟能详:
学习一种新的语言,其中一项重要的意义也在于此;有的框架,特别是提供了 DSL 特性的框架,也具备这样的意义。最典型的例子就是写一写前端代码对于程序员来说的意义,我写过一篇 《程序员,都去写一写前端代码吧》,但是其中漏掉了一点,前端的代码(HTML+CSS+JavaScript)带来的编程范型是非常丰富的,尤其是 JavaScript,你可以对比一下 JavaScript 的开源库和 Java 的开源库,Java 的开源库更多的是注重与功能和框架设计,而 JavaScript 的开源库则提供了大量崭新的写代码的风格。我可以不做前端的工作,但是我依然会学 JavaScript。
举例来说,D3,我以前的一位经理说,D3 实在是太反直觉了,不适合用到我们的项目里面去。说反直觉那确实也是正确的,但是很多情况下这是建立在人已有认识的基础上的,一旦熟悉并习惯了 D3 的编程范型(接近于声明式,核心是几个不同的状态,加上状态之间的变迁,而这些变迁的过程可以绑定上丰富的行为),你会发现它的代码可以写得如此优雅和简洁。
文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》