浅谈不同的语言派系产生的负面影响
一个创业团队的CTO(技术总监)说:「我要请Ruby工程师!」、「我在找Nodejs工程师!」、「你知道有谁会写Python吗?」。当我反问公司产品是什么,CTO通常都支支吾吾地说仍在尝试一个新的点子,Prototype还没做出来。选择程序语言和框架并不是什么坏事,但是在公司连第一个产品都尚未成形就先以开发语言和构架去筛选程序员,是错误的。
说到这,也点出了一个今天奇特的现象:程序开发派系化。
何谓派系化?
所谓派系化,意思就是程序开发派系的对立深化。慢慢的,许多程序员对于程序语言的诉求已经不是各取所长,而是先标明语言,再选择要解决什么问题,说穿了就是跟工程的理念背道而驰。
这种对立现象从程序语言扩张至开发构架,最后扩张至软件开发模式,最后就出现了“php是世界是最好的语言”这种类似的梗。
而程序开发派系化究竟产生了什么样的影响呢?
乐观而言,程序开发派系对立,使得个语言、构架和设计模式在剧烈竞争下相继成熟,使得程序员在工作时有更多的选择。
程序语言与构架的通用化
通用化,意思就是原本有特殊目的的程序语言或构架逐渐转变成多用途的程序语言。PHP与Javascript是程序语言通用化的最好典范,PHP原本是个HTML网页的前置处理器(Preprocessor),作用在于动态产生网页内容;而Javascript则是网景(Netscape)公司开发出的网页前端语言,用来设计浏览器端的互动功能(如检查表单内容)。如今,PHP已经发展成为通用Scripting语言,而Javascript也从浏览器端延伸到服务器端。而其他语言如Ruby,就慢慢衍伸出CoffeeScript来将类似Ruby的前端语言直接翻译成为Javascript。
为什么许多程序语言开始往通用化发展?
主要原因就是程序语言派系对立加剧,让许多程序员宁愿使用自己已习惯的语言和构架来取代整合多语言、多构架的开发环境。过去网络应用程序整合Scripting语言(如Ruby、Javascript、Python等)和系统语言(如C/C++)的开发模式今日几乎被Scripting语言完全取代。
通用化的好处是大幅减少了学习不同语言的时间,进而节省训练程序员的成本。
然而这优点带来了不小的副作用。程序开发语言和构架都很难在简洁度、效率和多元性之间找到完美的组合,三者则二,必须牺牲一者。现今许多程序语言都是Scripting语言,拥有直译、动态数据型态、跨操作系统等特性。这也意味着现代程序语言的执行性能很慢,若参考一些网络上的程序语言性能评比,现代的Scripting语言相对于C与C++这类的系统程序语言和C#、Java等虚拟机器语言,执行效率只有后者五分之一。
将执行效率慢的Scripting语言扩大应用,开发出的系统之性能是相当吃亏的。为了解决部分的性能问题,Facebook开发了PHP编译器HipHop,Facebook因而减少了将近50%的CPU用量。而Twitter从Ruby-on-Rails转至Java,其搜寻引擎效率成长了三倍。(注:Ruby当然也发展了自己的编译器来提升性能)
这么一说,不代表程序语言通用化是一件坏事;但是在大家图方便的同时,也要记得软件工程自身的复杂度,才能判断程序语言是否适合解决问题,所谓善其事、利其器。
一昧地要求一种语言甚么东西都要能做,有时反而扼杀了不同程序开发语言社群之间的合作、交流机会,搞到后面许多人都在土法炼钢。
Design Pattern、方法学绑架程序语言与构架
加入你是一位网络软件工程新人,想必你学的应该是Rails或Django。因为这两软件构架的流行,今天MVC(Model-View-Controller)被当作是种预设立场。
当然,世界上不是只有MVC而已。所以我们该问清楚,为什么只有学过MVC的人在没有比较过其他Design Pattern的情况下,会认为MVC就是最合适的选择呢?
让我们来看看MVC的构架,Controller就好像是交警,当车辆(使用者)驶入,交警会将车辆导入正确的道路(View),而在驶入道路时车辆驾驶人可以看到路边如路名、红绿灯、招牌等资讯,这些数据就是来自于Model。而开到下个路口,车辆可能又会碰到一位交通警察、再次驶入另一道路,如此反复直到开到目的地为止。
而这种Design Pattern的优点就是把所有的"交通管制"都集中在交通警察的手上,不管系统中发生甚么事情,都听从Controller发号师令。
而MVC当然也不是上方宝剑,他有一个致命的缺点就是若要从车辆(使用者)的角度去整理路线图,一位使用者使用的功能只是Controller的一小部分,但同时又是不同部分串连起来才能完成一工作,其是以管理流程、而不是管理使用者的角度去汇整的。
其实MVC有一位亲戚叫做MVP(Model-View-Presenter),其设计理念和MVC一样是要将界面和程序逻辑分开,但是其结构有明显不同。
Presenter相较之下,比较像是一家餐厅的服务生。当你(使用者)走进餐厅(View),你可以跟服务生点餐、询问厕所在哪里,或是请服务生帮你加白开水,当服务生(Presenter)知道你的请求后,变会与餐厅的内部人员(Model)互动,然后把你需要的东西交给你。
Presenter与Controller最大的差别在于每个View都有个独立的Presenter,而这独立的Presenter的角色就是处理一切使用者在该界面上需要完成的工作。因此不难理解,MVP的优点在于其代码是以使用者的角度进行整理,而缺点则是每个Presenter都有自己的独立流程。
说到这里,我们平台有些资深的程序员可能都想喷我了。
没错,两种Design Pattern的问题,如果代码重构简化(Refactoring)、代码界面化(Code Interfacing)、元件化与模块化(Componentization and Modularization)等技巧,其实问题都能迎刃而解。
重点来了:过去程序语言和程序构架的作用在于写程序,而至于要用Design Pattern、程序设计方法(Paradigm)都是程序员针对面临的问题去量身订做。这意味着程序员除了会写程序外还要有规划的能力。今天,因为简化工程师的训练时间,越来越多程序语言和构架被Design Pattern和特定设计方法绑架,语言派系对立让这情况更雪上加霜,变成许多新的软件工程师只会在一种特定的框架下思考,而不会根据面临的问题而改变规划。
一位程序员朋友曾说:“现在越来越多软件工程师只会用函式库(Library)写代码,而不是真的在做开发。如果你把一位Ruby-on-Rails的工程师丢到J2EE上去,就写得一蹋糊涂。”
同样地道理,在程序语言和构架中规范Design Pattern并不是甚么坏事,但我们不要忘记除了MVC以外,还有很多不同的Design Pattern,如服务型构架(Service-oriented Architecture)、事件导向构架(Event-driven Architecture)、规则导向系统(规则导向系统)、信息导向系统(Message-Driven Architecture)等不同的系统Designe Pattern可以参考,让我们能够对症下药。
动态与静态数据型态的“省时又省事”迷思
先前有提到,现今的Scripting语言多采用动态数据型态,意思就是变数依照「塞」进去的数值可以动态改变型态。典型的动态语言包括Ruby、PHP、Python、Javascript。
当今很多程序员喜欢动态语言,因为动态语言不必宣告数据型态,可减少写程序时间。而多数动态语言同时又是直译语言,直译的特性让程序员可以随时进行软件更新而无须担心服务器记忆体中的版本问题。可谓"省时又省事"。
然而,世界上当然没有那么便宜的事情。
之前提到的许多Scripting语言执行效率问题,动态数据型态和直译都是罪魁祸首。动态数据型态的特性代表每次程序执行,直译器都需要去解析变数的数据型态,自然增加了执行所需的资源;而直译语言在执行时,必须将所有代码即时转换成为机器语言,自然比已经编译成为机器码(或是位码)更耗资源。
执行效率除外,动态语言是否能够减少程序开发时间其实是有争议的,而小弟个人认为是否定的。
动态语言若发生数据错误,都是在执行时(Run time)才知道。反观编译语言,若数据型态不符合,代码根本完全无法编译,连跑的机会都没有。许多编译时期错误(Compile Time Error)在动态语言中都变成了执行时期错误(Runtime Error),而程序员都知道,抓执行时期错误比编译时期错误更困难、更耗时。因此在使用动态语言开发时,除了写程序的时间外,Unit Testing要写得比使用编译语言更完整。打字省下的时间,是否有比抓错和测试增加的时间多,恐有争议。
而编译语言在写程序时虽然需要打更多字,但由于编译语言的每个函式的标头(Function Signature)拥有数据型态,在团队合作时,即使不写注释也大致上能了解函式要怎么使用。若以动态语言开发,就可能需要花多点时间和心思在注释上,才能确保团队沟通良好。
动态与静态语言各有利弊,个人在此没有帮任何语言背书的意思,但当今程序开发派系化现象使许多程序员对于特别语言的加持和罢黜,实际上都与现实有所出入。
工具归工具,思维才是实力
说到这里,差不多告一段落了。
任何程序语言、构架,最后只是工具而已,在开发的世界中只有「在某个情况下」有相对优势,而没有甚么方法或工具是省时又省事的万金油。
开发的理念就是用对的工具去解决问题。因此,希望大家可以共同勉励,多接触不同的问题解决思维和方法、多交流,才能在困难时找到最有效(而不是最熟悉、最偷懒)的答案。