我们通过工具的设计者和使用者的视角观察工具,讨论工具背后的设计哲学和对应的学习曲线。

设计者和使用者

从某种意义上来说,工具是个联系使用者和设计者的桥梁。工具的设计者为解决一类问题而设计工具,工具的使用者也是为了解决一类问题而使用工具的。这两者对于工具都有自己的理解。工具的设计者通常会预测此工具会被用来解决怎样的问题,在设计工具时预见使用者的偏好,而工具的使用者则根据所面临的问题选择合适的工具。

在理想情况下,设计者和使用者对问题的理解 (Vision) 是类似或者重叠的。这时候,工具的设计者能够完全理解使用者的需求,而使用者完全理解这个工具的长处和短处。成语“得心应手”说的就是这样的一种情况:掌握了一样工具,因此使用起来非常顺利。

在现实中,设计者和使用者对问题的理解可能是有差异的,随之对工具的使用方法的理解也有所不同。有时候,工具的使用者能够将工具用到远超出设计者想象的地步。有两个著名的例子可以说明这个问题。第一个例子是出自 Bell 实验室的 AWK 语言。从 AWK 的三位作者的角度看,这是一个与 sed 互补的,处理文本记录的语言。而AWK在贝尔实验室内部推广开来后,三位作者发现其他团队用 AWK 来写作大的数据管理系统,而三位作者本人从来没有写过超过一百行的 AWK 脚本。第二个例子是 C++ 的模版 (template)。模版的设计者的初衷是让 C++ 在处理对象时更加灵活,而自从C++模版的图灵完全特性被发现后,写作一些通用的库成为可能。如C++的Boost库,即重度依赖于模版。设计模版的人未必想到 Boost 库那样对模版的奇妙运用。这两个例子,都充分说明了工具的设计者未必能够充分体验到自己设计的工具的强大之处。

另一种情况是工具的设计者是解决问题的专家,对问题有深刻的理解,而工具的使用者则没有能够完全体验到工具的强大。工具的使用者因此需要学习如何使用工具,学会像工具的设计者那样思考,从而领略到工具的强大之处,达到得心应手的地步。对于完成学校教育后的成人来说,大多数学习都是在学习工具的使用。那么,如何学习软件工具呢?

在我看来,虽然工具千差万别,学习工具的用法也大相径庭,不过如果细细总结,工具的设计者所奉行的套路无非就两种,所对应的学习方法也有两种。我把这两种设计讨论形象地称为瑞士军刀工具链。这两种设计哲学背后的假设,以及相应的学习曲线是不同的。

设计者的哲学一:瑞士军刀

瑞士军刀以方便著称,一把军刀包含了很多小部件,配合使用能够解决很多野外生存问题。不少计算机工具借用了这个类比。在Google 上搜索 “The swiss army knife of” 会发现很多软件工具号称某领域的瑞士军刀(见表一)

工具名称 用途领域 广告词
BusyBox 嵌入式 Linux 命令集 The Swiss Army Knife of Embedded Linux
Perian Apple QuickTime 组件管理 Perian – The swiss-army knife of QuickTime® components
Sox 音频文件编辑 The Swiss Army knife of sound processing programs
Netcat TCP/IP 数据包分析 TCP/IP Swiss Army Knife
FFMpeg 视频和视频流文件编/解码 The swiss army knife of Internet Streaming
Kanif 计算机集群管理 Cluster management and administration Swiss Army knife

从表一我们不难发现,这些宣称为“瑞士军刀”的工具有一个共同特点:都是针对某个特定领域的问题而设计的。这一点和瑞士军刀是相似的。瑞士军刀的多功能,并不是指这是一把砍树修桥盖房子的万能刀,而是说在野外生存这个领域内,军刀能够解决许多问题。回到表一,用过这些工具的读者一定有所体会:这些工具解决所在领域的复杂问题的能力是非常强的。比如,FFMpeg 内置多种视频编、解码器,众多格式能够相互转换,支持的视频编码格式比任何商业软件都要多。这些优势让 FFMpeg 几乎能解决所有现实中遇到的视频处理问题。因为这些工具针对特殊领域,且功能强大,用瑞士军刀做比喻是很形象的。

除了自称为“军刀”的这些工具外,还有许多耳熟能详的工具属于此类。如平面设计软件 Photoshop,是融许多复杂图像算法和插件于一体的图像处理之瑞士军刀;GNU 的 GCC 编译器,是集语法分析,代码优化,代码生成等功能于一体的专门负责程序编译的瑞士军刀。这些软件包的内部结构,都不是一个单一的程序,单一的部件,而是一揽子的部件,一箩筐的功能模块。

瑞士军刀巧妙地将众多小工具紧凑地组合到了一起。同样,我们说的军刀工具的一大优雅之处也是类似的,即通过一个统一的控制界面,将这一揽子的部件有机地组合在了一起。设计模式里,这种组织功能的方式称作为 Facade 模式,具体指以一个命令或者一个统一的界面抽象内部复杂的操作。如 FFMpeg 和 GCC 这样几乎无所不能的软件包,都可以通过一个单一的命令 (ffmpeg 或 gcc) 加不同的参数调用。在 GUI 程序的世界,所有的 Photoshop 插件都可以通过 Photoshop 的图形界面菜单调用。Facade 模式隔开了用户和“军刀”工具复杂的内部逻辑,让用户从一个”抽象”的层面理解工具。正如我们开车并不一定要了解引擎是怎么工作一样,工具的使用者不需要关系这些工具的内部是如何运转的。

总结这类工具的特点,瑞士军刀是一种把各种工具组合到一起,一起解决一个复杂问题的设计哲学。核心是。设计模式里的facet 模式,一个领域的问题,而不是一个特定的工具。这里实际上是一类工具,而非一个单独的工具。这一类工具被精巧地安排在一起,对外提供一个一致的服务接口,让用户以简单的控制方式完成相对复杂的任务。

领域知识假设和学习曲线

“瑞士军刀“工具都是面向特定领域的。无论是图片,视频处理,还是查看TCP包信息,这些工具都假设了使用者知道图片处理,视频处理或者TCP包的结构等知识。心理学上把这些为了解决特定领域问题而所需的知识叫做”领域模型“(domain model)。工具的设计者是不负责教授这些知识的。因此,作为工具的使用者,掌握这些特定领域的知识就成了使用工具的前提。一般的软件都是基于领域模型的。比如,平面图形处理工具 Photoshop 假设图片是个多层次的物体,如果我们不了解这个模型,就完全不知道为啥这个软件为啥比系统自带的 Paint 复杂几千倍。

因为军刀背后蕴含着领域知识假设,学习军刀工具就不仅仅是学习工具的使用本身,而是学习背后的领域知识。如果不了解背后的领域知识,即使我们会操作这样工具,依然谈不上是个熟练的使用者。相信诸位读者都属于“懂计算机”的一类人。现实中,我们这类人常常因为“懂计算机”,而被家人和朋友要求编辑图像,做个视频或者做个网页等等。有些要求还特别专业,让只会写程序的人非常吃力。究其原因,是因为程序员群体掌握的,只是编程的领域知识。如图像,视频处理所需要的领域知识,均是编程之外的了。即使像 Eclipse 这样专门给程序员用的软件,也隐含假设了使用者需要重构,设计模式等等的知识。不掌握这些知识的程序员用 Eclipse 也仅仅是将其当成一个花哨的编辑器使用而已。

因为需要领域知识做铺垫,军刀工具的学习曲线是因人而异的。对于有的人来说,如果本来就知道工具背后的假设,学习计算机操作就毫不费力。以我爸爸为例,本身用计算机并不熟练,但他学过画画,作图感一流,若干年前从书店随便买了一本 Photoshop 入门后就把 Photoshop 玩得很熟。而我知道这个软件每个选项的意义,却处理不出什么像样的图。对于我爸来说,他的学习曲线就很平缓,而对于我来说,就很陡峭。

学习曲线不一样这一点在计算机语言的学习上尤为明显(计算机语言是众多编程特性的大糅合,是典型的军刀设计)。像 Google 这样的公司面试时完全不考察语言细节,很多人入职的时候都不会 Java。因为我们相信任何合格的程序员都能很快学会 Java 这个语言。道理很简单,只要考察一个人掌握了Java 语言背后的领域知识,即面向对象的设计,数据结构,控制结构等等,就有充分理由相信这个人学 Java 毫不费力。但是,话说回来,每个语言背后的领域假设都有细微的差别。比如 Lua 只支持哈希表,Awk 支持哈希表也支持记录处理,Vim 里一切都是文本对象等等。真的领会这些领域模型并且将这些语言用得纯熟,没有长时间的投入是不可能的,这就是为何 Peter Norvig 强调“十年学会程序设计”。这两种对待编程语言的态度是不矛盾的。引申出来,对于语言或者工具的初学者来说,要分情况学习军刀工具。在不熟悉领域知识的情况下,应该找一本非常简单的书熟悉领域模型,获得一个 Big picture,而非上来就想着要解决手头的问题。而对于已经熟悉领域模型的人,比如会了一个 MVC 框架再学另一个的人,随便捡起一本 In Action 或者 Cookbook 也能事半功倍;有人甚至书都不需要买,看看文档就可以了。不清楚内情的可能会把这类人叫做强人,其实了解了背后的底细后,所有人都可以如此。

最后谈一下军刀工具的设计策略。我们说了,军刀工具背后蕴含的是领域知识。因此,设计军刀工具的人,必然需要是对问题有深刻理解的领域专家。这一点很好理解,因为很难想象一个不懂图像处理的人会指导 Photoshop 的设计,或者一个不懂统计的人会设计 R。不过,即使是最资深的专家,也未必能设计出一个能覆盖到所有问题的工具。这时候,工具的扩展性就成了一个重要的考量。可以这么说,几乎所有成熟的军刀工具,最后一定会进化出一个插件系统,用以扩展功能。插件系统的好与坏是考验设计者功力的时候。像 Firefox 和 Photoshop 这样的工具,若不是第三方插件,至多是一个普通的软件。加入插件系统后,这些工具成为了一个平台,从而衍生出了无数的新用例。即使像功能小巧和 Emacs 格格不入的 Vim, 最终也进化出了插件系统。这是一样工具成熟的体现。

总结一下军刀工具的特点如下:

  •  面向特定领域,覆盖该领域大部分问题
  • 由领域专家设计,使用者需要了解该领域的基本知识才能熟练运用工具
  • 内部由众多小模块组成,对外提供一致,简单的控制界面
  • 常常具有强大的插件系统,以方便用户扩展现有系统

Pingbacks to “编程珠玑番外篇 -M. 软件工具的设计哲学1”

  1. 编程珠玑番外篇之番外篇-N 答 UNIX 痛恨者王垠 « 4G spaces
  2. 网络阅读库 » 编程珠玑番外篇 -M. 软件工具的设计哲学1
  3. 网络阅读库 » 编程珠玑番外篇 -M. 软件工具的设计哲学1

20 comments to “编程珠玑番外篇 -M. 软件工具的设计哲学1”

  1. 与非 says:

    好文章~
    对专业领域的工具性质剖析地非常到位!

  2. gordon says:

    这个“领域知识假设和学习曲线”思想延伸到“数学课程的学习”上就是你不是不懂数学,而是不懂数学表达后面隐藏的思想。

  3. onlyxool says:

    请问博主,如何能在Google Reader中订阅你的blog呢,我没找到你的RSS地址

  4. shuge.lee says:

    > Vim 里一切都是文本对象等等

    Emacs 也是。“统一接口”。

  5. Guest says:

    你的备案号码已不符合现行,美ICP显示规则,请到官网申请新牌照
    http://www.meilijianhezhongguo.com/log/post-97.html

  6. Guest says:

    我在想,军刀工具的开发是否属于“自顶向下”类型的?而工具链则“自底向上”?

  7. Dning1 says:

    学到了一点编程之外的东西:P

  8. Anonymous says:

    感谢诸位读者细心挑错,发的时候太匆忙,好多typo 没有细细修订,现在又修订了一遍,通顺多了:)

  9. .rex says:

    自从C++模版的图灵完全特性被发现后,写作一些通用的库【编程可能】。=> 变成可能。

  10. Anonymous says:

    感谢指出,已改正。

  11. XN.Z says:

    再挑个拼音输入错误,最后一句话, “以方便用户扩展先(现)有系统” :)

  12. Tim Shen says:

    事倍功半 => 事半功倍 ?

  13. Edgar Wang says:

    抱歉,对这个评论系统不太熟悉,换邮箱登录时不小心提交了上面的评论。我觉得军刀工具最重要的就是它的模块化和插件系统。模块化可以让工具集下的各种小工具相互组合,既灵活又方便,有时甚至可以发挥出出乎意料的效果;而插件系统则让工具集可以与时俱进,但同时又不需要更改其核心,给使用者一致的使用体验。(ps:我觉得UNIX的核心工具集也是一把厉害无比的瑞士军刀。)

    • Anonymous says:

      恩,我同意你说的插件系统。我把 UNIX 工具归成了“工具链”这一类,下篇就是讲工具链。具体来说,工具链没有军刀这样统一的对外接口,而是靠通用的协议来达到工具的配合使用。我感觉模块化未必是军刀工具的特点,像 FFMpeg 其实内部乱得很,只是外面看上去整齐一些。等我尽快写出下篇后我们详细讨论:)

  14. Wang Edgar says:

    呼,终于又更新了。

Leave a Comment