<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>冒号空间 &#187; 冒号课堂</title>
	<atom:link href="http://blog.zhenghui.org/category/cis/colon-class/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.zhenghui.org</link>
	<description>自然、人类、机器</description>
	<lastBuildDate>Fri, 30 Dec 2011 03:14:59 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3</generator>
		<item>
		<title>答读者问(3)——再谈抽象</title>
		<link>http://blog.zhenghui.org/2011/04/18/more-words-on-abstraction/</link>
		<comments>http://blog.zhenghui.org/2011/04/18/more-words-on-abstraction/#comments</comments>
		<pubDate>Sun, 17 Apr 2011 19:21:55 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[OOP]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[抽象]]></category>
		<category><![CDATA[透明]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=829</guid>
		<description><![CDATA[回答上位读者的进一步提问 [...]]]></description>
			<content:encoded><![CDATA[
<h2><strong>答读者问(3)</strong></h2>
<p>以下内容试图回答上位读者在<a href="http://blog.zhenghui.org/2011/03/20/words-on-abstraction/">答读者问（2）</a>后的进一步提问。</p>
<p><strong>读者</strong>七心葵问：</p>
<blockquote>
<p>得蒙先生专门撰文答复，深感荣幸，先生针对我的问题做了详细的解答并给出大量的例证，点出了很多我没有想到过的东西。 需要说明的是我其实是更倾向于OO的，喜欢OO带来的代码的整洁感和易重用，也知道不同的开发方法或语言范型有不同的适用场合，不可一概而论。对于那些驳斥OO的牛人，我不是因为他们是牛人所以怀疑OO，只是觉得他们提出的问题，自己的确得不到解答。上面提出来来的两点，我不认为是足以推翻OO，但是的确认为其有独特的价值（尤其是对比OO来看）所在，因而专门询问这两方面的情况。</p>
<p>关于我提的两个问题：<br /> 1 OO的抽象的对立面（“反抽象”的叫法似乎太过了，或者应该说是 “反OO抽象”，或者更进一步“反’静态类型的OO’抽象”？）；<br /> 2 数据驱动编程；<br /> 我仔细想了想先生的话,并陆续读完了先生的整本书, 还是觉得有些地方想再听听先生的看法，这里写出来，请多多指教。</p>
<p>毋庸置疑，抽象是人类处理复杂性的利器。但是，在我看来，“抽象”这个词本身就够抽象，抽象有很多种，有不同角度的抽象，有不同层次的抽象。<br /> 在OO里面，我认为“抽象”通常的定义是“抽象就是有选择的忽略”。那么如果脱离开OO的范畴甚至脱离开软件程序设计的范畴来讲，什么是抽象呢? 很难定义，我们不妨从其反面看，什么不是抽象呢？具体的东西不抽象——具象，或叫做“具体事物”。相比于“抽象”，具象对人类来说显然是一种更直观、更可操控，因而显得更自然、更可理解的思维。</p>
<p>从这个角度讲，“everything is file”，这里的“file” 其实是一个概念模型，是一个“隐喻”；对比或与非门、0/1编码,它当然是一种抽象，但更是一种具象，因为Unix/Linux 真的把一切都实现成了文件！<br /> 1 你可以所有操作文件的API来操作它，也可以使用用户接口界面CLI通过操作它；你可以直接查看各种真正的文件的内容或属性，也可以通过查看/proc下相应文件来查看进程运行中的内存，还可以通过通过它查看各种I/O数据流。<br /> 2 如果以OO的眼光看，“文件”中的诸多子类型，其实存在“接口退化”的现象，例如：你无法删除/proc目录下表示进程运行时内存的文件（即便你有权限）。</p>
<p>这里的要点，我认为，是逻辑模型直接映射到了物理实现，因而显得透明，直观，可显，也就是“浅、平、透”。也就是透明性原则的副标题“设计要可见，以便审查和调试”所强调的要点。《Unix编程艺术》（以下称 TAOUP）中强调的其他原则，如 每个程序做一件事并做好，c语言的薄胶合层，文本化协议，多进程分离功能，数据驱动编程，微语言设计，无不体现着这一点：使逻辑模型直接映射到物理实现，可视可显、可以操作、可以度量、可以把玩可以打磨。</p>
<p>当然，OO设计当然也可以把逻辑设计映射到具体的物理模块，比如分目录的做法，比如分包的做法，比如最终编译为一个动态链接库的做法，都可以将抽象的模块（或层或区） 映射到这样 透明直观可显 的物理实现上，但是我认为这并不等价(尽管分包分库的做法确实已经使得程序的模块性和可理解性变得相对直观很多了)。 譬如，考虑Apache www服务器最早运行CGI动态生成网页的方式： 每一个CGI本身作为一个可执行程序，只是从stdin输入往stdout输出，每一个CGI可以通过C语言编写，也可以通过shell，perl等脚本语言编写，其本身既能和Apache配合，也可以在CLI命令行中直接使用、测试、把玩、打磨。并且，CGI还有其他的好处，例如可以按名字按目录控制权限，可以作为一项资源被引用（RESTful架构）等。 这让人联想到硬件部件具有的长宽高的物理属性，虽然这不是硬件设计的重点，但是一个集成芯片确实可以在关键时候垫桌脚；同样，一个CGI确实可以在必要的时候用作命令行管道中的一个过滤器，而这些都不是那些”由逻辑模块映射成物理模块的动态链接库”等可以做到的（也许能用到一些？比如延迟加载等特性）。</p>
<p>其实，在 TAOUP 一书中，作者有一次把这些 “已经实现的具象” 称为“基本抽象”： （见 第20章：Unix程序员在30年风雨中学到最有经验的回应，就是回到最初的准则–优先从 字节流，命名空间，进程 等 Unix基本抽象中得到更多的效用，而不是增加新的东西。）</p>
<p>先生一书中曾提到过硬件行业往往因为诸多原因比软件行业更能贯彻OOP的理念，这里的Unix下软件设计这种“保持基本抽象、十分警惕过度纯虚化抽象”的做法似乎更类似于硬件行业的做法。事实上，我认为硬件行业不是能更好的贯彻OOP理念，而是硬件行业的接口标准比较规范（正如书中所述的原因）而不易虚化不易重新制定抽象规范（双刃剑！让人想起来电子垃圾邮件，电子邮件由于其本身的低成本和世界范围内的连通而得以流行，也正是因为同样的原因被发送垃圾邮件的人得以利用并难以预防！）。 试推想，如果所有的操作系统的API都是符合Posix标准，所有的DBMS都遵从SQL标准，软件行业看起来也会像硬件行业一样规范有序而无需经常抽象！——如果具象层面就能解决的问题，为何要抽象呢；换言之，如果抽象的规范为所有应用软件所遵循，又何必强调接口和实现的形式上分离呢。呵呵，当然没有这么理想化的世界（但是好像硬件行业的世界的确是这样的？或者还是有些变动名堂的吧，要不也不会IDE转USB，USB又转SCSI这样的接口了，当然，这些也是规范化标准化了的），这让我想起来《c++沉思录》中提到的一个笑话，说Lisp程序员在处理那些“不能很好的符合s-expression模式的数据时，他们居然认为解决这些问题的惟一办法就是让全世界都只使用Lisp这一种语言！”（从这个角度来说，OO也许好一点，至少OO在想办法把周边非OO的东西先转化为OO再处理：平的数据文件转变成有嵌套层次的对象，关系型数据库中的数据映射为对象，协议内容包装为DTO对象，简单的值包装成 valueObject，分配内存并初始化 转换为 使用工厂生产对象，原本的功能函数 转变为 serviceObject；UI的对象似乎是天然的，真正映射到现实世界的就只剩下实体对象了！这不正是当年 smalltalk 的困境么？——感谢JVM吧，同时也别奇怪为什么C++程序员总是那么OO得不够专业）</p>
<p>在这里，与其说Unix文化所强调的是一种抽象，不如说是一种隐喻。抽象只给你接口，隐喻给你必要的梗概信息，以避免“抽象泄露”（见《Joel说软件》一书）。譬如，windows操作系统下，本地磁盘是一个磁盘，一个可访问的异地远程目录也可以挂载为一个磁盘；当你只是打开小文本文件的时候，这两个磁盘（共同的抽象）表现出来没什么区别，但是当你想分别在两个磁盘上运行大型游戏的时候，可能就会发现加载远程磁盘的游戏内容很慢，因为抽象遗漏了一些你在这种情况下必须注意的信息。</p>
<p>这里我再说我对两个问题的看法： 1 “透明” 说到“透明”这个词，我认为“透明”这个词犹如灰色之余黑色白色，说灰色更近于黑色或者更近于白色，其实还是取决于灰的程度如何，因此说“因为透明而看得见”，或者“因为透明而看不见” 应该都是可以的。而在TAOUP一书中，我认为“透明”一词的含义确实更偏重于“因为透明而看得见”，比如该书第20章有这么一段话： 透明性是重用性的关键，被重用组件不仅要透露给用户“做什么”，还有透露“怎么做”。 OO的做法显然与这里强调的不同，OO强调封装强调黑盒，只告诉用户“做什么”；这里是白盒，会告诉用户“做什么以及怎样做”。</p>
<p>2 “文本化” text文件比binary文件抽象程度更高更低当然并不一定，但是无结构的文本文件的确是平坦的，和关系型数据库一样，是反OO抽象的（映射成OO时是需要 序列化/反序列化或者ORM 的）；但是Unix并不关心于此，Unix关心文本文件内的内容(不论是内容的格式还是字符集)是可显的，透明的，可用来调试，有利于维护，是具象的，直观的，并且可以接合其他过滤器程序处理的，其本身的形式就是元（也许更具体的应该说包括 CSV、TAB键分隔等）。</p>
<p>以上这些，其实与您书中提到的两种抽象“规范抽象，参数抽象”并不矛盾； 我从内心深处喜欢OO，OO带给我更多的整洁感和美的享受。而我也总有一些时候认为，也许Unix文化下的这些准则 只是在更小的局部中解决问题会用到的原则等。 但是数据驱动编程，（我认为其起源于朴素的 参数化思想 和 代码生成技术）则真正的是一种导向与众不同的设计范型。</p>
<p>Booch 书中提到的三类软件设计方法：Top-down structured design、Data-driven design与Object-oriented design，(也是引述别人的)，我对其中的 Data-driven design （简称 DDD似容易和 domain-driven design 弄混，既然也有叫做 Data-driven programming，我这里简称 DDP）尤其感兴趣，因为我想知道可以和 结构化设计，OO设计 这样并驾齐驱，号称为众多软件设计方法之祖的这样一门编程语言有何奥妙，为何少听人说道？</p>
<p>这里先说一下我为何愿意相信这三类软件设计方法是众方法之祖，因为：</p>
<p>1 权威作者的认可 如 booch 书中所说“这些方法中的大多数基本上都是类似主题的一些变奏”，“绝大多数方法都可以归为以下三类之一”</p>
<p>2 我个人相信如果从某一个角度看，抓住本质的话，众多的分类可以归并成几类的</p>
<p>3 逻辑的分析： 所谓的“软件设计方法”决定了设计软件的方式（-orinted），我认为这3类方法是完全从不同的角度来看待 软件设计的， 其中 结构化世界 和 OO 不相容在同一个设计中不能并存；而 DDP 则与其他两种方法都各自相容； 众多编程范式可以归入这3类软件设计方法,例如: DDP 其实就是 机制与策略分离，DDP的起源应该是朴素的 参数化抽象 思想, 包含了以下这些编程范式： language-orinted programming( declarative programming、function programming、logic-driven、knowledge-driven ) meta-programming( generic programming、aspect-orinted programming、代码生成技术 ) 而 event-driven 这种范式似乎划归到 OO 中去更为合适，</p>
<p>TAOUP一书中专门提到了 DDP 和 OO 的对比： “数据驱动编程有时会跟面向对象混淆起来…他们之间至少有两点不同。第一，在数据驱动编程中，数据不仅仅是某个对象的状态，实际上还定义了程序的控制流；第二，OO首先考虑的是封装，而数据驱动编程看重的是编写尽可能少的固定代码。Unix中 数据驱动编程的传统比OO更深厚。” Booch书中也提到对DDP的描述 “在这种方法中，系统输入和输出之间的映射关系驱动着软件体系的结构。与 结构化设计 一起，数据驱动设计 已经成功地被应用于一些复杂的领域。” 这里的“映射关系”我理解为 “机制与策略相分离”的 机制的部分，也对应于 LOP 中的 解释器 或 虚拟机，即“程序中固定不变的部分”。 因为booch书引用的相关文献我并没有找到进一步参考学习，所以我只是从个人的理解上进行分析。先生内功深厚，涉猎广博，是否可以予以指点？</p>
<p>按照Tanenbaum在《结构化计算机组成》一书中所言： 机器的指令系统层提供了底层或与非门，0/1数据，电子部件 的一层虚拟机； 操作系统在此之上提供了另一层虚拟机（C语言层紧附于这层虚拟机，所以说是一个“薄胶合层”）， 高层语言在此基础上构建更高层的虚拟机（无论是 编译时面对的编译器平台和库 还是 运行时面对的解释器）。</p>
<p>沿用 Tanenbaum 的观点，我认为 数据驱动编程（包括LOP中的解释器）就是在操作系统层之上再构建一层虚拟机 的过程。 为什么这么说? 虚拟机之上 和 虚拟机之下 的部分的对比，其实不是数据和指令的对比，也不是虚拟机的软件指令和机器硬件指令的对比， 而是 相对固定的部分 和 相对易于变动的部分 的对比，是变化率的对比，这正是 数据驱动编程（策略与机制分离） 的本质，是一种朴素的 参数化 的思想。</p>
<p>数据与代码分离，本质上其实是 相对固定不变的部分（机制） 和 相对容易变动的部分（策略） 的分离。 数据驱动编程，本质上是符合分离原则和保变原则的，如 bob大叔所说，变化率 是程序始终应该注意的一个问题。 由此联想到的还有：（数据和代码的分离，微语言和解释器的分离，被生成代码和代码生成器的分离）； 更近一步：（微内核插件式体系结构，）</p>
<p>元编程 应该说是更加泛化的 数据驱动编程，元编程 不是新加入一个间接层，而是退居一步，使得当前的层变成一个间接层。 元编程 分为 静态元编程（编译时） 和 动态元编程（运行时）， 静态元编程本质上是一种 代码生成技术 或者 编译器技术； 动态元编程一般通过 解释器（或虚拟机）加以实现。</p>
<p>数据驱动编程当然也不应该说是“反抽象的”，但的确与 “OO抽象”的思维方式是迥然不同，泾渭分明的， 如TAOUP一书中所述：“在Unix的模块化传统 和 围绕OO语言发展起来的使用模式之间，存在着紧张的对立关系” 应该说 数据驱动编程的思路与 结构化编程 和 OO 是正交的，更类似一种”跳出三界外，不在五行中”的做法。 或许这是您书中言明的，抽象分为”规范抽象”和”参数化抽象”的精义所在？（我不知道您的这两种抽象思想划分的名字来自于何处，但是我很喜欢这一对词）</p>
<p>考虑 数据驱动编程 与 OO 的不同机制，可以考虑一下 虚拟机 和 类框架 的不同： 虚拟机将逻辑细节（策略）抛给微语言代码；（只提供域原语，更稳定更灵活）——给用户出的是主观题，要求用户具有一定能力 类框架提供所有可能的领域组件，供用户组合；（提供了所有需要的东西，比较僵化）——给用户出的是选择题，对用户要求没有上面那么高</p>
<p>总而言之，在我看来，TAOUP 强调的这两种Unix世界下的传统 对应于 OO所强调的，可以概括如下： 1 不强调 数据抽象，但强调模块化封装，强调浅平透的设计，强调直观简单的具象； 2 不强调 类层次结构，但强调 策略和机制的分离（从而最多2层，策略层再分为 相对固定的部分 和 相对变动的部分 从而形成新的虚拟机层不容易，但不是不可能） 3 多态本来就不是OO所特有的，Linux内核里面 实现 VFS和各种格式的文件系统时就采用了这种很自然的做法；考虑到 duck type，mixin 等动态语言中的多态机制就更不能如此说了。 多态对于人类来说其实是很自然的做法，它给程序设计带来了模糊语义，从而使得代码具有更强的表现力，更简单的形式。</p>
<p>是否可以这么说：有两个世界，OO的世界，和OO以外的世界。 OO的世界对于程序员来说是理想化的，层次分明，抽象整洁的，非常便于处理复杂性的，各司其职共同协作处理问题的，即您书中所言的公民社会； OO以外的世界，具象横陈的世界，其复杂性的来源正如brooks所说 分为本质复杂性（业务本身的） 和 非本质复杂性（技术上的）。 ——但是在我看来，这些两种复杂性的差别并不在于哪些是业务的哪些是技术的哪些是业务的，而在于哪些是已经被固化下来的。</p>
<p>某个领域之所以处理起来还比较复杂，是因为：</p>
<p>1 领域本身的确很复杂：<br />a 领域内的逻辑本身的确很复杂，很难有确定的算法来处理<br />b 领域内的逻辑还是在变化中的，<br />c 领域内逻辑的数量是极大的。</p>
<p>2 领域基础条件的不成熟：<br />a 还没有探索出好的模型，或者该模型还没有被普遍认识到，<br />b 还没有制定出好的各个层面的规范并集体遵守，<br />c 还没有固化足够多的恰当的机制的部分。</p>
<p>3 人类心智的限制，一切的背后都有 人的因素 作为依据：<br />a 人同时关注的信息数量：7+-2 （所以要分模块）<br />b 人接收一组新信息的平均时间5s （所以要简单，系统总的模块数不要太多）<br />c 人思维的直观性（人的视觉能力 和 模糊思维能力），这意味这两点：<br />A “直”——更善于思考自己能直接接触把玩的东西；（所以要“浅平透”、使用具象的设计），<br />B “观”——更善于观图而不是推算逻辑；（所以要 表驱动法，数据驱动编程，要UML，要可视化编程——当然MDA是太理想化了）<br />d 人不能持续集中注意力（人在一定的代码行数中产生的bug数量的比例是一定的，所以语言有具有表现力，要体现表达的经济性）<br />所以要 机制与策略分离，要 数据和代码分离（数据驱动编程），要 微语言，要 DSL，要LOP，<br />e 人是有创造欲，有现实利益心的（只要偶可能总是不够遵从规范，或想创造规范谋利——只要成本能承受，在硬件领域就不行）</p>
<p>您点出的一句话 “如果抽象解决的复杂不及其带来的复杂，则说明该抽象是不当的。” 可能正点出了要点所在。<br />我绝无意批判“抽象”，我只是想说明，的确有另外的思维方式的存在—— 1 直观具象的、浅平透的设计，2 数据驱动编程</p>
<p>小子浅薄，借贵宝地洋洋洒洒说了这么多，愿得先生指点，解后进心中之惑，不胜感谢。</p>
<p>&nbsp;</p>
</blockquote>
<p><strong>作者</strong>hui答：</p>
<blockquote>
<p>面对如此长篇的评论，作为博主我表示压力很大：）事实上，无论是从评论的篇幅还是内容上看，皆可独立成篇。这里如此大段地引用，窃感颇有掠人之美的嫌疑。不过作为作者，也为能有这么一位认真的读者而欣慰不已。</p>
<p>言归正题。关于抽象，《冒号课堂》中虽有不少阐述，但限于篇幅，未能完全展开。正因意犹未尽，总想找机会续谈，以解心中之痒，你的提问客观上加速了这一进程。</p>
<p>应当说，你对抽象的理解基本上是到位的，只是稍有偏差。比如你提到抽象是一种<em>选择性的忽略</em>（selective ignorance），这无疑是正确的，只可惜你前面加上了“OO”的限制，这就有点画蛇添足了。OO尽管有其侧重的抽象形式，如数据抽象（data abstraction）、类型层级（type hierarchy）、多态抽象（polymorphic abstraction），但它们并未脱离一般抽象的范畴。</p>
<p>你在文中提到：具象对人类来说显然是一种更直观、更可操控，因而是显得更自然、更可理解的思维。听上去不无道理，但似乎有点执着于“抽象”的表面字义了。大多数人对“抽象”一词的第一反应是<em>形容词</em>的抽象（abstract），于是总不自觉地将之与“深奥”、“模糊”、“不直观”、“不具体”等相关联，这固非大谬，然而在编程设计中，人们关注或强调的抽象更多地当是<em>名词</em>的抽象（abstraction）或<em>动词</em>的抽象（abstract，指“抽象化”）。比如著名的针对接口编程（programming to interface）原则、依赖反转（Dependency Inversion）原则，本质上都是提倡针对抽象编程，这样做的好处暂且不提，代码一定会因此变得更不直观、更难操纵、更不自然、更难理解吗？恐怕未必吧。如果说增加的抽象层（abstraction layer）（比如abstract class、interface等）有时会制造一些复杂的话（但最终目的是为了减少复杂），那么普通API的调用则是再常见不过了。相比一个API清晰的接口和明确的规范，它的冗长、复杂的实现代码虽然更加具体，但不是更难以把握和理解吗？</p>
<p>为避免空泛的语言分析，这里顺手拣个例子：不妨注意看上段话中的“形容词”、“名词”、“动词”的字体，我在html源码中特意为它们分别加上了&lt;em&gt;标签，以示强调。为什么不用更简单的&lt;i&gt;标签呢？表面原因是：&lt;i&gt;标签已经过时了，并且只要读者的浏览器支持，此处&lt;em&gt;中的内容会显示比较美观的楷体而不是斜体（根据css中的设置），而深层原因是：标签&lt;em&gt;比&lt;i&gt;的抽象层次更高。看到此处，恐怕许多人会暗生疑惑：两个再简单不过的html标签，竟也扯上了抽象，是不是有些小题大作？再说，&lt;em&gt;与&lt;i&gt;不是一样地直观、具象吗？哪里有一点抽象的影子？其实不然。往大处说，抽象存在于每一段代码之中，哪怕是不入一般程序员法眼的html代码；更广泛地，抽象存在于人们每时每刻的思维之中——如果不有意无意地忽略一些细节，人类的思维寸步难行。往小处说，i是italic（斜体）的缩写，而em是emphasis（强调）的缩写，前者是手段，后者是目的；前者是表象，后者是实质。从这个意义上看，后者当然更抽象。认清这一点，便更能认识到css的价值所在：它把传统的混杂了内容与形式的html进行了抽象分解，让html专注于表达数据（data），css专注于表达样式（style）。这样html标签不再身兼内容与形式的双重职责（如&lt;i&gt;），而仅仅让标签的名称来表达规范的语义，让标签的内容来表达实际的数据（如&lt;em&gt;）。其好处是显而易见的：一方面，在数据不变的情况下，程序员可根据客户喜好定制不同的css；另一方面，相同的css可以应用于不同的数据。于是，形式与内容两方面的可维护性和可重用性都得以增强。与此同时，它们的抽象性也得到了增强，因为每一次分解——或者说，关注点的分离（SoC）——都是被分解者之间彼此无视对方的产物，即所谓的“选择性的忽略”。标签&lt;em&gt;之所以比&lt;i&gt;更抽象，正是由于它忽略了样式的细节（注：尽管标签&lt;i&gt;同样也能用css来定制表现形式，但它没有明确的规范语义）。最后，不要忘记标签&lt;i&gt;本身也蕴涵着抽象：在html规范的保证下，编码者根本不用费心如何让一段文字变成斜体，也不用关心用户使用何种浏览器。饶舌至此，是欲说明一点：设计（不限于编程设计）中的抽象与口语中的抽象并不完全等同，由于其相对性和普适性，看似具体的事物也有抽象的一面，看似抽象的事物也有具体的一面。在提及抽象时，建议少通过形容词（abstract）来联想，多用名词（abstraction）或动词（抽象化）。或者更好地，根据上下文把抽象直接替换为“规范”（specification）、“约定”（convention）、“协议”（protocol）、“概念”（concept）、“语义”（semantic）、“接口”（interface）、“服务”（service）、“职责”（responsibility）之类的名词，或“忽略”、“分离”、“提炼”、“抽取”之类的动词。而抽象的对立面，用“具体”、“具象”、“明晰”、“直观”等就不如“细节”（detail）、“实现”（implementation）等来得更贴切。</p>
<p>你在文中还提到：“everything is file”中的“file” 其实是一个概念模型，是一个“隐喻”…与其说Unix文化所强调的是一种抽象，不如说是一种隐喻。前一句非常正确，后一句值得商榷。正如书中（P181）指出的那样：模型是抽象的结晶，而抽象成功的标志之一是形成引起共识的概念。这些概念在现实世界中通常有自然而朴素的对应，否则难以达成共识。OOP之所以盛行，与它在许多应用中比较符合人类的认知模式是密不可分的。比如，在图形界面的设计中，让Window类对应于窗口，Button类对应于按钮、TextField类对应于文本框；在银行业务中，让Bank类对应于银行、Account类对应于帐户、Transaction类对应于交易，等等。无论是设计者还是使用者，都能迅速地理解程序的设计和编码意图。相反，如果有人把一些关联度不大的数据与函数粘合为一个类，整体上没有形成一个合理有效的概念，那么这个类能称之为真正意义上的抽象吗？当然，有些概念在现实世界中难以找到直接的对应物，这时人们就会借助隐喻（metaphor）了。file把生活中的物理文件与磁盘中的信息块对应起来，二者具有共通性，且形象易记，是一个成功的抽象。总之，建立模型、形成概念、采用隐喻的过程，与抽象不仅毫不矛盾，而且正好合辙。</p>
<p>除了抽象的层次、抽象的角度、概念模型、隐喻等以外，你还涉及了抽象的一些其他关键点，可见你对抽象的思考的确是非常深入的。其中包括：</p>
<ul>
	<li><em>抽象的扩展</em>（expansion）</li>
</ul>
<p>实际上，如果追根究底的话，file还不算得真正的隐喻，因为最初计算机中的file指的真是物理的<a href="http://en.wikipedia.org/wiki/Punched_card">打孔卡片</a>。因此，说是抽象的扩展更加确切。同为抽象的概念，Unix用户脑中的file远比Windows用户的更为抽象。在一般的windows用户的眼中，文件与文件夹（folder）是泾渭分明的。而在Unix中，file不仅包含普通文件，而且包括目录（directory）。不仅如此，在Unix下的链接（link），用户终端（terminal）、硬件设备（device）、网络连接（network connection）以及包括管道（pipe）、共享内存（shared memory）、UNIX域套接字（unix domain socket）等在内的进程间通信（IPC），通通可看成file。顺便提一下，你为了强调file的具象，提到“Unix/Linux <strong>真的</strong>把一切都实现成了文件”。可你下面所举的proc文件系统就不是传统意义上<strong>真正</strong>的文件系统，而是伪文件系统（pseudo-filesystem）。其实这仍是抽象的体现，具体来说，是抽象的扩展——从打孔卡片扩展到磁盘字节块、到I/O字节流（byte stream）、到更广泛的信息资源（resources）。同样，你引用TAOUP中的那句话，“Unix优先从 字节流，命名空间，进程 等 Unix基本抽象中得到更多的效用，而不是增加新的东西”，也反映Unix中的一些抽象并非一蹴而就的，而是逐步扩展的。扩展抽象（而非新建抽象）的一个好处是，减少因新概念的引进而带来的复杂性，并且强化原有的抽象；另一好处是，能让针对该抽象而设计的代码具有更广泛的适用性。</p>
<ul>
	<li><em>抽象的退化</em>（reduction）</li>
</ul>
<p>理想的抽象是对某一概念本质特征进行精准的捕捉和描述，不要多也不要少。多则限制该抽象的适用范围，少则不能充分利用该抽象的特性。然而在实际编程中，这几乎是不可能的。当抽象过多时，便会产生退化现象。比如，除非被卸载（unmount），/proc下的文件无法被删除；比如，一个声明为List类型的UnmodifiableList类不支持add、remove等操作；等等。</p>
<ul>
	<li><em>抽象的泄露</em>（leakage）</li>
</ul>
<p>“抽象只给你接口，隐喻给你必要的梗概信息，以避免‘抽象泄露’”。如果你该处的抽象泄露来自Joel的<a href="http://www.joelonsoftware.com/articles/LeakyAbstractions.html">The Law of Leaky Abstractions</a> ，那么这种泄露绝非隐喻所能避免的。事实上，这是因为抽象过少之故，即在某些场合下，抽象没有提供足够的信息。为了挖掘必要的信息，人们不得不钻开抽象严密封装的外壳（crust），深入到它声称能够忽略的、此时却不能忽略的内部细节，由此形成了泄露。Joel提到的本地磁盘与远程磁盘的差别是一例，另一个典型的例子是：在Windows下用鼠标拖放文件图标时，将移动相应的文件。这种抽象操作明显来自隐喻，然而当目标文件与源文件处于不同的驱动器时，操作的结果却是文件拷贝，从而增加了用户的学习成本。尽管程序开发者有充分的技术理由区分不同驱动器，但他们没有理由让用户关心其中的细节。<strong>程序设计者一定要牢记：你可以嘲笑用户的无知，但必须重视用户的抽象</strong>。与前面的例子不同，这种抽象的泄露原本是可以避免的，故而Alan Cooper在《about face》中对此专门作出了批评。此外，在静态类型的语言中，向下转型(downcasting)之所以不受推荐，不仅因为它不够安全，也因为试图深入到更底层类型的代码可能是抽象泄露的征兆。</p>
<p>“如果所有的操作系统的API都是符合Posix标准，所有的DBMS都遵从SQL标准，软件行业看起来也会像硬件行业一样规范有序而无需经常抽象”。有道理，不过如果注意到“规范”本身就是一种抽象，这么说可能更准确：软件行业如果像硬件行业一样规范有序，许多不必要的<em>中间</em>抽象就能省去了。</p>
<p>抽象是对付复杂的有效工具。一方面，通过抽象剔除或屏蔽非本质的部分、提炼并合并本质的部分（即《冒号课堂》中提到的减法与除法），从而降低复杂性；另一方面，通过抽象对复杂分而治之，比如，宏观上水平分层（layer），垂直分区（partition），微观上分模块、分步骤，等等。说到这里，有读者可能会不屑一顾：不就是简化、合并、分层、分块、分步吗？听起来一点也不新鲜，为何一定要拽出“抽象”这个词呢？且慢，除了复杂外，软件设计的另一大敌人是变化。（注：当然，如果把变化作为复杂的一个因素也未尝不可。为方便说明问题，这里特意把变化单列）现实中有这样的程序员，他能独立地设计并开发具有一定难度和规模的软件系统，但他的代码对用户需求的变动具有异常的敏感性，并且很难被其他程序员理解，甚至也很难被以后的自己理解。这是何故？仔细审查，会发现他的代码虽然逻辑正确、格式规范、形式上井井有条，各种设计模式的身影不时出没，甚至还遵循着业界推崇的各种最佳实践，唯独缺乏“抽象”的神韵。例如，虽然遵循了模块化的原则，但每个模块的职责并不清晰，分工也不明确，模块之间的交互耦合不像是前世的约定，更像是今生的邂逅。重要的API没有详细的文档说明，即使有，实现代码也常常不买账。当具体的代码缺失了抽象的外壳，便丧失了必要的缓冲区，一旦变化来临，难免引发代码的强烈震荡。总而言之，把抽象作为设计的出发点，不仅能更合理地组织代码，而且为代码之间划分了明确的<strong>概念边界</strong>。这种边界的划分越合理、边界的规范越明晰、边界内的代码越遵循规范，系统越稳定、越易维护。</p>
<p>凡事有度，过犹不及。对于软件开发而言，抽象再重要，它也只是手段而非目的。如果待解决的问题并不足够复杂、或者并不足够多变，那么过多的抽象反而成为掣肘。Eric Raymond在TAOUP中对OOP抱怨最大的地方是，OOP语言容易让抽象泛滥。这种指责有一定的道理，相比Unix编程中最常见的C语言，OOP语言增加了多种抽象机制，加之OO界倡导的设计模式、各类流行的框架、企业级架构模式等等，使得OO程序员习惯了为代码裹上层层厚重的抽象（尽管他们可能并未意识到抽象）。一旦抽象层过多、抽象点过密，会造成代码难以理解、优化、调试和维护，运行效率也会大打折扣，这与Unix提倡的“简单、清晰、透明”（即你提到的“浅平透”）格格不入。话说回来，真正有错的不是OOP或抽象本身，而是那些唯对象论者和滥用抽象者的错。</p>
<p>关于Unix中透明（此处指“透明得看得见”）与文本化的问题，我上次的回答过于匆忙，既然你此次再度提起，这里专门补充一下。有一个重要背景不能不提，Unix环境下的编程设计有一个显著的特征：程序经常以进程（process)为单位来模块化，TAOUP中称之为“multiprogramming”。Unix推崇这种设计方式，是有其天然的优势作基础的：相对廉价的进程创建、多种强大的进程间通信（IPC）机制、丰富多样的命令和工具、方便的脚本环境，等等。且不论由此卷入了并发范式，这种设计方式本身就很独特，与人们习惯的以函数或类为基本模块单位的做法大相异趣。你指出了Unix下软件设计透明的一面，这固然在一定意义上与抽象相对，但可能没有意识到这种透明很大程度上仍源自抽象。何出此言呢？方才言及，Unix下有丰富的应用，它们一般都遵从“只做一件事、做好一件事”的哲学，并且每个应用通常都有详尽的文档（man page），这不是绝佳的抽象和规范吗？大多数应用都是二进制文件，这不是不透明（opaque）吗？（即使有公开的源码，恐怕也很少人费事去读）可见，正是底层应用建立了高密度的抽象，才使得上层应用的抽象较为稀疏，从而显得简单、浅显和透明。此外，Unix下大量采用脚本编程，而脚本语言的抽象一般比通用程序语言、尤其是编译型语言少，因为后者为防御变化带来的源码修改而引入的许多抽象，对于前者往往并不那么重要。</p>
<p>说到文本化，与Unix的multiprogramming设计也是密切相关的。由于鼓励通过合作的进程来分解复杂，进程之间的交互便显得尤为重要，而文本流（text stream）是最通用的接口。一旦文本作为接口，哪怕再透明，它也具有了一定的抽象职能。至于为什么是文本而非二进制（binary），我以为并非问题的本质。诚然，文本更简单、直观、易于人工编辑，但如果文本格式不规范，同样会增加代码的复杂度。相反，如果二进制文件有严格而公开的规范，并且有方便的API进行读写操作，那么同样适合作为进程之间的通信接口。只是事实上，Unix下有大量对文本处理的工具，如cut、grep、head、sort等之类过滤器（filter）以及更强大的小语言sed、awk等，乃至大语言perl、python等，而二进制格式的工具相应就少得多。文本的直观透明性倒是带来一个好处，可用于挖掘抽象规范之外信息。举个小例子，要求在Linux下获得硬盘（假设mount在/dev/hda上）的序列号。直接用C语言实现并不困难，但如何用一行命令来实现呢？通过hdparm命令可以获得硬盘的一些参数，但没有专门针对序列号的，故而无法直接获得所需的数据。好在该命令的输出是文本的，经过观察和试验，我们完成了任务：<code>hdparm -i /dev/hda | awk -F= '/SerialNo=/{print $NF}'</code>。不过这并非没有后顾之忧，由于hdparm对输出格式并无明确的规范，上述代码并不能保证对Linux的所有发行版本都适用。如果不追求理论上的完美，该解法倒也差强人意：换台机器如果不灵，大不了再作调整。这很公平，从抽象之外获取的”非法“信息，自然要付出一定的代价——或者是不确定性的代价，或者是额外编码的代价。</p>
<p>上次你提到数据驱动编程与语言导向编程（LOP），当时我只强调了二者的区别，没有仔细思考二者的联系，一时失察，还望见谅。从数据驱动的特点来看，参数抽象可看作其最初级的形式，领域特定语言（DSL）可看作其最高级的形式。具体分析非三言两语所能言明，或许另开专题更好。你的其他思考也非常有价值，我对抽象的论述也远未尽意，只是夜深人乏，恕我暂不能一一展开，下次有机会再讨论吧。</p>
<p>最后，请别再称呼我为“先生”，我会恍惚觉得自己老朽了。</p></blockquote>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2011%2F04%2F18%2Fmore-words-on-abstraction%2F&amp;title=%E7%AD%94%E8%AF%BB%E8%80%85%E9%97%AE%283%29%E2%80%94%E2%80%94%E5%86%8D%E8%B0%88%E6%8A%BD%E8%B1%A1" id="wpa2a_2">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2011年03月20日 -- <a href="http://blog.zhenghui.org/2011/03/20/words-on-abstraction/" title="答读者问(2)——关于抽象">答读者问(2)——关于抽象</a> (6)</li><li>2010年06月3日 -- <a href="http://blog.zhenghui.org/2010/06/03/advice-on-programmer/" title="答读者问(1)——对程序员的一些个人建议">答读者问(1)——对程序员的一些个人建议</a> (6)</li><li>2009年08月21日 -- <a href="http://blog.zhenghui.org/2009/08/21/contents-of-colonclass/" title="《冒号课堂》目录">《冒号课堂》目录</a> (29)</li><li>2010年03月10日 -- <a href="http://blog.zhenghui.org/2010/03/10/colonclass-sales-tw/" title="《冒号课堂》在台湾上市">《冒号课堂》在台湾上市</a> (5)</li><li>2010年01月1日 -- <a href="http://blog.zhenghui.org/2010/01/01/colonclass-bbs-openning/" title="《冒号论坛》开放">《冒号论坛》开放</a> (1)</li><li>2009年11月7日 -- <a href="http://blog.zhenghui.org/2009/11/07/colonclass-suggestions/" title="《冒号课堂》意见收集">《冒号课堂》意见收集</a> (56)</li><li>2009年10月26日 -- <a href="http://blog.zhenghui.org/2009/10/26/colonclass-sales/" title="《冒号课堂》上市">《冒号课堂》上市</a> (8)</li><li>2009年10月16日 -- <a href="http://blog.zhenghui.org/2009/10/16/preface-of-colonclass/" title="《冒号课堂》自序">《冒号课堂》自序</a> (7)</li><li>2009年09月10日 -- <a href="http://blog.zhenghui.org/2009/09/10/colon-class-3_3/" title="冒号课堂§3.3：切面范式">冒号课堂§3.3：切面范式</a> (4)</li><li>2009年09月6日 -- <a href="http://blog.zhenghui.org/2009/09/06/colon-class-2_3/" title="冒号课堂§2.3：对象范式">冒号课堂§2.3：对象范式</a> (3)</li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2011/04/18/more-words-on-abstraction/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>答读者问(2)——关于抽象</title>
		<link>http://blog.zhenghui.org/2011/03/20/words-on-abstraction/</link>
		<comments>http://blog.zhenghui.org/2011/03/20/words-on-abstraction/#comments</comments>
		<pubDate>Sun, 20 Mar 2011 15:27:42 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[对象导向]]></category>
		<category><![CDATA[抽象]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=799</guid>
		<description><![CDATA[答一位读者关于抽象的提问 [...]]]></description>
			<content:encoded><![CDATA[<h2><strong>答读者问(2)</strong></h2>
<p>本贴是临时回答一位读者的提问，匆匆草就，以后有空再整理。长时间未发博，此篇权作凑数。</p>
<p><strong>读者</strong>七心葵问：</p>
<blockquote>
<p>个人做linux下c/c++游戏开发，上学的时候一度 “抽象” “隐喻” 这类的东西似懂非懂；后来工作后慢慢觉得其实 “抽象”这个词还是很宽泛的，我一般将“抽象”狭义的理解成 OO里面 接口与实现 的提法，即“有选择地忽略”。 但是我认为在计算机科学或工程中，“抽象”一直有其对立面，“反抽象”，也就是《Unix编程文化》里强调的“浅平透”（个人理解），不知郑老师是否看过此书？对于该书尤其是对于“OO”的评价一节有何看法？ booch的《面向对象分析与设计》中曾经提到，有人做过统计，几乎所有的设计的方法学大体上都可以分为三类：1 结构化设计 2 数据驱动设计 3 面向对象设计。 对于“数据驱动设计（编程）”，我在工作中经常彷徨于是用c的过程式语言+数据驱动设计呢，还是完全OO化设计，郑老师《冒号课堂》一书中似没有提到多少（个人认为LOP可以看作是数据驱动编程的一种高级形式，也就是DSL或者微语言），是否可以做一个简单介绍？</p></blockquote>
<p>&nbsp;</p>

<p><strong>作者</strong>hui答：</p>
<blockquote>
<p>关于抽象，实是大有文章可做。早就计划就此主题展开一系列的讨论，苦于近来项目缠身，一直无暇顾及。The art of UNIX programming毫无疑问是本好书，但要说unix哲学是反抽象，则不太赞同。事实上，unix成功之处恰恰在于充分运用了抽象。“everything is file”就是最典型的抽象，文件这一概念在unix中的广泛而抽象的应用为unix的设计增色不少。unix提倡&#8221;write programs that do one thing and do it well&#8221;，便是强调每个小程序都是具有单一职责的抽象功能体。unix推崇filter模式，并极力推荐遵循规范的plain text作为程序之间最通用的接口。unix强调portability比efficiency更重要，而可移植的通常是普适的、抽象的，为了效率则常常“因地制宜”，其结果是以牺牲抽象为代价。（当然这并不意味着抽象是神圣不可侵犯的，也不意味着抽象越多越好）至于”Write simple parts connected by clean interfaces“，“Separate policy from mechanism; separate interfaces from engines“等更是抽象原则的贯彻。 抽象与封装紧密相关，而在字面上封装似乎与透明相对，或许你因此感到”透“（transparency）是抽象的反面？这恐怕是一种误解。其实”透“意味着简单、干净、优雅、可读性（readability）强、规范（specification）清晰、协议（protocol）明确。比如，text文件比binary文件更简单、更”透“，但并不意味着比后者的抽象程度低。相反，私有的（proprietary）文件格式由于规范程度低或不够公开而导致抽象程度较低，对接口的要求更苛刻，与其他程序难以合作，应用起来范围更狭窄或更困难。总之，unix提倡transparency，鼓励接口简单、明晰，以提高代码的可读性、可维护性，让bug难以藏身，这个与抽象性毫无矛盾。另一个类似的常见误解是，把设计上的封装与源码的封闭关联起来，或者以为封装的目的是为了代码的安全性，这些都源于对抽象或封装的认识上的模糊。此外，你提到的“浅、平”，或许指unix提倡的KISS原则（Keep it Simple, Stupid）。不过抽象本不一定意味着复杂、深奥，它只是处理复杂的一种行之有效的手段。<strong>如果抽象解决的复杂不及其带来的复杂，则说明该抽象是不当的</strong>。文字有时候是有欺骗性的。《冒号课堂》的附注中曾提到一个例子，正是transparent。很多时候它在英文中的意思不是“因透明而看得见”，而是”因透明而看不见“。如果一个接口后面的机制对用户而言是transparent的，正说明它是抽象的，而不是相反。另外值得指出的是，抽象是一个相对的概念，脱离上下文来谈论抽象或具体是毫无意义的。</p>
<p>提到对OO的评价，拥趸固然众多，大唱反调为数亦不少，其中不乏业界名流。如Linux之父Linus Torvalds，STL之父Alexander Stepanov，包括The art of UNIX programming的作者Eric Raymond都曾对OOP作出了十分尖锐的批判。其实名人同样会偏激，甚至比普通人更偏激，因为他们有牛气、有傲气、有底气。他们的批判或许一针见血，但一般人既无深厚的功力，又不了解评论的背景，看看热闹可以，动辄怀疑正在使用的技术就大可不必了。（这就好比一位手持木剑的高手胜了一位手持铁剑的低手，然后不屑地对后者说：其实，木剑更锋利）一切技术都只是工具，决定因素还是使用者对工具的理解程度和熟练程度。相比过程式，OO（准确地说，是融合过程式的OO）从理论上说肯定是更为强大，因为它在前者的基础上增加了新的抽象机制。但如果不理解抽象的本质，为了OO而OO，代码可能比纯粹过程式的更糟。高级的工具并不一定能带来高级的产品。这里多说一句，技术人员在没有达到足够火候的时候，不要随意臧否某项技术。当你尽情吹捧它时，小心背后的陷阱；当你大肆批驳它时，你可能根本未解其妙。淡定，淡定。</p>
<p>Booch提到的三类软件设计方法：Top-down structured design、Data-driven design与Object-oriented design自然都是重要的设计方式，但还存在其他的方法，如event-driven，stream-processing、logic-driven、knowledge-driven等。你提到究竟该用过程式语言+数据驱动设计，还是完全OO化设计，很抱歉，我只能回答：无法一概而论，因项目需求而异、因开发者而异。（不过“<strong>完全</strong>OO化”从来是不必要的）一方面，不同的项目domain不同，有的侧重数据、有的侧重算法、有的侧重流程、有的侧重交互、有的侧重事件，相应采用的设计与语言也会有所分别。另一方面，与开发者对设计方法和语言的有关，如果你特别擅长C，对C++不熟悉或者不喜欢，那么完全不必一定要用OO。（当然，OO思想同样可以用C来实现）</p>
<p>LOP与数据驱动还是有分别的，它更侧重的主要不是数据，而是某个特殊领域。它通过提高语言的抽象性（更贴近所关注的高层领域逻辑，远离底层语言的细节）来提高代码的可读性、可维护性和开发者的效率。</p>
<p>希望以上简略的回答能对你有所帮助，时间有限，恕未能完全展开。待得空时，会在这方面进行更具体、更深入的阐述。</p>
<p>&nbsp;</p>
</blockquote>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2011%2F03%2F20%2Fwords-on-abstraction%2F&amp;title=%E7%AD%94%E8%AF%BB%E8%80%85%E9%97%AE%282%29%E2%80%94%E2%80%94%E5%85%B3%E4%BA%8E%E6%8A%BD%E8%B1%A1" id="wpa2a_4">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2011年04月18日 -- <a href="http://blog.zhenghui.org/2011/04/18/more-words-on-abstraction/" title="答读者问(3)——再谈抽象">答读者问(3)——再谈抽象</a> (1)</li><li>2010年06月3日 -- <a href="http://blog.zhenghui.org/2010/06/03/advice-on-programmer/" title="答读者问(1)——对程序员的一些个人建议">答读者问(1)——对程序员的一些个人建议</a> (6)</li><li>2010年03月10日 -- <a href="http://blog.zhenghui.org/2010/03/10/colonclass-sales-tw/" title="《冒号课堂》在台湾上市">《冒号课堂》在台湾上市</a> (5)</li><li>2010年01月1日 -- <a href="http://blog.zhenghui.org/2010/01/01/colonclass-bbs-openning/" title="《冒号论坛》开放">《冒号论坛》开放</a> (1)</li><li>2009年11月7日 -- <a href="http://blog.zhenghui.org/2009/11/07/colonclass-suggestions/" title="《冒号课堂》意见收集">《冒号课堂》意见收集</a> (56)</li><li>2009年10月26日 -- <a href="http://blog.zhenghui.org/2009/10/26/colonclass-sales/" title="《冒号课堂》上市">《冒号课堂》上市</a> (8)</li><li>2009年10月16日 -- <a href="http://blog.zhenghui.org/2009/10/16/preface-of-colonclass/" title="《冒号课堂》自序">《冒号课堂》自序</a> (7)</li><li>2009年08月29日 -- <a href="http://blog.zhenghui.org/2009/08/29/colon-class-1_4/" title="冒号课堂§1.4：初识范式">冒号课堂§1.4：初识范式</a> (2)</li><li>2009年08月21日 -- <a href="http://blog.zhenghui.org/2009/08/21/contents-of-colonclass/" title="《冒号课堂》目录">《冒号课堂》目录</a> (29)</li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2011/03/20/words-on-abstraction/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>答读者问(1)——对程序员的一些个人建议</title>
		<link>http://blog.zhenghui.org/2010/06/03/advice-on-programmer/</link>
		<comments>http://blog.zhenghui.org/2010/06/03/advice-on-programmer/#comments</comments>
		<pubDate>Thu, 03 Jun 2010 04:35:30 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[计算机]]></category>
		<category><![CDATA[抽象]]></category>
		<category><![CDATA[数学]]></category>
		<category><![CDATA[程序员]]></category>
		<category><![CDATA[英语]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=716</guid>
		<description><![CDATA[答一位《冒号课堂》读者的提问 [...]]]></description>
			<content:encoded><![CDATA[
<h2><strong>答读者问</strong></h2>
<p>以下内容摘自<a href="http://bbs.zhenghui.org">冒号论坛</a>的一个<a href="http://bbs.zhenghui.org/topic/%E8%8F%9C%E9%B8%9F%E8%AF%BB%E3%80%8A%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%E3%80%8B%E5%90%8E%E7%9A%84%E6%84%9F%E8%A7%A6%EF%BC%8C%E6%9C%9B%E9%83%91%E6%99%96%E8%80%81%E5%B8%88%E8%A7%A3%E6%83%91">话题</a>，考虑其具有一定的典型性，故转至<a href="http://blog.zhenghui.org">冒号空间</a>，希望能对更多的读者有所帮助，也算是一种<span style="text-decoration: underline;">代码重用</span>吧。</p>
<p>&nbsp;</p>

<p><strong>读者Jee问</strong>：</p>
<blockquote>
<p>之前在Top  language里的一位网友像我推荐您的《冒号课堂》，书中eric向您建议开设社区，我猜测可能会有，就找到您的博客发现此地，很幸运您是一个如此有责任心的作者。</p>
<p>我是一名没有什么理论基础的不合格的计算机专业毕业生，毕业后却对软件方面技术非常感兴趣，可能与个性有关。于是在这近2年的找工作和工作过程中看了一些书，也和一些过来人聊过，总体来说让我对软件编程有了一点认识。在阅读您的冒号课堂之前，我曾一度认为我所差的是经验和一些诸如高级算法之类的进阶技术， 可现在，一个用了一个多月时间的夜晚阅读《冒号课堂》之后的我发现我所差的不仅仅是那些，而是最基础最根本的对计算机本身的认识，对数学的认识，对软件工程的认识。</p>
<p>我不想能有速成一说，只想能够现在正视自己，脚踏实地的一点一点的学习和进步，哪怕让我自学4年大学课程也未尝不可，只是有些时候有点找不到一个开始。我数学不好，作为一名程序员我想这是个令人沮丧的事实，我英语也不好，当看到蹩脚的一些翻译著作后痛苦不已。我想尝试着去改变这些，但是却不知该如何去做， 您知道，作为一名已经进入社会的成年人，我需要承受一些生存的压力和一些生活的负担，我希望能更好的利用每天那抽出来的时间，所以望郑晖老师能给我指出一条明道。</p>
<p>我一直没有说我从事的语言和方向，因为我知道这并不是核心，也不是想从您这得到如何学习XX语言等。万分打扰，还望见谅。</p></blockquote>
<p>&nbsp;</p>

<p><strong>作者</strong>hui答：</p>
<blockquote>
<p>你提到的问题十分典型，我非常理解你的心情，同时也非常乐意分享一些个人的看法。</p>
<p>虽然你在言语之中流露出不少负面的情绪，但我看到的却是正面的希望。首先，你对软件技术很感兴趣，而兴趣是学习和工作的最大动力。一般说来，我也没兴趣回答那些对编程不感兴趣者的有关编程的问题。一方面，我会劝他们改行，否则彼此都痛苦；另一方面，我建议的方法通常也不适合他们。其次，你很清楚地意识到自己在哪些方面不足，这是一切进步的基础。许多程序员意识不到自己的无知，甚至自以为足够有知，那又如何能进步呢？最后，你不指望任何捷径，愿意通过踏踏实实的学习来弥补不足。在浮躁之风盛行的当下，这点也是难能可贵的。</p>
<p>关于数学基础，窃以为并非什么太大的问题。几乎每个得知我数学背景的人都会对我说：哦，学数学的人来学计算机自然容易啦。事实上，这种观点虽然极为普遍，但也极为肤浅。本人从事数学14年（从本科算起）、从事计算机12年（与前者有部分重合），在这一点上还是比较有发言权的。事先说明，以下提到的数学不包括高中数学。其实大多数从事软件开发的人员用不到太多的数学知识，他们只需要正常的逻辑思维能力和抽象思维能力。整天拿数学说事，要么是无知，要么是找借口，要么是装高深。当然，我不否认一些高级算法、计算机理论以及人工智能等领域可能涉及到高深的数学知识（其实也只是图论、组合数学、数论、概率论、计算几何、抽象代数、数学逻辑等中的一小部分），但那毕竟只是少数。我也不否认自己的数学背景有助于对编程的理解，但投入产出比太低，不值得作为经验来推广。不过若想成为一位计算机科学家，那就另作别论了——这时数学懂得再多也会嫌少的。</p>
<p>倒是英语我希望你更重视些。我在<a href="http://blog.zhenghui.org/2009/09/10/colon- class-3_3/">《冒号课堂》中</a>专门提过阅读原著的必要性，而且你也意识到译著的质量问题。建议不必特地去学习英语（你本来就会了，不是吗？），只要坚持读经典原著即可。其实，计算机方面的英文算是很容易的了，关键是克服自己的惯性和惰性。开始可能不习惯，看多了就习惯了。在此提醒一点，在阅读时请有意识地培养自己对英语的语感，就像编程时要有意识地培养自己对编程语言的语感一样。</p>
<p>总之，<strong>对于程序员来说，数学没有人们认为的那么重要，英语没有人们认为的那么不重要</strong>。</p>
<p>再说说专业方面的问题。你提到愿意重新自学大学课程，虽精神可嘉，但未必可取。从软件（或建筑）设计的观点来看，这是<span style="text-decoration: underline;">bottom-up</span>法。作为学生，最好采用这种方法，但你已经参加工作了，所以我建议你更多地采用<span style="text-decoration: underline;">top-down</span>法。这当然不是轻视基础知识，而是认为获取知识最高效的方法莫过于<span style="text-decoration: underline;">按需</span>（on  demand）学习。在实际工作中意识到某个知识点的重要性，从而有针对性地弥补短板，这样学习起来不仅更有效率，也更有兴味。需要强调的是，绝不能只是 “头痛医头”，而要“拔萝卜带出泥”。只有寻根究底、以点带面，才能快速有效地建立起自己的知识结构体系。对于软件开发这类实践性很强的专业来说，该法尤其奏效。</p>
<p>话又说回来，这种项目驱动式的学习方法也是有一定局限的。毕竟大多项目涉及的深度和广度通常都很有限，单纯凭此建立起来的知识体系不可能非常完善。 这就需要平时有计划地阅读一些经典著作以加强深度，并定期浏览一些高质量的技术网站以加强广度。</p>
<p>以上谈的都是一些较为宏观的建议，我想你需要的是更加具体的建议。《冒号课堂》上已经阐述了不少关于编程语言、编程范式、设计原则方面的观点，此处不复赘言。我想特别强调一点——<strong>把握抽象</strong>（abstraction）。事实上，无论是在书中还是本论坛中，我都不厌其烦地再三提到抽象的重要性，今后有时间还会深入地挖掘这一主题。对编程的语言、范式、设计、实现体会得越深，对抽象体会得也越深。借用Hakell的设计者之一Paul  Hudak的一句略带夸张的话（overstatement）：<em>“abstraction, abstraction, abstraction” are  the three most important things in  programming</em>。一定会有人会问：难道编程语言就不重要了吗？设计模式就不重要了吗？算法设计就不重要了吗？那是他们尚未真正理解何为抽象——抽象不仅渗透在编程范式之中，也渗透在编程语言之中；不仅反映在设计原则之中，也反映在设计模式之中；不仅体现在架构设计之中，也体现在算法设计之中。</p>
<p>说来也怪，明明是想提“具体”建议的，偏偏又扯出了“抽象”，大概不是你想要的答案吧？既然你是计算机专业毕业的，又有一定的工作经验，其实也不需要太过具体的建议。你的苦恼是找不到努力的方向，而这个方向恐怕还是得靠自己去寻找。建议试试两种方法：研读一本有趣的名著或开发一个有趣的应用。只要深入其中，相信绝不会再为找不到方向而发愁，说不定倒会为方向太多而发愁呢。</p>
<p>最后，说句更实际点的话：如果平时能有意识地积累一些计算机以外的<span style="text-decoration: underline;">领域知识</span>（domain  knowledge），比如金融、电信、教育、企业管理等等，对提高个人在IT业的核心竞争力也是大有裨益的。当然，前提是你有兴趣或有条件获得这些知识。</p>
<p>一家之言，希望能对你有所帮助。</p></blockquote>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2010%2F06%2F03%2Fadvice-on-programmer%2F&amp;title=%E7%AD%94%E8%AF%BB%E8%80%85%E9%97%AE%281%29%E2%80%94%E2%80%94%E5%AF%B9%E7%A8%8B%E5%BA%8F%E5%91%98%E7%9A%84%E4%B8%80%E4%BA%9B%E4%B8%AA%E4%BA%BA%E5%BB%BA%E8%AE%AE" id="wpa2a_6">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2011年04月18日 -- <a href="http://blog.zhenghui.org/2011/04/18/more-words-on-abstraction/" title="答读者问(3)——再谈抽象">答读者问(3)——再谈抽象</a> (1)</li><li>2011年03月20日 -- <a href="http://blog.zhenghui.org/2011/03/20/words-on-abstraction/" title="答读者问(2)——关于抽象">答读者问(2)——关于抽象</a> (6)</li><li>2009年10月16日 -- <a href="http://blog.zhenghui.org/2009/10/16/preface-of-colonclass/" title="《冒号课堂》自序">《冒号课堂》自序</a> (7)</li><li>2009年08月21日 -- <a href="http://blog.zhenghui.org/2009/08/21/contents-of-colonclass/" title="《冒号课堂》目录">《冒号课堂》目录</a> (29)</li><li>2010年09月20日 -- <a href="http://blog.zhenghui.org/2010/09/20/hard-thinking-and-soft-thinking-3/" title="论思维的刚性与柔性（科学的迷信-3）">论思维的刚性与柔性（科学的迷信-3）</a> (0)</li><li>2010年03月10日 -- <a href="http://blog.zhenghui.org/2010/03/10/colonclass-sales-tw/" title="《冒号课堂》在台湾上市">《冒号课堂》在台湾上市</a> (5)</li><li>2010年01月1日 -- <a href="http://blog.zhenghui.org/2010/01/01/colonclass-bbs-openning/" title="《冒号论坛》开放">《冒号论坛》开放</a> (1)</li><li>2009年11月7日 -- <a href="http://blog.zhenghui.org/2009/11/07/colonclass-suggestions/" title="《冒号课堂》意见收集">《冒号课堂》意见收集</a> (56)</li><li>2009年10月26日 -- <a href="http://blog.zhenghui.org/2009/10/26/colonclass-sales/" title="《冒号课堂》上市">《冒号课堂》上市</a> (8)</li><li>2009年09月27日 -- <a href="http://blog.zhenghui.org/2009/09/27/colon-class-5_4/" title="冒号课堂§5.4：语言误区">冒号课堂§5.4：语言误区</a> (0)</li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2010/06/03/advice-on-programmer/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>《冒号课堂》在台湾上市</title>
		<link>http://blog.zhenghui.org/2010/03/10/colonclass-sales-tw/</link>
		<comments>http://blog.zhenghui.org/2010/03/10/colonclass-sales-tw/#comments</comments>
		<pubDate>Wed, 10 Mar 2010 03:14:53 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[上市]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=644</guid>
		<description><![CDATA[公告：《冒号课堂》今起在台湾上市 [...]]]></description>
			<content:encoded><![CDATA[
<h2><strong>公告</strong>：《冒号课堂》今起在台湾上市。</h2>
<p>&nbsp;</p>

<p><strong>基本信息</strong>：</p>
<div><img src="http://blog.zhenghui.org/img/colonclass/bookcover-tw.jpg" alt="" /></div>
<p>书名：程式設計範式與OOP的思考術：冒號老師的十三堂課</p>
<p>作者：鄭暉</p>
<p>书号：9789862013090</p>
<p>出版社：<a href="http://www.drmaster.com.tw/Bookinfo.asp?BookID=PG20288">博碩文化股份有限公司</a></p>
<p>出版日期：2010年03月10日</p>
<p>定价：NT$490</p>
<p>页码：496</p>
<p>规格：17*23</p>
<p>语言：中文/繁體</p>
<p>&nbsp;</p>

<p><strong>网站主页：</strong></p>
<p>博客：<a href="http://blog.zhenghui.org/">http://blog.zhenghui.org/</a></p>
<p>豆瓣：<a href="http://www.douban.com/subject/4031906/">http://www.douban.com/subject/4031906/</a></p>
<p>&nbsp;</p>

<p><strong>网上书店</strong>：</p>
<p>博客來網路書店：  <a href="http://www.books.com.tw/exep/prod/booksfile.php?item=0010462958">http://www.books.com.tw/exep/prod/booksfile.php?item=0010462958</a></p>
<p>誠品網路書店：<a href="http://www.eslite.com/product.aspx?pgid=1001120321928546&amp;cate=156&amp;sub=212">http://www.eslite.com/product.aspx?pgid=1001120321928546&amp;cate=156&amp;sub=212</a></p>
<p>天瓏網路書店：<a href="https://tlsj.tenlong.com.tw/WebModule/BookSearch/bookSearchViewAction.do?isbn=9789862013090&amp;sid=55495">https://tlsj.tenlong.com.tw/WebModule/BookSearch/bookSearchViewAction.do?isbn=9789862013090&amp;sid=55495</a></p>
<p>金石堂網路書店：<a href="http://books.yam.com/book/Book_Page.asp?ActId=future&amp;LID=1109&amp;KMCode=2014713210920">http://books.yam.com/book/Book_Page.asp?ActId=future&amp;LID=1109&amp;KMCode=2014713210920</a></p>
<p>金石堂網路書店：<a href="http://www.kingstone.com.tw/book/book_page.asp?LID=se008&amp;kmcode=2014713210920&amp;Actid=wise">http://www.kingstone.com.tw/book/book_page.asp?LID=se008&amp;kmcode=2014713210920&amp;Actid=wise</a></p>
<p>三民網路書店：<a href="http://www.sanmin.com.tw/page-product.asp?pf_id=99D155e9w102s87g103V70u111l129uOFfJOi1060LoB">http://www.sanmin.com.tw/page-product.asp?pf_id=99D155e9w102s87g103V70u111l129uOFfJOi1060LoB</a></p>
<p>華通書坊：<a href="http://www.huatung.com/index.php?PHP_action=view_book&amp;PHP_bookid=6qe9ml5wj9pm7qj3ck616291268305819">http://www.huatung.com/index.php?PHP_action=view_book&amp;PHP_bookid=6qe9ml5wj9pm7qj3ck616291268305819</a></p>
<p>法錸城網路書店：<a href="http://www.flytownbooks.com/itemdesc.asp?ProductID=PG20288">http://www.flytownbooks.com/itemdesc.asp?ProductID=PG20288</a></p>
<p>益大資訊：<a href="http://etaitbook08.pixnet.net/blog/post/30557874">http://etaitbook08.pixnet.net/blog/post/30557874</a></p>
<p>3dWoo電腦書店：<a href="http://www.3dwoo.com/showBookDetail.asp?nb=25377">http://www.3dwoo.com/showBookDetail.asp?nb=25377</a></p>
<p>華文網網路書店：<a href="http://www.book4u.com.tw/book_Detail.asp?goods_ser=kk0263881">http://www.book4u.com.tw/book_Detail.asp?goods_ser=kk0263881</a></p>
<p>&nbsp;</p>

<p>CP1897.com商務網上書店（香港）：<a href="http://www.cp1897.com.hk/product_info.php?BookId=9789862013090">http://www.cp1897.com.hk/product_info.php?BookId=9789862013090</a></p>
<p>&nbsp;</p>

<p><strong>各地书店</strong>：</p>
<p><a href="http://www.drmaster.com.tw/service-03.asp">http://www.drmaster.com.tw/service-03.asp</a></p>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2010%2F03%2F10%2Fcolonclass-sales-tw%2F&amp;title=%E3%80%8A%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%E3%80%8B%E5%9C%A8%E5%8F%B0%E6%B9%BE%E4%B8%8A%E5%B8%82" id="wpa2a_8">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2009年10月26日 -- <a href="http://blog.zhenghui.org/2009/10/26/colonclass-sales/" title="《冒号课堂》上市">《冒号课堂》上市</a> (8)</li><li>2011年04月18日 -- <a href="http://blog.zhenghui.org/2011/04/18/more-words-on-abstraction/" title="答读者问(3)——再谈抽象">答读者问(3)——再谈抽象</a> (1)</li><li>2011年03月20日 -- <a href="http://blog.zhenghui.org/2011/03/20/words-on-abstraction/" title="答读者问(2)——关于抽象">答读者问(2)——关于抽象</a> (6)</li><li>2010年06月3日 -- <a href="http://blog.zhenghui.org/2010/06/03/advice-on-programmer/" title="答读者问(1)——对程序员的一些个人建议">答读者问(1)——对程序员的一些个人建议</a> (6)</li><li>2010年01月1日 -- <a href="http://blog.zhenghui.org/2010/01/01/colonclass-bbs-openning/" title="《冒号论坛》开放">《冒号论坛》开放</a> (1)</li><li>2009年11月7日 -- <a href="http://blog.zhenghui.org/2009/11/07/colonclass-suggestions/" title="《冒号课堂》意见收集">《冒号课堂》意见收集</a> (56)</li><li>2009年10月16日 -- <a href="http://blog.zhenghui.org/2009/10/16/preface-of-colonclass/" title="《冒号课堂》自序">《冒号课堂》自序</a> (7)</li><li>2009年08月21日 -- <a href="http://blog.zhenghui.org/2009/08/21/contents-of-colonclass/" title="《冒号课堂》目录">《冒号课堂》目录</a> (29)</li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2010/03/10/colonclass-sales-tw/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>《冒号论坛》开放</title>
		<link>http://blog.zhenghui.org/2010/01/01/colonclass-bbs-openning/</link>
		<comments>http://blog.zhenghui.org/2010/01/01/colonclass-bbs-openning/#comments</comments>
		<pubDate>Fri, 01 Jan 2010 03:10:51 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[冒号论坛]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=625</guid>
		<description><![CDATA[公告：本博新开《冒号论坛》，欢迎赏光！ [...]]]></description>
			<content:encoded><![CDATA[
<h2 style="text-align: center;">致读者</h2>
<div>
<p>值此新年到来之际，本博特开辟<a href="http://bbs.zhenghui.org">冒号论坛</a>，欢迎各位赏光！</p>
<p>目前论坛上已开设一个主讨论区（forum）：<a href="http://bbs.zhenghui.org/forum/colonclass-discussion">《冒号课堂》讨论区</a>，用于<a href="http://www.douban.com/subject/4031906/">《冒号课堂——编程范式与OOP思想》</a>的作者和读者之间的交流，包括回复读者对书中的疑问、方便读者给本书提意见或建议等等。根据需要，今后也可能开设其他的讨论区。</p>
<p>稍有不便的是，您需要登录才能在该论坛回帖或开帖。好在手续极其简单，您只需在论坛上方点击<a href="http://bbs.zhenghui.org/register.php">Register</a>，填写一个用户名和email地址即可登记成功。不久您会收到一封email，里面有您的密码，凭此便可登录论坛。第一次登录后，您可以点击自己的用户名，修改密码和个人信息。如果您对某个讨论（forum）或话题（topic）感兴趣，可以订阅相应的RSS。另一个附带的好处是，该论坛与博客是集成的，登录论坛即可登录博客（反之亦然），这样在博客上回帖时就不必再输入个人信息了。如果您在登录时遇到困难，请在此帖留言。多谢您的合作！</p>
</div>
<div>
<p style="text-align: right;">郑晖</p>
<p style="text-align: right;">2010年1月1日</p>
</div>
<p>&nbsp;</p>

<div>
<p>PS:</p>
<p>由于近来广告贴过多，论坛用户登记请先到<a href="http://blog.zhenghui.org/contact/">此处</a>留言，博主会尽早答复。不便之处，还请多多见谅！</p>
</div>
<div>
<p style="text-align: right;">郑晖</p>
<p style="text-align: right;">2010年11月8日</p>
</div>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2010%2F01%2F01%2Fcolonclass-bbs-openning%2F&amp;title=%E3%80%8A%E5%86%92%E5%8F%B7%E8%AE%BA%E5%9D%9B%E3%80%8B%E5%BC%80%E6%94%BE" id="wpa2a_10">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2011年04月18日 -- <a href="http://blog.zhenghui.org/2011/04/18/more-words-on-abstraction/" title="答读者问(3)——再谈抽象">答读者问(3)——再谈抽象</a> (1)</li><li>2011年03月20日 -- <a href="http://blog.zhenghui.org/2011/03/20/words-on-abstraction/" title="答读者问(2)——关于抽象">答读者问(2)——关于抽象</a> (6)</li><li>2010年06月3日 -- <a href="http://blog.zhenghui.org/2010/06/03/advice-on-programmer/" title="答读者问(1)——对程序员的一些个人建议">答读者问(1)——对程序员的一些个人建议</a> (6)</li><li>2010年03月10日 -- <a href="http://blog.zhenghui.org/2010/03/10/colonclass-sales-tw/" title="《冒号课堂》在台湾上市">《冒号课堂》在台湾上市</a> (5)</li><li>2009年11月7日 -- <a href="http://blog.zhenghui.org/2009/11/07/colonclass-suggestions/" title="《冒号课堂》意见收集">《冒号课堂》意见收集</a> (56)</li><li>2009年10月26日 -- <a href="http://blog.zhenghui.org/2009/10/26/colonclass-sales/" title="《冒号课堂》上市">《冒号课堂》上市</a> (8)</li><li>2009年10月16日 -- <a href="http://blog.zhenghui.org/2009/10/16/preface-of-colonclass/" title="《冒号课堂》自序">《冒号课堂》自序</a> (7)</li><li>2009年08月21日 -- <a href="http://blog.zhenghui.org/2009/08/21/contents-of-colonclass/" title="《冒号课堂》目录">《冒号课堂》目录</a> (29)</li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2010/01/01/colonclass-bbs-openning/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>《冒号课堂》意见收集</title>
		<link>http://blog.zhenghui.org/2009/11/07/colonclass-suggestions/</link>
		<comments>http://blog.zhenghui.org/2009/11/07/colonclass-suggestions/#comments</comments>
		<pubDate>Sat, 07 Nov 2009 07:48:18 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=549</guid>
		<description><![CDATA[征求对《冒号课堂》一书的问题、意见和建议 [...]]]></description>
			<content:encoded><![CDATA[<h2 style="text-align: center;">致读者</h2>
<div>
<p>《冒号课堂》业已<a href="http://blog.zhenghui.org/2009/10/26/colonclass-sales/">上市</a>，如果您在本书中发现任何谬误或疑问，或对本书有任何意见或建议，敬请回复此帖。小至标点符号，大至思想观点，但凡有疑，尽可畅所欲言，笔者将不胜感谢！</p>
<p>本着对读者负责的精神，若发现书中错误，本博将及时勘正；若发现不够详尽之处，本博将另行补充。</p>
</div>
<div>
<p style="text-align: right;">郑晖</p>
<p style="text-align: right;">2009年11月7日</p>
</div>
<p><strong>后记</strong>：</p>
<p>本帖已成<em>deprecated</em> API，<em>不建议</em>读者继续在此回复。对本书如有疑问或建议，请移步<a href="http://bbs.zhenghui.org/">冒号论坛</a>。</p> 
 <p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F11%2F07%2Fcolonclass-suggestions%2F&amp;title=%E3%80%8A%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%E3%80%8B%E6%84%8F%E8%A7%81%E6%94%B6%E9%9B%86" id="wpa2a_12">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2011年04月18日 -- <a href="http://blog.zhenghui.org/2011/04/18/more-words-on-abstraction/" title="答读者问(3)——再谈抽象">答读者问(3)——再谈抽象</a> (1)</li><li>2011年03月20日 -- <a href="http://blog.zhenghui.org/2011/03/20/words-on-abstraction/" title="答读者问(2)——关于抽象">答读者问(2)——关于抽象</a> (6)</li><li>2010年06月3日 -- <a href="http://blog.zhenghui.org/2010/06/03/advice-on-programmer/" title="答读者问(1)——对程序员的一些个人建议">答读者问(1)——对程序员的一些个人建议</a> (6)</li><li>2010年03月10日 -- <a href="http://blog.zhenghui.org/2010/03/10/colonclass-sales-tw/" title="《冒号课堂》在台湾上市">《冒号课堂》在台湾上市</a> (5)</li><li>2010年01月1日 -- <a href="http://blog.zhenghui.org/2010/01/01/colonclass-bbs-openning/" title="《冒号论坛》开放">《冒号论坛》开放</a> (1)</li><li>2009年10月26日 -- <a href="http://blog.zhenghui.org/2009/10/26/colonclass-sales/" title="《冒号课堂》上市">《冒号课堂》上市</a> (8)</li><li>2009年10月16日 -- <a href="http://blog.zhenghui.org/2009/10/16/preface-of-colonclass/" title="《冒号课堂》自序">《冒号课堂》自序</a> (7)</li><li>2009年08月21日 -- <a href="http://blog.zhenghui.org/2009/08/21/contents-of-colonclass/" title="《冒号课堂》目录">《冒号课堂》目录</a> (29)</li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/11/07/colonclass-suggestions/feed/</wfw:commentRss>
		<slash:comments>56</slash:comments>
		</item>
		<item>
		<title>《冒号课堂》上市</title>
		<link>http://blog.zhenghui.org/2009/10/26/colonclass-sales/</link>
		<comments>http://blog.zhenghui.org/2009/10/26/colonclass-sales/#comments</comments>
		<pubDate>Mon, 26 Oct 2009 03:44:41 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[上市]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=527</guid>
		<description><![CDATA[公告：《冒号课堂》已正式上市 [...]]]></description>
			<content:encoded><![CDATA[
<h2><strong>公告</strong>：《冒号课堂》已正式上市。</h2>
<p>&nbsp;</p>
<p><strong>基本信息</strong>：</p>
<div style="float: left; padding-right: 5px;"><img src="http://blog.zhenghui.org/img/colonclass/bookcover_3D_small.jpg" alt="" /></div>
<p>书名：冒号课堂——编程范式与OOP思想</p>
<p>书号：978-7-121-09545-0</p>
<p>出版社： 电子工业出版社</p>
<p>出版日期：2009年10月</p>
<p>字数：570千字</p>
<p>定价：￥65.00</p>
<p>页码：476</p>
<p>开本：16</p>
<p>&nbsp;</p>
<p><strong><span style="font-family: Georgia,&amp;quot;">网站主页：</span></strong></p>
<p>博客：<a href="http://blog.zhenghui.org/">http://blog.zhenghui.org/</a></p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;">豆瓣：<a href="http://www.douban.com/subject/4031906/">http://www.douban.com/subject/4031906/</a></span></span></p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;">51CTO读书频道：<a href="http://book.51cto.com/art/200910/155991.htm">http://book.51cto.com/art/200910/155991.htm</a></span></span></p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;"><br /></span></span></p>
<p><strong>网上书店</strong>：</p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;">互动网：<a href="http://www.china-pub.com/196068">http://www.china-pub.com/196068</a></span></span></p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;">当当网：<a href="http://product.dangdang.com/product.aspx?product_id=20707892">http://product.dangdang.com/product.aspx?product_id=20707892</a></span></span></p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;">卓越网：<a href="http://www.amazon.cn/detail/product.asp?prodid=bkbk992557">http://www.amazon.cn/detail/product.asp?prodid=bkbk992557</a></span></span></p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;">京东商城：<a href="http://book.360buy.com/10067468.html">http://book.360buy.com/10067468.html</a></span></span></p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;">北发图书网：<a href="http://book.beifabook.com/product/BookDetail.aspx?Plucode=712109545">http://book.beifabook.com/product/BookDetail.aspx?Plucode=712109545</a> </span></span></p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;">新华书店.com：<a href="http://www.xinhuabookstore.com/product/1252467/">http://www.xinhuabookstore.com/product/1252467/</a></span></span></p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;">金桥书城：<a href="http://book.jqcq.com/product/1310853.html">http://book.jqcq.com/product/1310853.html</a> </span></span></p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;">电子工业出版社：<a href="http://www.phei.com.cn/bookshop/bookinfo.asp?booktype=main&amp;bookcode=TP095450">http://www.phei.com.cn/bookshop/bookinfo.asp?booktype=main&amp;bookcode=TP095450</a></span></span></p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;">中国图书网：<a href="http://www.bookschina.com/4344485.htm">http://www.bookschina.com/4344485.htm</a></span></span></p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;">99网上书城：<a href="http://www.99read.com/product/detail.aspx?proid=541928">http://www.99read.com/product/detail.aspx?proid=541928</a><br /></span></span></p>
<p>华储网：<a href="http://www.huachu.com.cn/itbook/bookinfo.asp?lbbh=10110053">http://www.huachu.com.cn/itbook/bookinfo.asp?lbbh=10110053</a></p>
<p>新风雨：<a href="http://www.cnforyou.com/query/bookdetail1.asp?viBookCode=6625">http://www.cnforyou.com/query/bookdetail1.asp?viBookCode=6625</a></p>
<p>拓普网：<a href="http://www.toopoo.com/cmml/cmmlwb/query/query_info_det.asp?sdh=1138291">http://www.toopoo.com/cmml/cmmlwb/query/query_info_det.asp?sdh=1138291</a></p>
<p>百度有啊：<a href="http://youa.baidu.com/item/42141d9fcd0e3a9696ea5021">http://youa.baidu.com/item/42141d9fcd0e3a9696ea5021</a></p>
<p>儒商书库：<a href="http://www.sijibook.com/BookDetails.aspx?bookid=135505">http://www.sijibook.com/BookDetails.aspx?bookid=135505</a></p>
<p>金书网：<a href="http://www.golden-book.com/booksinfo/12/1200741.html">http://www.golden-book.com/booksinfo/12/1200741.html</a></p>
<p>蔚蓝网：<a href="http://www.wl.cn/5951937">http://www.wl.cn/5951937</a></p>
<p>中教图书：<a href="http://www.book1993.com/pro-821338.html">http://www.book1993.com/pro-821338.html</a></p>
<p>ANI图书网：<a href="http://www.anibook.cn/ProductDetail/2009/10/27/PBA97871210954500001.html">http://www.anibook.cn/ProductDetail/2009/10/27/PBA97871210954500001.html</a></p>
<p>希望书店：<a href="http://www.hopebook.net/tushumulu/301117/">http://www.hopebook.net/tushumulu/301117/</a></p>
<p>拍拍网：<a href="http://auction1.paipai.com/2419A70400000000002438DF0568A984">http://auction1.paipai.com/2419A70400000000002438DF0568A984</a></p>
<p>天下书城：<a href="http://book110.cn/bookshow/200911/442565/">http://book110.cn/bookshow/200911/442565/</a></p>
<p>广购中心：<a href="http://www.gzbookcenter.com/search/view.jsp?proID=1214051">http://www.gzbookcenter.com/search/view.jsp?proID=1214051</a></p>
<p>&nbsp;</p>
<p><strong>全国各地书店有售</strong></p>
<div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px;">
<table width="100%" border="0" cellspacing="1" cellpadding="1">
<tbody>
<tr>
<td valign="bottom" bgcolor="#fbf9f5" height="25"><strong>字数：</strong>570千字</td>
<td valign="bottom" bgcolor="#fbf9f5" width="21%" height="25"></td>
<td valign="bottom" bgcolor="#fbf9f5" width="36%"><strong>定价：</strong>￥65.00元</td>
</tr>
<tr>
<td align="center" valign="top"></td>
<td valign="bottom" bgcolor="#fbf9f5" width="19%" height="25"><strong>页码：</strong>476</td>
<td valign="bottom" bgcolor="#fbf9f5"></td>
<td valign="bottom" bgcolor="#fbf9f5"><strong><span style="color: #ff0000;">会员价：</span></strong><span style="color: #ff0000;">￥52.00元</span></td>
</tr>
<tr>
<td align="center" valign="top"></td>
<td valign="bottom" bgcolor="#fbf9f5" height="25"><strong>开本：</strong>16</td>
</tr>
</tbody></table>
</div>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F10%2F26%2Fcolonclass-sales%2F&amp;title=%E3%80%8A%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%E3%80%8B%E4%B8%8A%E5%B8%82" id="wpa2a_14">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2010年03月10日 -- <a href="http://blog.zhenghui.org/2010/03/10/colonclass-sales-tw/" title="《冒号课堂》在台湾上市">《冒号课堂》在台湾上市</a> (5)</li><li>2011年04月18日 -- <a href="http://blog.zhenghui.org/2011/04/18/more-words-on-abstraction/" title="答读者问(3)——再谈抽象">答读者问(3)——再谈抽象</a> (1)</li><li>2011年03月20日 -- <a href="http://blog.zhenghui.org/2011/03/20/words-on-abstraction/" title="答读者问(2)——关于抽象">答读者问(2)——关于抽象</a> (6)</li><li>2010年06月3日 -- <a href="http://blog.zhenghui.org/2010/06/03/advice-on-programmer/" title="答读者问(1)——对程序员的一些个人建议">答读者问(1)——对程序员的一些个人建议</a> (6)</li><li>2010年01月1日 -- <a href="http://blog.zhenghui.org/2010/01/01/colonclass-bbs-openning/" title="《冒号论坛》开放">《冒号论坛》开放</a> (1)</li><li>2009年11月7日 -- <a href="http://blog.zhenghui.org/2009/11/07/colonclass-suggestions/" title="《冒号课堂》意见收集">《冒号课堂》意见收集</a> (56)</li><li>2009年10月16日 -- <a href="http://blog.zhenghui.org/2009/10/16/preface-of-colonclass/" title="《冒号课堂》自序">《冒号课堂》自序</a> (7)</li><li>2009年08月21日 -- <a href="http://blog.zhenghui.org/2009/08/21/contents-of-colonclass/" title="《冒号课堂》目录">《冒号课堂》目录</a> (29)</li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/10/26/colonclass-sales/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>冒号课堂§10.2：抽象类型</title>
		<link>http://blog.zhenghui.org/2009/10/24/colon-class-10_2/</link>
		<comments>http://blog.zhenghui.org/2009/10/24/colon-class-10_2/#comments</comments>
		<pubDate>Sat, 24 Oct 2009 01:37:53 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[mixin]]></category>
		<category><![CDATA[trait]]></category>
		<category><![CDATA[抽象数据类型]]></category>
		<category><![CDATA[抽象类]]></category>
		<category><![CDATA[抽象类型]]></category>
		<category><![CDATA[接口]]></category>
		<category><![CDATA[继承]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=518</guid>
		<description><![CDATA[<b>抽象类型</b>——实中之虚（<em>介绍抽象类型的种类、意义及其用法</em>）<br/> • 浅显的比方只是门槛前的台阶，借之或可拾级入门，却无法登堂入室<br/> • 具体类型是创建对象的模板，抽象类型是创建类型的模块<br/> • 抽象数据类型的核心是数据抽象，而抽象类型的核心是多态抽象<br/> • 必先以术养道，而后以道御术<br/> • 以社会身份而非个人身份作为公民之间联系的纽带，正是针对接口而非实现来编程的社会现实版<br/> • 个体身份对应的规范抽象借助封装，以数据抽象的形式出现<br/> • 家庭身份对应的规范抽象借助继承，以类型层级的形式出现<br/> • 社会身份对应的规范抽象借助多态，以多态抽象的形式出现 [...]]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center"><span style="font-family: 宋体">冒号课堂</span></h1>
<strong><span style="font-size: 13pt; font-family: 宋体">第十课 多态机制（2）</span></strong>

<!-- below comes from generated html -->
<head><link rel="stylesheet" href="http://blog.zhenghui.org/css/colonclass.css" type="text/css"></head>

<div lang="zh-CN" class="article" title="抽象类型"><div class="titlepage"><div><div><h1 class="title"><a name="id603878"></a>抽象类型——实中之虚</h1></div><div><div class="author"><h3 class="author">郑晖</h3></div></div><div><div class="abstract" title="摘要"><p class="title"><b>摘要</b></p><p>介绍抽象类型的种类、意义及其用法</p></div></div></div><hr /></div><div class="toc"><p><b>目录</b></p><dl><dt><span class="section"><a href="#preview">！预览</a></span></dt><dt><span class="section"><a href="#question">？提问</a></span></dt><dt><span class="section"><a href="#explaination">：讲解</a></span></dt><dt><span class="section"><a href="#note">，插语</a></span></dt><dt><span class="section"><a href="#summary">。总结</a></span></dt><dt><span class="section"><a href="#reference">“”参考</a></span></dt></dl></div><div class="epigraph"><div class="literallayout"><p>有无相生，难易相成</p></div><div class="attribution"><span>—<span class="attribution">《老子•道经》</span></span></div></div><div class="section" title="！预览"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="preview"></a>！预览</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    浅显的比方只是门槛前的台阶，借之或可拾级入门，却无法登堂入室
                </p></li><li class="listitem"><p>
                    具体类型是创建对象的模板，抽象类型是创建类型的模块
                </p></li><li class="listitem"><p>
                    抽象数据类型的核心是数据抽象，而抽象类型的核心是多态抽象
                </p></li><li class="listitem"><p>
                    必先以术养道，而后以道御术
                </p></li><li class="listitem"><p>
                    以社会身份而非个人身份作为公民之间联系的纽带，正是针对接口而非实现来编程的社会现实版
                </p></li><li class="listitem"><p>
                    个体身份对应的规范抽象借助封装，以数据抽象的形式出现
                </p></li><li class="listitem"><p>
                    家庭身份对应的规范抽象借助继承，以类型层级的形式出现
                </p></li><li class="listitem"><p>
                    社会身份对应的规范抽象借助多态，以多态抽象的形式出现
                </p></li></ul></div></div><div class="section" title="？提问"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="question"></a>？提问</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>具体类型与抽象类型的区别是什么？</p></li><li class="listitem"><p>抽象数据类型与抽象类型的区别是什么？</p></li><li class="listitem"><p>除接口与抽象类外还有其他抽象类型吗？它们有何特点和意义？</p></li><li class="listitem"><p>抽象类型的主要作用是什么？</p></li><li class="listitem"><p>在系统中应采用何种类型作为模块之间通讯的数据类型？</p></li><li class="listitem"><p>接口是为了克服（Java或C#中）抽象类不能多重继承的缺点吗？</p></li><li class="listitem"><p>接口与抽象类在语法和语义上各有什么不同？</p></li><li class="listitem"><p>标记接口有何作用？</p></li></ul></div></div><div class="section" title="：讲解"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="explaination"></a>：讲解</h2></div></div></div><p>
            冒号调整了焦点：“鉴于目前专注的范式是OOP，参数多态最好放在以后的GP专题再作探讨。除非特别说明，下面提到的多态专指子类型多态。谈到这类多态，就不得不提及抽象类型。谁来说说，究竟什么是抽象类型？”
        </p><p>
            冒号抬手内扬，摆出了对练的姿势。
        </p><p>
            叹号率先抢攻：“抽象类型指的是至少含有一个抽象方法的类型。”
        </p><p>
            冒号轻松化解：“在C++中这句话尚可勉强成立，但在Java和C#中则大不尽然：一个类即使没有一个抽象方法也可以被申明为抽象的；一个没有任何成员的空接口或称<span class="term">标记接口</span>同样属于抽象类型。”
        </p><p>
            “抽象类型是指无法实例化的类型。”逗号发起二次进攻。
        </p><p>
            冒号见招拆招：“Java中的Math类也不能实例化，原因是它只有private构造器，并且没有一个能返回实例的静态方法。C#中的Math类是静态类，同样不能实例化。”
        </p><p>
            问号纵身而上：“抽象类型指能且只能通过继承来实例化的类型。Math类是final类，无法被继承。最主要的是，它的价值体现在它的静态方法上，压根儿就没有实例化的必要。”
        </p><p>
            冒号借力反打：“为什么要强调无法实例化呢？”
        </p><p>
            引号一旁助攻：“一个抽象类型代表着一个抽象概念，而抽象概念自然是无法具化的。比如你无法实例化抽象的形状，但可以实例化长方形、三角形等具体的形状；无法实例化抽象的水果，但可以实例化苹果、桔子等具体的水果。”
        </p><p>
            “很官方的说法。这就好比将继承关系说成‘is-a’关系一样，理论上虽通俗易懂，实践上却不足为训。”冒号收起架势，“要说抽象，Java和C#中的Object类可谓包罗万象，该够抽象了吧？不照样实例化？<span class="term">列表</span>（list）与<span class="term">映射</span>（map）是抽象的还是具体的？在C++中它们是具体类型，而在Java和C#中它们却是抽象类型<a class="link" href="#note1"><sup>[1]</sup></a>。这又是为什么？”
        </p><p>
            一连串的反问让大家陷入沉思。
        </p><p>
            “相比其他编程范式，OOP更贴合客观世界，人们经常用打比方的形式来描述和理解OOP的一些概念和思想。这本身并无不妥，但一定要保持清醒的头脑：浅显的比方只是门槛前的台阶，借之或可拾级入门，却无法登堂入室。”冒号谆戒道，“天下之理皆同，天下之人皆同，故凡学问殿堂之前皆一般景象：入门者众，入室者寡。本班的目的便是，引导诸位从徘徊于编程之门左右的人群中越众而出，早达内室。”
        </p><p>
            “那就成了传说中的内室弟子吧？大伙在门边转悠很久了，头都发晕了，师父还是快些领我等入室吧。” 逗号近乎戏谑地恳求。
        </p><p>
            冒号一笑：“我可算不得你们的师父，只不过是个闻道在先的师兄而已。”
        </p><p>
            一直没有出手的句号忽然开腔：“抽象是个相对概念，一个类型是否是抽象的完全取决于设计者对它的角色定位。如果想用它来创建对象，它就是可实例化的具体类型；如果想用它来作为其他类型的基类，它就是不可实例化的抽象类型。”
        </p><p>
            “这才击中了要害！”冒号不禁喝彩道，“整理一下你的观点：<span class="strong"><strong>具体类型是创建对象的模板，抽象类型是创建类型的模块</strong></span>。一个是为对象服务的，一个是为类型服务的。显然，后者的抽象性正是源自其服务对象的抽象性。就拿刚才的实例来说，模板方法模式中的Authenticator类是抽象的，是为创建子类型SimpleAuthenticator、Sha1Authenticator等服务的；策略模式中的Authenticator类是具体的，是为创建对象服务的，但它合成的两个接口KeyValueKeeper和Encrypter又是为创建算法类型服务的。值得注意的是，不要把抽象类型与抽象数据类型（ADT）混为一谈，后者的抽象指的是类型的接口不依赖其实现。或者说，<span class="strong"><strong>抽象数据类型的核心是数据抽象，而抽象类型<a class="link" href="#note2"><sup>[2]</sup></a>的核心是多态抽象</strong></span>。”
        </p><p>
            问号想让概念更明确些：“抽象类型就只有<span class="term">接口</span>（interface）和<span class="term">抽象类</span>（abstract class）两种吗？”
        </p><p>
            “在Java和C#中基本上是这样，但在C++中这两种类型没有显式的区别<a class="link" href="#note3"><sup>[3]</sup></a>。”冒号，“此外，动态OOP语言如Ruby、Python、Perl、Scala、Smalltalk等还至少支持mixin和trait中的一种类型。mixin直译为‘混入’，trait直译为‘特质’，为避免翻译上的问题，今后我们还是采用英文术语。这两种类型大同小异，为简便起见，下面以mixin类型为代表<a class="link" href="#note4"><sup>[4]</sup></a>。它们的出现是为了弥补接口与抽象类的一些不足，更好地实现代码重用。我们知道，接口的主要目的是创建多态类型，本身不含任何实现。子类型通过接口继承只能让代码被重用，却无法重用超类型的实现代码。抽象类可以重用代码，可又有多重继承的问题。Java和C#不支持这种机制，C++虽支持但有不少弊端。”
        </p><p>
            引号奇道：“这个问题上节课不是已经解决了吗？用合成来代替继承啊。”
        </p><p>
            冒号解释：“合成是一种解决办法，但也不是没有缺陷。首先，合成的用法不如继承那么简便优雅，这也是许多人喜欢用继承的主要原因；其次，合成不能产生子类型，而有时这正是设计者所需要的；再次，合成无法覆盖基础类的方法，也无法访问它的protected成员；最后，却可能是最大的缺点是：合成的基础类只能是具体类型，不能是抽象类型<a class="link" href="#note5"><sup>[5]</sup></a>。”
        </p><p>
            逗号不明所以：“这能算是缺点吗？”
        </p><p>
            “如前所述，具体类型的主要任务是<span class="emphasis"><em>创造新对象</em></span>，如果用作合成或继承的基础类，等于是又承担了原本抽象类型的任务——<span class="emphasis"><em>创造新类型</em></span>。这不仅有越俎代庖之嫌，而且这两个任务往往也是冲突的。我们曾提出，一个类的服务应该有纯粹性和完备性。一方面，人们希望创造的新对象无所不能，因此更看重服务的<span class="emphasis"><em>完备性</em></span>，倾向它包含尽可能多的功能；另一方面，人们又希望创造的新类型有所不依，因此更看重服务的<span class="emphasis"><em>纯粹性</em></span>，倾向它包含尽可能少的功能。”冒号擘肌分理，“妥协的结果是，一个新类型往往只用到基础类型的部分功能，却可能受到其他功能变动的影响。虽然这种影响在良好的封装之下会大大削弱，但也难以完全消弭。”
        </p><p>
            句号思索片刻，已明其意：“换句话说，以具体类型为代码重用的基本单位，难免颗粒度过大？”
        </p><p>
            “然也！”冒号的手在空中挽了个花，“其实作为抽象类型的接口也有类似的尴尬：对它的客户类来说，它承诺的服务是多多益善；对它的实现类来说，承诺越多负担却越重。如果能有这样一种可重用的模块，既不像具体类型那样面面俱到，又不像接口那样有名无实，也没有抽象类的多重继承之弊，岂不妙哉？”
        </p><p>
            “想必就是mixin了！”叹号眼中闪过一道光芒，旋即又暗淡下来，“只可惜Java并不支持啊。”。
        </p><p>
            “Java不支持就没兴趣了？” 冒号听出他的话里有话，“要成为优秀的程序员，千万不能画地为牢、自我禁锢。始终要保持一颗开放的心，不要拘于某些语言或范式，也不要囿于某些概念或技术。”
        </p><p>
            叹号的耳根有点发热。
        </p><p>
            “陌生的理论和技术开始总是拒人千里，不过一旦你了解其问题来源，它们会慢慢变得和蔼可亲起来。”冒号循循善诱，“既然具体类型和现存的两种抽象类型均有不足之处，mixin的产生便合情合理了。它是具体类型与接口类型的一种折衷，既可有抽象方法，也可有具体方法。这一点类似抽象类，但又没有抽象类的多重继承问题。举例来说，Ruby中的Comparable就是一个简单却很典型的mixin。”
        </p><p>
            问号插话：“Java中也有Comparable接口啊。”
        </p><p>
            冒号道出其中差异：“Java中的Comparable和C#中的IComparable只有一个抽象的比较方法，而Ruby中的除了有类似的抽象方法——比较（&lt;=&gt;）之外，还提供了小于（&lt;）、小于等于（&lt;=）、等于（==）、大于（&gt;）、大于等于（&gt;=）和介于（between?）等六种具体方法。显而易见，多出的方法均可通过唯一抽象的比较方法来实现。”
        </p><p>
            引号一点即通：“如此一来，重用Comparable的类只需实现一个抽象方法，便可自动拥有另外六个有用的功能。这既满足了客户类的需求，又不增加实现类的负担。”
        </p><p>
            “买一送六，这买卖划算！”逗号来劲了。
        </p><p>
            冒号双眼微眯：“更划算的买卖是Ruby中Enumerable。任何包含该mixin的类只要实现一个遍历方法each，便可免费得到二十多个有关遍历和搜寻的方法。如果再实现比较方法&lt;=&gt;，还可获赠排序和最值方法。相比Java中Enumeration和Iterator接口，优势历然。”
        </p><p>
            问号很好奇：“为什么称为mixin呢？”
        </p><p>
            冒号述说由来：“冰淇淋中经常会掺混一些薄荷、香草、巧克力之类的调味料和花生、坚果之类的小零碎，人们管它们叫mix-in。后来被借用来表示一种抽象类型，主要有如下特点：一、抽象性和依赖性：本身没有独立存在的意义，必须融入主体类型才能发挥作用；二、实用性和可重用性：不仅提供接口，还提供部分实现；三、专一性和细粒度性：提供的接口职责明确而单一；四、可选性和边缘性：为主体类型提供非核心的辅助功能。”
        </p><p>
            “这些特点与风味添加料还真的颇为神似。”叹号想着想着，嘴里不自觉地咂摸了一下。
        </p><p>
            “虽然C++、Java和C#在语法上尚不支持mixin，但C++可通过多重继承、Java和C#可通过合成和接口来分别模拟mixin。不仅如此，借助<span class="term">切面式编程</span>（AOP），Java和C#甚至可完全实现mixin；借助<span class="term">泛型式编程</span>（GP），C++也能通过<span class="term">模板</span>更好地实现mixin<a class="link" href="#note6"><sup>[6]</sup></a>。”冒号点到为止，“就此我们重温前面提到的两个观点。一是编程范式之间的合作性：mixin属于OOP的范畴，但其他编程范式如切面式、泛型式以及二者背后的元编程都能与之相通；二是设计与语言的相关性：C++、Java和C#以及其他诸如Ruby、Python等动态语言对mixin有着不同的支持方式，这在一定程度上会影响系统的OOP设计。”
        </p><p>
            引号憧憬道：“语言是在发展的，说不定哪天Java也会支持mixin的。”
        </p><p>
            冒号以实相应：“Java的动态小兄弟Groovy在1.6版已经开始支持mixin ，而C#3.0也新引入了对mixin更友好的语法特性<a class="link" href="#note7"><sup>[7]</sup></a>。”
        </p><p>
            逗号提了一个长期困惑大家的问题：“每当一个新技术出现，我就觉得很矛盾：不追怕落伍，追吧又怕落空。如何判断一个它是昙花一现，还是大势所趋呢？”
        </p><p>
            “任何技术都是在赞美与批判中成长起来的，预测它们是流星还是恒星绝非易事。就拿OOP来说，上个世纪六十年代就出现了支持OOP的语言<a class="link" href="#note8"><sup>[8]</sup></a>，但直到九十年代中后期它才真正成为主流的编程范式。这段时间恐怕比大多数人的程序员生涯还长吧。再说mixin，其实并非今日的重点，介绍它的目的不是盲目追新，而是希望透过其背后的需求驱动点，重新审视现有技术。至于它今后会不会为主流语言所接纳，反倒不是那么重要了。如果一定要我给个建议，那就是八个字：‘<span class="strong"><strong>不执一法，不舍一法</strong></span>’。”冒号以禅语作答，“软件技术这棵大树经过多年的快速成长，早已枝蔓丛生。欲臻不执不舍之境，当如开班导言中所说：究其根本以知过去，握其主干以知现在，察其生长点以知未来。我之所以倾向于用抽象的方式来谈论技术，正是因为抽象的东西更接近根、更接近干、更接近生长点，从而更普泛深刻，也更稳定持久。”
        </p><p>
            句号借机问道：“您认为抽象比具体更重要？”
        </p><p>
            “抽象与具体无所谓孰高孰低，它们只是功用不同而已。”冒号轻轻晃了晃脑袋，“正所谓：<span class="strong"><strong>必先以术养道，而后以道御术</strong></span>。也就是说，在学习时应注重从具体知识中领悟抽象思想，在应用时应注重用抽象理论来指导具体实践。类似地，软件开发也是如此：从具体需求中构建出抽象模型，再根据抽象模型来完成具体实现。因此，在设计阶段抽象类型尤为关键，而在实现阶段则是具体类型更为重要。”
        </p><p>
            问号表示理解：“假如从具体需求直接跨到具体实现，省去中间的抽象建模过程，那还用得着架构师和分析师吗？”
        </p><p>
            “话虽不错，但疑似倒果为因。”冒号洞若观火，“是否有必要抽象建模，关键看项目需求。如果需求简单而稳定，一步到位又何尝不可？甚至软件的开发效率和运行效率还更高——为劈几根细柴而磨刀，值吗？如果需求复杂而多变，引入抽象方有‘磨刀不误砍柴工’之效。毕竟抽象不是目的而是手段，对它片面的追求反会导致过度的设计。”
        </p><p>
            众人这才发现，给老冒戴顶“抽象派”的帽子是有些冤枉他了，应该是“抽象现实派”的。
        </p><p>
            冒号续道：“为进一步认识抽象类型，我举个非常实用的例子。它只适用于C++，而不适用于Java和C#。如果你对这一点感到遗憾的话，不要忘记我们的原则：具体实例永远是为抽象思想服务的。”
        </p><p>
            幻灯一闪，现出一段C++代码——
        </p><div class="informalexample"><pre class="programlisting">
/** 一个不可复制的类 */
class NonCopyable
{
protected:
    // 非公有构造函数防止创建对象
    NonCopyable() {}  
    // 非公有非虚析构函数建议子类非公有继承
    ~NonCopyable() {}
private: 
    // 私有复制构造函数防止直接的显式复制和通过参数传递的隐式复制
    NonCopyable(const NonCopyable&amp;);
    // 私有赋值运算符防止通过赋值来复制
    const NonCopyable&amp; operator=(const NonCopyable&amp;); // copy assignment
};

/** NonCopyable的一个私有继承类 */
class SingleCopy : private NonCopyable {};

/** 测试代码 */
int main()
{
    SingleCopy singleCopy1;
    SingleCopy copy(singleCopy1); // 编译器报错：企图复制singleCopy1

    SingleCopy singleCopy2;
    singleCopy2 = singleCopy1; // 编译器报错：企图复制singleCopy1
    return 0;
}</pre></div><p>
            冒号讲解道：“有些对象是不希望被复制的。比如一些代表网络连接、数据库连接的资源对象，它们的复制要么意义不大，要么实现困难。由于C++的编译器为每个类提供了默认的<span class="term">复制构造函数</span>（copy constructor）和<span class="term">赋值运算符</span>（assignment operator），要想阻止对象的复制，通常做法是将这两个函数私有化。引入NonCopyable后，它的任何子类将自动拥有不可复制的特性。这样为开发者节省了代码编写量，还免掉了相应的文档说明，使用者也一望而知其意，可说是一石三鸟。虽然NonCopyable从语法上说不是抽象类，但从本质上看是一种类似mixin功能的抽象类型。”
        </p><p>
            引号考量一番后说道：“单就它的功效而言，的确非常符合mixin的四大特点，只是它的子类用的是私有继承，而不是类继承或接口继承。”
        </p><p>
            “你说得很对。可问题是，我们并没有要求mixin或者trait一定要通过继承的方式来重用啊？事实上，有些mixin甚至可在运行期间产生，还能克服继承的静态缺陷。即使采用继承，一般也不满足‘is-a’关系。你总不能说草莓冰淇淋是一种草莓吧？”冒号淡淡地说，“先前你们总结出抽象类型有两个特征：需要继承和无法实例化，但它们并非本质，关键还是它的目的——为类型服务。提供可被继承的超类型只是一种服务方式，却非唯一的方式；无法实例化只因它不是为对象服务的，禁止实例化不过是语法上的加强，目的是让用户在编译期间就能发现用法错误。其实，即便NonCopyable类的构造函数是公有的，也不会有人去实例化。原因很简单，它的价值只有通过子类才能体现，这是由其抽象的本性所决定的。”
        </p><p>
            逗号有些奇怪：“为什么在Java中就没有类似的对象复制问题呢？”
        </p><p>
            “这是一个非常基础的问题，请容我下次再回答你。”冒号破天荒地没有立即解疑，“以下重点还是放在接口和抽象类上面，我们称之为基本抽象类型，以别于mixin、trait等其他抽象类型。我们先从语法上简单地对比一下这两种类型。”
        </p><p>
            屏幕上显示出一张表格（如表10-1所示）——
        </p><div class="table"><a name="id604285"></a><p class="title"><b>表 10-1. Java/C#的抽象类与接口在语法上的区别</b></p><div class="table-contents"><table summary="Java/C#的抽象类与接口在语法上的区别" border="1"><colgroup><col><col><col></colgroup><thead><tr><th> </th><th>抽象类</th><th>接口</th></tr></thead><tbody><tr><td>提供实现代码</td><td>能</td><td>否</td></tr><tr><td>多重继承</td><td>否</td><td>能</td></tr><tr><td>拥有非public成员</td><td>能</td><td>否</td></tr><tr><td>拥有域成员</td><td>能</td><td>否（Java中的static final域成员除外）</td></tr><tr><td>拥有static成员</td><td>能</td><td>否（Java中的static final域成员除外）</td></tr><tr><td>拥有非abstract方法成员</td><td>能</td><td>否</td></tr><tr><td>方法成员的默认修饰符</td><td>无</td><td>public abstract（Java：可选；C#：不能含有任何修饰符）</td></tr><tr><td>域成员的默认修饰符</td><td>无</td><td>Java：public static final；C#：不允许域成员</td></tr></tbody></table></div></div><br class="table-break"><p>
            冒号简明扼要地总结：“C#的语法与Java的稍有不同，但二者在接口与抽象类的关键区别上还是一致的：接口不能提供实现但能多重继承，抽象类则正相反；接口只能包含<span class="emphasis"><em>公有</em></span>的、<span class="emphasis"><em>非静态</em></span>的、<span class="emphasis"><em>抽象</em></span>的方法成员<a class="link" href="#note9"><sup>[9]</sup></a>，抽象类则无此限制。”
        </p><p>
            问号言明难处：“从语法上区分它们并不难，难的是从设计上区分它们。”
        </p><p>
            逗号实话实说：“按照上节课‘提倡接口继承，慎用实现继承’的方针，应该倾向用接口而非抽象类。但总觉得接口太虚了，没有抽象类实在。”
        </p><p>
            引号反驳：“要说实在，具体类型更实在啊。”
        </p><p>
            叹号坦言：“在编程中经常需要用到标准的或第三方的类库，可查起API来经常是左一个接口右一个接口的，迟迟不见具体类型现身，心里哪个急啊！”
        </p><p>
            冒号打了个比方：“如果到包子铺买包子，作为客户你也许会认为包子是具体类型，但对提供包子的人来说它却是抽象类型。他一定会问你：是要肉包、菜包还是豆沙包？是要蒸包、煎包还是小笼包？他的铺子开得越专业，给你出的选择题越多，众口难调嘛。同样道理， 要建一个高度可重用的类库，一些接口是必不可少的。”
        </p><p>
            句号悟道：“接口的意义就在于：提供者不是擅作主张，而是推迟决定，让客户选择实现方式。”
        </p><p>
            “言之有理！类似地，抽象类的意义就在于：父类推迟决定，让子类选择实现方式。‘推迟’二字道出了抽象类型除创建类型之外的另一功用：<span class="strong"><strong>提供动态节点</strong></span>。如果是具体类型，节点已经固定，没有太多变化的余地<a class="link" href="#note10"><sup>[10]</sup></a>。反过来，要使节点动态化，一般通过多态来实现。由此，抽象类型常常与多态机制形影不离。”冒号稍加引申，“就说前面的验证类吧，用模板方法模式实现的Authenticator类将关键的方法交给子类SimpleAuthenticator或Sha1Authenticator处理，用策略模式实现的Authenticator类将关键的方法交给内嵌接口KeyValueKeeper和Encrypter的实现类处理。后者的两次接口继承比前者的一次实现继承多了一个动态节点，因而更加灵活。这也是为什么一个需要（M×N）个实现类，一个只要（M+N）个的原因。当然，这也不是完全没有代价的。比如要创建一个用SHA-1算法加密的验证类实例，两种方法对比如下——”
        </p><div class="informalexample"><pre class="programlisting">
模板方法模式：new Sha1Authenticator()
策略模式：    new Authenticator(new MemoryKeeper(), new Sha1Encrypter())
</pre></div><p>
            冒号指点着黑板：“显然，后者无论是使用上还是性能上都比前者稍有不如。但权衡利弊，多数时候它仍是更好的选择。” 
        </p><p>
            “包子铺的包子用料种类越多、做法越多，买一个包子越费事。但只要不到饿得发昏的地步，大家还是更喜欢花样更多的包子铺。看来我也不该再抱怨类库的接口过多了。”叹号心下释然。
        </p><p>
            “大家再看看这个电脑主板，开过机箱攒过机的人应该对它并不陌生。”冒号终于亮出了蓄藏已久的道具， “上面密密麻麻地布满了各种元件，那是它的实部，而我们关注的是它的虚部——各种插槽和接口，包括CPU插槽、内存插槽、PCI插槽、AGP插槽、ATA接口、PS/2接口、USB接口以及其他林林总总的扩展插槽等等。这些接口的存在，使得主板与CPU、内存条、外围设备以及扩展卡等不必硬性焊接在一起，大大增强了电脑主机的可定制性。”
        </p><p>
            引号受到启发：“主板与其他硬件就好比一个个的<span class="emphasis"><em>具体类型</em></span>，那些插槽和接口就相当于一个个的<span class="emphasis"><em>接口类型</em></span>。所有的硬件以接口为桥来<span class="emphasis"><em>组装合成</em></span>，以机箱为壳来<span class="emphasis"><em>封装隐藏</em></span>，一个新的<span class="emphasis"><em>具体类型</em></span>——具有完整功能的主机便产生了。”
        </p><p>
            “比喻非常到位！” 冒号很满意，“不过准确地说，与接口类型对应的不是物理接口，而是<span class="strong"><strong>接口规范</strong></span>。如果仅仅是物理接口，只能保证该接口适用于某种特定型号的硬件产品，却不能保证同时适用于其他型号或者其他类型的硬件。以大家熟悉的USB（Universal Serial Bus）接口为例，它能接入各种外部设备，包括鼠标、键盘、打印机、外置硬盘、闪存和形形色色的数码产品。这当然不是偶然的，因为所有厂家在生产这些硬件时均遵循了相同的业界标准——USB协议规范。换言之，任何一个与USB接口兼容的设备，都可看作是实现了此接口的具体类型，而主机对该设备的自动识别能力则可看作一种多态机制。”
        </p><p>
            “这下我更深刻地理解那句话了：接口继承不是为了重用，而是为了被重用。”句号品味道，“比如一个鼠标，可以有串行接口、PS/2接口、USB接口或者无线接口，还可以同时拥有多个不同类型的接口。无论怎样，它本身都是完整的产品，根本不需要重用主机上的其他硬件，它实现某些接口的目的完全是为了能被主机所用。”
        </p><p>
            逗号意识到：“看样子，硬件设计也需要OOP思想呢。”
        </p><p>
            “相比软件设计师，硬件设计师往往能更好地贯彻OOP的理念。”冒号加强了语气，“他们的对象化概念更清晰更自然，因为硬件模块比软件模块更实在更具体；他们更注重设计，因为硬件比软件的修改成本大得多；他们更注重设计重用，因为硬件重新发明轮子的成本普遍很高；他们更注重实现重用，因为无法在举手之间完成‘复制-粘贴’工作；他们更注重接口明确、封装完好，因为把内部的接口或结构暴露在外不仅难看，还容易带来缠绕、磨损、短路等问题；他们采用合成和接口来组装模块，因为硬件没有类似实现继承的机制。”
        </p><p>
            “看起来我们真得向硬件设计师取经了。”叹号有些信服了。
        </p><p>
            冒号旧话重提：“我们曾对OOP有过这样的描述：如果把OOP系统看作民主制社会，每个对象是独立而平等的公民，那么封装使得公民拥有个体身份，继承使得公民拥有家庭身份，多态使得公民拥有社会身份。补充一下，其中的继承主要指类继承，多态主要指接口继承带来的多态。经过这段时间的学习，大家对此有何见解？”
        </p><p>
            问号发表看法：“广义封装让每个类成为独立的模块，从而让每个对象具备了个体身份。狭义封装又进一步地把类的接口与实现分离，从而让每个对象具有显著的外在行为和隐藏的内在特性。继承机制可使一个类成为其他类的子类或父类，从而确立了对象在类型家族中的身份。至于多态嘛，嗯。。。”
        </p><p>
            问号努力想抓住若隐若现的头绪。
        </p><p>
            句号接过话头：“一个公民的社会身份是指他在社会中所处的地位和扮演的角色。比如，一个人在学校里是学生，在公司里是职员，在商店里是顾客，他真正的个体身份往往是被掩盖的。同样地，一个对象在与外界联系时，通常不以其实际类型的身份出现，而是在不同的场合下以不同的抽象类型的身份出现。我想，这大概就是多态带来的社会身份吧。”
        </p><p>
            “这种社会身份的意义何在？”冒号不动声色地问。
        </p><p>
            句号接着回答：“社会身份既是一种资格也是一种义务。比如在列车上有人得了急病，可以通过广播找医生。人们不用事先知道来者的具体个人身份，只要他是医生，就会放心地让他第一时间去救人。”
        </p><p>
            “这个比喻很恰当。”冒号赞道，“不用<span class="emphasis"><em>事先</em></span>知道个人身份，不正说明广播呼叫的对象是一个多态的抽象类型吗？同理，当一个具体类型显式继承了一个接口，它的对象便拥有了个体身份之外社会身份：有资格以该接口的形式与外界打交道，也有义务履行该接口的职责。”
        </p><p>
            “咦，那为什么把社会身份归功于多态而不是继承呢？”问号发出疑问。
        </p><p>
            冒号释疑：“继承自然有功劳，毕竟子类型多态要建立在它的基础上。但如果没有多态机制，要确保一个对象的实际方法而不是其超类型的方法被调用，必须将其还原为具体类型，从而使社会身份变得几乎有名无实。”
        </p><p>
            问号憬然醒悟。
        </p><p>
            冒号继续深入：“对象每多一种社会身份，便多一条与外界交流的渠道。为什么遮遮掩掩地不肯以本来面目示人呢？非是羞于见人，盖因一般的具体类型在公共场合是不为人知的，只有少数核心库里的核心类是例外。即使侥幸被认识，也难被认可，因为那会以代码的复杂度和耦合度为代价。社会身份则不然，它远比一般的个体身份更容易被接受。”
        </p><p>
            逗号举出例证：“这就好比上课得有学生证，上班得有工作证，上火车得有火车票，上飞机得有登机牌。只要不是炙手可热的公众人物，很多场合都是认牌认证不认人的。”
        </p><p>
            “道理人人都懂，可总有不少人以为自己编写的类都是明星大腕，大有‘天下谁人不识我’的豪迈，无牌无证就敢到处乱窜。更有甚者，不用多态就算了，连封装也不要，简直是在裸奔嘛。”冒号揶揄道。
        </p><p>
            全班笑不可仰。
        </p><p>
            冒号恢复肃容：“谈到这里，我们不能不再次提到‘针对接口编程’的基本原则。它有一种建立于数据抽象之上的形式，能让用户只关心抽象数据类型的API接口而无视其具体实现。不过，它至少有两大局限。其一，虽然在接口不变的情况下，实现代码的改变不会影响客户代码，但仍需要重新编译，对于需要头文件的C++来说则需要更多的编译链接时间。其二，虽然相同的接口可以有多种实现方法，但它们不能同时并存，更无法动态切换。于是，另一种建立于多态抽象之上的形式应运而生。它把抽象数据类型隐藏在抽象类型的背后，从而提升了抽象接口。同一个抽象接口允许有多种实现并存，且能动态切换，新增、删除或修改某种实现也不会导致其他代码的修改或重新编译。方才我们从主体类的角度来看，它的对象尽量以社会身份参与社会活动；现在再从客户类的角度看，它会尽量召集有社会身份的对象。两相结合，以社会身份而非个人身份作为公民之间联系的纽带，正是针对接口而非实现来编程的<span class="strong"><strong>社会现实版</strong></span>。”
        </p><p>
            问号有所顾虑：“可是，有不少具体类型并没有实现任何接口，也就没有社会身份。”
        </p><p>
            “排除设计不良的因素，没有抽象超类型的具体类型最常见的有两种可能。一种是与世隔绝，一辈子几乎足不出户，至多在小圈子里活动。典型的有非公有类、内部类、局部类等等。一种是名满天下，他的脸就是一张天然名片，他的个人身份也就是社会身份。典型的有基本数据类型、字符串类型、日期类型等通用数据类型以及特定领域的通用数据类型。可见，个人身份与社会身份并无绝对的界限。同样，家庭身份与社会身份也有交合之处，正如名门望族也可成为社会身份一样。典型的有Java IO库中的InputStream和OutputStream、Reader和Writer，以及UI库中的Component和JComponent等等。”冒号信手拈来，“因此我们谈到的社会身份，不必拘泥于接口，甚至不必限于抽象类型，关键是该类型是否具备了足够的<span class="strong"><strong>通用性和规范性、稳定性和独立性、灵活性和专业性</strong></span>。还是应了那句话：抽象不是目的而是手段。再拿现实社会说事，每种社会身份都代表了个体与社会缔结的一种契约，它有如下的特点：<span class="emphasis"><em>独立而稳定</em></span>——先于个体而存在，且不随个体的变化而变化；<span class="emphasis"><em>公开而权威</em></span>——为人所知、为人所信；<span class="emphasis"><em>规范而开放</em></span>——制定的协议标准明确，且允许个体在遵守协议的前提下百花齐放。毫无疑问，推行契约制将使社会大受其惠。首先，相同身份的个体可相互替换、新型个体可随时加入，而且不会影响整体框架和流程，保证了系统的灵活性和扩展性。其次，整体不因某一个体的变故而受冲击，保证了系统的稳定性和可靠性；最后，个体角色清晰、分工明确，保证了系统的规范性和可读性。”
        </p><p>
            引号非常注重概念：“社会身份所代表的契约对应的正是<span class="term">规范抽象</span>吧。”
        </p><p>
            “每种身份都是规范抽象的结果。” 冒号推而广之，“具体地说，个体身份对应的规范抽象借助封装，以<span class="term">数据抽象</span>（data abstraction）的形式出现；家庭身份对应的规范抽象借助继承，以<span class="term">类型层级</span>（type hierarchy）的形式出现；社会身份对应的规范抽象借助多态，以<span class="term">多态抽象</span>（polymorphic abstraction）的形式出现。至此，我们分别从行为和规范两个角度分别诠释了OOP的三大特征与公民的三大身份之间的关系。这也非常合乎情理：一个合理设计和实现的类，其对象的行为与规范本应保持一致。”
        </p><p>
            句号欲印证自己的想法：“我的理解是，接口是一个携带契约的角色标签，接口继承的作用就是<span class="emphasis"><em>静态地</em></span>为对象贴上该标签，而多态机制的作用就是<span class="emphasis"><em>动态地</em></span>让对象发挥该角色。因此，要赋予对象某个角色，就应该让相应的类去继承相应的接口。”
        </p><p>
            “你的前半部分表述得非常精当，后半部分则稍有瑕疵。”冒号评论道，“接口可用来代表角色，但角色却不一定要通过接口。正如你提到的，接口继承是静态的，而角色却可能是动态的。比如学生毕业后变成职员，职员升迁后变成经理等等。对于静态类型语言来说，这类问题的解决单靠接口继承是不够的，还需要利用合成等手段，或者利用前面提到的其他抽象类型如mixin或trait。”
        </p><p>
            叹号仍有疑惑：“接口的意义已经很清楚了，那抽象类呢？它们的区别真的很大吗？”
        </p><p>
            “我们已经从语法上比较了它们的区别，那些只是表象的东西。如果对语言规则的理解仅仅停留于语法层面，那么它更多体现为<span class="strong"><strong>对实现的束缚</strong></span>。只有提升到语义层面，它才更多体现为<span class="strong"><strong>对设计的保障</strong></span>。”冒号保持一贯高举高打的风格，“从语义上看，抽象类与接口的区别，并不比它与具体类的区别小多少。”
        </p><p>
            叹号错愕不已：“怎么可能？抽象类与接口好歹都是抽象类型啊。”
        </p><p>
            冒号反诘：“为什么不说抽象类与具体类好歹都是类呢？”
        </p><p>
            叹号一时无语。
        </p><p>
            “先看段历史吧。”冒号幽幽地说，“开始C++是没有抽象类型的，直到1989年C++ Release 2.0发布前的最后一刻，Bjarne Stroustrup才力排众议引入抽象类。从C++的前身C with Classes 开始算起，其间已经整整十年了。即便如此，它的意义在当时仍不为大多数人所认识。推出一个看似小小的语法特征竟会如此艰难，恐怕远远超出诸位的想象吧！有人幻想只通过看语法书就能完全领会语言的精髓，又与痴人说梦何异？”
        </p><p>
            冒号的声音渐渐激昂起来。
        </p><p>
            逗号为自己找到了安慰：“难怪当初学到抽象类时，总感到只知其意而不知其用。”
        </p><p>
            冒号紧接着说：“抽象类的出现，让两种不同角色的类在语法上有了明确的界定：具体类<span class="strong"><strong>描述对象</strong></span>，重在实现；抽象类<span class="strong"><strong>描述规范</strong></span>，重在接口。这种分工降低了用户与实现者之间的耦合度，大大减少了代码的维护成本以及编译时间<a class="link" href="#note11"><sup>[11]</sup></a>。由于抽象类不是为了创建对象，它的实例化自然是没有意义的。又由于它是接口规范，在子类没有实现其所有规范之前，是不能实例化的，否则规范岂不成了一纸空文？在没有抽象类的语法之前，要实现类似的功能，唯一的办法是：在本该抽象的方法被调用时强行中止程序。烦琐丑陋不说，还只能在运行期间捕捉错误。在<span class="term">纯虚函数</span>（pure virtual function）——相当于Java和C#中的抽象方法——被引入之后，任何含有抽象方法的类都是抽象类，编译器将保证它不会被实例化。”
        </p><p>
            问号连连点头：“从这个角度来理解抽象类的语法，一切都顺理成章了。不过，抽象类与接口的区别好像还是没有看到。”
        </p><p>
            谈到兴头，冒号出言更如下阪走丸：“从具体类中分离出抽象类是一次质的飞跃，从抽象类中进一步地分离出接口则是另一次飞跃。Java推出接口类型之时同样饱受质疑，最终还是经受了实践的考验，后又为C#所采纳。其实最初C++的抽象类是为了定义一组协议并强令各子类遵守，实质上正是Java和C#中的接口所起的作用。但在协议规范的实现过程中，可能会产生一些不完全实现类。允许这种类的存在固然是一种灵活的举措，但必须认识到它们与纯规范的抽象类已判若云泥。打个比方，如果把对象看作产品，把具体类看作一个制作产品的模具，那么接口就是模具的规格标准，而抽象类是在模具加工过程中产生的半成品。接口与抽象类无法实例化，模具规格与模具半成品也不能直接制作产品；一个具体类可以有多个接口，一个模具也可有多个不同方面的规格；一个具体类至多只能继承一个抽象类，一个模具也至多只能在一种模具半成品的基础上直接加工。”
        </p><p>
            引号细加回味：“如果具体类、抽象类和接口分别对应于模具、模具半成品和模具规格，那后两者的区别的确比前两者的区别还大。可是假如一个抽象类完全没有任何实现呢？抛开多重继承的限制，它与接口又有何区别呢？”
        </p><p>
            冒号辨析其别：“一个抽象类可以没有任何实现，但也随时可以加入实现。接口则不同，永远都不能有实现代码。这正是引入关键字interface的目的，明明白白地表明：此乃规范集合所在，杜绝任何自以为是、画蛇添足的实现。初看似乎不合常理：这不是自缚手脚、自废武功吗？殊不知<span class="strong"><strong>自由源于自制</strong></span>。许多人为了贪恋一点点代码重用，总忍不住把一些实现放在本该只是规范的地方。一来，这模糊了规范与实现的界限，背离了接口与实现相分离的设计初衷。要知道，再完美的实现都有改动的余地，将其捆绑到规范中只会增加不稳定因素；再完美的实现也不应该影响其他的实现，先入为主只会降低灵活性。二来，带有实现的抽象类无法用于合成，必须通过类继承才能起作用，而实现继承的弊端我们已经见识过了。在有些情况下，规范的实现比较复杂，需要渐进实现，保留一些中间状态的抽象类也是合理的，但最初的接口最好保留。总不能因为有了模具半成品，就抛弃模具规格吧？以Java Collections Framework为例，既规范了Collection、Set、List、Map等接口，又为这些接口提供了抽象类和具体类，从而给了用户三种选择：直接利用具体类、扩展抽象类、直接实现接口，方便程度递减而灵活程度递增。”
        </p><p>
            句号进行反思：“我在想，为什么以前对接口总有本能的排斥心理？原因在于：满脑子更多想的是怎么让程序工作，而不是想怎么让程序工作得更好。因此更重视代码实现，比较忽视规范设计。”
        </p><p>
            众人皆有同感。
        </p><p>
            “确实，在缺乏设计观念的人看来，使用接口和脱裤放屁差不多。”冒号轻笑道，“特别需要注意一种常见的说法：接口是为了克服Java或C#中抽象类不能多重继承的缺点。这句话具有相当大的误导性，因为该处的多重继承是指多重实现继承，而接口甚至连单重实现继承都做不到！许多人对接口与抽象类的认识之所以模糊不清，原因是他们习惯于从<span class="emphasis"><em>定义和语法</em></span>中寻找<span class="emphasis"><em>表象的答案</em></span>，不习惯从<span class="emphasis"><em>本源和语义</em></span>上进行<span class="emphasis"><em>本质的分析</em></span>。然而不可否认，毕竟接口与抽象类提供了相似的抽象机制，在实践中往往确难抉择。因此光从语法上对比二者的差别是远远不够的，需要进一步在语义上进行对比（如表10-2所示）——”
        </p><div class="table"><a name="id604782"></a><p class="title"><b>表 10-2. Java/C#的抽象类与接口在语义上的区别</b></p><div class="table-contents"><table summary="Java/C#的抽象类与接口在语义上的区别" border="1"><colgroup><col><col><col><col><col><col><col><col><col></colgroup><thead><tr><th> </th><th>关系</th><th>共性</th><th>特征</th><th>联系</th><th>重用</th><th>实现</th><th>重点</th><th>演变</th></tr></thead><tbody><tr><td><span class="strong"><strong>接口</strong></span></td><td>can-do</td><td>相同功能</td><td>边缘特征</td><td>横向联系</td><td>规范重用</td><td>多种实现</td><td>可置换性</td><td>新增类型</td></tr><tr><td><span class="strong"><strong>抽象类</strong></span></td><td>is-a</td><td>相同种类</td><td>核心特征</td><td>纵向联系</td><td>代码重用</td><td>多级实现</td><td>可扩展性</td><td>新增成员</td></tr></tbody></table></div></div><br class="table-break"><p>
            冒号展开叙述：“先从本性上看：接口是一套功能规范集合，因此相同的接口代表相同的功能，多表示‘can-do’关系，常用后缀为‘-able’的形容词命名，如Comparable、Runnable、Cloneable等等。接口一般表述的是对象的边缘特征<a class="link" href="#note12"><sup>[12]</sup></a>，或者说一个对象在某一方面的特征，因此能在本质不同的类之间建立起横向联系。由于一个对象可拥有多方面的角色特征，故而可有多种接口。与之相对地，抽象类是一类对象的本质属性的抽象，因此相同的抽象基类代表相同的种类，多表示‘is-a’关系，常用名词命名。抽象类一般表述的是对象的核心特征，只能在本质相同的类之间沿着继承树建立起纵向联系。由于一个对象通常只有一个核心，故而只能有一种基类。再从目的上看：接口是为了规范重用，让一个规范有多种实现，看重的是<span class="emphasis"><em>可置换性</em></span>；抽象类主要是为了代码重用<a class="link" href="#note13"><sup>[13]</sup></a>，能逐级分步实现基类的抽象方法，看重的是<span class="emphasis"><em>可扩展性</em></span>。”
        </p><p>
            叹号追问：“演变指的又是什么呢？”
        </p><p>
            冒号答道：“严格说来，演变不属语义范畴，属于语法规则的一个推论。在系统演变过程中，接口与抽象类的表现差异很大。接口由于是被广泛采用的规范，相当于行业标准，一经确立不能轻易改动。一旦被广泛采用，它的任何改动——包括增减接口、修改接口的签名或规范——将波及整个系统，必须慎之又慎。抽象类的演变则没有那么困难，一则它在系统中用得没有接口那么广泛，更多地是家庭身份而非社会身份；二则它可随时新增域成员或有默认实现的方法成员<a class="link" href="#note14"><sup>[14]</sup></a>，所有子类将自动得以扩充。这是抽象类的最大优点之一。不过接口也有抽象类所不具备的优点，虽然自身难以演化，但很容易让其他类型演化为该接口的子类型。例如，JDK5.0之前的StringBuffer、CharBuffer、Writer和PrintStream本是互不相关的，在引进了接口Appendable并让以上类实现该接口后，它们便有了横向联系，均可作为格式化输出类Formatter的输出目标。”
        </p><p>
            问号还留有一个疑点：“现在接口与抽象类之间的差异是越来越清晰了，我只是有一点一直没想通：标记接口究竟有什么用？它一个方法都没有，也就谈不上规范，也无法利用多态机制，继承这类接口又有何意义呢？”
        </p><p>
            逗号随口说：“这就好比有些社会身份是光挂名头不干事的虚衔，不足为奇。”
        </p><p>
            冒号回应道：“先需澄清一点，一个类型的规范不限于单个的方法，类型整体上也有规范，比如主要目的、适用场合、限定条件、类不变量等等。另外，接口的目的是为了产生<span class="emphasis"><em>多态类型</em></span>，不能只看到‘多态’而忽略‘类型’。一个接口哪怕没有一个方法，也是有意义的。首先，接口是一种类型，有严格的语法保障和明确的语义提示，这也是静态类型的优势所在。让一个具体类继承特定接口，既凸显了设计者的用意，也授予用户针对性地处理该类型的权力。比如java.util.EventListener接口为所有的事件监听器提供了统一的根类型。其次，有时需要对某些类型提出特殊要求、提供特殊服务或进行特殊处理，而这些并不能通过公有方法来办到，也没有其他有效的语言支持。标记接口可担此任，成为类型<span class="term">元数据</span>（metadata）的载体。比如给一个类贴上一个java.io.Serializable的标签，它的对象便能被序列化<a class="link" href="#note15"><sup>[15]</sup></a>，具体工作由JVM来完成。用户也可以通过自定义私有的writeObject 、readObject等方法来定制序列化方式。值得指出的是，当标记接口仅仅用于元数据时，更好的办法是采用<span class="term">属性导向式编程</span>（@OP），Java中的annotation、C#中的attribute即作此用。”
        </p><p>
            逗号摸了摸后脑勺：“原来标记接口并非虚有其名，还是在偷偷地干实事呢。”
        </p><p>
            冒号见时候已到，准备落下帷幕：“至此，我们探讨了OOP中最基本的机制——封装、最独特的机制——继承、最重要的机制——多态。在今天的课结束之前，请大家每人用一个关键词来形容自己眼中的OOP，并作简要说明。”
        </p><p>
            引号说：“责任——在契约化的公民社会中，最重要的是对自己、对家庭、对社会的责任感。”
        </p><p>
            问号说：“变化——采用封装以防个人之变，慎用继承以防家庭之变，采用多态以防社会之变。”
        </p><p>
            逗号说：“分合——数据与运算结合，接口与实现分离。”
        </p><p>
            句号说：“抽象——无论是封装、继承还是多态，都是施诸众对象之上的抽象机制。”
        </p><p>
            叹号说：“虚伪——用封装来掩盖内心，用多态来掩盖外表，提倡继承责任却不提倡继承财富！”
        </p><p>
            冒号欣赞道：“不错不错，虽然角度各异，但均深中肯綮。我也大可安心下课了！”
        </p><p>
            众人也乐得打道回府。
        </p></div><div class="section" title="，插语"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="note"></a>，插语</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p><a name="note1"></a>
                    C#中表示列表和映射的抽象类型（具体类型）分别是IList（List）和IDictionary（Dictionary）。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    此处主要指以继承为基础的抽象类型，如接口与抽象类。
                </p></li><li class="listitem"><p><a name="note3"></a>
                    在C++中，如果一个类不含数据只含抽象的成员函数（即pure virtual function），则该类有时被称为纯抽象类（pure abstract class），与Java和C#中的interface的功用大致相当。
                </p></li><li class="listitem"><p><a name="note4"></a>
                    参考文献【4】和【5】对trait有深入的介绍，并与mixin作了详细的比较。
                </p></li><li class="listitem"><p><a name="note5"></a>
                    该问题的一个解决方式是依赖注射，即把创建被合成对象的职责交给外界。但严格说来这不是合成，而是聚合或关联。它们之间的详细区别请参见§11.2。
                </p></li><li class="listitem"><p><a name="note6"></a>
                    C++可利用CRTP（<span class="strong"><strong>C</strong></span>uriously <span class="strong"><strong>R</strong></span>ecurring <span class="strong"><strong>T</strong></span>emplate <span class="strong"><strong>P</strong></span>attern）的惯用法来实现mixin。
                </p></li><li class="listitem"><p><a name="note7"></a>
                    指C#3.0的扩展方法（extension method）。
                </p></li><li class="listitem"><p><a name="note8"></a>
                    第一个支持OOP的语言是Simula 67（1967年）。
                </p></li><li class="listitem"><p><a name="note9"></a>
                    例外情形：Java的interface可含static final域成员，C#的interface还可含property、event或indexer成员。
                </p></li><li class="listitem"><p><a name="note10"></a>
                    虽然具体类型有可能被继承，但通常并不提倡。
                </p></li><li class="listitem"><p><a name="note11"></a>
                    据参考文献【1】中介绍，一些大型系统在引入抽象类后，编译时间少了一个数量级。
                </p></li><li class="listitem"><p><a name="note12"></a>
                    接口也可能描述对象的核心特征，但一个类至多一个这样的接口。
                </p></li><li class="listitem"><p><a name="note13"></a>
                    由于类继承同时也继承了接口，抽象类也能规范重用，但更侧重代码重用。
                </p></li><li class="listitem"><p><a name="note14"></a>
                    前提是新增的方法成员不与子类型的方法发生冲突。
                </p></li><li class="listitem"><p><a name="note15"></a>
                    严格说来，还要求该类所有非static非transient的域都是可序列化的。
                </p></li></ol></div></div><div class="section" title="。总结"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="summary"></a>。总结</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    具体类型是创建对象的模板，抽象类型是创建类型的模块。
                </p></li><li class="listitem"><p>
                    抽象数据类型的核心是数据抽象，而抽象类型的核心是多态抽象。
                </p></li><li class="listitem"><p>
                    抽象类型除了接口和抽象类外，还有mixin、trait等，它们用来克服以下问题——
                </p><p class="simpara"><span class="emphasis"><em>合成的缺陷：</em></span></p><p class="simpara">用法不如继承那么简便优雅；</p><p class="simpara">不能产生子类型；</p><p class="simpara">无法覆盖基础类的方法，也无法访问它的protected成员；</p><p class="simpara">不能以抽象类型为基础类。</p><p class="simpara"><span class="emphasis"><em>具体类型的矛盾与缺陷：</em></span></p><p class="simpara">作为创造对象的单位，功能越多越好；</p><p class="simpara">作为可重用的单位，功能越少越好。</p><p class="simpara">不宜被继承。</p><p class="simpara"><span class="emphasis"><em>接口的矛盾与缺陷：</em></span></p><p class="simpara">客户类希望它提供尽可能多的服务；</p><p class="simpara">实现类希望尽可能少的实现代码。</p><p class="simpara">无法代码重用。</p><p class="simpara"><span class="emphasis"><em>抽象类的缺陷：</em></span></p><p class="simpara">多重类继承或复杂晦涩或未获支持。</p></li><li class="listitem"><p>
                    mixin的特点：抽象性和依赖性；实用性和可重用性；专一性和细粒度性；可选性和边缘性。
                </p></li><li class="listitem"><p>
                    在设计阶段，从具体需求中构建出抽象模型，此时抽象类型尤为关键；在实现阶段，根据抽象模型来完成具体实现，此时具体类型更为重要。
                </p></li><li class="listitem"><p>
                    抽象类型除了能创建类型外，还能提供动态节点，以增加软件的灵活性和可扩展性。
                </p></li><li class="listitem"><p>
                    社会身份代表了个体与社会缔结的一种契约，具有独立、稳定、公开、权威、规范、开放等特点。
                </p></li><li class="listitem"><p>
                    以社会身份作为公民之间联系的纽带，以接口类型作为对象之间联系的纽带。
                </p></li><li class="listitem"><p>
                    系统中广泛用于模块之间通讯的数据类型相当于社会身份，提倡使用接口类型。但也不必拘泥，甚至不必限于抽象类型，关键是要确保该类型的通用性、规范性、稳定性、独立性、灵活性和专业性。
                </p></li><li class="listitem"><p>
                    个体身份对应的规范抽象借助封装，以数据抽象的形式出现；家庭身份对应的规范抽象借助继承，以类型层级的形式出现；社会身份对应的规范抽象借助多态，以多态抽象的形式出现。
                </p></li><li class="listitem"><p>
                    从具体类中分离出抽象类是一次质的飞跃，从抽象类中分离出接口则是另一次飞跃。
                </p></li><li class="listitem"><p>
                    接口与抽象类的语法区别：接口不能提供实现但能多重继承，抽象类则正相反；接口只能包含公有的、非静态的、抽象的方法成员，抽象类则无此限制。
                </p></li><li class="listitem"><p>
                    接口与抽象类的语义区别：接口是一套功能规范集合，相同的接口代表相同的功能，多表示“can-do”关系，一般是对象的边缘特征，在本质不同的类型之间建立横向联系；抽象类是一类对象的本质属性的抽象，相同的抽象基类代表相同的种类，多表示“is-a”关系，一般是对象的核心特征，在本质相同的类型之间建立纵向联系。接口看重规范重用和可置换性；抽象类看重代码重用和可扩展性。
                </p></li><li class="listitem"><p>
                    接口与抽象类的演变：抽象类的演变较为容易；接口自身很难演变，但很容易让其他类型演变为它的子类型。
                </p></li><li class="listitem"><p>
                    标记接口除了能定义类型外，还可作为类型元数据的载体。
                </p></li></ul></div></div><div class="section" title="“”参考"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="reference"></a>“”参考</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>
                    Bjarne Stroustrup．The Design and Evolution of C++．Reading, MA：Addison-Wesley，1994．277-281
                </p></li><li class="listitem"><p>
                    Joshua Bloch．Effective Java: Programming Language Guide．Boston, MA：Addison-Wesley，2001．84-88
                </p></li><li class="listitem"><p>
                    Nathanael Schärli，Stéphane Ducasse，Oscar Nierstrasz，Andrew P. Black．Traits: Composable Units of Behaviour．ECOOP，2003，LNCS 2743：248–274
                </p></li><li class="listitem"><p>
                    Stéphane Ducasse，Oscar Nierstrasz，Nathanael Schärli，Roel Wuyts，Andrew P. Black．Traits: A Mechanism for Fine-grained Reuse．ACM Transactions，2006，28(2)：331-388
                </p></li><li class="listitem"><p>
                    Wikipedia．Mixin．<a class="link" href="http://en.wikipedia.org/wiki/Mixin" target="_top">http://en.wikipedia.org/wiki/Mixin</a>
                </p></li></ol></div></div></div>

<!-- below is edited manually -->
<strong><span style="font-family: 宋体">课后思考</span></strong>
<ul style="margin-top: 0cm; list-style-type: none">
    <li>10-01 多态类型在何种程度上解放了静态类型的束缚？</li>
    <li>10-02 请总结参数多态与子类型多态的特点和适用场合。</li>
    <li>10-03 为什么抽象类型如此重要？</li>
    <li>10-04 你认为有必要引入mixin或trait类型吗？</li>
    <li>10-05 区分接口与抽象类的意义何在？</li>
    <li>10-06 你常有往接口中放置代码的冲动吗？</li>
    <li>10-07 如何理解文中“多态使得公民拥有社会身份”这句话？</li>
    <li>10-08 “针对接口编程”与“公民之间以社会身份互相交流”有何相似之处？</li>
    <li>10-09 你是如何理解OOP中抽象、封装、继承和多态的？</li>
    <li>10-10 每当一项新技术出现时，你通常抱什么态度？</li>
    <li>10-11 你会在编程中对某些语法上的限制感到恼火吗？</li>
    <li>10-12  在理解或比较一些编程概念时，你是更习惯从定义和语法的角度，还是更习惯从本源和语义的角度？</li>
    <li>10-13  本课与前课均提到了编程与武术相通之处：攻守兼备，动静得宜，刚柔并济，虚实结合。对此你有何心得体会？</li>
</ul><p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F10%2F24%2Fcolon-class-10_2%2F&amp;title=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A710.2%EF%BC%9A%E6%8A%BD%E8%B1%A1%E7%B1%BB%E5%9E%8B" id="wpa2a_16">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2009年10月20日 -- <a href="http://blog.zhenghui.org/2009/10/20/colon-class-10_1/" title="冒号课堂§10.1：多态类型">冒号课堂§10.1：多态类型</a> (8)</li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/10/24/colon-class-10_2/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>冒号课堂§10.1：多态类型</title>
		<link>http://blog.zhenghui.org/2009/10/20/colon-class-10_1/</link>
		<comments>http://blog.zhenghui.org/2009/10/20/colon-class-10_1/#comments</comments>
		<pubDate>Tue, 20 Oct 2009 09:37:45 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[包含多态]]></category>
		<category><![CDATA[参数多态]]></category>
		<category><![CDATA[多态]]></category>
		<category><![CDATA[子类型多态]]></category>
		<category><![CDATA[模板方法模式]]></category>
		<category><![CDATA[策略模式]]></category>
		<category><![CDATA[继承]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=506</guid>
		<description><![CDATA[<b>多态类型</b>——静中之动（<em>通过实例展示多态类型的三种用法</em>）<br/> • 继承是多态的基础，多态是继承的目的<br/> • 多态是动静结合的产物，将静态类型的安全性和动态类型的灵活性融为一体<br/> • 前者（参数多态）是发散式的，让相同的实现代码应用于不同的场合<br/> • 后者（包含多态）是收敛式的，让不同的实现代码应用于相同的场合<br/> • 模板方法模式突出的是稳定坚固的骨架，策略模式突出的是灵活多变的手腕 [...]]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center;"><span style="font-family: 宋体;">冒号课堂</span></h1>
<p><strong><span style="font-size: 13pt; font-family: 宋体;">第十课 多态机制（1）</span></strong></p>
<p><strong><span style="font-family: 宋体;">课前导读</span></strong></p>
<p style="text-indent: 18pt;"><span style="font-family: 宋体;">本课通过实例编程和对抽象类型的解读，显示了OOP中多态机制和抽象类型的重要性，有助于培养和加深读者的OOP语感。</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 18pt;"><span style="font-family: 宋体;">本课共分两节——</span></p>
<p style="text-indent: 18pt;"><em>1.</em><em><span style="font-family: 宋体;">多态类型——静中之动</span></em></p>
<p style="text-indent: 18pt;"><em>2.</em><em><span style="font-family: 宋体;">抽象类型——实中之虚</span></em></p>
<!-- below comes from generated html -->
<p>&nbsp;</p>

<div class="article" title="多态类型" lang="zh-CN">
<div class="titlepage">
<div>
<div>
<h1 class="title"><a name="id525234"></a>10.1 多态类型——静中之动</h1>
</div>
<div>
<div class="author">
<h3 class="author">郑晖</h3>
</div>
</div>
<div>
<div class="abstract" title="摘要">
<p class="title"><strong>摘要</strong></p>
<p>通过实例展示多态类型的三种用法</p>
</div>
</div>
</div>
<hr /></div>
<div class="toc">
<p><strong>目录</strong></p>
<dl><dt><span class="section"><a href="#preview">！预览</a></span></dt><dt><span class="section"><a href="#question">？提问</a></span></dt><dt><span class="section"><a href="#explaination">：讲解</a></span></dt><dt><span class="section"><a href="#note">，插语</a></span></dt><dt><span class="section"><a href="#summary">。总结</a></span></dt><dt><span class="section"><a href="#reference">“”参考</a></span></dt></dl></div>
<div class="epigraph">
<div class="literallayout">
<p>动静屈伸，唯变所适</p>
</div>
<div class="attribution"><span>—<span class="attribution">《王弼•周易略例》</span></span></div>
</div>
<div class="section" title="！预览">
<div class="titlepage">
<div>
<div>
<h2 class="title" style="clear: both;"><a name="preview"></a>！预览</h2>
</div>
</div>
</div>
<div class="itemizedlist">
<ul class="itemizedlist" type="disc">
	<li class="listitem">
<p>继承是多态的基础，多态是继承的目的</p></li>
	<li class="listitem">
<p>多态是动静结合的产物，将静态类型的安全性和动态类型的灵活性融为一体</p></li>
	<li class="listitem">
<p>前者（参数多态）是发散式的，让相同的实现代码应用于不同的场合</p></li>
	<li class="listitem">
<p>后者（包含多态）是收敛式的，让不同的实现代码应用于相同的场合</p></li>
	<li class="listitem">
<p>模板方法模式突出的是稳定坚固的骨架，策略模式突出的是灵活多变的手腕</p></li>
</ul>
</div>
</div>
<div class="section" title="？提问">
<div class="titlepage">
<div>
<div>
<h2 class="title" style="clear: both;"><a name="question"></a>？提问</h2>
</div>
</div>
</div>
<div class="itemizedlist">
<ul class="itemizedlist" type="disc">
	<li class="listitem">
<p>多态与继承有何关系？</p></li>
	<li class="listitem">
<p>多态的重要意义何在？</p></li>
	<li class="listitem">
<p>多态有哪几种形式？它们各自有什么特点？</p></li>
	<li class="listitem">
<p>什么是策略模式？它与模板方法模式有何相同点和不同点？多态在其中起到了什么作用？</p></li>
</ul>
</div>
</div>
<div class="section" title="：讲解">
<div class="titlepage">
<div>
<div>
<h2 class="title" style="clear: both;"><a name="explaination"></a>：讲解</h2>
</div>
</div>
</div>
<p>当冒号迈着不变的步伐出现在教室时，手上有了一点变化：左手仍拎着笔记本包，右手却多了一样东西。大家定睛一看，原来是个电脑主板，不由得暗自纳闷：难道软件课改成了硬件课？</p>
<p>冒号照例直入主题：“上节课我们对继承的利弊作了详细的分析，其中最重要的观点是：继承的主要用途不是代码重用，而是代码被重用。这依赖于两个前提，一个是在语义上遵循里氏代换原则，另一个是在语法上支持<span class="term">多态</span>（polymorphism）机制。因此不妨说，对于静态类型语言来说，<span class="strong"><strong>继承是多态的基础，多态是继承的目的</strong></span>。”</p>
<p>问号忍不住问：“为什么要强调静态类型呢？”</p>
<p>“还记得<span class="term">鸭子类型</span><a class="link" href="#note1"><sup>[1]</sup></a>吗？那就是一种不依赖于继承的多态类型，也是动态类型语言一大优劣参半的特色。”冒号提醒道，“静态类型语言中的多态是动静结合的产物，将静态类型的安全性和动态类型的灵活性融为一体。它一般有两种实现方式：一种利用GP（泛型编程）中的<span class="term">参数多态</span>（parametric polymorphism），一种利用OOP中的<span class="term">包含多态</span>（inclusion polymorphism）或称<span class="term">子类型多态</span>（subtyping polymorphism）。从实现机制上看，二者的不同之处在于何时将一个变量与其实际类型所定义的行为挂钩。前者在编译期，属于<span class="term">早绑定 </span>（early binding）或<span class="term">静态绑定</span>（static binding）<a class="link" href="#note2"><sup>[2]</sup></a>；后者在运行期，属于<span class="term">迟绑定 </span>（late binding）或<span class="term">动态绑定</span>（dynamic binding）。从应用形式上看，前者是<span class="emphasis"><em>发散式</em></span>的，让相同的实现代码应用于不同的场合；后者是<span class="emphasis"><em>收敛式</em></span>的，让不同的实现代码应用于相同的场合。从思维方式上看，前者是泛型式编程风格，看重的是算法的普适性；后者是对象式编程风格，看重的是接口与实现的分离度。尽管二者从范式到语法、语义都大相径庭，但都是为着同一个目的：在保证必要的类型安全的前提下，突破编译期间过于严苛的类型限制。对于既是静态类型语言又是静态语言、既支持OOP又支持GP的C++、Java和C#而言，多态机制是保证代码的灵活性、可维护性和可重用性的<span class="emphasis"><em>终极武器</em></span>。为了说明问题，我们看一个简单而实用的例子：编写一个类，让它能储存用户名和密码，以作今后验证之用。”</p>
<p>叹号一愣：“这题是不是太简单了？还有别的要求吗？”</p>
<p>冒号摇摇头。</p>
<p>引号却认为：“要求太少反而不好做。比如是把数据放在内存、还是文件或者数据库？密码以明文还是密文的形式存储？”</p>
<p>句号提出：“无论是数据的存放方式还是密码的加密方式，都不应该硬编码。”</p>
<p>“循此思路，我们就来编写一个可重用的抽象类。”冒号投放了一段Java代码——</p>
<div class="informalexample">
<pre class="programlisting">/** 一个可以验证用户名和密码的类  */
abstract class Authenticator
{
    /** 保存用户名和密码  */
    final public void save(String user, String password)
    {
        if (password == null)
            password = "";
        store(user, encrypt(password));
    }

    /** 验证用户名和密码  */
    final public boolean authenticate(String user, String password)
    {
        String storedPassword = retrieve(user);
        if (storedPassword == null) return false; // 无此用户
            
        if (password == null)
            password = "";
        return storedPassword.equals(encrypt(password));
    }

    /** 保存用户名和加密过的密码  */
    protected abstract void store(String user, String encryptedPassword);

    /** 从用户名获取相应的加密过的密码  */
    protected abstract String retrieve(String user);

    /** 给明文单向（one-way）加密，默认不加密  */
    protected String encrypt(String text) { return text; }
}</pre>
</div>
<p>冒号解说道：“该抽象类有两个public接口，一个用来保存，一个用来验证。它们用final修饰符来禁止子类覆盖，因为真正的扩展点是三个protected方法。其中store和retrieve是抽象的，encrypt有一个平凡实现。以此为基础，再根据实际需要来编写子类，具体实现这三个方法。”</p>
<p>幻灯片转到下一页——</p>
<div class="informalexample">
<pre class="programlisting">import java.util.Map;
import java.util.HashMap;

/** 一个简单的验证类，数据放在内存，密码保持明文  */
class SimpleAuthenticator extends Authenticator
{
    private Map&lt;String, String&gt; usrPwd = new HashMap&lt;String, String&gt;();

    @Override protected void store(String user, String encryptedPassword)
    {
        usrPwd.put(user, encryptedPassword);  
    }

    @Override protected String retrieve(String user)
    {
        return usrPwd.get(user);  
    }
}</pre>
</div>
<p>“我们利用HashMap来储存数据，密码保持明文。这大概是最简单的一种子类了。”冒号仿佛在轻轻地把玩着一件小物什，“为安全起见，最好还是将密码加密。于是我们设计了稍微复杂一点的子类——”</p>
<div class="informalexample">
<pre class="programlisting">import java.security.MessageDigest;

/** 一个安全的验证类，数据放在内存，密码经过SHA-1加密  */
class Sha1Authenticator extends SimpleAuthenticator
{
    private static final String ALGORITHM = "SHA-1"; // SHA-1算法
    private static final String CHARSET = "UTF-8"; // 避免依赖平台

    @Override protected String encrypt(String plainText)
    {
        try
        {
            MessageDigest md = MessageDigest.getInstance(ALGORITHM);
            md.update(plainText.getBytes(CHARSET));
            byte digest[] = md.digest();
            // BASE64编码比十六进制编码节省空间
           //为简便起见用到了非标准的API，因此以下代码有警告 
            return (new sun.misc.BASE64Encoder()).encode(digest); 
        }
        catch (java.security.NoSuchAlgorithmException e)
        {
            throw new InternalError(e.getMessage());  // 不可能发生
        }
        catch (java.io.UnsupportedEncodingException e)
        {
            throw new InternalError(e.getMessage());  // 不可能发生
        }
    }
}</pre>
</div>
<p>逗号质疑道：“不是具体类不宜被继承的吗？怎么Sha1Authenticator类却继承了具体类SimpleAuthenticator？”</p>
<p>冒号略表赞许：“很高兴你没有忘记这个原则。不过考虑到Sha1Authenticator类需要覆盖父类的encrypt方法，这么做也是情有可原的。当然最好选择让该类直接继承抽象类Authenticator，但作为示例代码，我们还是希望它简洁一些，不想让过多的细枝末节掩盖核心主干。下面是测试代码——”</p>
<div class="informalexample">
<pre class="programlisting">public class TestAuthenticator 
{  // 为避免额外依赖，没有采用JUnit等单元测试工具
    public static void main(String[] args)
    {
        test(new SimpleAuthenticator());
        test(new Sha1Authenticator());
    }

    // 测试给定的Authenticator 
    private static void test(Authenticator authenticator) // 子类型多态
    {
        test(authenticator, "user", "password");
        test(authenticator, "user", "newPassword");
        test(authenticator, "admin", "admin");
        test(authenticator, "guest", null);
        test(authenticator, null, "pass");

        authenticator.save("scott", "tiger");
        assert(!authenticator.authenticate("scott", "TIGER")); // 大小写敏感
        assert(!authenticator.authenticate("SCOTT", "tiger")); // 大小写敏感
    }

    private static void test(Authenticator authenticator, String user, String password)
    {
        authenticator.save(user, password);
        assert(authenticator.authenticate(user, password));
    }
}</pre>
</div>
<p>引号觉得眼熟：“这不是上节课讲的<span class="term">模板方法模式</span>吗？”</p>
<p>“正是此公。”冒号确认，“该模式的核心思想是：固定整体框架和流程以保证可重用性，留出一些子类定制点以保证可扩展性。在测试代码的两个test方法中，传入的参数是Authenticator类，但数据存放和密码加密的方式是在运行中才确定的，即先后遵照SimpleAuthenticator类和Sha1Authenticator类的实现。这就是我们所说的子类型多态的效果——让不同的实现代码应用于相同的场合。假设没有多态机制，这种效果就只能靠if/else或switch之类的条件语句才能实现，非常地痛苦。”</p>
<p>冒号的眉头皱成了粗体的“川”字。</p>
<p>“还有更好的方法吗？”句号察言观色，断定老冒还留有后手。</p>
<p>果不其然，冒号的眉毛立刻又舒展开来，中气充沛地应道：“有！诸位请看——”</p>
<div class="informalexample">
<pre class="programlisting">// 键值对的存取接口
interface KeyValueKeeper
{
    public void store(String key, String value);
    public String retrieve(String key);
}

// 加密接口
interface Encrypter
{
    public String encrypt(String plainText);
}

class Authenticator
{
    private KeyValueKeeper keeper;
    private Encrypter encrypter;

    public Authenticator(KeyValueKeeper keeper, Encrypter encrypter)
    {
        this.keeper = keeper;
        this.encrypter = encrypter;
    }

    public void save(String user, String password)
    {
        if (password == null)
            password = "";
        keeper.store(user, encrypter.encrypt(password));
    }

    public boolean authenticate(String user, String password)
    {
        String storedPassword = keeper.retrieve(user);
        if (storedPassword == null) return false;

        if (password == null)
            password = "";
        return storedPassword.equals(encrypter.encrypt(password));
    }
}</pre>
</div>
<p>冒号加以引导：“如果仔细比较两种设计，就会发现它们很相似。后者只不过把前者对子类开放的接口合成为自己的两个成员。再看接口的实现类——”</p>
<div class="informalexample">
<pre class="programlisting">class MemoryKeeper implements KeyValueKeeper
{
     private Map&lt;String, String&gt; keyValue = new HashMap&lt;String, String&gt;();

    @Override public void store(String key, String value)
    {
        keyValue.put(key, value);  
    }

    @Override public String retrieve(String key)
    {
        return keyValue.get(key);  
    }
}

class PlainEncrypter implements Encrypter
{
    @Override public String encrypt(String plainText)
    {
        return plainText;
    }
}

class Sha1Encrypter implements Encrypter
{
    private static final String ALGORITHM = "SHA-1";
    private static final String CHARSET = "UTF-8"; 

    @Override public String encrypt(String plainText)
    {
        try
        {
            MessageDigest md = MessageDigest.getInstance(ALGORITHM);
            md.update(plainText.getBytes(CHARSET));
            byte digest[] = md.digest();
            return (new sun.misc.BASE64Encoder()).encode(digest); 
        }
        catch (java.security.NoSuchAlgorithmException e)
        {
            throw new InternalError(e.getMessage());
        }
        catch (java.io.UnsupportedEncodingException e)
        {
            throw new InternalError(e.getMessage());
        }
    }
}</pre>
</div>
<p>逗号比较后得出结论：“MemoryKeeper与SimpleAuthenticator、Sha1Encrypter与Sha1Authenticator除了超类型和方法访问修饰符外，其他毫无二致。”</p>
<p>屏幕滚动出另一段代码——</p>
<div class="informalexample">
<pre class="programlisting">public class TestAuthenticator
{
    public static void main(String[] args)
    {
        test(new Authenticator(new MemoryKeeper(), new PlainEncrypter()));
        test(new Authenticator(new MemoryKeeper(), new Sha1Encrypter()));
    }	

    private static void test(Authenticator authenticator) // 隐含子类型多态
    { /* 同上，略 */}
}</pre>
</div>
<p>“测试代码区别也不大，只是Authenticator的多态性更加隐蔽。”冒号如是说。</p>
<p>叹号挑剔说：“后一种创建实例稍显麻烦一些。”</p>
<p>“但它是以小弊换大利。”冒号朗声而道，“首先，后者用的是合成与接口继承，比前者的实现继承更值得推荐，理由在上堂课业已阐明。其次，假设共有M种数据存取方式，包括内存、文件、数据库等等；共有N种加密方式，包括明文、SHA-1、SHA-256、MD5等等。按第一种设计，需要（M×N）个实现类；按第二种设计，只要（M+N）个实现类。这还只是两种变化因素，假如需要考虑更多的因素，二者差距将更大。比如增加编码方式：加密后的数据可以选择费空间省时间的十六进制编码、费时间省空间的BASE64编码、省时间省空间却包含非打印字符的原始形式等；比如增加安全强度：引入salt、nonce或IV等<a class="link" href="#note3"><sup>[3]</sup></a>；比如增加密码状态：已生效密码、未生效密码、已过期密码等等。对比下面的UML类图，孰优孰劣更加一目了然。”</p>
<p>众人眼前出现了两幅图——</p>
<div class="figure"><a name="id525517"></a>
<p class="title"><strong>图10-1. Authenticator的UML类图（模板方法模式）</strong></p>
<div class="figure-contents">
<div class="mediaobject"><img src="http://blog.zhenghui.org/img/colonclass/figure10-1.jpg" alt="Authenticator的UML类图（模板方法模式）" /></div>
</div>
</div>
<br class="figure-break" />
<div class="figure"><a name="id525535"></a>
<p class="title"><strong>图10-2. Authenticator的UML类图（策略模式）</strong></p>
<div class="figure-contents">
<div class="mediaobject"><img src="http://blog.zhenghui.org/img/colonclass/figure10-2.jpg" alt="Authenticator的UML类图（策略模式）" /></div>
</div>
</div>
<br class="figure-break" />
<p>冒号指着屏幕问：“图二不仅比图一少了三个实现类，而且可重用性也更高。大家说是为什么？”</p>
<p>引号应答：“图一中的九个Authenticator的子类只能作为验证类来重用，而图二中六个实现类不仅可以合作完成验证类的功能，还能分别单独提供键值存储和加密字符串的功能。”</p>
<p>冒号作出肯定：“这就是职责分离的好处。存储与加密本是两样不相干的工作，必要时可以合作，但平时最好分开管理，符合‘低耦合、高内聚’的原则。”</p>
<p>问号注意到图中的注释，遂问：“第二种采用的是策略模式？”</p>
<p>冒号颔首：“简单地说，<span class="term">策略模式</span>（strategy pattern或policy pattern）的基本思想是：把一个模块所依赖的某类算法委交其他模块实现。比如Java中的Comparable和Comparator、C#中的IComparer就是比较算法的接口，当一个类的某个方法接收了此种类型的参数，实质上就采用了策略模式。”</p>
<p>逗号不以为奇：“这岂非很平常？”</p>
<p>“你认为设计模式真的高不可攀吗？”冒号反问道，“包括模板方法模式，你们很可能也在编程实践中采用过，只不过相交不相识罢了。”</p>
<p>句号看出：“模板方法模式与策略模式非常神似，都是把一个类的可变部分移交给其他类处理。”</p>
<p>“照你这么说，绝大多数设计模式都是神似的，这也是为什么我们不专门谈设计模式的缘故。GoF设计模式是OOP大树上结出的硕果，在你心中培养的OOP成熟之前，匆忙缔结的果实多半是青涩弱小的。”冒号忠告，“我们也不会对设计模式避而不谈，但凡提及都是水到渠成的产物。再说回这两种设计模式，虽然有相通的思想，也能解决相同的问题，在稳定性与灵活性之间都取得了某种平衡，但还是各有侧重的。模板方法模式突出的是稳定坚固的骨架，策略模式突出的是灵活多变的手腕。不妨拿国家政策作比：一个强调对内要稳，老一辈制订了大政方针，下一代必须在坚持原则的前提下进行完善；一个强调对外要活，不能或不便自行开发的技术不妨从国外引进。”</p>
<p>叹号一乐：“哈！设计模式上升到了政策模式。”</p>
<p>冒号抽丝剥茧：“正如模板方法模式可看作控制反转的特例，策略模式与<span class="term">依赖注射</span>（Dependency Injection）也异曲同工。第二个Authenticator所依赖的两个功能KeyValueKeeper和Encrypter，就是是通过构造方法‘注射’进来的<a class="link" href="#note4"><sup>[4]</sup></a>。当然策略只是一种特殊的依赖，是自内而外的——将算法抽出来外包；依赖注射的机制更复杂、涵盖面更广，是自外而内的——从外部嵌入定制功能。后者被广泛地用于框架应用之中，尤以Spring Framework和Google Guice为代表。”</p>
<p>引号听得起劲：“这下热闹了，设计模式、框架与OOP范式全搅和到一块了。”</p>
<p>“还有GP范式呢。”冒号顺接话题，“让我们再用C++的模板来实现一下Authenticator类吧。没有继续采用Java，是因为它的泛型仍离不开子类型多态。”</p>
<p>说着，他换上了C++代码——</p>
<div class="informalexample">
<pre class="programlisting">#include &lt;string&gt;
#include &lt;map&gt;

using namespace std;

template &lt;typename KeyValueKeeper, typename Encrypter&gt;
class Authenticator
{
private:
    KeyValueKeeper keeper;
    Encrypter encrypter;
public:
    void save(const string&amp; user, const string&amp; password)
    {
        keeper.store(user, encrypter.encrypt(password));
    }

    bool authenticate(const string&amp; user, const string&amp; password) const
    {
        string storedPassword;
        if (!keeper.retrieve(user, storedPassword)) return false;

        return storedPassword == encrypter.encrypt(password);
     }
};

class MemoryKeeper
{
private:
    map&lt;string, string&gt; keyValue;
public:
    void store(const string&amp; key, const string&amp; value)
    {
        keyValue[key] = value;  
    }

    bool retrieve(const string&amp; key, string&amp; value) const
    {
        map&lt;string, string&gt;::const_iterator itr = keyValue.find(key);
        if (itr == keyValue.end()) return false;

        value = itr-&gt;second;  
        return true;
    }
};

class PlainEncrypter
{
public:
    string encrypt(const string&amp; plainText) const { return plainText; }
};

class Sha1Encrypter
{
public:
    string encrypt(const string&amp; plainText) const { /* 省略代码  */ }
};

namespace
{
    template &lt;typename K, typename E&gt;
    void test(Authenticator&lt;K, E&gt; authenticator) // 参数多态
    { /* 省略代码  */ }
}

int main()
{ 
    test(Authenticator&lt;MemoryKeeper, PlainEncrypter&gt;());
    test(Authenticator&lt;MemoryKeeper, Sha1Encrypter&gt;());
    return 0;
}</pre>
</div>
<p>“以上代码与Java版的策略模式代码很相似，主要的区别是把KeyValueKeeper和Encrypter两个接口换成了模板参数。由于模板是在编译期间实例化的，因此没有动态绑定的运行开销，但缺点是不能动态改变策略<a class="link" href="#note5"><sup>[5]</sup></a>。”冒号分析道，“至此，我们通过一个验证类的三种解法，分别展示了三种形式的多态：基于类继承的多态、基于接口继承的多态和基于模板的多态。它们殊途同归，都能让代码更简洁、更灵活、可重用性更高、更易维护和扩展。”</p>
<p>问号想到一个问题：“C语言既没有子类型多态也没有参数多态，又如何保证高质量的C程序呢？”</p>
<p>冒号眉梢轻挑：“C语言有指针啊，C++、Java和C#的多态在底层就是用指针实现的。C中的函数指针比Java中的接口更加灵活高效，当然对程序员的要求也更高。”</p>
<p>引号蓦地记起：“重载不也是一种多态吗？”</p>
<p>“刚才所说的多态都属于<span class="term">通用多态</span>（universal polymorphism）。此外，还有一类<span class="term">特别多态</span>（ad-hoc polymorphism），常见有两种形式。一种是<span class="term">强制多态</span>（coercion polymorphism），即一种类型的变量在作为参数传递时隐式转换成另一种类型，比如一个整型变量可以匹配浮点型变量的函数参数。另一种就是<span class="term">重载多态</span>（overloading polymorphism），它允许不同的函数或方法拥有相同的名字。特别多态浅显易懂，其重要性与通用多态也不可同日而语，故不在我们关注之列。只是要注意一点，正如子类型应遵守超类型的规范，同名的函数或方法也应遵守相同的规范。如果为贪图取名方便而滥用重载，早晚因小失大。”冒号告诫道。</p>
<p>逗号突发奇论：“一个多态类型的对象可以在不同的类型之间变来变去，是不是叫‘变态类型’更生动些？”</p>
<p>“我看你就属于典型的变态类型。”句号乘机拿他开涮。</p>
<p>全班哈哈大笑。</p>
</div>
<div class="section" title="，插语">
<div class="titlepage">
<div>
<div>
<h2 class="title" style="clear: both;"><a name="note"></a>，插语</h2>
</div>
</div>
</div>
<div class="orderedlist">
<ol class="orderedlist" type="1">
	<li class="listitem">
<p><a name="note1"></a> 参见§5.2。</p></li>
	<li class="listitem">
<p><a name="note2"></a> 虽然C#具体的泛型类型是在运行期间实例化的，但每类泛型对应相同的实现代码，故变量的行为仍是在编译期间决定的。</p></li>
	<li class="listitem">
<p><a name="note3"></a> salt、nonce和IV都是密码学中的术语，是在加密过程中混入的一次性数据，以增加预计算攻击（如字典攻击）的难度。</p></li>
	<li class="listitem">
<p><a name="note4"></a> 这被称为constructor injection，另外两种常用的注射方法是setter injection和interface injection。</p></li>
	<li class="listitem">
<p><a name="note5"></a> 对用Java实现的Authenticator类（策略模式版）稍作修改，就能让客户动态改变策略。</p></li>
</ol>
</div>
</div>
<div class="section" title="。总结">
<div class="titlepage">
<div>
<div>
<h2 class="title" style="clear: both;"><a name="summary"></a>。总结</h2>
</div>
</div>
</div>
<div class="itemizedlist">
<ul class="itemizedlist" type="disc">
	<li class="listitem">
<p>在静态类型语言中，继承是多态的基础，多态是继承的目的。</p></li>
	<li class="listitem">
<p>多态结合了静态类型的安全性和动态类型的灵活性。</p></li>
	<li class="listitem">
<p>多态可分为通用多态和特别多态两种。</p></li>
	<li class="listitem">
<p>通用多态主要包括参数多态和包含多态（或子类型多态）。它们都是为了克服静态类型过于严格的语法限制。</p></li>
	<li class="listitem">
<p>特别多态主要包括强制多态和重载多态。</p></li>
	<li class="listitem">
<p>参数多态是静态绑定，重在算法的普适性，好让相同的实现代码应用于不同的场合。</p></li>
	<li class="listitem">
<p>包含多态是动态绑定，重在接口与实现的分离度，好让不同的实现代码应用于相同的场合。</p></li>
	<li class="listitem">
<p>策略模式授予客户自由选择算法（策略）的权力。</p></li>
	<li class="listitem">
<p>模板方法模式重在稳定坚固的骨架，策略模式重在灵活多变的手腕。</p></li>
	<li class="listitem">
<p>合理地运用基于类继承的多态、基于接口继承的多态和基于模板的多态，能增强程序的简洁性、灵活性、可维护性、可重用性和可扩展性。</p></li>
</ul>
</div>
</div>
<div class="section" title="“”参考">
<div class="titlepage">
<div>
<div>
<h2 class="title" style="clear: both;"><a name="reference"></a>“”参考</h2>
</div>
</div>
</div>
<div class="orderedlist">
<ol class="orderedlist" type="1">
	<li class="listitem">
<p>Erich Gamma，Richard Helm，Ralph Johnson，John Vlissides．Design Patterns: Elements of Reusable Object-Oriented Software．Boston, MA：Addison-Wesley，1994．315-323</p></li>
	<li class="listitem">
<p>Luca Cardelli，Peter Wegner．On understanding types, data abstraction, and polymorphism．Computing Surveys，1985，17(4)：471-522</p></li>
</ol>
</div>
</div>
</div>
<p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F10%2F20%2Fcolon-class-10_1%2F&amp;title=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A710.1%EF%BC%9A%E5%A4%9A%E6%80%81%E7%B1%BB%E5%9E%8B" id="wpa2a_18">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2009年10月24日 -- <a href="http://blog.zhenghui.org/2009/10/24/colon-class-10_2/" title="冒号课堂§10.2：抽象类型">冒号课堂§10.2：抽象类型</a> (7)</li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/10/20/colon-class-10_1/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>《冒号课堂》自序</title>
		<link>http://blog.zhenghui.org/2009/10/16/preface-of-colonclass/</link>
		<comments>http://blog.zhenghui.org/2009/10/16/preface-of-colonclass/#comments</comments>
		<pubDate>Fri, 16 Oct 2009 04:58:31 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[IT]]></category>
		<category><![CDATA[数学]]></category>
		<category><![CDATA[简历]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=493</guid>
		<description><![CDATA[《冒号课堂》一书的序言 [...]]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center"><span style="font-family: 宋体">序</span></h1>

<!-- below comes from generated html -->
<head><link rel="stylesheet" href="http://blog.zhenghui.org/css/colonclass.css" type="text/css"></head>

<div lang="zh-CN" class="article" title="《冒号课堂》自序"><div class="titlepage"><div><div><div class="author"><h3 class="author">郑晖</h3></div></div></div><hr /></div><div class="toc"><dl><dt><span class="section"><a href="#content"></a></span></dt></dl></div><div class="section"><div class="titlepage"></div><p>
            去年3月的一个下午，过于明媚的春光唤醒了一份久违的情怀，书摊上的一本《青年文摘》便成了合宜的载体。与其说是为了阅读，不如说是为了回忆——对20年前读书心境的回忆。孰料读罢开篇，怀旧之窗随即悄然关掩，一扇求新之门却戛然开启。那是一篇人物介绍，讲述一位籍籍无名的年轻人是如何因撰写博客而声名鹊起的。抚卷思之，网络平台已成大众舞台，人人皆可登台献技，自己何不前去一试？心念甫动，顿感技痒难耐，当晚寝不安席，于辗转反侧之中磨出了一本书的轮廓。
        </p><p>
            尽管钟书先生认为鸡与蛋应为松耦合关系，但一只来历不明的鸡确会招致人们对其产品可靠性的怀疑。故而在介绍《冒号课堂》的创作思路之前，先自我介绍一番。1986年我怀揣着成为数学家的梦想，考入武汉大学数学系。7年的大学生涯在浑浑噩噩中度过，毕业后在广州一所高校教了3年的高等数学。在混沌与迷茫中挣扎了10年，终于不堪蹉跎，1996年赴美攻读数学博士。始料不及的是，在大洋彼岸不仅没能一圆数学之梦，反倒从一个未曾碰过鼠标的电脑排斥者变成了一名IT工作者。“罪魁祸首”正是电脑和Internet，它们潜移默化地改变了人们的生活和思维方式，我亦未能幸免。1998年开始选修计算机课程，两年后拿到硕士学位，并在华尔街的一家软件公司找到了工作。2004年年底，选择回国发展，再度主导了人生的一次急转弯。回到广州后，顺利地进入了一家著名的外企。平淡而安逸的生活似乎注定与我无缘，不久又转去一家小公司作技术总监。如果用一句话来描述自己的职业生涯，那便是：数学是我的初恋情人，计算机是我的终生伴侣。无论成败，都是命运与人生双向选择的结果。
        </p><p>
            本书的创作虽出偶然，却也有其必然性。一方面，市面上的计算机书籍多为拼凑之作，且不少带有应试教育的痕迹。另一方面，论坛上充斥着各种谬言妄论，人们或目空一切，或人云亦云；每当争论一起，常常硝烟弥漫，出言无状者甚众。如此诸般，不忍卒睹。深感激浊扬清之必要，此念一直郁积于心，终至一朝爆发。自知虽无澄清玉宇之力，唯奢念带来一缕清风。
        </p><p>
            《冒号课堂》采用对话体，是为了借不同背景、不同水平、不同性格的人物之口，多层次、多维度、多角度地展现知识的内涵与活性。人物皆以标点符号命名，是为了塑造让人过目不忘的形象：冒号善解释，引号善引用，问号善提问，逗号善缓冲，叹号善感叹，句号善总结。此外，6个标点符号还对应着每小节的6个部分：冒号是正文讲解，引号是文献参考，问号是问题列表，逗号是补充插语，叹号是精华预览，句号是本节总结。与一般纯技术类图书不同，本书非常强调学习方法和学习精神的重要性。在内容组织上也一反常规，以思想为主、以知识为辅，以抽象为主、以具体为辅，以范式为主、以语言为辅。人们常把书籍比作一种食物，其实书籍也是一种药物。一本书应当同时提供两种价值：一种是让人获取正确知识的食用价值，一种是让人抛弃错误观点的药用价值。《冒号课堂》更侧重后者，这多少给读者带来一定的阅读障碍，因为抛弃往往比获取更加困难。此外，全书涉及的知识点较多，覆盖的知识面较广，一些流行的语言或技术反被刻意地淡化。假如读者没有足够的计算机理论和实践基础，难免会感到一些困难和不适。古语有云：“学然后知不足”，认识到不足何尝不是学习的一种收获呢？从另一面说，假如读者发现书中疏谬，还请不吝赐教，本人将不胜感激。坦而言之，随着写作的深入，自得之心日敛，惴惴之心日甚，正应了上面古语的后半句：“教然后知困”。
        </p><p>
            本书的完成首先需要感谢3位母亲：我的母亲、我太太的母亲、我女儿的母亲。没有她们默默无闻的支持和帮助，冒号课堂只能在梦中开班。还要感谢博文视点的周筠老师对本书的大力支持和关怀；白爱萍编辑负责而又耐心，不厌其烦地和我讨论一个个文字和版式的细节；博文的陈宜、杨小勤、陈琼、徐定翔、许莹、胡文佳等编辑也以同样的热情带给我很好的出版体验。我从其他亲友和网友那里也得到了许多热情的鼓励和有益的启示，是他们让虚拟的课堂变得真实和生动。
        </p><p dir="rtl">郑晖</p><p dir="rtl">2009年8月24日于广州</p></div></div><p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F10%2F16%2Fpreface-of-colonclass%2F&amp;title=%E3%80%8A%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%E3%80%8B%E8%87%AA%E5%BA%8F" id="wpa2a_20">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2010年06月3日 -- <a href="http://blog.zhenghui.org/2010/06/03/advice-on-programmer/" title="答读者问(1)——对程序员的一些个人建议">答读者问(1)——对程序员的一些个人建议</a> (6)</li><li>2011年04月18日 -- <a href="http://blog.zhenghui.org/2011/04/18/more-words-on-abstraction/" title="答读者问(3)——再谈抽象">答读者问(3)——再谈抽象</a> (1)</li><li>2011年03月20日 -- <a href="http://blog.zhenghui.org/2011/03/20/words-on-abstraction/" title="答读者问(2)——关于抽象">答读者问(2)——关于抽象</a> (6)</li><li>2010年09月20日 -- <a href="http://blog.zhenghui.org/2010/09/20/hard-thinking-and-soft-thinking-3/" title="论思维的刚性与柔性（科学的迷信-3）">论思维的刚性与柔性（科学的迷信-3）</a> (0)</li><li>2010年03月10日 -- <a href="http://blog.zhenghui.org/2010/03/10/colonclass-sales-tw/" title="《冒号课堂》在台湾上市">《冒号课堂》在台湾上市</a> (5)</li><li>2010年01月1日 -- <a href="http://blog.zhenghui.org/2010/01/01/colonclass-bbs-openning/" title="《冒号论坛》开放">《冒号论坛》开放</a> (1)</li><li>2009年11月7日 -- <a href="http://blog.zhenghui.org/2009/11/07/colonclass-suggestions/" title="《冒号课堂》意见收集">《冒号课堂》意见收集</a> (56)</li><li>2009年10月26日 -- <a href="http://blog.zhenghui.org/2009/10/26/colonclass-sales/" title="《冒号课堂》上市">《冒号课堂》上市</a> (8)</li><li>2009年08月21日 -- <a href="http://blog.zhenghui.org/2009/08/21/contents-of-colonclass/" title="《冒号课堂》目录">《冒号课堂》目录</a> (29)</li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/10/16/preface-of-colonclass/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>冒号课堂§6.4：后台脚本</title>
		<link>http://blog.zhenghui.org/2009/10/10/colon-class-6_4/</link>
		<comments>http://blog.zhenghui.org/2009/10/10/colon-class-6_4/#comments</comments>
		<pubDate>Sat, 10 Oct 2009 01:38:35 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[LAMP]]></category>
		<category><![CDATA[Perl]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[RoR]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[后台脚本]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=474</guid>
		<description><![CDATA[<b>后台脚本</b>——敏捷开发的利器（<em>简谈Perl、PHP、Python和Ruby</em>）<br/> • 语言的发展趋势一定是动静结合、刚柔并济<br/> • Perl凝练晦涩，Python优雅明晰，Ruby精巧灵动，PHP简明单纯<br/> • 或许优雅正是来自对细节和规范的重视<br/> • （RoR）与Ruby结合之后，便如一只猱身而上灵猫，立刻衬托出Java和.NET大象般的身影 [...]]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center"><span style="font-family: 宋体">冒号课堂</span></h1>
<strong><span style="font-size: 13pt; font-family: 宋体">第六课 语言简评(4)</span></strong>

<!-- below comes from generated html -->
<head><link rel="stylesheet" href="http://blog.zhenghui.org/css/colonclass.css" type="text/css"></head>

<div lang="zh-CN" class="article" title="后台脚本"><div class="titlepage"><div><div><h1 class="title"><a name="id600349"></a>6.4 后台脚本——敏捷开发的利器</h1></div><div><div class="author"><h3 class="author">郑晖</h3></div></div><div><div class="abstract" title="摘要"><p class="title"><b>摘要</b></p><p>简谈Perl、PHP、Python和Ruby</p></div></div></div><hr /></div><div class="toc"><p><b>目录</b></p><dl><dt><span class="section"><a href="#preview">！预览</a></span></dt><dt><span class="section"><a href="#question">？提问</a></span></dt><dt><span class="section"><a href="#explaination">：讲解</a></span></dt><dt><span class="section"><a href="#note">，插语</a></span></dt><dt><span class="section"><a href="#summary">。总结</a></span></dt><dt><span class="section"><a href="#reference">“”参考</a></span></dt></dl></div><div class="epigraph"><div class="literallayout"><p>操千曲而后晓声，观千剑而后识器</p></div><div class="attribution"><span>—<span class="attribution">《文心雕龙•知音》</span></span></div></div><div class="section" title="！预览"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="preview"></a>！预览</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    语言的发展趋势一定是动静结合、刚柔并济
                </p></li><li class="listitem"><p>
                    Perl凝练晦涩，Python优雅明晰，Ruby精巧灵动，PHP简明单纯
                </p></li><li class="listitem"><p>
                    或许优雅正是来自对细节和规范的重视
                </p></li><li class="listitem"><p>
                    （RoR）与Ruby结合之后，便如一只猱身而上灵猫，立刻衬托出Java和.NET大象般的身影
                </p></li></ul></div></div><div class="section" title="？提问"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="question"></a>？提问</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Perl、Python、Ruby和PHP各自有何特点？</p></li><li class="listitem"><p>为什么动态语言多作为轻量级的解决方案？</p></li><li class="listitem"><p>LAMP为什么受欢迎？</p></li><li class="listitem"><p>Ruby on Rails为什么会流行？</p></li><li class="listitem"><p>编程语言的发展趋势是什么？</p></li></ul></div></div><div class="section" title="：讲解"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="explaination"></a>：讲解</h2></div></div></div><p>
            “剩下四种动态语言，我们将之归为<span class="emphasis"><em>后台脚本语言</em></span>。”冒号说着画了张图（如图6-1所示）——
        </p><div class="figure"><a name="id603926"></a><p class="title"><b>图6-1. 常用编程语言的分类</b></p><div class="figure-contents"><div class="mediaobject"><img src="http://blog.zhenghui.org/img/colonclass/figure6-1.jpg" alt="常用编程语言的分类"></div></div></div><br class="figure-break"><p>
            引号听得仔细：“我记得您开始是把这些语言划分为C族静态语言、非C族静态语言和动态语言三类的。”
        </p><p>
            冒号解释：“那是按语法来划分的，偏重理论；现在是按应用来划分，偏重实践。”
        </p><p>
            句号旋即联想到：“这种分法貌似三层架构——前台语言对应表现层；平台语言和后台脚本语言对应业务逻辑层；系统语言对应数据层。”
        </p><p>
            “的确有几分神似，但千万不可混淆。”冒号提醒道，“三层架构（three-layer architecture）是模块设计上的<span class="emphasis"><em>逻辑划分</em></span><a class="link" href="#note1"><sup>[1]</sup></a>；而这里是按语言应用范围进行的<span class="emphasis"><em>物理划分</em></span>——与用户交互的是<span class="term">前台语言</span>，与机器交互的是<span class="term">系统语言</span>，介于其中的为前台提供服务同时又需要底层系统服务的是<span class="term">后台语言</span>。”
        </p><p>
            逗号询问：“后台语言又细分成平台语言与后台脚本语言？”
        </p><p>
            “这是基于程序（program）与脚本（script）、静态与动态而分的。”冒号进行说明，“其实Perl，PHP，Python和Ruby都有自己的<span class="term">虚拟机</span>（virtual machine），从这种意义上说它们也可作为平台语言。但在实际应用中，它们没有Java平台和.NET平台那种整合凝聚力和核心作用，通常作为轻量级的解决方案。”
        </p><p>
            问号想探个究竟：“这是由于它们都是动态语言的缘故吗？”
        </p><p>
            冒号回答：“理论上动态语言同样能承担大型应用，但实践上它们多作为粘合语言或用于中小型应用。用句时髦的话来形容，暂时还是<span class="emphasis"><em>主流的配角</em></span>或<span class="emphasis"><em>非主流的主角</em></span>。毕竟在运行效率、类型安全、可用资源、开发工具、技术支持等方面，它们与Java、C#相比尚有一定差距。另外它们同属‘草根’语言，虽有开源社区的大力支持，在影响力上与后者未可同日而语。”
        </p><p>
            叹号揣测：“说不定在不久的将来，动态语言也会成为主流的主角。”
        </p><p>
            “世易时移，殊难逆料。但有一点可以肯定，语言的发展趋势一定是动静结合、刚柔并济。”冒号断言，“一方面以Java和C#为代表的静态语言中嫁接了动态语言的枝条；另一方面以Java和.NET为代表的平台与动态语言的交壤地带也在逐步扩大。比如JRuby允许Ruby与Java之间互相调用，类似的还有Jython、IronRuby、IronPython等等。此外值得一提的是，动态语言最活跃的舞台当数LAMP，L-A-M-P。”
        </p><p>
            引号接茬：“L是Linux，A是Apache，M是MySQL，P是PHP。这四大组件形成了一个完整的开源网络开发平台。”
        </p><p>
            冒号补充道：“P也可指Perl、Python，甚至Ruby。”
        </p><p>
            逗号调侃：“可惜Ruby的‘R’比‘P’多了一根尾巴。”
        </p><p>
            “有人为了自圆其说，干脆让P表示‘Programming language’，这下所有语言都囊括其中了。老外就喜欢玩这种首字母缩略（acronym）的文字游戏，尤其LAMP正好还有‘灯’的含义，寓意开源世界的一盏明灯，他们一定更得意了。”冒号语带调笑，“前面我们曾提及，网络应用是生长动态语言最肥沃的土壤，而LAMP就是这块土壤上搭建的平台。作为网络平台，LAMP以其开放灵活、开发迅速、部署方便、高可配置、安全可靠、成本低廉等特色而与Java平台和.NET平台鼎足三分，尤其受中小企业的欢迎。LAMP中Linux是操作系统，Apache是Web服务器，MySQL是数据库系统，而我们当下最关心的是‘P族语言’：PHP、Perl、Python还有Ruby。”
        </p><p>
            问号建议：“作为动态语言，它们的共性上节课已经谈了不少，能说说它们的个性吗？”
        </p><p>
            “它们的个性极为鲜明：<span class="strong"><strong>Perl凝练晦涩，Python优雅明晰，Ruby精巧灵动，PHP简明单纯</strong></span>。先看老大哥Perl，它博采众家之长，综合了C语言的结构、sed的正则表达式、AWK的关联数组（associative array）、Lisp的表（list）和Unix Shell的命令，此外还有借鉴了一种语言，你们知道是哪种吗？”冒号忽然卖了个关子。
        </p><p>
            叹号猜想：“应该是某种OOP语言吧。”
        </p><p>
            “Perl中确有不少C++的影子，但它的对象模型在5.0以后才引入，典型的半路出家，远不如前面的特征那么自然。与其说是一种自然而然的发展，不如说是在OOP潮流裹挟下的一种身不由己的迎合。真正深入骨髓的借鉴是自然语言。”冒号给出了答案，“我们提过，Perl的发明者Larry Wall是一名语言学家，他认为程序语言应该与自然语言一样，简洁自然、易读易写、表达多样、不拘一格。Perl还有不少的格言或哲学，使得编程语言一改严谨刻板的面孔，散发出浓郁的人文气息。”
        </p><p>
            逗号幽了一默：“我见过Perl的代码，人文气息没闻出来，但我怀疑有乙醚气息——看一会就觉得晕晕乎乎的。”
        </p><p>
            众人大笑。
        </p><p>
            “有人仅用一行Perl代码就实现了RSA算法，你看了那还不得当场晕倒啊？”冒号打趣道，“Perl的各种魔符好似一把把锋利的剪刀，做起文本裁剪之类的工作来游刃有余。这是它最大的长处，当初Perl就是Wall用来做Unix系统管理的，以后在CGI上的广泛应用也得益于此。这也赋予Perl极强的粘合力，因而有‘internet上的胶带（duct tape）<a class="link" href="#note2"><sup>[2]</sup></a>’的说法。它又号称瑞士军刀，精练而复杂，实用而强大。但Perl过于灵活自由，缺乏规范，影响了程序的可读性、一致性、整洁性和可维护性。不熟悉该语言的固然如读天书，熟悉语言而不熟悉问题的也颇费思量。相比之下Python被认为是Perl有力的挑战者，不仅在于它天然的OO设计和丰富的类库，更重要的是它对程序员友好度大大超过Perl。Python也有一系列的被称为禅（Zen）的哲学，不少与Perl是针锋相对的。比如：Perl认为做一件事可以有多种方法，而Python认为一件事应该最好只有一种方法；Perl追求语言的表现力，Python追求简单优雅；Perl喜欢隐性暗示，Python强调显性明示；Perl强调紧凑，Python强调松散； Perl的语法和语义丰富，Python的语法和语义简单而类库丰富。或许Python最让人不习惯的是它对空白符敏感性。”
        </p><p>
            引号感到惊奇：“对空白符敏感？这个倒真怪异。”
        </p><p>
            冒号见惯不怪：“虽然有点违反习惯，但非常符合Python一贯的规范简洁的风格——一方面从语法上保证了良好的编码风格；另一方面，每个代码块不再需要起始的大括号或begin/end之类的，减少了的代码行数。顺便插一句，另外一种优雅的语言Haskell同样对空白符敏感，或许优雅正是来自对细节和规范的重视吧。此外许多人抱怨Python中的自引用self太多，殊不知这也是它倡导显式表达的一种体现。总的看来，Python主要的问题还是在性能效率上不尽如人意。”
        </p><p>
            叹号好奇地问：“Ruby怎么样？据说它将取代Java。”
        </p><p>
            “不要轻言‘取代’二字。”冒号规诫道，“Java没有取代C++，也不会被Ruby取代，至多只是一种再分配。不过Ruby的确是门很可爱的语言，兼具Perl的表现力和Python的可读性。Ruby背后最具特色的理念是：关注程序员使用语言时的感受超过语言本身的功能。通俗地说，兵器的称手比锋利更重要；文雅地说，应给予程序员更多的人文关怀。就拿<span class="term">代码块</span>（block）和<span class="term">迭代器</span>（iterator）来说，虽然均非Ruby首创，但其语法最为赏心悦目。类似的例子比比皆是。Ruby的元编程能力特别强，也是它高度灵活的一种体现，但并不是所有人都喜欢这种风格。Ruby的主要弱点有两个：一个与Python类似，在性能上还有待提高；另一个是它的线程由<span class="term">用户空间</span>（user space）而不是<span class="term">内核空间</span>（kernel space）来管理<a class="link" href="#note3"><sup>[3]</sup></a>，不能充分利用多核或多CPU。真正让Ruby变得炙手可热的是web应用框架 Ruby on Rails（RoR）的成功，它们还催生了Java平台上的Groovy语言和Groovy on Grails框架。RoR奉行的CoC（Convention over Configuration）和DRY（Don&#8217;t repeat yourself ）原则以及MVC架构看似了无新意，但与Ruby结合之后，便如一只猱身而上灵猫，立刻衬托出Java和.NET大象般的身影。”
        </p><p>
            逗号有些怀疑：“框架竟然捧红了语言，框架真有这么重要吗？”
        </p><p>
            “如果web应用中动态页面较少或业务逻辑不复杂，框架的价值并不大。以前CGI编程就是往Perl之类的代码中嵌入HTML代码，如同Java中的Servlet；PHP则单纯地在HTML代码中插入PHP代码，如同早期的JSP。没有MVC，也不管什么三层架构，更没有ORM。但是——”冒号拖了个转折音，“一旦业务逻辑变得复杂，开发人员增多，手工作坊式编程开始捉襟见肘，引入框架这个流水生产线来提高生产力便是大势所趋。”
        </p><p>
            句号不解：“我想Perl、Python和PHP一定也有不少框架，Java中的框架更是泛滥成灾，何以独独RoR脱颖而出？”
        </p><p>
            冒号作出分析：“正值web2.0和敏捷开发（agile development）的概念流行之际，RoR将AJAX与Ruby组合在一起成为绝佳的回应。以前各种web应用框架是不少，但在RoR之前轻量级<span class="emphasis"><em>套餐式解决方案</em></span>并不多。Perl中的Catalyst、Python中的Pylon还有PHP中的CakePHP等应是效仿之作。因此RoR出现的时机可说是不早不晚，正当其时。此外，Perl和PHP由于过于流行，反而有不少的历史包袱，人们习惯了将表示逻辑和业务逻辑编织在一起。至于Java企业解决方案，框架太多，搭配组合更多，增加了选择的难度。即使采用最常见的轻量级SSH（Struts+Spring+Hibernate）组合，维护起来也比RoR繁杂得多。”
        </p><p>
            叹号愈发担忧：“听这意思，Java还是危险啊！”
        </p><p>
            “言之过早。”冒号不以为然，“首先RoR还有待进一步检验，目前无论是应用广度还是深度上尚无法与Java相提并论；其次Java在性能、安全等方面还是有不少优势，而这些对于大型和关键性的应用来说尤为重要。即使在中小型web应用中，RoR较之PHP还远为不及。”
        </p><p>
            问号接下话题：“PHP为何如此流行？”
        </p><p>
            “因为它简单、专一。”冒号答得很干脆，“与Python和Ruby一开始就定位通用语言不同，PHP是专为网络而生的。同早期的Perl相似，PHP起初主要起文本过滤器的作用，只不过Perl多处理文件流（file stream），而PHP多处理套接字流（socket stream）。PHP的语法简单，且为网络应用度身定造，受到网络开发人员的追捧当在情理之中。它虽很实用很流行，但并不完美。比如：变量名大小写敏感而函数名大小写不敏感；函数命名规则不一致；不支持namespace和unicode<a class="link" href="#note4"><sup>[4]</sup></a>；与Perl一样，它的对象模型不是先天的，直到PHP 5才真正完善；对线程支持不足；相比Perl、Python和Ruby，它的功能稍显单薄等等。”
        </p><p>
            引号突然想起：“我记得您在第一堂课提到PHP还能用于桌面应用。”
        </p><p>
            “不仅PHP，Perl、Python还有Ruby，都能作为前台语言来开发命令行或图形界面的应用。同样地，VB、Delphi和JavaScript也能作为后台语言。现代的程序语言既有自己的专长，又向通用化和全能化发展，以争取更多的生存空间。试想一下，现代的程序员又何尝不是如此呢？”言及于此，冒号收住话题，“语言简评告一段落，还有不少既有趣又有用的语言，在此就不一一评说了。我们看到，每种编程语言都有其独特的惯例用法和哲学理念，它们与编程范式一道形成了语言的编程风格。体悟愈深者编程语感愈强，思维与语言愈交融无碍，渐从必然王国走向自由王国。”
        </p><p>
            逗号满怀憧憬：“那是不是一种人剑合一的境界？”
        </p><p>
            “或许人器合一更准确吧，程序员可不能只会一种兵器哟。”冒号故意抠他的字眼，“现在请大家每人写一句对本节课的感言。”
        </p><p>
            众人沉思片刻，齐齐挥笔而就——
        </p><p>
            <span class="emphasis"><em>叹号——没有最好的语言，只有最合适的语言。</em></span>
        </p><p>
            <span class="emphasis"><em>问号——没有糟糕的语言，只有糟糕的程序员。</em></span>
        </p><p>
            <span class="emphasis"><em>逗号——没有一种语言是万能的，只会一种语言是万万不能的。</em></span>
        </p><p>
            <span class="emphasis"><em>引号——废除对语言的宗教信仰，建立对语言的哲学思维。</em></span> 
        </p><p>
            <span class="emphasis"><em>句号——编程就是在人脑和电脑之间寻找最佳平衡点的过程。</em></span>
        </p><p>
            冒号读罢大悦，顺手一掌拍出五记马屁：“精彩之极！可谓字字珠玑、句句联璧啊。兹决定，给诸位的奖赏是——立时下课！”
        </p><p>
            众人欣然领赏而去。
        </p></div><div class="section" title="，插语"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="note"></a>，插语</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p><a name="note1"></a>
                    有两种三层架构，一种是three-layer architecture，一种是three-tier architecture。它们经常换用，但其实是有分别的：前者仅仅在逻辑进行划分，而后者在物理上也进行了划分——不同层次的模块运行在不同的主机上。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    不少地方译作“输送带”、“传送带”，因为duct有“输送管”、“导管”之意，于是想当然地认为这表明Perl在internet上起着输送作用。殊不知“duct tape”专指一种万能的粘性极强的胶带，用以比喻Perl的粘合力。
                </p></li><li class="listitem"><p><a name="note3"></a>
                    这类线程被称为绿色线程（green thread），也称伪线程。据称Ruby2.0将支持原生线程（native thread）。
                </p></li><li class="listitem"><p><a name="note4"></a>
                    PHP将在5.3.0支持namespace，将在6.0支持unicode。
                </p></li></ol></div></div><div class="section" title="。总结"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="summary"></a>。总结</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    比起Java平台和.NET平台，动态语言轻便灵活、开发效率高，但整合凝聚力还不够，在运行效率、类型安全、可用资源、开发工具、技术支持以及影响力等方面也有一定差距，故通常作为轻量级的解决方案。
                </p></li><li class="listitem"><p>
                    LAMP是由Linux、Apache、MySQL和包括PHP、Perl、Python或Ruby在内的脚本语言组成的网络开发平台，具有开放灵活、开发迅速、部署方便、高可配置、安全可靠、成本低廉等优点。
                </p></li><li class="listitem"><p>
                    Perl精练、复杂、强大、灵活、自由、隐晦、表现力强，但规范性、可读性、一致性、整洁性和可维护性较差。
                </p></li><li class="listitem"><p>
                    Python优雅规范、简洁明晰、易学易用、类库丰富，但效率稍差，有些人不喜欢它对空白符敏感的特性。
                </p></li><li class="listitem"><p>
                    Ruby语法精巧、高度灵活，兼具Perl的表现力和Python的可读性，尤其注重程序员的感受，但其性能和线程模型尚有待改进。
                </p></li><li class="listitem"><p>
                    PHP简单、专一、实用、流行，在但相比其他三种语言，在语法和功能上稍有欠缺。
                </p></li><li class="listitem"><p>
                    RoR是一种轻量级套餐式的web应用解决方案，是由好的设计（MVC架构和CoC、DRY原则）加上好的语言（Ruby）在好的时机（web2.0和敏捷开发风行之际）打造出的好的框架。
                </p></li><li class="listitem"><p>
                    静态语言与动态语言从语言特征到运行环境都在逐渐融合。
                </p></li><li class="listitem"><p>
                    程序员应该与程序语言一样，既要有自己的专长，又要向通用化和全能化发展。
                </p></li><li class="listitem"><p>
                    编程语言惯例用法、哲学理念和编程范式形成了语言的编程风格。
                </p></li></ul></div></div><div class="section" title="“”参考"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="reference"></a>“”参考</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>
                    Larry Wall, Tom Christiansen, Jon Orwant．Programming Perl, 3<sup>rd</sup> ed.．Sebastopol, CA：O&#8217;Reilly，2000
                </p></li><li class="listitem"><p>
                    Mark Lutz, David Ascher．Learning Python, 2<sup>nd</sup> ed.．Sebastopol, CA：O&#8217;Reilly，2003
                </p></li><li class="listitem"><p>
                    Michael Fitzgerald．Learning Ruby．Sebastopol, CA：O&#8217;Reilly，2007
                </p></li><li class="listitem"><p>
                    Michele E. Davis, Jon A. Phillips．Learning PHP &amp; MySQL, 2<sup>nd</sup> ed.．Sebastopol, CA：O&#8217;Reilly，2007
                </p></li></ol></div></div></div>

<!-- below is edited manually -->
<strong><span style="font-family: 宋体">课后思考</span></strong>
<ul style="margin-top: 0cm; list-style-type: none">
    <li>06-01 有人认为掌握的语言过多，容易互相混淆，不如精学一门。你认为呢?</li>
    <li>06-02 在实际编程中，你有选择编程语言的权力吗？如果有，选择的原则是什么？</li>
    <li>06-03 在前台编程、后台编程和系统编程中，你主要专注于哪方面？你认为它们之间的主要差别在哪里？</li>
    <li>06-04 你所熟悉的编程语言有哪些惯例用法和哲学理念？</li>
    <li>06-05 试着学习一门新的语言，最好与目前掌握的知识集合互补。</li>
</ul><p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F10%2F10%2Fcolon-class-6_4%2F&amp;title=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A76.4%EF%BC%9A%E5%90%8E%E5%8F%B0%E8%84%9A%E6%9C%AC" id="wpa2a_22">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>无相关文章</li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/10/10/colon-class-6_4/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>冒号课堂§6.3：前台语言</title>
		<link>http://blog.zhenghui.org/2009/10/08/colon-class-6_3/</link>
		<comments>http://blog.zhenghui.org/2009/10/08/colon-class-6_3/#comments</comments>
		<pubDate>Wed, 07 Oct 2009 17:00:56 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[AJAX]]></category>
		<category><![CDATA[Delphi]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[VB]]></category>
		<category><![CDATA[前台语言]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=471</guid>
		<description><![CDATA[<b>前台语言</b>——视觉与交互的艺术（<em>简谈VB、Delphi和JavaScript</em>）<br/> • Delphi让复杂的事情变得简单，VB让简单的事情变得更简单<br/> • 它（JavaScript）宛如一只神奇的魔袋，乍看平淡无奇，却总能变出意想不到的宝贝<br/> • 待友之道，贵在放大其优点而缩小其缺点，对待语言亦当如是<br/> • 大道相通，难者亦易，易者亦难<br/> • 得道者化腐朽为神奇，离道者化神奇为腐朽 [...]]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center"><span style="font-family: 宋体">冒号课堂</span></h1>
<strong><span style="font-size: 13pt; font-family: 宋体">第六课 语言简评(3)</span></strong>

<!-- below comes from generated html -->
<head><link rel="stylesheet" href="http://blog.zhenghui.org/css/colonclass.css" type="text/css"></head>

<div lang="zh-CN" class="article" title="前台语言"><div class="titlepage"><div><div><h1 class="title"><a name="id603250"></a>6.3 前台语言——视觉与交互的艺术</h1></div><div><div class="author"><h3 class="author">郑晖</h3></div></div><div><div class="abstract" title="摘要"><p class="title"><b>摘要</b></p><p>简谈VB、Delphi和JavaScript</p></div></div></div><hr /></div><div class="toc"><p><b>目录</b></p><dl><dt><span class="section"><a href="#preview">！预览</a></span></dt><dt><span class="section"><a href="#question">？提问</a></span></dt><dt><span class="section"><a href="#explaination">：讲解</a></span></dt><dt><span class="section"><a href="#note">，插语</a></span></dt><dt><span class="section"><a href="#summary">。总结</a></span></dt><dt><span class="section"><a href="#reference">“”参考</a></span></dt></dl></div><div class="epigraph"><div class="literallayout"><p>世人反不难而易之，用是通者亦罕 </p></div><div class="attribution"><span>—<span class="attribution">《欧阳修•诗解统序》</span></span></div></div><div class="section" title="！预览"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="preview"></a>！预览</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    Delphi让复杂的事情变得简单，VB让简单的事情变得更简单
                </p></li><li class="listitem"><p>
                    它（JavaScript）宛如一只神奇的魔袋，乍看平淡无奇，却总能变出意想不到的宝贝
                </p></li><li class="listitem"><p>
                    待友之道，贵在放大其优点而缩小其缺点，对待语言亦当如是
                </p></li><li class="listitem"><p>
                    大道相通，难者亦易，易者亦难
                </p></li><li class="listitem"><p>
                    得道者化腐朽为神奇，离道者化神奇为腐朽
                </p></li></ul></div></div><div class="section" title="？提问"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="question"></a>？提问</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>Visual Basic和Delphi有何共同点和不同点？</p></li><li class="listitem"><p>相比其他的富客户端技术，JavaScript的前景如何？</p></li><li class="listitem"><p>JavaScript是一门严肃的语言吗？</p></li><li class="listitem"><p>前台编程、后台编程与系统编程，哪个最难？</p></li></ul></div></div><div class="section" title="：讲解"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="explaination"></a>：讲解</h2></div></div></div><p>
            稍事休息后，冒号切换了话题：“接下来简单聊聊Visual Basic和Delphi。”
        </p><p>
            叹号略带得色：“Visual Basic最好用了，建个窗体，从工具箱中拖些控件，再定义一下它们的属性和行为，搞定！”
        </p><p>
            冒号点点头：“VB最大的优点就是易学易用、上手快开发快，对非计算机专业人士尤其具有吸引力。”
        </p><p>
            引号咬文嚼字：“言外之意，VB对于专业人士就不那么合适了？”
        </p><p>
            “恐怕也不好这么说。”冒号打了个哈哈，“当然简单易用不是没有代价的，随便拖放一个控件，就生成了一大堆代码，程序员的发挥余地自然受限，代码效率也难以保证。不过VB多用于前台的可视化开发，对运行效率要求相对较低，因此还是很有市场的。另外，VB进化到VB.NET后，从单纯的事件驱动式的编程范式发展为对象式、泛型式和函数式等多种范式，越来越强大、越来越严格也越来越复杂，不再是人们戏称的‘玩具语言’了。”
        </p><p>
            逗号插话：“用Delphi来开发图形界面不也很快吗？”
        </p><p>
            冒号接过话来：“正因为它们擅长可视化快速应用开发（RAD），才常常作为<span class="emphasis"><em>前台语言</em></span>来使用。事实上，它们不单单是语言，也是开发环境。没有IDE，人们照样能写C、C++、Java、Perl等代码，但恐怕很少人会抛开IDE去开发VB或Delphi程序。Delphi又称Object Pascal，前身是<span class="emphasis"><em>教学语言</em></span>Pascal，VB的前身是<span class="emphasis"><em>入门语言</em></span>BASIC（<span class="strong"><strong>B</strong></span>eginner&#8217;s <span class="strong"><strong>A</strong></span>ll-purpose <span class="strong"><strong>S</strong></span>ymbolic <span class="strong"><strong>I</strong></span>nstruction <span class="strong"><strong>C</strong></span>ode），故而均具有易学易用的特点。”
        </p><p>
            问号要求：“能否比较一下二者的优缺点？”
        </p><p>
            冒号回应：“引用Computerworld的评论：‘Delphi让复杂的事情变得简单，VB让简单的事情变得更简单’。Delphi优秀的VCL框架使得它作为前台语言略胜一筹；更高的运行效率使得它同样能胜任后台工作；VB仅限于Windows，而Delphi可移植到Linux下的Kylix；对指针的支持则是便利与复杂的双刃剑。反过来，VB的学习曲线更短；如果专注微软技术，VB更便于扩展开发包括MS Office在内的应用程序，VB.NET更能充分利用.NET平台上的资源；不过VB版本更新较快，在紧跟时代的同时，也给程序员带来恼人的兼容性问题。”
        </p><p>
            句号再次体会到：“正如在Java和C#中作选择一样，平台是语言选择的一个重要因素。”
        </p><p>
            “说得没错。”冒号表示认可，“由于BASIC是微软的起家语言，VB的门槛又低，同时VB.NET也是.NET的一部分，它的前景还是乐观的，有些不利的是微软明显把重心放在C#上。谈到Delphi，不得不提Borland公司。这位曾经的软件巨人开发了无数经典的产品，却因经营不善而日渐没落，不久前将子 公司CodeGear连同包括Delphi在内的产品贱卖给了他人<a class="link" href="#note1"><sup>[1]</sup></a>。”
        </p><p>
            “也许，从当初Delphi的首席架构师Hejlsberg被微软挖走的那一天起，这样的结局就已注定了。”冒号不由得轻叹一声，惋惜之情溢于言表，“这再次说明技术的成功并不意味着商业的成功，同时语言的发展与背后支持的公司也是休戚相关的。Borland和微软是两面最好的镜子。”
        </p><p>
            叹号听出弦外之音：“看来Delphi前景不妙啊。”
        </p><p>
            冒号不置可否：“这也很难说，毕竟Delphi的铁杆拥护者也不少。不过随着基于internet的应用日趋广泛，桌面应用受到网络应用的猛烈冲击，VB和Delphi的市场份额必然都会有所下降。在以网络为中心的企业解决方案中，作为平台语言的Java和C#是两棵比肩而立的大树，在它们周围生长着郁郁葱葱的动态语言的小草。”
        </p><p>
            话题自然而然地转到了动态语言。
        </p><p>
            引号看了看笔记：“按计划，是不是该谈动态语言Perl、PHP、Python、Ruby 和JavaScript了？”
        </p><p>
            冒号说道：“在这些语言中，JavaScript是个异类，虽然也有服务器端的JavaScript（SSJS），但用得最多的还是在客户端。从这种意义上说，它也是一种<span class="emphasis"><em>前台语言</em></span>。”
        </p><p>
            逗号微有疑惑：“您是说将它与方才的VB和Delphi并列？”
        </p><p>
            “有一点不同的是，JavaScript必须与HTML、CSS、XML和XSLT等语言配合才能完成前台工作。”冒号作了补充。
        </p><p>
            问号似乎明白了：“您指的就是流行的AJAX技术吧。”
        </p><p>
            “不完全是。”冒号轻轻摇了摇头，“动态HTML技术（Dynamic HTML，简称DHTML）照样可以胜任前台事务，事实上以前B/S应用<a class="link" href="#note2"><sup>[2]</sup></a>都是靠它来完成浏览器端工作的。DHTML在静态标记语言如HTML、XHTML等的基础上，添加客户端脚本语言如JavaScript、VBScript等，并辅以样式语言如CSS。不少人以为动态HTML与动态网页是同义词，但实际上前者的动态效果来自客户端，后者的动态效果还会来自服务端。至于AJAX（<span class="strong"><strong>A</strong></span>synchronous <span class="strong"><strong>J</strong></span>avaScript <span class="strong"><strong>A</strong></span>nd <span class="strong"><strong>X</strong></span>ML），比DHTML无非多了个XHR （XMLHttpRequest） 的API。XHR最大的功用是能与后台服务器进行<span class="emphasis"><em>异步</em></span>通讯，从而实现页面的局部刷新<a class="link" href="#note3"><sup>[3]</sup></a>，而在此之前类似的效果只能通过IFrame来实现。正是这种异步技术，大大提升了用户体验，使得网页效果开始前所未有地逼近桌面效果。虽然它并非一项新技术——早在2000年的IE5便实现了，但真正流行开来还只是近几年的事。”
        </p><p>
            引号不无担忧：“目前富客户端技术层出不穷，JavaScript的前景如何呢？”
        </p><p>
            冒号给了一颗定心丸：“JavaScript在网页技术中的主流地位在很长时间内恐怕都难以撼动。相比其他的RIA（Rich Internet application）技术，如Adobe的Flex、微软的Silverlight、Sun的JavaFX等，以JavaScript为核心的AJAX技术最大的优势在于：标准、普及、成熟；不需安装插件，只要浏览器支持JavaScript即可；因其<span class="emphasis"><em>文本特征</em></span>而对搜索引擎更加友好。”
        </p><p>
            叹号谈及他的感受：“JavaScript给人的感觉是：看起来很简单，写起来很随意，用起来很头痛。”
        </p><p>
            “原因是多方面的。”冒号谆谆而言，“从JavaScript语言本身来说，其弱类型和动态语言的特征给了程序员更多的自由，也带来更多的混乱；基于原型（prototype-based）的OOP特征远不如基于类(class-based）的OOP那样为人熟知；缺乏丰富的API；不同的浏览器不同的版本之间兼容性差。更主要的是，JavaScript一直被严重低估和误解，自视甚高的程序员往往不屑去编写网页，大多数JavaScript代码由不那么专业的程序员编写。此外IDE与调试工具的缺乏也给开发带来掣肘。这些都加深了人们对JavaScript的简单随意乃至丑陋难用的印象。其实JavaScript作为一门动态语言，集过程式、对象式、函数式、事件驱动式、元编程等于一身，其强大和优雅绝对是超乎想象的。它宛如一只神奇的魔袋，乍看平淡无奇，却总能变出意想不到的宝贝。各位若有机会深入地学习和运用JavaScript，定知我所言非虚。”
        </p><p>
            问号追问：“JavaScript最大的缺点是什么？”
        </p><p>
            “最大的缺点是起了个极无个性的名字。”冒号半开玩笑地说，“为了沾Java的光，Netscape愣将LiveScript改成JavaScript，带来不少混乱。”
        </p><p>
            引号也注意到：“书店、图书馆常常将JavaScript的书与Java的放在一起。”
        </p><p>
            冒号话锋一转：“谈论JavaScript的缺点并没有什么实际意义，因为一时半会还没有同类的替代品。VBScript仅限于微软的IE，在追求跨浏览器和强调标准的今天是不具备竞争力的。ActionScript具有强大的多媒体功能，但必须借助Flash插件，与JavaScript应该是互补共生的关系。另请记住：<span class="strong"><strong>待友之道，贵在放大其优点而缩小其缺点，对待语言亦当如是</strong></span>。这既是一种态度，也是一种境界。”
        </p><p>
            句号发现一个有趣的现象：“初级程序员往往偏爱前台编程，中级以上的偏爱后台编程，有些人则认为只有底层编程的才是真正的高手。”
        </p><p>
            冒号解释道：“前台编程涉及面专，绚丽花哨的界面更容易让初涉编程者产生成就感；后台编程涉及面广，需要深厚的技术积累和缜密的设计思维；底层编程涉及面深，给人神秘莫测之感。<span class="strong"><strong>然大道相通，难者亦易，易者亦难</strong></span>。设计一个用户友好的界面与设计操作系统的一个模块，一边是与人直接打交道，一边是与机器直接打交道，孰难孰易、孰轻孰重，焉能一概而论？”
        </p><p>
            见冒号说得铿锵有力，问号虽不忍置喙，然终难掩疑窦：“用C写操作系统的内核难道不比用VB做一个图形界面难得多吗？”
        </p><p>
            “不可否认，系统编程的门槛确实要高得多，需要程序员具备更多的硬件和操作系统知识和更强的编程能力。”冒号以退为进，“它最大的挑战在于：在相对贫乏的软件资源和相对简陋的开发环境下，能高效、安全地管理包括CPU、内存等在内的系统资源。再说前台编程，虽然入门容易，要想登堂入室却是大为不易。且不说VB、Delphi和JavaScript各具奥妙，连看似简单的HTML和CSS中也蕴含着种种奇思妙想。得道者化腐朽为神奇，离道者化神奇为腐朽，凡事皆然。当然前台最大的挑战还不在代码编写，而在界面设计。软件最终是为人服务的，如果没有良好的用户体验，无论背后的设计如何高明、算法如何精妙，用户也是不会买账的。”
        </p><p>
            叹号充分表示理解：“一个女孩无论多么蕙质兰心、冰雪聪明，如果相貌丑陋、脾气古怪，我也是不会买账的。”
        </p><p>
            逗号故意把脸一沉：“岂有此理，竟然自比为人家的用户！”
        </p><p>
            众人嘎嘎而笑。
        </p><p>
            问号仍有不解：“界面设计不是美工的职责吗？”
        </p><p>
            “美工设计的一般是静态的视觉界面，更重要的是动态的交互界面。相貌再好，脾气不好也不行啊！”冒号顺手拾起了叹号的比方，“虽然近来出现了交互设计师的职位，专门负责UI / UE（<span class="strong"><strong>U</strong></span>ser <span class="strong"><strong>I</strong></span>nterface / <span class="strong"><strong>U</strong></span>ser <span class="strong"><strong>E</strong></span>xperience），但毕竟不是每个公司都有的。即便有，程序员如果缺乏充分的认识，也难以达到设计要求。这可是融美学、心理学、统计学、计算机科学等等于一体的高难度的技术活儿啊！”
        </p><p>
            问号心底泛起的串串疑问的泡泡渐渐开始消散。
        </p></div><div class="section" title="，插语"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="note"></a>，插语</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p><a name="note1"></a>
                    2008年5月，Borland将CodeGear以两千多万美元卖给Embarcadero Technologies。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    B/S指Browser/Server（即浏览器/服务器）架构，区别于传统的C/S（Client/Server客户机/服务器）架构。
                </p></li><li class="listitem"><p><a name="note3"></a>
                    严格说来，局部页面刷新本身并不需要XHR，但刷新的数据来自XHR。
                </p></li></ol></div></div><div class="section" title="。总结"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="summary"></a>。总结</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    Visual Basic和Delphi均擅长可视化快速应用开发，易学易上手，常用于前台的桌面应用。
                </p></li><li class="listitem"><p>
                    Delphi相对VB的优势在于：可视化框架更优秀；运行效率更高；不限于Windows；支持指针。
                </p></li><li class="listitem"><p>
                    VB相对Delphi的优势在于：学习曲线更短；便于扩展开发包括MS Office在内的应用程序；与.NET平台更融合（主要指VB.NET）；有大公司（微软）的支持。
                </p></li><li class="listitem"><p>
                    以JavaScript为核心的DHTML和AJAX技术，相比其他的富客户端技术有其明显的优势：标准、普及、成熟、毋需插件、对搜索引擎友好。
                </p></li><li class="listitem"><p>
                    作为弱类型的动态语言，JavaScript有它的一些问题：基于原型的对象式令人陌生；API相对贫乏；浏览器标准不够统一；IDE和调试工具不够理想；多由不够专业的程序员编写等等。但它本身是一门功能齐全、强大而优美的语言，只要严肃地对待它，它就是一门严肃的语言。
                </p></li><li class="listitem"><p>
                    前台编程涉及面专，更关注界面设计；后台编程涉及面广，更关注业务逻辑；底层编程涉及面深，更关注系统资源。它们只是侧重点有所不同，并无真正的难易之别、高下之分。
                </p></li></ul></div></div><div class="section" title="“”参考"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="reference"></a>“”参考</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>
                    Ernesto De Spirito．A comparison between Delphi and Visual Basic．<a class="link" href="http://www.latiumsoftware.com/en/articles/00010.php" target="_top">http://www.latiumsoftware.com/en/articles/00010.php</a>
                </p></li><li class="listitem"><p>
                    David Flanagan．JavaScript: The Definitive Guide, 4<sup>th</sup> ed.．Sebastopol, CA：O&#8217;Reilly，2001
                </p></li></ol></div></div></div><p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F10%2F08%2Fcolon-class-6_3%2F&amp;title=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A76.3%EF%BC%9A%E5%89%8D%E5%8F%B0%E8%AF%AD%E8%A8%80" id="wpa2a_24">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>无相关文章</li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/10/08/colon-class-6_3/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>冒号课堂§6.2：平台语言</title>
		<link>http://blog.zhenghui.org/2009/10/03/colon-class-6_2/</link>
		<comments>http://blog.zhenghui.org/2009/10/03/colon-class-6_2/#comments</comments>
		<pubDate>Sat, 03 Oct 2009 11:48:57 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[平台语言]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=468</guid>
		<description><![CDATA[<b>平台语言</b>——先搭台后唱戏（<em>简谈Java和C# </em>）<br/> • Java的目的是让一种语言在多种平台上运行，而C#（.NET）的目的是让多种语言在一种平台上运行<br/> • 两个平台语言（Java和C#），一个重在语言，让语言向平台扩散；一个重在平台，让平台来凝聚语言<br/> • C++提供了一马平川的大路，也提供了陷阱密布的小道；Java则在大路上铺设水泥，同时封锁了捷径小道；C#同样填平了大路上的坑坑洼洼，但把一些小道上“此路不通”的牌子悄悄换成了“此路危险” [...]]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center"><span style="font-family: 宋体">冒号课堂</span></h1>
<strong><span style="font-size: 13pt; font-family: 宋体">第六课 语言简评(2)</span></strong>

<!-- below comes from generated html -->
<head><link rel="stylesheet" href="http://blog.zhenghui.org/css/colonclass.css" type="text/css"></head>
 
<div lang="zh-CN" class="article" title="平台语言"><div class="titlepage"><div><div><h1 class="title"><a name="id622911"></a>6.2 平台语言——先搭台后唱戏</h1></div><div><div class="author"><h3 class="author">郑晖</h3></div></div><div><div class="abstract" title="摘要"><p class="title"><b>摘要</b></p><p>简谈Java和C#</p></div></div></div><hr /></div><div class="toc"><p><b>目录</b></p><dl><dt><span class="section"><a href="#preview">！预览</a></span></dt><dt><span class="section"><a href="#question">？提问</a></span></dt><dt><span class="section"><a href="#explaination">：讲解</a></span></dt><dt><span class="section"><a href="#note">，插语</a></span></dt><dt><span class="section"><a href="#summary">。总结</a></span></dt><dt><span class="section"><a href="#reference">“”参考</a></span></dt></dl></div><div class="epigraph"><div class="literallayout"><p>大巧在所不为，大智在所不虑</p></div><div class="attribution"><span>—<span class="attribution">《荀子•天论篇》</span></span></div></div><div class="section" title="！预览"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="preview"></a>！预览</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    Java的目的是让一种语言在多种平台上运行，而C#（.NET）的目的是让多种语言在一种平台上运行
                </p></li><li class="listitem"><p>
                    两个平台语言（Java和C#），一个重在语言，让语言向平台扩散；一个重在平台，让平台来凝聚语言
                </p></li><li class="listitem"><p>
                    C++提供了一马平川的大路，也提供了陷阱密布的小道；Java则在大路上铺设水泥，同时封锁了捷径小道；C#同样填平了大路上的坑坑洼洼，但把一些小道上“此路不通”的牌子悄悄换成了“此路危险”
                </p></li></ul></div></div><div class="section" title="？提问"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="question"></a>？提问</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>在C++的基础上，Java 与C#作了哪些改进？</p></li><li class="listitem"><p>Java与C#在设计理念上有何不同？</p></li><li class="listitem"><p>Java与C#是如何互相借鉴的？</p></li><li class="listitem"><p>Java与C#为什么擅长企业应用开发？它们能作系统开发语言吗？</p></li></ul></div></div><div class="section" title="：讲解"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="explaination"></a>：讲解</h2></div></div></div><p>
            逗号提出：“现在应该到了Java时间吧。”
        </p><p>
            冒号顺水推舟：“下面谈论的重心从刚才的系统语言转到平台语言——Java和C#。”
        </p><p>
            问号听得一头雾水：“平台语言？Java不是平台无关的语言吗？”
        </p><p>
            “平台的意义很广，可以是硬件平台，可以是软件平台，也可以是二者的结合。”冒号解释道，“你说的‘平台’主要指<span class="emphasis"><em>硬件和操作系统平台</em></span>，而我说的‘平台’指的是<span class="emphasis"><em>运行环境平台</em></span>。当然这两种平台是密不可分的，Java与C#之所以能独立于前一种平台，正因为它们自带了后一种平台。具体地说，Java运行于以<span class="term">Java Virtual Machine</span>（JVM） 为核心的Java平台，而C#运行于以<span class="term">Common Language Runtime</span>（CLR）为核心的.NET平台。”
        </p><p>
            句号打了个比方：“这就好比在不同的山路上铺设相同的沥青，车子跑到哪儿都没区别了。”
        </p><p>
            “是这么个理儿。”冒号表示同意，“从传承的角度看，二者皆为基于C++之上的改进，剔除了其中易于出错或不易掌握的语法特征。我们先来看看它们区别于C++的共同之处。首先，二者均编译成<span class="emphasis"><em>与机器无关</em></span>的<span class="term">托管码</span>（managed code）或称<span class="term">字节码</span>（bytecode），运行于<span class="term">虚拟机</span>（virtual machine）之上。其次，禁用或限用指针并支持自动垃圾回收，由此极大地解放了程序员的生产力。起初这种做法并不是没有争议的，但随着机器性能的提高以及软件规模的扩大，对软件的健壮性、安全性和生产率的要求超过了对运行效率的要求，屏蔽指针和自动内存管理逐渐得到认同。尽管如此，指针运算和手动内存管理对于系统软件和在性能、实时性方面要求极高的软件还是必不可少的。因为这个原因，C#还是留了一手：允许标有修饰符unsafe的代码包含指针操作。”
        </p><p>
            引号较起真来：“这是否意味着C#也能作为系统语言呢？”
        </p><p>
            “或许吧。”冒号淡淡地说，“C#的胃口很大，上管天、下管地、中间还要管空气。企业级应用和桌面应用自不必说，底层应用也不放过。早在2003年，微软研究院就开始了用C#开发的名为Singularity的操作系统研究项目，另外还有SharpOS、Cosmos等开源操作系统。虽然它们目前仅用于研究，但不难看出C#的勃勃野心。说起来这也不新鲜，1998年Sun就搞了个针对嵌入式系统的JavaOS，可惜没能成功，后为Java ME所取代。”
        </p><p>
            轮到叹号糊涂了：“刚才还说Java和C#是平台语言，怎么又同C和C++一样当系统语言来用了呢？”
        </p><p>
            冒号释疑：“Java可通过JNI调用C、C++等程序；C#可通过P/Invoke访问系统API，加之保留了指针操作，用于系统编程更加方便。其实一种语言胜任多种领域并不难，难的是在每个领域都独占鳌头。可问题是，有谁不乐见自己支持的语言被广泛应用呢？无论是微软还是Sun，他们都会竭尽所能地将自己的语言扩张到每个角落。程序员作为语言的用户，虽然很多时候并没有选择语言的权力，但心中要有杆秤，可不要轻易被商家的广告迷惑啊。”
        </p><p>
            逗号不无自嘲地说：“秤倒是有一杆，就是不太准啊！”
        </p><p>
            有人“噗哧”笑出声来。
        </p><p>
            “多学习、多实践、多体悟，秤的精度自然会提高。” 冒号鼓励道，“如果自己没有足够的鉴别商品能力，建议从商家的竞争对手那里获取真实的信息。要看Java的优点，不要听Sun的一面之词，可以看看微软的C#模仿了Java的哪些特征，因为模仿是最高的赞扬；要看Java的缺点，可以看看C#摈弃了Java的哪些特征，可以听听微软对Java的批判——虽然可能过激，但一般也不会太离谱。此法同样适用于包括C#在内的其他语言。”
        </p><p>
            句号有所感慨：“这既是程序员之幸——语言之间可以互相取长补短，共同进步；也是程序员之不幸——往往不得不在众多语言中作艰难选择，或者多花精力学更多的语言。”
        </p><p>
            冒号一边点头一边接着说：“下面继续看看Java与C#的共性。借着刚才的话题，我们来看看C#与Java是如何互相学习的。”
        </p><p>
            引号举手插问：“C#到底更接近C++还是Java？”
        </p><p>
            “这是个有趣的问题。不妨这么说：C#看起来更像C++，用起来更像Java。C#借鉴了包括Java、C++、Delphi等在内的语言特征，其中Java的影响最大，但由于微软与Sun在Visual J++上的过节，宁愿强调与C++的关系而淡化与Java的关系<a class="link" href="#note1"><sup><a class="link" href="#note1"><sup>[1]</sup></a></sup></a>。C#的设计者Hejlsberg以前是Delphi和Visual J++的架构师，在一次访谈中首先声明C#不是Java的克隆，但从此话本身和他本人的背景都暗示了与Java千丝万缕的联系。例如，C#采用了大量与Java相同或相似功用却不同拼写的关键字。”冒号投影出一张表格（如表6-1所示）——
        </p><div class="table"><a name="id623019"></a><p class="title"><b>表6-1. Java与C#关键字的比较</b></p><div class="table-contents"><table summary="Java与C#关键字的比较" border="1"><colgroup><col><col><col><col><col><col><col><col><col><col></colgroup><tbody><tr><td><span class="strong"><strong>Java</strong></span></td><td>super</td><td>import</td><td>package</td><td>synchronized</td><td>instanceof</td><td>deprecated</td><td>final</td><td>native</td><td>boolean</td></tr><tr><td><span class="strong"><strong>C#</strong></span></td><td>base</td><td>using</td><td>namespace</td><td>lock</td><td>is</td><td>obsolete</td><td>sealed</td><td>extern</td><td>bool</td></tr></tbody></table></div></div><br class="table-break"><p>
            “这是Java与C#关键字的不完全对照表。中立地看，C#的关键字似乎更简明，但明显避嫌的意义大过改进的意义。关键字只是一个缩影，那么C#究竟从Java那里学到了什么，相比于C++又有什么优势或特点呢？”冒号切换了幻灯片——
        </p><div class="informalexample"><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                        <span class="strong"><strong>更加安全</strong></span>——限用或禁用指针；检查字符串和数组的边界；增加类型安全；禁用未初始化的变量；增加了对资源的安全管理；
                    </p></li><li class="listitem"><p>
                        <span class="strong"><strong>更加简单</strong></span>——自动垃圾回收；废除了头文件和宏（macro）；支持接口（interface）；废除多继承以及非公开继承；摒弃了C++中一些生僻、难用的语法；
                    </p></li><li class="listitem"><p>
                        <span class="strong"><strong>更加中性</strong></span>——编译成机器无关代码；运行于统一平台；
                    </p></li><li class="listitem"><p>
                        <span class="strong"><strong>更加OO</strong></span>——不支持全局变量和自由函数；所有的类都可上溯到Object类；对象一般通过new分配在堆（heap）上；
                    </p></li><li class="listitem"><p>
                        <span class="strong"><strong>更加丰富</strong></span>——支持反射（reflection）；支持并发编程；提供图形界面、网络编程、数据库、XML等API；
                    </p></li><li class="listitem"><p>
                        <span class="strong"><strong>更加标准</strong></span>——更好的Unicode支持；支持注释性文档；类或方法可以被废弃（deprecated）；
                    </p></li></ul></div></div><p>
            问号就势而问：“Java向C#又借鉴了什么呢？”
        </p><p>
            冒号答道：“在Java1.5的增强特征中，有不少C#的影子。如增强版的for循环；可变长参数（varargs）；自动装箱（autoboxing）；类型安全的枚举（typesafe enum）等等。此外，支持元数据的的注释（annotation）与C#的特性（attribute）大同小异，C#从Delphi中引入的属性（property）也出现在Java 7的提案中。”
        </p><p>
            逗号双手一摊：“这么多相同之处，不如合并算了。”
        </p><p>
            “很傻很天真的想法。等它们合并了，估计世界也大同了。”冒号笑言，“它们之间的差别也不容忽视。C#重新捡起了C++中被Java抛弃的一些特征，可谓是对C++的<span class="strong"><strong>否定之否定</strong></span>——除了支持<span class="term">引用类型</span>（reference type）外，还支持<span class="term">值类型</span>（value type）<a class="link" href="#note2"><sup>[2]</sup></a>；除了<span class="term">按值传递</span>（pass-by-value）外，还可<span class="term">按引用传递</span>（pass-by-reference）；支持<span class="term">操作符重载</span>（operator overloading）；在一定条件下保留指针和指针运算；所有的方法默认是非虚的（non-virtual）；访问权限默认是私有的（private）；将关键字‘extends’和‘implements’统一为‘：’；支持<span class="term">预处理指令</span>（preprocessor directive）；C#4.0还将引入<span class="term">可选参数</span>（optional parameter）等等。公允地说，这些大都为明智之举。”
        </p><p>
            引号追问：“C#有什么独到之处吗？”
        </p><p>
            “C#并非一味地效仿其他语言，也有创新之举。曾在Visual J++中加入的<span class="term">委托</span>（delegate）和相关的<span class="term">事件</span>（event），如今可以堂而皇之地用在C#上，再不用担心被Sun告了。”冒号语带调侃，“C#还是第一个支持<span class="term">组件导向</span>（Component-Oriented）的C族语言。除此之外，C#支持<span class="term">索引器</span>（indexer）、<span class="term">版本控制</span>（versioning）、XML注释、特色指令如#region、特色关键字如checked、yield等等，还引入了查询句法（query syntax）、匿名类型（anonymous type）、扩展方法（extension method）等新特征。还有一点不得不提，C#对函数式编程的支持力度逐渐加大：1.0的delegate可作为头等函数（first-class function），2.0的匿名delegate可作为闭包，3.0又引入了Lambda表达式，几乎可号称函数式语言了。及至C#4.0，进一步向动态语言靠拢，将引入关键字dynamic以支持动态类型和动态操作。”
        </p><p>
            叹号眉头微蹙：“听起来C# 比Java复杂多了，好像大杂烩。”
        </p><p>
            “当初Java对C++的改革的一个重要原则是：让语言变得简单而有亲和力，降低程序员门槛，提高软件生产效率。但如今Java已经不那么简单了，C#则更复杂，在某些方面较之C++也不遑多让。” 冒号语调转强，“以前人们试图在语言的<span class="emphasis"><em>威力</em></span>（power）和<span class="emphasis"><em>生产率</em></span>（productivity）中寻求最佳平衡点，现在似乎更想鱼与熊掌兼得。”
        </p><p>
            问号再次提问：“Java与C# 最大的区别是什么？”
        </p><p>
            “虽然同为平台语言，二者的设计理念还是有所不同的。<span class="strong"><strong>Java的目的是让一种语言在多种平台上运行<a class="link" href="#note3"><sup>[3]</sup></a>，而C#（.NET）的目的是让多种语言在一种平台上运行</strong></span><a class="link" href="#note4"><sup>[4]</sup></a>。”冒号钟爱这种具有对称美的对比，“虽然理论上以C#为核心的.NET平台可以用在任何操作系统上，但千万不要指望微软会心甘情愿地提供Windows之外的实现<a class="link" href="#note5"><sup>[5]</sup></a>，原因不言自明。而Sun的Solaris无法与Windows抗衡，最大的王牌就是Java语言，当然会致力于跨平台和面向Internet的应用，对桌面应用的投入相对较少。另外，Sun不像微软那样控制着包括C#、VB、C++/CLI、J#、JScript.NET等在内的多种语言，对跨语言的热情度相应也低得多。尽管Java能与Groovy、JRuby、JPython等以JVM为基础相互调用，不过与其说这是Java的初衷，不如说是自然发展的结果。因此两个<span class="strong"><strong>平台语言</strong></span>，一个<span class="strong"><strong>重在语言</strong></span>，让语言向平台扩散；一个<span class="strong"><strong>重在平台</strong></span>，让平台来凝聚语言。在此并不想对两公司的商业策略评头论足，而是认为了解这种背景会对理解语言特征及其发展趋势有所裨益。编程语言绝非象牙塔中之物，实乃技术和商业合力推动的结果。”
        </p><p>
            逗号拖着怪腔：“原来如彼！”
        </p><p>
            众人大笑。
        </p><p>
            句号深思后问道：“可不可以这么理解：如果开发仅限于Windows平台的应用，C#是更好的选择，它一方面与Windows系统存在天然的纽带，另一方面可有效地利用.NET的包括ASP.NET、VB.NET、ADO.NET、Windows Forms、WPF、WCF、WF等在内的各种软件资源；如果开发不限于Windows的企业应用，则选择Java更合适？”
        </p><p>
            冒号微微颔首：“有一定道理，平台的确是语言选择的一个考虑因素。不过说起可利用的软件资源，Java毫不逊色。除了形形色色的开源框架和工具外，与动态语言如Groovy、Scala、Ruby、Python等也能严丝合缝。作为平台语言，Java和C#均有极为丰富的资源和极强的整合能力，背后又有大公司不遗余力的支持和推广，理所当然地成为大型企业应用的主流选择。”冒号匆匆作结，“关于Java与C#的比较暂时谈到这里<a class="link" href="#note6"><sup>[6]</sup></a>，由于时间关系，我们不可能面面俱到，难免挂一漏万。许多概念术语只是点到为止，具体细节留待以后展开，暂且按下不表。总而言之，每种语言都为解决问题提供了一定的途经，C++、Java和C#的不同之处在于：C++提供了一马平川的大路，也提供了陷阱密布的小道；Java则在大路上铺设水泥，同时封锁了捷径小道；C#同样填平了大路上的坑坑洼洼，但把一些小道上‘此路不通’的牌子悄悄换成了‘此路危险’。” 
        </p></div><div class="section" title="，插语"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="note"></a>，插语</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p><a name="note1"></a>
                    在C#的语言规范（language specification）中，多处提到C和C++，但对Java只字不提。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    C#保留了C++中的struct关键字作为值类型，以区别于class所代表的引用类型。
                </p></li><li class="listitem"><p><a name="note3"></a>
                    此处的平台主要指操作系统。另外，近来Java平台也大有凝聚其他语言的倾向，尤以Groovy、Ruby、Python、Scala等动态语言为代表。
                </p></li><li class="listitem"><p><a name="note4"></a>
                    严格来说应当是“.NET 的目的是让多种语言在一种平台上运行”。文中说法是因为C#是本节的谈论主体，并且作为.NET的核心语言，其设计也遵循该理念。
                </p></li><li class="listitem"><p><a name="note5"></a>
                    Novell公司开发的Mono项目是.NET平台的一种开源实现，可以在Windows以外的操作系统如Unix、Linux、Mac OS X等上运行。
                </p></li><li class="listitem"><p><a name="note6"></a>
                    参考文献【3】和【4】对Java和C#进行了非常详尽的比较。
                </p></li></ol></div></div><div class="section" title="。总结"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="summary"></a>。总结</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    相比C++，Java与C#更加安全（限用指针、数组边界检查、类型安全、资源管理等）、简单（自动垃圾回收、废除多继承和头文件等）、中性（编译成机器无关的字节码，运行于虚拟机）、OO（无全局变量和函数等）、丰富（支持反射和并发编程、更完备的API等）、标准（注释性文档、更好的Unicode支持等）。
                </p></li><li class="listitem"><p>
                    Java的目的是让一种语言在多种平台上运行，而C# （.NET）的目的是让多种语言在一种平台上运行。Java重在语言，让语言向平台扩散；C#重在平台，让平台来凝聚语言。
                </p></li><li class="listitem"><p>
                    虽然系统开发并非Java与C#的禁区，它们更多地还是用于企业级应用开发。丰富的资源、强大的整合能力和巨头公司的鼎力支持是它们得天独厚的优势。
                </p></li><li class="listitem"><p>
                    编程语言的发展是技术和商业合力推动的结果。
                </p></li></ul></div></div><div class="section" title="“”参考"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="reference"></a>“”参考</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>
                    Jesse Liberty．Programming C#, 2<sup>nd</sup> ed.．Sebastopol, CA：O&#8217;Reilly，2002
                </p></li><li class="listitem"><p>
                    Ecma International．Standard ECMA-334(C# Language Specification, 4<sup>th</sup> ed.)．<a class="link" href="http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf" target="_top">http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf</a>
                </p></li><li class="listitem"><p>
                    Dare Obasanjo．A COMPARISON OF MICROSOFT&#8217;S C# PROGRAMMING LANGUAGE TO SUN MICROSYSTEMS&#8217; JAVA PROGRAMMING LANGUAGE．<a class="link" href="http://www.25hoursaday.com/CsharpVsJava.html" target="_top">http://www.25hoursaday.com/CsharpVsJava.html</a>
                </p></li><li class="listitem"><p>
                    Wikipedia．Comparison of Java and C Sharp．<a class="link" href="http://en.wikipedia.org/wiki/Comparison_of_Java_and_C_Sharp" target="_top">http://en.wikipedia.org/wiki/Comparison_of_Java_and_C_Sharp</a>
                </p></li><li class="listitem"><p>
                    Mads Torgersen．New features in C# 4.0．<a class="link" href="http://code.msdn.microsoft.com/csharpfuture/Release/ProjectReleases.aspx?ReleaseId=1686" target="_top">http://code.msdn.microsoft.com/csharpfuture/Release/ProjectReleases.aspx?ReleaseId=1686</a>
                </p></li></ol></div></div></div><p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F10%2F03%2Fcolon-class-6_2%2F&amp;title=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A76.2%EF%BC%9A%E5%B9%B3%E5%8F%B0%E8%AF%AD%E8%A8%80" id="wpa2a_26">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2010年05月27日 -- <a href="http://blog.zhenghui.org/2010/05/27/preface-of-distributedjavaapp/" title="分布&#038;分享——《分布式Java应用：基础与实践》推荐序">分布&#038;分享——《分布式Java应用：基础与实践》推荐序</a> (1)</li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/10/03/colon-class-6_2/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>冒号课堂§6.1：系统语言</title>
		<link>http://blog.zhenghui.org/2009/09/29/colon-class-6_1/</link>
		<comments>http://blog.zhenghui.org/2009/09/29/colon-class-6_1/#comments</comments>
		<pubDate>Tue, 29 Sep 2009 04:18:29 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[C族语言]]></category>
		<category><![CDATA[C语言]]></category>
		<category><![CDATA[D语言]]></category>
		<category><![CDATA[系统语言]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=466</guid>
		<description><![CDATA[<b>系统语言</b>——权力的双刃剑（<em>简谈C、C++和D</em>）<br/> • 通禅悟道者拈花不语，坐井观天者蛙鸣鼓噪<br/> • Java程序员大多被惯坏了，环保意识要淡薄得多<br/> • （指针）用得好可以是削铁如泥的神兵利器，用得不好则可能是自我毁灭的罪恶渊薮<br/> • OOP又不是金子，含量越高越好。试图把一切都装进OOP的箱子里的想法无异于削足适履<br/> • 它们（系统语言）的理念是：优化机器的时间而不是人的时间，优化机器的记忆而不是人的记忆；假设编译器是愚蠢的而程序员是聪明的，因此赋予程序员更多的权利、义务与责任<br/> • C++是匹无辔无鞍的野马，看似桀骜不驯，若能顺性而御，必能足踏飞燕，行千里而不劳 [...]]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center;"><span style="font-family: 宋体;">冒号课堂</span></h1>
<strong><span style="font-size: 13pt; font-family: 宋体;">第六课 语言简评（1）</span></strong>

<p><strong><span style="font-family: 宋体;">课前导读</span></strong></p>
<p style="text-indent: 18pt;"><span style="font-family: 宋体;">本课对一些主流语言进行了简单的比较和评价。一家之言，权作助兴。</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 18pt;"><span style="font-family: 宋体;">本课共分四节——</span></p>
<p style="text-indent: 18pt;"><em>1.</em><em><span style="font-family: 宋体;">系统语言——权力的双刃剑</span></em></p>
<p style="text-indent: 18pt;"><em>2.</em><em><span style="font-family: 宋体;">平台语言——先搭台后唱戏</span></em></p>
<p style="margin-left: 18pt;"><em>3.</em><em><span style="font-family: 宋体;">前台语言——视觉与交互的艺术</span></em></p>
<p style="margin-left: 18pt;"><em>4.</em><em><span style="font-family: 宋体;">后台脚本——敏捷开发的利器</span></em></p>

<!-- below comes from generated html -->
<head><link rel="stylesheet" href="http://blog.zhenghui.org/css/colonclass.css" type="text/css"></head>

<div lang="zh-CN" class="article" title="系统语言"><div class="titlepage"><div><div><h1 class="title"><a name="id656771"></a>6.1 系统语言——权力的双刃剑</h1></div><div><div class="author"><h3 class="author">郑晖</h3></div></div><div><div class="abstract" title="摘要"><p class="title"><b>摘要</b></p><p>简谈C、C++和D</p></div></div></div><hr /></div><div class="toc"><p><b>目录</b></p><dl><dt><span class="section"><a href="#preview">！预览</a></span></dt><dt><span class="section"><a href="#question">？提问</a></span></dt><dt><span class="section"><a href="#explaination">：讲解</a></span></dt><dt><span class="section"><a href="#note">，插语</a></span></dt><dt><span class="section"><a href="#summary">。总结</a></span></dt><dt><span class="section"><a href="#reference">“”参考</a></span></dt></dl></div><div class="epigraph"><div class="literallayout"><p>居高者形逸而神劳，处下者形劳而神逸</p></div><div class="attribution"><span>—<span class="attribution">《洪应明•菜根谭》</span></span></div></div><div class="section" title="！预览"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="preview"></a>！预览</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    通禅悟道者拈花不语，坐井观天者蛙鸣鼓噪
                </p></li><li class="listitem"><p>
                    Java程序员大多被惯坏了，环保意识要淡薄得多
                </p></li><li class="listitem"><p>
                    （指针）用得好可以是削铁如泥的神兵利器，用得不好则可能是自我毁灭的罪恶渊薮
                </p></li><li class="listitem"><p>
                    OOP又不是金子，含量越高越好。试图把一切都装进OOP的箱子里的想法无异于削足适履
                </p></li><li class="listitem"><p>
                    它们（系统语言）的理念是：优化机器的时间而不是人的时间，优化机器的记忆而不是人的记忆；假设编译器是愚蠢的而程序员是聪明的，因此赋予程序员更多的权利、义务与责任
                </p></li><li class="listitem"><p>
                    C++是匹无辔无鞍的野马，看似桀骜不驯，若能顺性而御，必能足踏飞燕，行千里而不劳
                </p></li></ul></div></div><div class="section" title="？提问"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="question"></a>？提问</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>为什么C++不支持自动垃圾回收？</p></li><li class="listitem"><p>在C++中如何解决内存释放问题？</p></li><li class="listitem"><p>系统语言有哪些特点？</p></li><li class="listitem"><p>在不引入OOP的前提下，C语言可以借鉴C++的哪些特征？</p></li><li class="listitem"><p>D语言比C++有哪些改进？</p></li><li class="listitem"><p>在电脑性能日益提升的今天，还有必要在乎程序的性能和效率吗？</p></li></ul></div></div><div class="section" title="：讲解"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="explaination"></a>：讲解</h2></div></div></div><p>
            教室里，学员们正热火朝天地讨论着流行的编程语言。冒号推门而入，仿佛沸水锅里被浇了一瓢冷水，立刻平静下来。
        </p><p>
            冒号笑吟吟地看着大家：“怎么不讨论了？”
        </p><p>
            众人齐道：“该您了！”
        </p><p>
            “首先需要声明的是，本课评论编程语言，乃是应众位之邀，实非本意。”冒号变得严肃起来，“因为这种评论，不可避免地会带上个人色彩，容易产生误导。有道是，<span class="strong"><strong>通禅悟道者拈花不语，坐井观天者蛙鸣鼓噪</strong></span>。”
        </p><p>
            众人迅速自动对号：合着我们就是一群蛤蟆。
        </p><p>
            “这样一来，我的处境就很尴尬了。”冒号自嘲着。
        </p><p>
            有人在幸灾乐祸地偷笑。
        </p><p>
            “也罢，即使作蛙鸣，至少也要先跳出井来。”冒号毅然决然地加入了蛤蟆的行列，“要谈，就旗帜鲜明地谈，该赞叹的就赞叹，该鄙视的就鄙视。说些你好我好大家好之类不痛不痒的话，倒不如不说。”
        </p><p>
            叹号一拍大腿：“好，这样才够痛快！”
        </p><p>
            问号忍不住问：“您究竟打算比较哪些主流语言呢？”
        </p><p>
            冒号回答：“就谈谈第一堂课提到的最流行的十二种语言吧。按语法特征可将它们分为三类：C族静态语言五种——C、C++、Java、C#和D；非C族静态语言两种——VB和Delphi；动态语言五种——Perl、PHP、Python、Ruby 和JavaScript 。”
        </p><p>
            叹号表示怀疑：“这么多种语言怎么比较得过来？”
        </p><p>
            冒号解释：“我们主要比较第一类的C族语言，这些也是今后学习的重点，其他的只是泛泛而谈。”
        </p><p>
            引号猜测：“因为他们更重要？”
        </p><p>
            “可以这么说。”冒号直截了当，“毋庸讳言，在当今的主流语言中，C族语言应用范围之广、使用人数之多、影响力之巨都是其他类语言所无法比拟的。它们之间的关系从名字上就能看出：C语言的前身是B语言；其后是C++；Java曾被称为C++++&#8211;，意思是在C++上增点东西再减点东西；C#中#就是四个叠起的加号<a class="link" href="#note1"><sup>[1]</sup></a>；最后D语言干脆在字母上进行升级。”
        </p><p>
            句号推断：“B语言、C语言、D语言，下一个该D++、D#或E语言了。”
        </p><p>
            谁知冒号却说：“E语言已经有了，与Java的语法很像。甚至F语言也有了，但不是C族语言，而是Fortran族的。这不，微软还在.NET平台上推出了F#语言，不过这里的F指的是‘Functional’，即函数式。”
        </p><p>
            逗号向往着：“不如直接搞个终极的Z语言，成为全世界程序员的唯一指定语言，多省事！”
        </p><p>
            “这难度不亚于全人类共用一种语言。”冒号笑道，“愿望是美好的，我们还得面对现实。不扯远了，你们先谈谈一下这些C族语言各自的特点吧。”
        </p><p>
            众人心想：老冒怎么跟国足一个毛病，老喜欢回传，就是不直接射门，真是急煞人也！
        </p><p>
            问号拣了个软柿子：“C语言是C族老大，又是唯一的纯过程式语言，当然与众不同啦。”
        </p><p>
            引号一板一眼：“C++在过程式的基础上又引入对象式和泛型式，同时保持了C的高效性和底层开发能力。”
        </p><p>
            逗号接道：“Java既继承了C++的优点，又克服了C++的复杂性，虽然底层开发能力有所减弱，但具备平台无关性。”
        </p><p>
            句号不紧不慢：“C#兼具C++与Java各自的优点，但效率上不如C++，跨平台方面不如Java。”
        </p><p>
            叹号后悔嘴慢：“剩下一个最陌生的D语言，在第一堂课之前还真没听说过，怎么挤上主流语言位置的？我想。。。呃，它总该比C++要高级吧。”
        </p><p>
            冒号评价：“各位谈得虽然简单了些，也算八九不离十吧。下面我稍微展开些来讲。”
        </p><p>
            此时众人有一个共同的愿望，希望老冒这次能痛快地单刀赴会、直捣黄龙。
        </p><p>
            冒号似乎看出大家的心思，开始口若悬河：“关于C语言，前面多次提到。这是一把历久弥新的宝剑，一旦出鞘，依旧寒光逼人，锋利无俦。有了它，便如战将有了佩剑，平添一分独闯敌营的胆气。尽管以现代的眼光来看，它存在不少缺点，但即使抛开C语言辉煌的历史不谈，单就其以如此高龄在诸多后辈冲击之下仍屹立不倒而论，让人无法对其多加苛求。”
        </p><p>
            逗号提出异议：“但语言不是让人崇拜的，而是让人运用的。一门语言无论过去如何荣光，如果不适应现代发展趋势，还是可能被淘汰。”
        </p><p>
            “说得非常好！”冒号竟然鼓起掌来，“迄今为止本课堂对于具体知识的讲授不算太多，但一直提倡独立思考，不要盲从权威。如果你们能做到这一点，本班的目标也就实现了一半。回头再说说C语言，它源自Unix操作系统的开发，以其良好的抽象性和可移植性取代了汇编语言作为系统开发语言。因其简洁实用、灵活高效，很快从系统领域发展到其他领域而成为通用语言。随着新兴语言的崛起以及硬件性能的大幅提高，C语言的缺点也日益显著：过于宽松的类型检查、容易出错的内存管理、相对贫乏的语言特征等等。虽然自身还在发展，它的市场份额日益减少乃是不争的事实。但在相当长的时间内，它在其所擅长的领域里仍会占举足轻重的一席之地。如果C能借鉴C++的 命名空间、重载、异常处理和STL等非OOP的特征，它的生命力绝不会比任何OOP语言弱。附带说一句，C虽然没有直接支持OOP的语法，但经过适当的设计还是能实现OOP的<a class="link" href="#note2"><sup>[2]</sup></a>。”
        </p><p>
            引号咨询道：“关于C语言的学习，您有何建议？”
        </p><p>
            “精读K&amp;R的《The C Programming Language》，此书不过200多页，堪称C语言的剑诀。其中的R即Dennis Ritchie，是C的创造者，同时也是Unix的缔造者之一，是真正的大师。如今的大师，同博士、教授、院士等头衔一样，严重地通货膨胀了。”冒号不无感慨。
        </p><p>
            问号尖锐地问：“C++既保持了C的底层开发能力，又引入了OOP，C的处境想必更加艰难吧？”
        </p><p>
            冒号坦承：“这是不假。C++成功的一个重要因素是对C语言的兼容，由此吸引了大批的C程序员。但这不是没有代价的，C++在兼容C的同时也保留了C的许多缺陷。Java成功的地方有很多，一个不容忽视的因素是它彻底摆脱了与C兼容的桎梏。由于C++对C的改革不彻底，又过于庞杂，并且效率上不如C，这使得C仍有其生存空间。略有讽刺意味的是，对C++批判最激烈的往往来自C的社区，比如Linux之父Linus Torvalds就曾激烈地批判过C++。”
        </p><p>
            “Linus？那可是我的偶像呢！”叹号惊讶道。
        </p><p>
            冒号劝诫：“如果你因为是他的粉丝而后悔学C++，那就是为他人的偏执买单，不管那人名气有多大。”
        </p><p>
            句号指出：“C++最为人诟病的地方有：语法过于复杂，学习曲线陡、开发效率低；支持的范式过多；OOP不彻底；反射（reflection）功能不足；支持指针操作导致安全隐患；没有自动垃圾回收，容易内存泄漏；没有线程支持；没有丰富的标准库支持图形界面、网络编程等。”
        </p><p>
            “罪状不少哇！这些说法都有一定道理，但有些也有失公允。且听我一一道来。”冒号当起了辩护律师，“C++比较复杂这点没错，Stroustrup说过一句耐人寻味的话： 一种语言不够复杂是因为它还不够成熟。成人肯定比儿童复杂，因为他要承担更大的责任。大家不妨看看Java从1.0到即将问世的7.0、C#从1.0到即将的4.0的发展过程，是否应证了这一点？当然C++的复杂度的确高于其他语言，但如果不执着于奇技淫巧，它绝非高不可攀。C++的开发效率相比Java与C#，差距主要在两个方面：一是标准库不够完善，二是需要手工回收垃圾。关于前者，的确是C++的一大软肋，标准库竟然连企业应用中最常用的图形界面、网络编程、数据库处理、多线程等都不能涵盖，严重障碍了生产力。其实C++也有苦衷，不像Java和C#那样有大公司的鼎力支持，只靠效率极为低下的标准委员会来维护。98年的一个标准直到03年还在修订，下一个标准至少要到09年。连Stroustrup都在哭穷，声称没有足够的人力和时间来开发标准库，可为何广受赞誉的Boost库至今仍徘徊在标准门外？考虑到Boost的创办人大多出自标准委员会，其他无此背景的类库恐怕更难登C++之大堂了。相比之下D语言更惨，虽然天生丽质，苦无豪门青睐，只好一直待字闺中。”
        </p><p>
            叹号感慨：“金钱才是技术的最大推动力啊！”
        </p><p>
            “话糙理不糙。”冒号也很无奈，“再来谈谈有关自动垃圾回收的问题。在C++中，程序员也不是非得手工清理垃圾不可的。更好的办法是遵循RAII的惯用法（idiom）<a class="link" href="#note3"><sup>[3]</sup></a>，通过<span class="term">智能指针</span>（smart pointer）来解决内存释放问题。”
        </p><p>
            逗号听不明白：“什么是RAII？”
        </p><p>
            “RAII是Resource Acquisition Is Initialization的缩写，直译为‘资源获取即初始化’。”冒号解释，“其实更准确的叫法应该是RRIF（Resource Release Is Finalization），即‘资源释放即终结化’<a class="link" href="#note4"><sup>[4]</sup></a>。其思想是：将资源的取放与某一对象的生命周期绑定，初始化对象时获取资源，终结化对象时释放资源。用户代码不再直接管理资源，只需控制相应的对象即可。这样代码得以简化，资源的有效性也得以保障，并且还是<span class="term">异常安全的</span>（exception-safe）<a class="link" href="#note5"><sup>[5]</sup></a>。”
        </p><p>
            问号猜想：“在Java中没有这种用法，是因为它已经有了垃圾回收器吗？”
        </p><p>
            冒号摇摇头：“问题的关键不在这里。资源不只限于内存，还包括文件、线程锁、数据库连接等等，这些都不是垃圾回收器所能解决的。看看Java的数据库应用代码吧，对于那些频繁出现的被try/catch/finally包裹的resultset.close()、statement.close()、connection.close()，你是习以为常呢，还是不胜其烦？C#的设计者显然意识到这一点，专门提供了using语句来简化释放资源（IDisposable）的代码。”
        </p><p>
            逗号眼睛一亮：“如果采用RAII的技巧，这些都可以省去了吗？”
        </p><p>
            冒号再次摇头：“可惜Java不像C++或D那样，能在栈（stack）上创建对象。栈对象有一个特点，一旦超出其作用范围，便自动释放内存。在此之前会调用<span class="term">析构函数</span>（destructor），后者继而调用释放资源的代码。”
        </p><p>
            几声叹息清晰可闻。
        </p><p>
            冒号续道：“另一方面，尽管自动垃圾回收机制逐渐为大众所接受——据说C++0x也将部分地支持它——但这种机制也存在缺陷。比如一个Java程序如果在某一时段极耗内存，由于自动垃圾回收的不定时性，不能保证及时清理内存，可能会抛出OutOfMemoryError的错误。对于内存有限的系统或实时系统来说，这绝对是一个致命的软肋。C++不支持自动垃圾回收，正是基于这些方面的考虑。Stroustrup非常顾虑自动垃圾回收带来的时间和空间上的过多开销，并且担心它会影响C++完成其所肩负的底层任务。另外，千万不要以为有了自动垃圾回收机制就一劳永逸、万事大吉了。Java程序一样会有内存泄漏，其几率甚至可能比C++的更大，因为C++程序员对此更有戒心，而Java程序员大多被惯坏了，环保意识要淡薄得多。”
        </p><p>
            问号直奔要害：“您如何看待C与C++中的指针？”
        </p><p>
            冒号欣然接招：“指针是C与C++最大的特色，其他语言要么不支持，要么支持得有限。C与C++可以说是成也指针，败也指针。用得好可以是削铁如泥的神兵利器，用得不好则可能是自我毁灭的罪恶渊薮。但由于二者定位于系统语言，而指针对于底层操作是必不可少的。同样道理，二者的数据类型的转换比其他静态类型语言更自由，也是源出于此。”
        </p><p>
            句号总结：“<span class="strong"><strong>能力越大，责任越大，风险越大</strong></span>。”
        </p><p>
            “正是此意！”冒号重重地敲了一下桌子，“此话既适用于编程语言，也适用于程序员。至于C++缺少对反射功能的支持，也是因为追求效率，不愿在元数据上花时间和空间。说到C++支持的范式过多，程序员过于自由，代码不标准难维护，这就如同埋怨餐馆提供的菜式过多以致难以摆出一桌酒席一样可笑。最后，指责C++不是100％OOP的说法更是荒谬至极。OOP又不是金子，含量越高越好。<span class="strong"><strong>试图把一切都装进OOP的箱子里的想法无异于削足适履</strong></span>。典型的如Java中的Math类，逻辑上压根儿就不存在什么Math对象，清一色的static方法和常量就是最好的讽刺。在C++中只要在math的namespace中定义一些自由函数就可以了，自然而简洁。作为一个佐证，Java于J2SE5.0引进了<span class="term">静态导入</span>（static import）机制<a class="link" href="#note6"><sup>[6]</sup></a>，C#也在2.0增加了<span class="term">静态类</span><a class="link" href="#note7"><sup>[7]</sup></a>（static class），不仅在<span class="emphasis"><em>形式上</em></span>简化了代码，也在<span class="emphasis"><em>思维上</em></span>容忍了非OOP的过程式。”
        </p><p>
            引号发觉：“您好像把对C++所有的责难都化解了。”
        </p><p>
            “可恨之人必有可怜之处嘛。”冒号俗语反用，“其实C++仍有不少亟待改进之处，D语言就是很好的启示。D语言的提供了<span class="emphasis"><em>可控制的</em></span>垃圾回收器；支持线程同步；支持<span class="term">动态数组</span>（dynamic array）；支持<span class="term">嵌套函数</span>（nested function）；支持<span class="term">契约式设计</span>（design by contract）；废除了C与C++ 中令人头痛的<span class="term">头文件</span>（header file）等等。这些都是C++程序员梦寐以求的特征。”
        </p><p>
            逗号很奇怪：“为什么D语言名气这么小？”
        </p><p>
            句号吟道：“千里马常有，而伯乐不常有，大腕伯乐更稀有。”
        </p><p>
            众乐。
        </p><p>
            冒号拔高了调门：“既然系统语言主要为底层系统的开发服务，这就决定了它们的理念是：<span class="strong"><strong>优化机器的时间而不是人的时间，优化机器的记忆而不是人的记忆；假设编译器是愚蠢的而程序员是聪明的，因此赋予程序员更多的权利、义务与责任</strong></span>。无视这种背景和理念而去与其他语言相较，完全是不着筋节，不值一哂。当然这并不排斥系统语言用于应用开发，尤其是C++和D语言。需要强调的是，常见的‘C/C++’的说法很不科学。C与C++虽有千丝万缕的联系，但一个简单，一个复杂；一个纯过程式，一个集过程式、对象式、泛型式和元编程于一体。貌合神离，不宜混为一谈。”
        </p><p>
            叹号一个问题憋了半天，不吐不快：“我有一个问题：如今电脑性能这么高，C与C++如此强调程序的性能和效率还有必要吗？”
        </p><p>
            “绝对有必要！”冒号斩钉截铁，“其一、纵向看，用户的耐心与电脑的性能成反比，早年一个386人们就满足了，如今却忍受不了586；以前16M内存就不错了，现在1G都嫌小。其二、横向比，占用资源过多、运行相对缓慢的软件竞争力也低。其三、在一些应用领域如人工智能、大型计算等方面，普通电脑的性能还远远不能满足要求，超级计算机的存在就是明证。其四、仍有些程序跑在资源有限的主机上，比如嵌入式系统。”
        </p><p>
            引号再次要求：“能推荐一些C++方面的书吗？”
        </p><p>
            冒号直言相告：“学好C一本书足矣，学好C++即使推荐十本仍有遗珠之憾。可以说C++是苦了编程者，甜了著书人。开个小书单：初级——《C++ Primer》和《Thinking in C++》；中级——《The C++ Programming Language》和《Effective C++》系列；高级——《The C++ In-Depth》系列。这里还要特别推荐一下《The Design and Evolution of C++》，从中你可以看到 C++的设计和演变的来龙去脉，极具启发性。其他的C++精品书籍还有不少，恕不一一列举了。C++是匹无辔无鞍的野马，看似桀骜不驯，若能顺性而御，必能足踏飞燕，行千里而不劳。”
        </p></div><div class="section" title="，插语"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="note"></a>，插语</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p><a name="note1"></a>
                    更官方的说法是“#”来自音乐中的高半音符号。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    比如文献【4】设计了一种用C来实现OOP的机制。
                </p></li><li class="listitem"><p><a name="note3"></a>
                    RAII是由Bjarne Stroustrup首先提出的，参见文献【2】。
                </p></li><li class="listitem"><p><a name="note4"></a>
                    说它更准确，是因为资源获取的代码不一定需要出现在constructor中，但资源释放的代码一定出现在destructor中。
                </p></li><li class="listitem"><p><a name="note5"></a>
                    指即使发生异常（exception）也不会导致资源泄露。
                </p></li><li class="listitem"><p><a name="note6"></a>
                    比如，在静态导入Math类（import static java.lang.Math.*;）后，代码中可以直接调用sqrt、log、max等数学方法，不再需要“Math.”的前缀了。
                </p></li><li class="listitem"><p><a name="note7"></a>
                    静态类只能拥有静态成员，不能实例化，不能被继承，也不能继承除Object之外的类或接口。
                </p></li></ol></div></div><div class="section" title="。总结"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="summary"></a>。总结</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    C++对C语言的兼容是其成功的一个重要因素，但同时也继承了C的一些缺陷。
                </p></li><li class="listitem"><p>
                    C++设计者没有直接支持自动垃圾回收，是担心它造成过大的时空开销，同时会削弱底层开发能力。
                </p></li><li class="listitem"><p>
                    除了手工释放内存外，C++提倡运用RAII原则解决包括内存在内的资源管理问题。
                </p></li><li class="listitem"><p>
                    C与C++对指针的全面支持和宽松的类型转换限制，均出于底层系统开发的需要。
                </p></li><li class="listitem"><p>
                    C可以借鉴C++的 命名空间、重载、异常处理和STL等非OOP的特征。
                </p></li><li class="listitem"><p>
                    D语言提供了可控制的垃圾回收器，支持线程同步、动态数组、嵌套函数和契约式设计，并废除了头文件和前置声明（forward declaration）。
                </p></li><li class="listitem"><p>
                    在程序性能与生产效率之间，系统语言更看重前者，它们在赋予程序员更多的权利的同时，也带给程序员更多的负担。
                </p></li><li class="listitem"><p>
                    程序的性能和效率永远是重要的。一方面，用户对软件性能的期望越来越高；另一方面，有时硬件性能与软件需求并不匹配：有些应用（如人工智能、大型计算）对程序的性能和效率要求极高，有些系统（如嵌入式系统）的资源十分有限。
                </p></li></ul></div></div><div class="section" title="“”参考"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="reference"></a>“”参考</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>
                    Bjarne Stroustrup．The Design and Evolution of C++．Reading, MA：Addison-Wesley，1994．219-222
                </p></li><li class="listitem"><p>
                    Bjarne Stroustrup．The C++ Programming Language，Special ed.．Reading, MA：Addison-Wesley，2000．364-387
                </p></li><li class="listitem"><p>
                    Walter Bright．D Programming Language．<a class="link" href="http://www.digitalmars.com/d/" target="_top">http://www.digitalmars.com/d/</a>
                </p></li><li class="listitem"><p>
                    Axel Tobias Schreiner．Object-oriented Programming with ANSI-C．<a class="link" href="http://www.planetpdf.com/codecuts/pdfs/ooc.pdf" target="_top">http://www.planetpdf.com/codecuts/pdfs/ooc.pdf</a>
                </p></li></ol></div></div></div><p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F09%2F29%2Fcolon-class-6_1%2F&amp;title=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A76.1%EF%BC%9A%E7%B3%BB%E7%BB%9F%E8%AF%AD%E8%A8%80" id="wpa2a_28">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>无相关文章</li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/09/29/colon-class-6_1/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>冒号课堂§5.4：语言误区</title>
		<link>http://blog.zhenghui.org/2009/09/27/colon-class-5_4/</link>
		<comments>http://blog.zhenghui.org/2009/09/27/colon-class-5_4/#comments</comments>
		<pubDate>Sun, 27 Sep 2009 00:50:01 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[宗教]]></category>
		<category><![CDATA[架构师]]></category>
		<category><![CDATA[程序员]]></category>
		<category><![CDATA[编程语言]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=457</guid>
		<description><![CDATA[<b>语言误区</b>——语言的宗教情结（<em>关于编程语言认识上的一些误区</em>）<br/> • 如果说编程范式是一种文化，那么编程语言更像是一种宗教——尽管它本不该是<br/> • 语言只有两种：一种怨声载道，一种无人问津<br/> • 对待一门语言的态度应该是：与其抱怨争执，不如扬长避短<br/> • 过分拔高一种语言与抹煞语言之间的差别是两种极端，皆为秕言谬说<br/> • 这些人若有幸拜关公为师，他们最艳羡的一定他的赤兔马和青龙偃月刀，或许还会抹红脸蓄长须什么的，就是不太愿学他的盖世武功<br/> • 理想的架构师应当如文学大师，既有恢弘大气的构思，又有细腻深刻的笔法；应当如统军大帅，既有运筹帷幄的韬略，又有冲锋陷阵的武功 [...]]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center"><span style="font-family: 宋体">冒号课堂</span></h1>
<strong><span style="font-size: 13pt; font-family: 宋体">第五课 语言小谈(4)</span></strong>

<!-- below comes from generated html -->
<head><link rel="stylesheet" href="http://blog.zhenghui.org/css/colonclass.css" type="text/css"></head>

<div lang="zh-CN" class="article" title="语言误区"><div class="titlepage"><div><div><h1 class="title"><a name="id592979"></a>5.4 语言误区——语言的宗教情结</h1></div><div><div class="author"><h3 class="author">郑晖</h3></div></div><div><div class="abstract" title="摘要"><p class="title"><b>摘要</b></p><p>关于编程语言认识上的一些误区</p></div></div></div><hr /></div><div class="toc"><p><b>目录</b></p><dl><dt><span class="section"><a href="#preview">！预览</a></span></dt><dt><span class="section"><a href="#question">？提问</a></span></dt><dt><span class="section"><a href="#explaination">：讲解</a></span></dt><dt><span class="section"><a href="#note">，插语</a></span></dt><dt><span class="section"><a href="#summary">。总结</a></span></dt><dt><span class="section"><a href="#reference">“”参考</a></span></dt></dl></div><div class="epigraph"><div class="literallayout"><p>爱而不知其恶，憎而遂忘其善</p></div><div class="attribution"><span>—<span class="attribution">《吴兢•贞观政要》</span></span></div></div><div class="section" title="！预览"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="preview"></a>！预览</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    如果说编程范式是一种文化，那么编程语言更像是一种宗教——尽管它本不该是
                </p></li><li class="listitem"><p>
                    语言只有两种：一种怨声载道，一种无人问津
                </p></li><li class="listitem"><p>
                    对待一门语言的态度应该是：与其抱怨争执，不如扬长避短
                </p></li><li class="listitem"><p>
                    过分拔高一种语言与抹煞语言之间的差别是两种极端，皆为秕言谬说
                </p></li><li class="listitem"><p>
                    这些人若有幸拜关公为师，他们最艳羡的一定他的赤兔马和青龙偃月刀，或许还会抹红脸蓄长须什么的，就是不太愿学他的盖世武功
                </p></li><li class="listitem"><p>
                    理想的架构师应当如文学大师，既有恢弘大气的构思，又有细腻深刻的笔法；应当如统军大帅，既有运筹帷幄的韬略，又有冲锋陷阵的武功
                </p></li></ul></div></div><div class="section" title="？提问"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="question"></a>？提问</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>为什么那么多人对某些编程语言都有浓厚的宗教情结？</p></li><li class="listitem"><p>存在至高无上的语言吗？</p></li><li class="listitem"><p>“语言不过是工具，其实都差不多”。这种论调正确吗？</p></li><li class="listitem"><p>IDE、框架、设计工具等比语言更重要吗？</p></li><li class="listitem"><p>语言是低级的代名词，设计是高级的代名词吗？</p></li><li class="listitem"><p>架构师就不关心语言了吗？</p></li></ul></div></div><div class="section" title="：讲解"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="explaination"></a>：讲解</h2></div></div></div><p>
            “最后该谈谈你们最为关心的语言问题了。”冒号环视一番，众人眼球的瓦数显见增大，“如果说编程范式是一种文化，那么编程语言更像是一种宗教——尽管它本不该是。每种流行的语言都有大批忠实的信徒，随时可能与其他的信徒发生宗教战争。”
        </p><p>
            逗号深有同感：“周围和网上关于语言的争论从来不绝于耳，志不同道不合者动辄恶语相向。”
        </p><p>
            提起这些，冒号不免有些忿忿然：“这些信徒或准信徒们认为他们所崇拜的语言鹤立鸡群，远比其他语言高明得多，恨不得成为全天下程序员的通用语言。对其他语言的使用者，或居高临下地同情，或不屑一顾地讥讽。无知与偏见总是相辅相成的，他们中的绝大多数顶多熟悉一两种语言，其他的或浅尝辄止、或道听途说，却敢妄评优劣，岂不可笑之极？”
        </p><p>
            引号点头称是：“是啊，没有深入的了解，怎会有深入的比较。”
        </p><p>
            冒号进而尖锐地指出：“从心理学上分析，一个人在某种观点形成后，会通过自我的暗示和倾向性的证据不断地强化这种观点，并对其他观点本能地选择性失明。这背后折射的其实是一种懒人心态——认定自己掌握的语言是最好的，便不必费事再学其他语言了；这更是一种弱者心态——无论是耿耿忠心的铁杆卫士，还是振振有词的辩护律师，一旦丧失<span class="strong"><strong>自我批判的勇气和精神</strong></span>，声嘶力竭的挞伐只能反证他们的偏狭浅薄与自信缺失。”
        </p><p>
            冒号刻意借辛辣十足的嘲讽再次传递精神的信号。
        </p><p>
            叹号道出苦衷：“只是真正掌握好一门语言已殊为不易，同时掌握几种就更难。学完一种语言再看另一种，怎么都觉得别扭。”
        </p><p>
            “这就是第一堂课所说的‘学会’与‘会学’的差距。”冒号挑明要害，“每种语言都有天然设计上的不同，当你用得很别扭时，恰恰说明没有真正掌握。正如刀法以劈为主，枪法以扎为主，你若反其道而行之，刀扎枪劈，能不别扭吗？如果愣拿双节棍当单节棍使，恐怕没砸到别人倒先砸晕自己了。可见不是兵器问题，而是招法问题。”
        </p><p>
            下面隐约传来周杰伦《双截棍》的调子。
        </p><p>
            问号注意到：“似乎不只是初学者，大师们也会对一些语言提出过尖锐的批评。”
        </p><p>
            冒号引用道：“C++的发明者Bjarne Stroustrup说过这样一句话，语言只有两种：一种怨声载道，一种无人问津（There are only two kinds of languages: the ones people complain about and the ones nobody uses）。”
        </p><p>
            众笑。
        </p><p>
            句号有所体会：“一种语言如果用者甚众，自有其可取之处。没有一种语言是完美的，爱之深者尚苛之切，何况其余者乎？”
        </p><p>
            冒号忽然问：“你们知道对一种语言最大的批判是什么吗？”
        </p><p>
            众人纷纷摇头。
        </p><p>
            “那就是发明一种与此语言有类似功用的新语言。”冒号语速放缓，“Stroustrup认为Simula太慢、BCPL又太底层，于是发明了C++；Gosling觉得C++用得不爽，于是发明了Java；微软恼恨Java，于是请Hejlsberg发明了C#；Matsumoto用了两年多的C++仍不顺手，于是发明了Ruby。如此这般，不一而足。”
        </p><p>
            叹号无奈地说：“那是牛人的批评方法，常人怎能办到？”
        </p><p>
            “没有能力发明语言，就老老实实地用别人的吧。”冒号酷酷地说，“对待一门语言的态度应该是：与其抱怨争执，不如扬长避短。”
        </p><p>
            引号顺势道：“按这种说法，编程语言的发展史就是一种批判史咯？”
        </p><p>
            冒号祭起辩证法：“从另一个角度看，发明一种语言也是对先前语言的一种最高的赞美。C++之于C，Java之于C++，C#之于Java，都是后者对前者的一种承认，哪怕是极不情愿的承认。批判与赞美，继承与发展，谓之扬弃。”
        </p><p>
            众人心想，语言课又改哲学课了。
        </p><p>
            冒号续道：“对于编程语言还有一种论调：语言只是一种工具，哪种都差不多。”
        </p><p>
            逗号接茬：“是啊，经常在论坛上看到一些高手这么说。”
        </p><p>
            “一群伪高手，是工具就差不多？能拿锤子当刀使吗？”冒号嗤之以鼻，“<span class="strong"><strong>过分拔高一种语言与抹煞语言之间的差别是两种极端，皆为秕言谬说</strong></span>。图灵奖获得者Alan Perlis曾说过：如果一种语言不能影响你对编程的看法，那么就不值得去了解。试想，一种没有独特魅力的语言如何吸引程序员趋之若骛？了解这些独特之处对于编程往往至关重要，如果沿袭以前语言的做法，会显得不伦不类甚至可能铸成大错。”
        </p><p>
            问号要求：“能说得具体点吗？”
        </p><p>
            冒号举例：“有人说，学C++要先学C；还有人说，学会了C，C++就不在话下了。”
        </p><p>
            叹号一惊：“难道不是吗？”
        </p><p>
            冒号解释：“其实C++的真正来源是Simula而不是C，向下兼容C只是因为C效率高、普及广。换句话说，C++与C形同而神异。学会了C当然对C++有一定帮助，但若不能理解C++的OOP思想，那么C的背景反而是一种障碍。”
        </p><p>
            逗号想当然：“C++与Java都是OOP语言，差别该不大了吧？”
        </p><p>
            冒号再次否定：“不同编程范式的语言差别固是不可以道里计，即使同一范式的语言也是千差万别。一个纯粹的C++程序员用Java编程，会非常不习惯没有指针运算、没有内存控制、没有运算符重载、没有自由函数等等。一个纯粹的Java程序员用C++编程，会极度不适应相对贫乏的标准库；会困惑于指针、引用、数组、字符串还有头文件等的用法；会毫不吝惜地在<span class="term">堆</span>（heap)上创建新对象，并且从来不去释放它们。这些概念或用法都是贯穿整个程序始终的，差别能不大吗？”
        </p><p>
            叹号听罢叹曰：“做一个好的程序员还真不容易。”
        </p><p>
            “如果容易，我们开这个班做什么？” 冒号笑道，“提一个问题，程序员最不能离开的应用软件是什么？”
        </p><p>
            逗号顺嘴道：“当然是IDE了。”
        </p><p>
            引号纠正：“应该是编辑器，有些程序员用vi甚至记事本来写程序。”
        </p><p>
            冒号插一句：“有人还在命令行下写程序。”
        </p><p>
            叹号眼瞪得溜圆：“怎么可能？”
        </p><p>
            冒号眉毛一挑：“当然可能，一些简单的程序完全可以在命令行下完成，然后重定向（redirect）到一个文件中或者直接编译执行。这种情况虽有些极端，对于那些离开IDE就没法编程的人来说更是不可思议，但至少说明编程离开编辑器还是有可能的<a class="link" href="#note1"><sup>[1]</sup></a>。”
        </p><p>
            句号突然明白了：“是编译器！”
        </p><p>
            “不错，是编译器或解释器。”冒号作了一点修正。
        </p><p>
            问号有点茫然：“为什么谈这个呢？”
        </p><p>
            “因为编译器或解释器是语言实现的标志。”冒号提高了声调，“我想借此说明一个简单的道理：<span class="strong"><strong>对一个程序员而言，编程语言乃立身之本</strong></span>。许多人偏偏本末倒置，常常为在IDE、框架、设计工具等中挖掘到某些新功能而欣喜不已，或者津津乐道于各种语言的优劣高下，却对正在使用的语言中大量的宝藏视而不见，与执金碗而行乞者何异？这些人若有幸拜关公为师，他们最艳羡的一定他的赤兔马和青龙偃月刀，或许还会抹红脸蓄长须什么的，就是不太愿学他的盖世武功。”
        </p><p>
            引号质疑：“我明白您是想修研强调内功的重要性，但似乎有些唯语言论，设计思想不是更重要吗？”
        </p><p>
            冒号补充道：“我所谈的语言，自然不是孤立的语法和用法，也包括背后的编程范式和设计思想。当然更高层的架构设计可能会脱离具体的语言，但那不在考虑之列，因为我们谈论的主体是程序员，而不是架构师。”
        </p><p>
            提到架构师，众人神往之情油然而生。
        </p><p>
            冒号看透了大家的心思：“架构师并没有多么神秘，他们也是从程序员过来的。也不要以为架构师就不关心语言了，相反需要对语言有更广博、更深刻的认识。理想的架构师应当如文学大师，既有恢弘大气的构思，又有细腻深刻的笔法；应当如统军大帅，既有运筹帷幄的韬略，又有冲锋陷阵的武功。那些在语言与低级之间、设计与高级之间毫不犹豫地划等号的人，多半高不成低不就，既不懂语言，也不懂设计。”
        </p><p>
            逗号仍有疑惑：“语言真有那么重要吗？IDE的一些集成工具可以辅助生成许多代码，或许到了某一天，拖拖鼠标、画些UML图之类的，程序就大功告成了。”
        </p><p>
            冒号哈哈一笑：“这不就是元编程吗？元编程也是用语言实现的。且不说你描绘的美景是否可能，即便实现了，程序员的任务也是用更高级的语言做更高级的编程——还是离不开语言。别忘了，技术含金量是与工资含金量成正比的，你永远需要比别人多一门扎实的功夫。程序员这门职业，你选择，你担当。好了，闲话打住，欲知在下如何评价各种编程语言，且听下回分解。”
        </p><p>
            众人听兴正浓之际，课堂却在说书式的结语中戛然而止了。
        </p></div><div class="section" title="，插语"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="note"></a>，插语</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p><a name="note1"></a>
                    当然，命令行本身也可看作编辑器，比如Bash提供vi和emacs两种命令行编辑模式。
                </p></li></ol></div></div><div class="section" title="。总结"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="summary"></a>。总结</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    凡事因了解而喜爱，因无知而憎恶。
                </p></li><li class="listitem"><p>
                    破除语言的宗教情结，保持自我批判的勇气和精神。
                </p></li><li class="listitem"><p>
                    过分拔高一种语言与抹煞语言之间的差别是两种极端观点，皆不可取。
                </p></li><li class="listitem"><p>
                    每种语言都有其特到之处和不足之处，与其抱怨争执，不如扬长避短。
                </p></li><li class="listitem"><p>
                    编程语言在批判与赞美中逐步发展。
                </p></li><li class="listitem"><p>
                    编程语言是程序员的立身之本。切不可本末倒置，忽视语言的学习，却热衷于挖掘IDE、框架、设计工具等的新功能。
                </p></li><li class="listitem"><p>
                    语言不等于低级，设计也不等于高级。
                </p></li><li class="listitem"><p>
                    架构师同样关心语言，并且需要对语言有更深更广的认识。
                </p></li></ul></div></div><div class="section" title="“”参考"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="reference"></a>“”参考</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>
                    Bjarne Stroustrup．The Design and Evolution of C++．Reading, MA：Addison-Wesley，1994．19-25
                </p></li><li class="listitem"><p>
                    Alan Perlis．EPIGRAMS IN PROGRAMMING．<a class="link" href="http://www.cs.yale.edu/homes/perlis-alan/quotes.html" target="_top">http://www.cs.yale.edu/homes/perlis-alan/quotes.html</a>
                </p></li></ol></div></div></div>

<!-- below is edited manually -->
<strong><span style="font-family: 宋体">课后思考</span></strong>
<ul style="margin-top: 0cm; list-style-type: none">
    <li>
        05-01 程序员这个职业合适你吗？你在工作中胜任愉快吗？
    </li>
    <li>
        05-02 你认为理想的类型系统应该有哪些规则？
    </li>
    <li>
        05-03 你喜欢duck typing吗？它有何优点？如何防止它的误用和滥用？
    </li>
    <li>
        05-04 你是如何看待动态语言的？与静态语言相比，哪些是你所欣赏的，哪些是你所不满的？
    </li>
    <li>
        05-05 你最熟悉哪种编程语言？你认为它是最好的语言吗？
    </li>
    <li>
        05-06 对一门不熟悉的编程语言，你是乐于了解和学习它，还是本能地漠视或排斥它？
    </li>
    <li>
        05-07 爱可以不需要理由，恨却需要。能说说你恨某种语言的理由吗？你确信它们站得住脚吗？
    </li>
    <li>
        05-08 你是更愿意把时间花在编程工具的功能挖掘上，还是花在编程语言的深造学习上？
    </li>
</ul><p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F09%2F27%2Fcolon-class-5_4%2F&amp;title=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A75.4%EF%BC%9A%E8%AF%AD%E8%A8%80%E8%AF%AF%E5%8C%BA" id="wpa2a_30">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2011年07月12日 -- <a href="http://blog.zhenghui.org/2011/07/12/hard-thinking-and-soft-thinking-4/" title="论思维的刚性与柔性（科学的迷信-4）">论思维的刚性与柔性（科学的迷信-4）</a> (0)</li><li>2010年06月3日 -- <a href="http://blog.zhenghui.org/2010/06/03/advice-on-programmer/" title="答读者问(1)——对程序员的一些个人建议">答读者问(1)——对程序员的一些个人建议</a> (6)</li><li>2009年09月21日 -- <a href="http://blog.zhenghui.org/2009/09/21/colon-class-5_1/" title="冒号课堂§5.1：教学计划">冒号课堂§5.1：教学计划</a> (1)</li><li>2009年09月17日 -- <a href="http://blog.zhenghui.org/2009/09/17/colon-class-4_3/" title="冒号课堂§4.3：汇总范式">冒号课堂§4.3：汇总范式</a> (5)</li><li>2009年09月13日 -- <a href="http://blog.zhenghui.org/2009/09/13/colon-class-4_1/" title="冒号课堂§4.1：函数范式">冒号课堂§4.1：函数范式</a> (0)</li><li>2009年08月29日 -- <a href="http://blog.zhenghui.org/2009/08/29/colon-class-1_4/" title="冒号课堂§1.4：初识范式">冒号课堂§1.4：初识范式</a> (2)</li><li>2009年08月26日 -- <a href="http://blog.zhenghui.org/2009/08/26/colon-class-1_2/" title="冒号课堂§1.2：首轮提问">冒号课堂§1.2：首轮提问</a> (1)</li><li>2009年08月24日 -- <a href="http://blog.zhenghui.org/2009/08/24/colon-class-1_1/" title="冒号课堂§1.1：开班发言">冒号课堂§1.1：开班发言</a> (2)</li><li>2009年08月21日 -- <a href="http://blog.zhenghui.org/2009/08/21/contents-of-colonclass/" title="《冒号课堂》目录">《冒号课堂》目录</a> (29)</li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/09/27/colon-class-5_4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>冒号课堂§5.3：动态语言</title>
		<link>http://blog.zhenghui.org/2009/09/25/colon-class-5_3/</link>
		<comments>http://blog.zhenghui.org/2009/09/25/colon-class-5_3/#comments</comments>
		<pubDate>Fri, 25 Sep 2009 01:47:56 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[动态语言]]></category>
		<category><![CDATA[程序]]></category>
		<category><![CDATA[脚本]]></category>
		<category><![CDATA[脚本语言]]></category>
		<category><![CDATA[静态语言]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=455</guid>
		<description><![CDATA[<b>动态语言</b>——披着彩衣飞舞的脚本语言（<em>动态语言简谈</em>）<br/> • 程序是为终端用户服务的，而脚本是为程序员服务的<br/> • 动态语言秉承的一个理念是：优化人的时间而不是机器的时间<br/> • 待静态语言披盔戴甲、备马抬枪之际，动态语言已衣袂飘飘，长剑出手了<br/> • 当脚本语言披上动态语言的彩衣，昔日不起眼的毛毛虫便羽化成碟，开始飘舞在众人追逐的目光之中 [...]]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center"><span style="font-family: 宋体">冒号课堂</span></h1>
<strong><span style="font-size: 13pt; font-family: 宋体">第五课 语言小谈(3)</span></strong>

<!-- below comes from generated html -->
<head><link rel="stylesheet" href="http://blog.zhenghui.org/css/colonclass.css" type="text/css"></head>

<div lang="zh-CN" class="article" title="动态语言"><div class="titlepage"><div><div><h1 class="title"><a name="id565672"></a>5.3 动态语言——披着彩衣飞舞的脚本语言</h1></div><div><div class="author"><h3 class="author">郑晖</h3></div></div><div><div class="abstract" title="摘要"><p class="title"><b>摘要</b></p><p>动态语言简谈</p></div></div></div><hr /></div><div class="toc"><p><b>目录</b></p><dl><dt><span class="section"><a href="#preview">！预览</a></span></dt><dt><span class="section"><a href="#question">？提问</a></span></dt><dt><span class="section"><a href="#explaination">：讲解</a></span></dt><dt><span class="section"><a href="#note">，插语</a></span></dt><dt><span class="section"><a href="#summary">。总结</a></span></dt><dt><span class="section"><a href="#reference">“”参考</a></span></dt></dl></div><div class="epigraph"><div class="literallayout"><p>故凡天下之理，欲向动上求静，静上求动</p></div><div class="attribution"><span>—<span class="attribution">《蔡牧堂•发微论》</span></span></div></div><div class="section" title="！预览"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="preview"></a>！预览</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    程序是为终端用户服务的，而脚本是为程序员服务的
                </p></li><li class="listitem"><p>
                    动态语言秉承的一个理念是：优化人的时间而不是机器的时间
                </p></li><li class="listitem"><p>
                    待静态语言披盔戴甲、备马抬枪之际，动态语言已衣袂飘飘，长剑出手了
                </p></li><li class="listitem"><p>
                    当脚本语言披上动态语言的彩衣，昔日不起眼的毛毛虫便羽化成蝶，开始飘舞在众人追逐的目光之中
                </p></li></ul></div></div><div class="section" title="？提问"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="question"></a>？提问</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>脚本与程序的区别是什么？</p></li><li class="listitem"><p>脚本语言有什么特点？为什么适合作粘合语言？</p></li><li class="listitem"><p>动态语言有什么特点？它与脚本语言究竟有什么分别？</p></li><li class="listitem"><p>动态语言也能用于大型应用开发吗？</p></li><li class="listitem"><p>动态语言会最终取代静态语言吗？</p></li></ul></div></div><div class="section" title="：讲解"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="explaination"></a>：讲解</h2></div></div></div><p>
            叹号急不可耐地问：“现在可以谈动态语言了吧？”
        </p><p>
            冒号感言：“曾几何时，动态语言还只是陪太子读书的角色，那时候它们的名字是‘脚本语言’。近来却迅速崛起，俨然有与静态语言分庭抗礼之势。”
        </p><p>
            问号忍不住问道：“动态语言与脚本语言是一回事吗？”
        </p><p>
            “相比动态语言定义上的模糊，脚本语言的概念还是比较明确的。”冒号回避直接给出答案，“<span class="term">脚本</span>（script）的提法，是为了区别于一般的<span class="term">程序</span>（program）。Perl的发明者Larry Wall不愧为语言学家，对此有一个精彩的说法：‘A script is what you give the actors, a program is what you give the audience’。直译为：脚本是给演员看的，节目是给观众看的。此言妙在一语双关——program兼有‘节目’和‘程序’的意思。”
        </p><p>
            句号领会其意：“这里的演员指的是程序员，观众指的是用户。换言之，<span class="strong"><strong>程序是为终端用户服务的，而脚本是为程序员服务的</strong></span>。”
        </p><p>
            “正解！”冒号肯定道，“脚本最常见的形式是<span class="term">壳脚本</span>（shell script），在非Unix类的操作系统中也称为<span class="term">批处理文件</span>（batch file）。”
        </p><p>
            “批处理文件倒是很熟悉，壳脚本听起来就怪怪的。”逗号嘀咕着。
        </p><p>
            “那是因为你在Windows的世界里长大，听不惯Unix的方言。”冒号一语道破缘由，“操作系统的内核称为<span class="term">核</span>（kernel），出于安全考虑不便直接与用户交互，因此裹上一层<span class="term">壳</span>（shell），即人们常说的<span class="term">命令行解释器</span>（command line interpreter）。壳脚本是在壳上运行的脚本，扩展了命令行下可执行的命令。它最初主要是内建（built-in）命令的组合，用于<span class="emphasis"><em>系统程序</em></span>的调度，是系统管理员的必备武器。其后，壳脚本也发展到用于<span class="emphasis"><em>应用程序</em></span>的调度、连接、调试等，成为<span class="emphasis"><em>粘合</em></span>（glue）语言。”
        </p><p>
            逗号不禁有些疑问：“难道一般的程序语言如C之类的不能作此用吗？”
        </p><p>
            引号回应道：“这些语言通常需要‘编写－编译－链接－运行’的循环过程，十分繁琐。脚本语言编写后即可运行，快捷方便得多。”
        </p><p>
            冒号点点头：“不错，既然脚本主要用于整合其他程序，本身并不占用太多的资源，同时逻辑也不太复杂，因此脚本语言注重简洁、实用，语法要求不那么严格，性能上的要求也不高。除壳脚本外，还有一些专用于<span class="emphasis"><em>文本处理</em></span>（text processing）的语言或工具如AWK、sed和grep等，多用于读写配置文件和日志文件、过滤处理各种程序的输入和输出，对于整合各种程序也非常实用。随着对脚本语言需求的增长，其局限性日益突出，以Perl为代表的高级脚本语言便应运而生了。Perl在壳脚本、AWK、sed的基础上，融合了命令式的C与函数式的Lisp的特征，渐渐成为最流行的脚本语言之一。”
        </p><p>
            问号注意到：“JavaScript是浏览器端的脚本，来路似乎有些不同。”
        </p><p>
            冒号解释道：“除了命令行程序外，脚本语言在其他的应用程序中也身影频现，如图形界面应用、多媒体应用、网络应用等。尤其是网络应用，成为滋生和繁荣脚本语言最肥沃的土壤。例如：Perl非常广泛地用于网络服务器端的CGI编程；PHP更是专为动态网页而设计的语言；Ruby虽与Java同岁，但真正开始风行得益于网络应用框架Ruby on Rails的成功；至于JavaScript，长期被边缘化为网页设计人员的语言，是web2.0的新宠AJAX真正将其带入程序员的视线。”
        </p><p>
            逗号有些好奇：“什么时候脚本语言变成了动态语言呢？”
        </p><p>
            “尽管动态语言并无确切的定义，但不是所有的脚本语言都能称作动态语言的——比如Bash之类的<span class="term">壳脚本语言</span>（shell script language）；另一方面，也不是所有的动态语言天生就是为脚本服务的——比如Lisp<a class="link" href="#note1"><sup>[1]</sup></a>。不妨这么理解，脚本语言以语言的<span class="emphasis"><em>实际用途</em></span>为标志，动态语言以语言的<span class="emphasis"><em>语法特征</em></span>为标志。”冒号回答，“单从<span class="emphasis"><em>用途</em></span>上看，一个脚本语言如果不再局限于命令行工具和粘合工具，从<span class="emphasis"><em>专用语言</em></span>发展为<span class="emphasis"><em>通用语言</em></span>，并能胜任复杂的应用开发，或许更有资格归为动态语言。”
        </p><p>
            句号发现：“动态语言似乎对字符处理都特别擅长。”
        </p><p>
            冒号道：“脚本语言与一般程序一个不同之处是，它一般是面向字符而非数值的，因为字符是最通用的接口，正好发挥其粘合作用，而数值运算对性能要求较高，多由核心程序来完成。动态语言继承了这个特点，并且除了<span class="term">正则表达式</span>（regular expression）外，为字符串、数组、列表、集合、映射等常用结构提供了丰富简洁的运算，远比静态语言依赖于库（library）的方便自然得多。” 
        </p><p>
            叹号问：“我们清楚了脚本语言中‘脚本’的来历，那动态语言中‘动态’又体现在何处呢？”
        </p><p>
            “问得好！”冒号闻言，正中下怀，“再从<span class="emphasis"><em>用法</em></span>上看，动态语言能在<span class="emphasis"><em>运行中</em></span>增加或改变数据结构、函数定义、对象行为或指令流程等。如果说动态类型语言的动态体现在类型上，动态语言的动态则体现在结构和功能上。相比而言，静态语言虽然也可能实现同样的效果，但既不方便也不自然<a class="link" href="#note2"><sup>[2]</sup></a>。另外不容忽视的一点是，动态语言大多是开源的，其本身的发展也更具动态性。” 
        </p><p>
            引号非常注重理论：“动态语言的语法特征有那些？”
        </p><p>
            “动态语言秉承的一个理念是：<span class="strong"><strong>优化人的时间而不是机器的时间</strong></span>。为提高人的生产率，宁肯牺牲部分的程序性能或者购买更高配置的硬件。由于<span class="strong"><strong>硬件相对于人件一直在贬值</strong></span>，该理念便有了合理的现实基础。”冒号讲述着，“从<span class="emphasis"><em>语法</em></span>上看，动态语言为了更好地粘合来自不同系统、不同语言的程序，对类型的要求一般不如静态语言那么严格，代码更加简洁自由，故而多为动态类型的和弱类型的，天然支持<span class="term">泛型式编程</span>。当然这不是绝对的，比如Groovy也支持静态类型，Scala完全是静态类型的，Python一般认为是强类型的。大多数动态语言支持eval函数，能动态执行任意字符串形式的代码，并有丰富的反射（reflection）机制，天然支持<span class="term">元编程</span>。动态语言很多还支持包括<span class="term">高阶函数</span>（high-order function）和<span class="term">闭包</span>（closure）等在内的<span class="term">函数式编程</span>。此外，大多动态语言也支持<span class="term">对象式编程</span>，如Python、Ruby、Perl 5、PHP 3等。”
        </p><p>
            句号补充道：“许多动态语言还支持<span class="term">过程式编程</span>和<span class="term">并发式编程</span>，简直把主要的编程范式一网打尽了！”
        </p><p>
            “其实Python、Ruby和Groovy等还可以进行<span class="term">切面式编程</span>，这对于支持元编程的动态语言来说非常自然，因为切面式编程一般都是通过元编程来实现的。”冒号进一步指出，“而<span class="term">逻辑式编程</span>语言的代表Prolog，同样有动态语言的特征。至于<span class="term">事件驱动式编程</span>嘛，对支持callback的语言来说都不是难事。”
        </p><p>
            引号高兴地看到：“九大编程范式无一漏网啊！”
        </p><p>
            叹号较为感性：“静态语言给人的感觉是沉稳持重，而动态语言则活泼轻快。如果同时用静态语言和动态语言编程，岂不培养出双重人格？”
        </p><p>
            “程序员本就是双重人格的。”冒号淡淡地说，“你总结得没错，两类语言的风格的确大相异趣：待静态语言披盔戴甲、备马抬枪之际，动态语言已衣袂飘飘，长剑出手了。不过，如果是应付强敌的长期大规模作战，静态语言还是有优势的。”
        </p><p>
            引号听声辨音：“这意味着动态语言不适用大型应用开发吗？“
        </p><p>
            “这么说未免有些武断。”冒号并不同意，“诚然，动态语言的语法比较宽松，相对容易出错。但也有人辩称，动态语言的代码量少于相应的静态语言，bug应该更少。有人认为动态语言调试不如静态语言方便，有人却说随着IDE的日益强大，出错几率和找错成本也在减少。谈到运行效率，动态语言虽然多为<span class="term">解释型语言</span>（interpreted language），但许多也提供了与Java类似的<span class="term">字节码编译</span>（bytecode compilation）甚至<span class="term">JIT编译</span>(just-in-time compilation)。动态语言在某方面甚至还更胜一筹：譬如一个类的接口如果发生变动，在静态语言中所有该类的子类和一些相关类都可能需要重新编译、连接，这在大型应用中是非常耗时的，而动态语言则大可不必，这当然不足为奇——在它眼里类结构本来就是能动态改变的。除此之外，越是大型的程序，越耗费人力和时间成本，客户需求的变化也越大，因而对程序的灵活性、适应性和开发周期提出了更高的要求。动态语言在这些方面比静态语言更有优势，并且还能作为<span class="term">快速原型</span>（rapid prototyping）开发的工具。”
        </p><p>
            “快速原型开发？”问号一脸的疑惑。
        </p><p>
            冒号简作介绍：“这是一种软件开发的方式。举例来说，为了快速搭建一个系统，以适应不断变化的客户需要，可以先采用开发效率更高的动态语言。在交付时再将其转化为编译型的静态语言。如果系统对性能的要求不高，这种转化至多是局部的。有的干脆一字不易，不仅省了当下的时间，以后维护起来也更方便。”
        </p><p>
            逗号耍起了贫嘴：“这就叫：替补变成了主力，配角变成了主角，媳妇熬成了婆婆。”
        </p><p>
            叹号开始担忧起来：“听您这意思，动态语言优点突出而弱点并不突出，这样下去静态语言还有市场吗？”
        </p><p>
            冒号坦然道：“动态语言小快灵的风格的确吸引了越来越多人的注意，也渐渐走入静态语言的世界。Java平台和.NET平台不仅为Ruby和Python等动态语言铺设了跑道，而且为培植诸如Groovy等动态语言提供了土壤。同时，Java和C#本身也融进了越来越多的动态特征。”
        </p><p>
            句号断言：“静态语言这种融合性结合内在的安全性、稳定性，以及较高的性能、成熟度和接受度，都决定了它不可能被动态语言完全取代。”
        </p><p>
            “对！”冒号坚定地表示赞同，“当脚本语言披上动态语言的彩衣，昔日不起眼的毛毛虫便羽化成蝶，开始飘舞在众人追逐的目光之中。但静态语言也绝不会淡出人们的视线，它如矫健的苍鹰，依然有搏击长空的雄力。程序员只要保持严谨的作风和开放的心态，既有稳如泰山的马步，又有一跃凌空的飞腿，静如处子，动如脱兔，如履平地般游走于高高的梅花桩上，绝无跌落之虞。” 
        </p><p>
            一股豪情在众人心中荡漾开来。
        </p><p>
            冒号看了看时间，敛起眼中精光，同时收起话匣：“关于动态语言，今天还是先简单谈到这里吧。”
        </p></div><div class="section" title="，插语"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="note"></a>，插语</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p><a name="note1"></a>
                    Lisp本身以及一些变种如Emacs Lisp、AutoLISP等也能作为脚本语言，但那毕竟不是Lisp语言的初衷。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    一些设计模式（如装饰模式、职责链模式、状态模式、访问者模式等）就是为了赋予静态语言一定的动态特征。
                </p></li></ol></div></div><div class="section" title="。总结"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="summary"></a>。总结</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    程序是为终端用户服务的，脚本是为程序员服务的。
                </p></li><li class="listitem"><p>
                    脚本语言一般是解释型语言，不需要通过“编写－编译－链接－运行”的循环圈，便利快捷，加之简洁宽松的语法、面向字符的特性以及较强的文本处理能力，尤其适合作为粘合语言，多用于系统管理和集成。
                </p></li><li class="listitem"><p>
                    脚本语言与动态语言尽管并不完全重合，但更多地还是提法上的区别。前者强调作为命令行工具和粘合工具的语言用途，后者强调动态的语言特征。当脚本语言不再局限于粘合语言，从专用语言发展为通用语言，并且胜任复杂的应用开发的时候，动态语言的提法显然更加合理。
                </p></li><li class="listitem"><p>
                    动态语言能在程序运行期间改变数据结构、函数定义、对象行为或指令流程等，相比静态语言在结构和功能上的更具动态性。
                </p></li><li class="listitem"><p>
                    动态语言重在优化人工时间而非机器时间，因此相比静态语言，其开发效率较高，但运行效率较低。
                </p></li><li class="listitem"><p>
                    动态语言的以下特点决定了它在大型应用开发中的价值：代码量较少，从一定程度减轻了维护难度；不少提供了字节码编译或JIT编译，弥补了运行效率上的不足；一些模块的结构和功能上的变化不会导致相关模块的重新编译和连接；具有灵活、适应力强和开发周期短的特点，能快速响应客户需求的变化，并且适合快速原型开发。
                </p></li><li class="listitem"><p>
                    静态语言安全稳定、性能优越、成熟普及，并且逐渐开始吸纳动态语言的一些优点，这些都决定了它不可能被后者完全替代。
                </p></li></ul></div></div><div class="section" title="“”参考"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="reference"></a>“”参考</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>
                    Wikipedia．Dynamic programming language．<a class="link" href="http://en.wikipedia.org/wiki/Dynamic_language" target="_top">http://en.wikipedia.org/wiki/Dynamic_language</a>
                </p></li><li class="listitem"><p>
                    David Ascher．Dynamic Languages—ready for the next challenges, by design．<a class="link" href="http://www.activestate.com/corporate/publications/ActiveState_Dynamic_Languages.pdf" target="_top">http://www.activestate.com/corporate/publications/ActiveState_Dynamic_Languages.pdf</a>
                </p></li></ol></div></div></div><p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F09%2F25%2Fcolon-class-5_3%2F&amp;title=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A75.3%EF%BC%9A%E5%8A%A8%E6%80%81%E8%AF%AD%E8%A8%80" id="wpa2a_32">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>无相关文章</li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/09/25/colon-class-5_3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>冒号课堂§5.2：数据类型</title>
		<link>http://blog.zhenghui.org/2009/09/23/colon-class-5_2/</link>
		<comments>http://blog.zhenghui.org/2009/09/23/colon-class-5_2/#comments</comments>
		<pubDate>Wed, 23 Sep 2009 06:52:28 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[Duck类型]]></category>
		<category><![CDATA[动态类型]]></category>
		<category><![CDATA[弱类型]]></category>
		<category><![CDATA[强类型]]></category>
		<category><![CDATA[数据类型]]></category>
		<category><![CDATA[类型安全]]></category>
		<category><![CDATA[静态类型]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=445</guid>
		<description><![CDATA[<b>数据类型</b>——规则与变通（<em>关于数据类型的讨论</em>）<br/> • Duck类型的哲学是：名义不重要，重要的是能力<br/> • 将一个会叫会游的家伙放进池塘看起来不算坏主意，但如果一艘轮船趁机也轰隆隆地开了进来，事情恐怕就不那么美妙了<br/> • 静态类型检查类似“疑罪从有”的有罪推定制，动态类型检查类似“疑罪从无”的无罪推定制<br/> • 尽可能守规则，必要时求变通<br/> • 规则如裤带，过于宽松和过于束缚都不好 <!--more--> [...]]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center"><span style="font-family: 宋体">冒号课堂</span></h1>
<strong><span style="font-size: 13pt; font-family: 宋体">第五课 语言小谈(2)</span></strong>

<!-- below comes from generated html -->
<head><link rel="stylesheet" href="http://blog.zhenghui.org/css/colonclass.css" type="text/css"></head>

<div lang="zh-CN" class="article" title="数据类型"><div class="titlepage"><div><div><h1 class="title"><a name="id600625"></a>5.2 数据类型——规则与变通</h1></div><div><div class="author"><h3 class="author">郑晖</h3></div></div><div><div class="abstract" title="摘要"><p class="title"><b>摘要</b></p><p>关于数据类型的讨论</p></div></div></div><hr /></div><div class="toc"><p><b>目录</b></p><dl><dt><span class="section"><a href="#preview">！预览</a></span></dt><dt><span class="section"><a href="#question">？提问</a></span></dt><dt><span class="section"><a href="#explaination">：讲解</a></span></dt><dt><span class="section"><a href="#note">，插语</a></span></dt><dt><span class="section"><a href="#summary">。总结</a></span></dt><dt><span class="section"><a href="#reference">“”参考</a></span></dt></dl></div><div class="epigraph"><div class="literallayout"><p>操纵于规矩之中，神明于规矩之外</p></div><div class="attribution"><span>—<span class="attribution">《俞震•古今医案按》</span></span></div></div><div class="section" title="！预览"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="preview"></a>！预览</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    Duck类型的哲学是：名义不重要，重要的是能力
                </p></li><li class="listitem"><p>
                    将一个会叫会游的家伙放进池塘看起来不算坏主意，但如果一艘轮船趁机也轰隆隆地开了进来，事情恐怕就不那么美妙了
                </p></li><li class="listitem"><p>
                    静态类型检查类似“疑罪从有”的有罪推定制，动态类型检查类似“疑罪从无”的无罪推定制
                </p></li><li class="listitem"><p>
                    尽可能守规则，必要时求变通
                </p></li><li class="listitem"><p>
                    规则如裤带，过于宽松和过于束缚都不好
                </p></li></ul></div></div><div class="section" title="？提问"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="question"></a>？提问</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>动态语言与动态类型语言是一回事吗？</p></li><li class="listitem"><p>数据类型有哪两个要素？其意义何在？</p></li><li class="listitem"><p>什么是动态类型和静态类型？它们的区别是什么？各有什么优缺点？</p></li><li class="listitem"><p>什么是鸭子类型（duck typing）？它有什么优缺点？</p></li><li class="listitem"><p>什么是强类型与弱类型？什么是类型安全的？</p></li></ul></div></div><div class="section" title="：讲解"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="explaination"></a>：讲解</h2></div></div></div><p>
            待教室平静下来，冒号再度开腔：“在谈论动态语言之前，最好先澄清一下它与动态类型语言之间的区别。”
        </p><p>
            叹号讶然道：“它们不是一回事吗？一直以为动态语言是动态类型语言的简称呢。”
        </p><p>
            “有亲戚之名，却无血缘之亲。名称上相似，加之动态语言绝大多数确是动态类型语言，造成混淆实属在所难免，但二者之间并无必然联系——动态语言不一定是动态类型语言<a class="link" href="#note1"><sup>[1]</sup></a>，动态类型语言也不一定是动态语言<a class="link" href="#note2"><sup>[2]</sup></a>。”冒号飞跑的舌头几乎绊蒜，同时把众人的脑子搅成了一锅粥。
        </p><p>
            见势不妙，冒号改用迂回战术：“我们不妨再谈开些，大家对<span class="term">数据类型</span>是如何理解的？”
        </p><p>
            逗号随口道：“数据类型不就是数据的种类吗？”
        </p><p>
            众人暗笑：说了跟没说差不多。
        </p><p>
            冒号说道：“数据类型包含两个要素：一个是<span class="emphasis"><em>允许取值的集合</em></span>，一个是<span class="emphasis"><em>允许参与的运算</em></span>。例如int类型在Java中既定义了介于-2<sup>31</sup>和2<sup>31</sup> &#8211; 1之间的整数集合，也定义了该集合上的整数所能进行的运算。现在的问题是：数据类型的意义何在？”
        </p><p>
            句号回答：“限定一个变量的数据类型，就意味着限制了该变量的取值范围和所参与的运算，这从一定程度上保证了代码的<span class="emphasis"><em>安全性</em></span>。”
        </p><p>
            冒号追问：“还有吗？”
        </p><p>
            句号略作思考后说：“用户自定义的数据类型，如C中的结构和Java中的类或接口，赋予数据以逻辑内涵，提高了代码的<span class="emphasis"><em>抽象性</em></span>。”
        </p><p>
            “精辟！”冒号赞道，“数据类型既有针对机器的<span class="emphasis"><em>物理意义</em></span>，又有针对人的<span class="emphasis"><em>逻辑意义</em></span>。前者用于进行<span class="emphasis"><em>底层</em></span>的内存分配和数值运算等，后者用于表达<span class="emphasis"><em>高层</em></span>的逻辑概念。既然类型如此重要，类型检查就必不可少了<a class="link" href="#note3"><sup>[3]</sup></a>。所谓<span class="term">动态类型语言</span>（dynamic typing language），正是指类型检查发生在<span class="emphasis"><em>运行期间</em></span>（run-time）的语言。”
        </p><p>
            “那<span class="term">静态类型语言</span>（static typing language）自然是类型检查发生在<span class="emphasis"><em>编译期间</em></span>（compile-time）的语言咯。”引号接话道。
        </p><p>
            冒号回应：“一般的说法是这样，但我更愿意将‘编译期间’四个字改为‘运行之前’，否则容易让人<span class="emphasis"><em>误解</em></span>为静态类型语言一定是<span class="term">编译型语言</span>（compiled language）。”
        </p><p>
            问号问道：“是否可以这么说：静态类型语言需要变量声明，而动态类型语言则不需要？”
        </p><p>
            “这话只对了一半。”冒号评论，“动态类型语言固然不需要显式的变量声明（explicit declaration），一些静态类型语言有时也不需要。典型的如ML、Haskell之类的函数式语言，编译器可以通过上下文来进行<span class="emphasis"><em>类型推断</em></span>（type inference）。另外，C#从3.0起也开始支持局部变量的类型推断。”
        </p><p>
            “如何进行类型推断？”问号有点丈二和尚摸不着头脑。
        </p><p>
            冒号打了个比方：“假设‘+’号只限于同类型的数据运算，那么从表达式a + 1中可以推出a是整型变量，从b + 1.0中推出b是浮点型变量，从c + “1”中推出c是字符串型变量。这些变量不必事先声明，但一旦类型被推断确定后，便不再更改。由于这些推断都是在程序运行之前进行的，因此仍属于静态类型。它既有动态类型的简洁性，又不失声明式静态类型的安全性，可谓裁长补短啊。”
        </p><p>
            叹号有些羡慕地说：“还是动态类型语言好，不仅不必声明变量，而且一个变量在不同地方还可以代表不同类型，多省事多方便啊！”
        </p><p>
            冒号微微颔首：“虽然这种机制也有为人诟病之处，但不可否认，动态类型语言的确有它的优势：简明、快捷、灵活，并且天然具有泛型（generic）特征。值得一提的是，动态类型有一种被称作<span class="term">鸭子类型</span>（duck typing）的形式。”
        </p><p>
            逗号感到有趣：“鸭子类型？很滑稽的名字。”
        </p><p>
            “这种类型通俗的说法是：如果一个对象既会走鸭步又会呷呷叫，何妨将其视作鸭子呢？”冒号说着投影出一段Ruby代码——
        </p><div class="informalexample"><pre class="programlisting">
class Duck                      #会叫会游的鸭
    def shout
        puts '呷呷呷'
    end
    def swim
        puts '鸭泳'
    end
end

class Frog                      #会叫会游的蛙
    def shout
        puts '呱呱呱'
    end
    def swim
       puts '蛙泳'
    end
end

def shoutAndSwim(duck)          #让一只会叫会游的家伙边叫边游
    duck.shout
    duck.swim
end

shoutAndSwim(Duck.new)          #让一只鸭边叫边游
shoutAndSwim(Frog.new)          #让一只蛙边叫边游 </pre></div><p>
            冒号继续讲解：“在Smalltalk、Python和Ruby等动态类型的OOP语言中，只要一个类型具有shout和swim的方法，它就可以为shoutAndSwim所接受。这在C++、Java、C#等静态类型语言中是不可能的<a class="link" href="#note4"><sup>[4]</sup></a>，除非鸭和蛙在同一继承树上，或者二者均<span class="emphasis"><em>显式</em></span>实现了一个包含shout和swim的公用接口。”
        </p><p>
            句号敏锐地指出：“C++是静态类型语言，但它的模板也可实现类似功能，并不需要引入继承关系。”
        </p><p>
            “说得很对！但请接着看下去。”冒号又放出一段投影——
        </p><div class="informalexample"><pre class="programlisting">
class Cock                      #会叫不会游的鸡
    def shout
        puts '喔喔喔'
    end
end
 
class Fish                      #会游不会叫的鱼
    def swim
        puts '自由泳'
    end
end

def shoutOrSwim(duck, flag)     #让一只会叫或会游的家伙叫或游
    flag ? duck.shout : duck.swim
end

shoutOrSwim(Cock.new, true)     #让一只鸡叫
shoutOrSwim(Fish.new, false)    #让一只鱼游 </pre></div><p>
            “这里鸡没有swim的方法，鱼没有shout的方法。若采用C++的模板，shoutOrSwim是无法通过编译的。但在支持Duck 类型的语言中，只要在<span class="emphasis"><em>运行期间</em></span>不让鸡swim、让鱼shout——除非你突发奇想——一切平安无事。”冒号作了个OK的手势。
        </p><p>
            “动态类型语言真是越看越可爱。”叹号简直垂涎欲滴了。
        </p><p>
            “Duck类型的哲学是：名义不重要，重要的是能力，颇有些实用主义的味道。这种<span class="emphasis"><em>非继承性多态</em></span>为软件重用开启了新的窗口，同时也埋下了一些陷阱。由于Duck类型的接口组合是<span class="emphasis"><em>隐性的</em></span>，其使用者需要比普通interface更小心以避免误用；其维护者也需要更小心以避免破坏客户代码；另外它也可能造成滥用——将一个会叫会游的家伙放进池塘看起来不算坏主意，但如果一艘轮船趁机也轰隆隆地开了进来，事情恐怕就不那么美妙了。”
        </p><p>
            众皆莞尔。
        </p><p>
            “再来看看静态类型语言的好处：由于在运行之前进行了类型检查，一方面代码的可靠性增强，符合‘发现错误要尽早’的原则；另一方面编译器有可能藉此优化机器代码以提高运行效率，同时相比前者节省了运行期的耗费在类型检查上的时间和空间。此外，变量类型的声明彰显了编程者的意图，有辅助文档的功效。”冒号一五一十地介绍着，“两种类型的体制可以用两种法律原则来类比：<span class="strong"><strong>静态类型检查类似‘疑罪从有’的有罪推定制</strong></span>——在被证明合法之前是非法的，<span class="strong"><strong>动态类型检查类似‘疑罪从无’的无罪推定制</strong></span>——在被证明非法之前是合法的。至于如何取舍，套用一句话：‘Static Typing Where Possible, Dynamic Typing When Needed’。不妨理解为：<span class="strong"><strong>尽可能守规则，必要时求变通</strong></span>。”
        </p><p>
            句号俏皮地说：“规则如裤带，过于宽松和过于束缚都不好。”
        </p><p>
            问号提出新问题：“动态类型语言与弱类型语言有何不同？”
        </p><p>
            冒号喟言：“它们也常常被混为一谈，但类型的动静与强弱完全是<span class="emphasis"><em>正交</em></span>的两个概念。静态类型语言中，有强类型的Java，也有弱类型的C；动态类型语言中，有强类型的Smalltalk，也有弱类型的JavaScript。前者以类型的<span class="emphasis"><em>绑定（binding）时间</em></span>来划分，后者以类型的<span class="emphasis"><em>约束强度</em></span>来划分。通常<span class="term">弱类型语言</span>（weakly-typed language）允许一种类型的值<span class="emphasis"><em>隐性</em></span>转化为另一种类型<a class="link" href="#note5"><sup>[5]</sup></a>。举个例子，1＋&#8221;2&#8243;在VB中等于3——第二个字符串转化为整数；在JavaScript中等于&#8221;12&#8243;——第一个整数转化为字符串；在C中则等于一个不定的整数值——第二个字符串作为地址来运算。这样似乎很有趣很方便，但程序容易藏污纳垢，滋生臭虫（bug）。与此相对地，<span class="term">强类型语言</span>（strongly-typed language）着意贯彻类型控制，为保障数据的完整性和代码的安全有效性，一般不允许隐性类型转换<a class="link" href="#note6"><sup>[6]</sup></a>。如果一定需要类型转换，必须是显性转换，一般通过我们熟知的<span class="term">铸型</span>（cast）来完成。”
        </p><p>
            引号想起：“好像还有一种所谓的类型安全语言？”
        </p><p>
            逗号紧紧抱着头，仿佛害怕裂开。
        </p><p>
            “类型按<span class="emphasis"><em>安全性</em></span>来划分，可分为<span class="term">类型安全语言</span>（type-safe language）和<span class="term">类型不安全语言</span>（type-unsafe language）。类型检查的目的就是为了避免类型错误（type error）<a class="link" href="#note7"><sup>[7]</sup></a>，即杜绝因类型问题而产生的错误或不良代码。如果一个类型系统能完全做到这一点，它就被称为类型安全的。虽然尚存争议，但一般认为强类型语言对类型控制更严格，因而是类型安全的，弱类型语言是类型不安全的。类型安全固然对保障程序的合理性和可靠性十分重要，但若过于严苛，程序也就失去了活力，正所谓‘水至清则无鱼’啊。” 冒号有条不紊地解说着，“至此，我们已论及数据类型的三种划分方式。需要说明的是，这些划分并非泾渭分明的<a class="link" href="#note8"><sup>[8]</sup></a>，更多的是定性而非定量的描述，甚至没有公认统一的定义。但了解它们，对我们理解编程语言和编程原则是大有裨益的。”
        </p></div><div class="section" title="，插语"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="note"></a>，插语</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p><a name="note1"></a>
                    Scala是动态语言，却是静态类型的。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    Visual Basic（不包括VB.NET） 支持动态类型，却是静态语言。
                </p></li><li class="listitem"><p><a name="note3"></a>
                    极少数语言没有类型检查（untyped或typeless），如大多数汇编语言、Forth语言等。
                </p></li><li class="listitem"><p><a name="note4"></a>
                    C#4.0将支持duck typing。
                </p></li><li class="listitem"><p><a name="note5"></a>
                    隐式转换也称为强制转换（coercion）。有人将显式转换的铸型（cast）译为强制转换，并不准确。
                </p></li><li class="listitem"><p><a name="note6"></a>
                    但许多强类型语言对于宽转换（widening conversion）还是允许隐性的，如必要时int可自动转换为float。
                </p></li><li class="listitem"><p><a name="note7"></a>
                    典型的类型错误是：一个函数本来期待的参数类型是A，实际传入的变量a却不是A类型或其兼容类型。
                </p></li><li class="listitem"><p><a name="note8"></a>
                    比如，静态类型的OOP语言如C++、Java、C#等支持多态类型以及downcasting，能在运行期间进一步细化数据类型，从某种意义上也具有动态类型的特征。
                </p></li></ol></div></div><div class="section" title="。总结"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="summary"></a>。总结</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    尽管动态语言大多数是动态类型语言，但二者并不是一回事。
                </p></li><li class="listitem"><p>
                    数据类型包含两个要素：允许取值的集合和允许参与的运算。
                </p></li><li class="listitem"><p>
                    数据类型既有针对机器的物理意义，又有针对人的逻辑意义，提高了代码的安全性和抽象性。
                </p></li><li class="listitem"><p>
                    动态类型的类型检查发生在运行期间，静态类型的类型检查发生在编译期间（运行之前）。
                </p></li><li class="listitem"><p>
                    动态类型的变量不需要显式声明，静态类型的变量需要通过显式声明或类型推断。
                </p></li><li class="listitem"><p>
                    鸭子类型是动态类型的一种风格，允许非继承性多态，即一个对象的类型可以由其接口集合来确定，不需要通过显式继承。它有利于代码重用，但也可能造成误用和滥用。
                </p></li><li class="listitem"><p>
                    动态类型语言的优点：代码简明灵活、易于重用，适合泛型编程和快速原型开发。
                </p></li><li class="listitem"><p>
                    静态类型语言的优点：运行之前的类型检查增强了代码的可靠性，使编译器有可能进行优化处理从而提高运行效率，节省了运行期的类型检查所占用的时间和空间，同时类型声明有辅助文档的功效。
                </p></li><li class="listitem"><p>
                    静态类型检查实行“疑罪从有”的有罪推定制，动态类型检查实行“疑罪从无”的无罪推定制。取舍的原则是：Static Typing Where Possible， Dynamic Typing When Needed。即尽可能守规则，必要时求变通。
                </p></li><li class="listitem"><p>
                    类型的动静以类型的绑定时间来划分，类型的强弱以类型的约束强度来划分，它们之间没有必然联系。弱类型语言允许类型的隐性转化，被认为是类型不安全的；而强类型语言则一般不允许这种转化，被认为是类型安全的。
                </p></li></ul></div></div><div class="section" title="“”参考"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="reference"></a>“”参考</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>
                    Wikipedia．Type system．<a class="link" href="http://en.wikipedia.org/wiki/Type_system" target="_top">http://en.wikipedia.org/wiki/Type_system</a>
                </p></li><li class="listitem"><p>
                    Erik Meijer，Peter Drayton．Static Typing Where Possible, Dynamic Typing When Needed．<a class="link" href="http://research.microsoft.com/~emeijer/Papers/RDL04Meijer.pdf" target="_top">http://research.microsoft.com/~emeijer/Papers/RDL04Meijer.pdf</a>
                </p></li><li class="listitem"><p>
                    Ravi Sethi．Programming Languages: Concepts &amp; Constructs(英文版第2版)．北京：机械工业出版社，2002．136-143
                </p></li></ol></div></div></div><p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F09%2F23%2Fcolon-class-5_2%2F&amp;title=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A75.2%EF%BC%9A%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B" id="wpa2a_34">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>无相关文章</li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/09/23/colon-class-5_2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>冒号课堂§5.1：教学计划</title>
		<link>http://blog.zhenghui.org/2009/09/21/colon-class-5_1/</link>
		<comments>http://blog.zhenghui.org/2009/09/21/colon-class-5_1/#comments</comments>
		<pubDate>Mon, 21 Sep 2009 02:39:08 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[程序员]]></category>
		<category><![CDATA[编程范式]]></category>
		<category><![CDATA[迭代学习法]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=440</guid>
		<description><![CDATA[<b>教学计划</b>——接下来的故事（<em>讨论下一步教学计划</em>）<br/> • 编程水平的提升之道是：在实战中演练招法，在招法中领会心法，心法反过来提升招法，进而提高实战水平，如此循环往复呈螺旋式上升过程。正所谓熟能生巧，巧能生通<br/> • 迭代学习法：即在具体知识与抽象理论之间进行折返式学习<br/> • 网页的迷人之处就在于，能够用精美的画皮来包裹冗长低效的代码<br/> • 无论干哪一行，要想胜任愉快，离不开四样东西：才能、兴趣、方法和努力 [...]]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center;"><span style="font-family: 宋体;">冒号课堂</span></h1>
<strong><span style="font-size: 13pt; font-family: 宋体;">第五课 语言小谈（1）</span></strong>

<p><strong><span style="font-family: 宋体;">课前导读</span></strong></p>
<p style="text-indent: 18pt;"><span style="font-family: 宋体;">本课承上启下，为今后的学习作一些铺垫。在确定教学计划后，对数据类型和动态语言作了简单的介绍，并对有关编程语言的一些观点作出评论。</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 18pt;"><span style="font-family: 宋体;">本课共分四节——</span></p>
<p style="text-indent: 18pt;"><em>1.</em><em><span style="font-family: 宋体;">教学计划——接下来的故事</span></em></p>
<p style="text-indent: 18pt;"><em>2.</em><em><span style="font-family: 宋体;">数据类型——规则与变通</span></em></p>
<p style="margin-left: 18pt;"><em>3.</em><em><span style="font-family: 宋体;">动态语言——穿着彩衣飞舞的脚本语言</span></em></p>
<p style="margin-left: 18pt;"><em>4.</em><em><span style="font-family: 宋体;">语言误区——语言的宗教情结</span></em></p>

<!-- below comes from generated html -->
<head><link rel="stylesheet" href="http://blog.zhenghui.org/css/colonclass.css" type="text/css"></head>

<div lang="zh-CN" class="article" title="教学计划"><div class="titlepage"><div><div><h1 class="title"><a name="id600625"></a>5.1 教学计划——接下来的故事</h1></div><div><div class="author"><h3 class="author">郑晖</h3></div></div><div><div class="abstract" title="摘要"><p class="title"><b>摘要</b></p><p>讨论下一步教学计划</p></div></div></div><hr /></div><div class="toc"><p><b>目录</b></p><dl><dt><span class="section"><a href="#preview">！预览</a></span></dt><dt><span class="section"><a href="#question">？提问</a></span></dt><dt><span class="section"><a href="#explaination">：讲解</a></span></dt><dt><span class="section"><a href="#note">，插语</a></span></dt><dt><span class="section"><a href="#summary">。总结</a></span></dt><dt><span class="section"><a href="#reference">“”参考</a></span></dt></dl></div><div class="epigraph"><div class="literallayout"><p>褚小者不可以怀大，绠短者不可以汲深</p></div><div class="attribution"><span>—<span class="attribution">《庄子•至乐》</span></span></div></div><div class="section" title="！预览"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="preview"></a>！预览</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    编程水平的提升之道是：在实战中演练招法，在招法中领会心法，心法反过来提升招法，进而提高实战水平，如此循环往复呈螺旋式上升过程。正所谓熟能生巧，巧能生通
                </p></li><li class="listitem"><p>
                    迭代学习法：即在具体知识与抽象理论之间进行折返式学习
                </p></li><li class="listitem"><p>
                    网页的迷人之处就在于，能够用精美的画皮来包裹冗长低效的代码
                </p></li><li class="listitem"><p>
                    无论干哪一行，要想胜任愉快，离不开四样东西：才能、兴趣、方法和努力
                </p></li></ul></div></div><div class="section" title="？提问"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="question"></a>？提问</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>什么是迭代学习法？</p></li><li class="listitem"><p>一个合格的程序员需要学习和掌握不同领域的许多知识，如何能胜任愉快？</p></li></ul></div></div><div class="section" title="：讲解"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="explaination"></a>：讲解</h2></div></div></div><p>
            新课开始了，冒号一反常态，并没有直奔主题：“在谈论新话题之前，先请诸位畅所欲言，谈谈这段时间的学习感受。”
        </p><p>
            叹号表情有点复杂：“通过前面的学习，让我长了不少见识，只是——”
        </p><p>
            “但讲无妨。” 见叹号有些吞吞吐吐，冒号鼓励道。
        </p><p>
            “只是觉得有点——纸上谈兵。”叹号鼓足勇气说出心里话。
        </p><p>
            引号不以为然：“我倒觉得应该更深入地去了解编程范式，现在有了一些感性认识，但还非常肤浅，希望以后能进一步展开。”
        </p><p>
            逗号挺实在：“有些地方似懂非懂，听起来挺费劲的，直到情景编程才觉得轻松了些。以后可不可以多谈些具体的编程知识、编程技巧和编程经验？”
        </p><p>
            句号拍了拍他的肩膀：“你想学的是招法，老冒传的是心法。”
        </p><p>
            逗号不服：“没有招法再多心法也白搭——光说不练假把式。”
        </p><p>
            句号反驳：“只关注招法，境界永远得不到提升——光练不说傻把式。”
        </p><p>
            “二位请暂停争论。”冒号把头转向问号，“你怎么看？”
        </p><p>
            问号很干脆：“管它心法还是招法，能解决问题的就是好法。”
        </p><p>
            “你倒滑头，整个一白猫黑猫论嘛！”冒号哈哈一笑，“首先，编程范式绝非中看不中用的屠龙之术，它有助于我们更快速地掌握、更深刻地理解、更纯熟地运用编程语言，故有心法之谓。其次，心法只有通过招法才能落到实处，也只有通过招法才能融会贯通。”
        </p><p>
            “那传说中的‘无招胜有招’呢？”引号问。
        </p><p>
            冒号哂道：“武侠小说看多了，容易想入非非，那种境界岂是一般人所能达到的？对绝大多数人来说，无招就意味着自己没招而将中他人之招。”
        </p><p>
            众人窃笑。
        </p><p>
            “还有一样是至关重要的。”冒号提醒道，“那就是实战。”
        </p><p>
            句号深有体会：“以前在学校里编程似乎还得心应手，到了公司就时感力有不逮。”
        </p><p>
            “花拳绣腿对付小喽啰绰绰有余，真碰到高手自然漏洞百出了。”冒号直言道，“编程水平的提升之道是：在实战中演练招法，在招法中领会心法，心法反过来提升招法，进而提高实战水平，如此循环往复呈螺旋式上升过程。正所谓<span class="strong"><strong>熟能生巧，巧能生通</strong></span>。”
        </p><p>
            问号询道：“下面我们的主题是什么？”
        </p><p>
            冒号亮出他的一套学习理论：“软件工程中有个<span class="emphasis"><em>迭代开发法</em></span>，本班则采用<span class="emphasis"><em>迭代学习法</em></span>：即在具体知识与抽象理论之间进行折返式学习。当然这种迭代不是机械式的重复，而是<span class="emphasis"><em>增量式的循环</em></span>。假定你们以前更关注具体的编程语言，那么遵循这种方式，先介绍抽象的编程范式是合适的。在初步了解范式之后，不妨重新回到编程语言上来。”
        </p><p>
            叹号唯唯连声：“是啊，在空中飘久了，会染上恐高症的。”
        </p><p>
            冒号笑着警告：“不要高兴太早，着陆后我们还会再次起飞的——别忘了我们的迭代式学习是周而复始的。至于眼下谈什么，还是先征求各位的意见，这样开放式教学才名副其实嘛。”
        </p><p>
            众人开始交头接耳、七嘴八舌地议论起来。
        </p><p>
            一阵商讨之后，大家似乎未能达成共识。冒号见状，便让他们一一道来。
        </p><p>
            问号再次充当急先锋：“能不能比较一些当今主流语言各自的优缺点？”
        </p><p>
            冒号笑言：“我怎么恍惚间又回到了第一堂课？你的潜台词还是那句话：到底学哪种语言好？”
        </p><p>
            问号被窥破心事，微露窘色。
        </p><p>
            “不过我非常理解你们的想法。”冒号体谅道，“虽然这是编程中最易提出却又最难回答的问题，但考虑到大家对它如此兴致盎然，我决定不顾引火烧身之危，铤而走险一回。”
        </p><p>
            众人鼓掌。
        </p><p>
            冒号故作疑惑：“你们这是对问题的答案表示期待呢，还是对我的勇气表示赞赏？”
        </p><p>
            众皆笑曰：“兼而有之，兼而有之！”
        </p><p>
            叹号提出：“近来动态语言非常流行，能说说它与静态语言到底有何不同，是否会取而代之？”
        </p><p>
            “嗯，这个问题总算简单了些。”冒号如释重负。
        </p><p>
            逗号坚持道：“我还是那个建议，希望学些具体的编程知识和技巧，比如将最流行的Java语言中的一些重点和难点分几个专题来讨论。”
        </p><p>
            冒号颔许：“这是个很好的建议，可以采纳。”
        </p><p>
            引号有不同意见：“Java没有C++来劲：要说难点，C++多得多；要说流行度，按照TIOBE的数据，C与C++之和还超过Java六个百分点呢。”
        </p><p>
            逗号不服：“你没看到C和C++正在逐渐没落吗？”
        </p><p>
            引号冷哼一声：“开玩笑，什么时候操作系统、数据库、游戏软件和嵌入式系统都改用Java了再说这话。别忘了，Java的虚拟机都还是C或C++写成的呢。”
        </p><p>
            冒号忙止住干戈：“我还没来得及成为众矢之的呢，你们二位倒先掐上了。”
        </p><p>
            句号提议：“最好找一个项目实例，从头至尾演练一次，既能贯穿各个知识点，又能让我们对软件开发有个整体认识。”
        </p><p>
            “这个想法听起来非常不错。”冒号沉吟了一会又道，“只是一个真正企业级的项目，涉及面太广。比如一个完整的web应用，不论是采用重量级的Java EE或.NET技术，还是采用轻量级的Perl、PHP、Ruby、Python等动态语言技术，除了要掌握各自的主体语言外，还涉及到相应的框架、集成环境和各种工具，以及JavaScript、CSS、HTML和XML等技术，同时数据库的知识也是不可或缺的。”
        </p><p>
            问号奇道：“JavaScript、CSS和HTML这些不主要是网页设计人员的语言吗？”
        </p><p>
            冒号解释：“网页设计人员大多用Dreamweaver之类的工具来设计HTML页面，开发一般网站尚可勉力而为，若开发企业级软件则有些力不从心了。更何况web应用的趋势是具有更丰富用户体验的Rich Internet application （RIA），采用大量的AJAX、FLEX等技术，需要熟悉Javascript或Actionscript之类的语言，这些就更非一般网页设计人员所能胜任。”
        </p><p>
            引号插言：“据我所知，许多公司都是请网页设计师来编写HTML和Javascript等代码的。”
        </p><p>
            冒号指出：“网页的迷人之处就在于，能够用精美的画皮来包裹冗长低效的代码。出于软件开发的时间和成本的考虑，公司的选择无可非议，但如果要提高软件竞争力，这些代码至少要经过程序员的加工处理。”
        </p><p>
            逗号复言：“那数据库总该是数据库管理员的事吧？程序员至多用到hibernate、iBATIS之类的ORM<a class="link" href="#note1"><sup>[1]</sup></a>框架。”
        </p><p>
            冒号断然否定：“数据库绝不只是DBA的事，ORM也不能取代数据库的设计和SQL的使用。另外，复杂的应用需要编写大量的<span class="term">存储过程</span>（stored procedure），故还应掌握PL/SQL或Transact-SQL等扩展数据库语言。换句话说，从web开发最前端的网页到最后端的数据库，都应该有程序员的身影。”
        </p><p>
            句号联想到：“借用前面餐馆的例子，不妨把接待员看作<span class="term">客户层</span>（client tier），把服务员看作<span class="term">表现层</span>（presentation tier），把厨师看作<span class="term">业务层</span>（business tier），把收银员看作<span class="term">数据层</span>（data tier），把厨工看作常用的<span class="term">辅助类</span>（helper class）<a class="link" href="#note2"><sup>[2]</sup></a>。”
        </p><p>
            冒号微微点头：“比较靠谱。要说不当之处，就是服务器端的表现层不会如服务员那样直接与客户打交道，此外业务层常与数据层打交道，但厨师好像不会与收银员有什么瓜葛。”
        </p><p>
            叹号夸张地捂住脸：“天哪，程序员竟然身兼五职，太惨了吧？”
        </p><p>
            “当然程序员会各有分工，不过如果你总局限某一层的应用开发，今后凭什么谋取更高的职位？”冒号苦口婆心，“<span class="strong"><strong>无论干哪一行，要想胜任愉快，离不开四样东西：才能、兴趣、方法和努力。没有才能则难以胜任；没有兴趣则难以愉快；没有方法则事倍功半；没有努力则一事无成</strong></span>。我相信好的方法最终能激发人的才能、兴趣和努力，这也是本班的一个理念。希望大家能通过各种问题的讨论，获得一些方法上的启示。”
        </p><p>
            问号急切地询问：“那么我们的下一步是什么？”
        </p><p>
            “综合各位的意见，我们从简到繁。”冒号公布计划，“先简单谈谈动态语言；再对主流语言作简评；然后范式上以OOP为主，语言上以C++、Java和C#为主，作一些专题讨论；最后如果时间允许，找一个项目来实践一番。”
        </p><p>
            “呕耶！”众人皆大欢喜。
        </p></div><div class="section" title="，插语"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="note"></a>，插语</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p><a name="note1"></a>
                    ORM（Object-Relational Mapping）是一种编程技术，能将OOP中的对象模型映射到数据库的关系模型。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    传统的三层架构为：表现层、业务层和数据层。其中表现层可进一步分为客户端的客户层和服务器端的表现层，数据层有时用集成层（integration tier）和资源层（resource tier）代替。
                </p></li></ol></div></div><div class="section" title="。总结"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="summary"></a>。总结</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    所谓迭代学习法，是指在具体知识与抽象理论之间进行增量式的循环学习。
                </p></li><li class="listitem"><p>
                    一个合格的程序员不应只局限某一层的应用开发。
                </p></li><li class="listitem"><p>
                    要想工作胜任愉快，才能、兴趣、方法和努力缺一不可。一套好的方法可以激发才能、兴趣和努力。
                </p></li></ul></div></div><div class="section" title="“”参考"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="reference"></a>“”参考</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>
                    Deepak Alur，John Crupi，Dan Malks．Core J2EE Patterns: Best Practices and Design Strategies．Upper Saddle River, NJ：Prentice Hall PTR，2003．120-121
                </p></li></ol></div></div></div><p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F09%2F21%2Fcolon-class-5_1%2F&amp;title=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A75.1%EF%BC%9A%E6%95%99%E5%AD%A6%E8%AE%A1%E5%88%92" id="wpa2a_36">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2009年08月21日 -- <a href="http://blog.zhenghui.org/2009/08/21/contents-of-colonclass/" title="《冒号课堂》目录">《冒号课堂》目录</a> (29)</li><li>2010年06月3日 -- <a href="http://blog.zhenghui.org/2010/06/03/advice-on-programmer/" title="答读者问(1)——对程序员的一些个人建议">答读者问(1)——对程序员的一些个人建议</a> (6)</li><li>2009年09月27日 -- <a href="http://blog.zhenghui.org/2009/09/27/colon-class-5_4/" title="冒号课堂§5.4：语言误区">冒号课堂§5.4：语言误区</a> (0)</li><li>2009年09月19日 -- <a href="http://blog.zhenghui.org/2009/09/19/colon-class-4_4/" title="冒号课堂§4.4：情景范式">冒号课堂§4.4：情景范式</a> (0)</li><li>2009年09月17日 -- <a href="http://blog.zhenghui.org/2009/09/17/colon-class-4_3/" title="冒号课堂§4.3：汇总范式">冒号课堂§4.3：汇总范式</a> (5)</li><li>2009年09月15日 -- <a href="http://blog.zhenghui.org/2009/09/15/colon-class-4_2/" title="冒号课堂§4.2：逻辑范式">冒号课堂§4.2：逻辑范式</a> (0)</li><li>2009年09月13日 -- <a href="http://blog.zhenghui.org/2009/09/13/colon-class-4_1/" title="冒号课堂§4.1：函数范式">冒号课堂§4.1：函数范式</a> (0)</li><li>2009年09月11日 -- <a href="http://blog.zhenghui.org/2009/09/11/colon-class-3_4/" title="冒号课堂§3.4：事件驱动">冒号课堂§3.4：事件驱动</a> (2)</li><li>2009年09月10日 -- <a href="http://blog.zhenghui.org/2009/09/10/colon-class-3_3/" title="冒号课堂§3.3：切面范式">冒号课堂§3.3：切面范式</a> (4)</li><li>2009年09月9日 -- <a href="http://blog.zhenghui.org/2009/09/09/colon-class-3_2/" title="冒号课堂§3.2：超级范式">冒号课堂§3.2：超级范式</a> (2)</li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/09/21/colon-class-5_1/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>冒号课堂§4.4：情景范式</title>
		<link>http://blog.zhenghui.org/2009/09/19/colon-class-4_4/</link>
		<comments>http://blog.zhenghui.org/2009/09/19/colon-class-4_4/#comments</comments>
		<pubDate>Sat, 19 Sep 2009 04:21:30 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[情景编程]]></category>
		<category><![CDATA[编程范式]]></category>
		<category><![CDATA[规则引擎]]></category>
		<category><![CDATA[闭包]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=432</guid>
		<description><![CDATA[<b>情景范式</b>——餐馆里的编程范式（<em>编程范式在餐馆中的应用</em>）<br/> • 厨师只需提供一种服务：把纸上菜变成盘中菜，至于蒸、煮、炒、炖等具体做法纯属实现细节<br/> • 可以这么理解（闭包）：所谓包，指函数与其周围的环境变量捆绑打包；所谓闭，指这些变量是封闭的，只能为该函数所专用<br/> • 合理地使用闭包能使代码更加简洁清晰，散发出函数式特有的优雅气质<br/> • 换盘子有两种方式：一种是服务员主动换，一种是客人要求换。前者是轮询，后者是通知<br/> • 如果把待加工的菜看作数据，技法看作算法，将数据与算法分离，以算法为中心，那是什么范式？ [...]]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center"><span style="font-family: 宋体">冒号课堂</span></h1>
<strong><span style="font-size: 13pt; font-family: 宋体">第四课 重温范式(4)</span></strong>

<!-- below comes from generated html -->
<head><link rel="stylesheet" href="http://blog.zhenghui.org/css/colonclass.css" type="text/css"></head>

<div lang="zh-CN" class="article" title="情景范式"><div class="titlepage"><div><div><h1 class="title"><a name="id639173"></a>4.4 情景范式——餐馆里的编程范式</h1></div><div><div class="author"><h3 class="author">郑晖</h3></div></div><div><div class="abstract" title="摘要"><p class="title"><b>摘要</b></p><p>编程范式在餐馆中的应用</p></div></div></div><hr /></div><div class="toc"><p><b>目录</b></p><dl><dt><span class="section"><a href="#preview">！预览</a></span></dt><dt><span class="section"><a href="#question">？提问</a></span></dt><dt><span class="section"><a href="#explaination">：讲解</a></span></dt><dt><span class="section"><a href="#note">，插语</a></span></dt><dt><span class="section"><a href="#summary">。总结</a></span></dt><dt><span class="section"><a href="#reference">“”参考</a></span></dt></dl></div><div class="epigraph"><div class="literallayout"><p>理论是认生的孩童，多陪他玩玩，自会活泼起来</p></div><div class="attribution"><span>—<span class="attribution">题记</span></span></div></div><div class="section" title="！预览"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="preview"></a>！预览</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    厨师只需提供一种服务：把纸上菜变成盘中菜，至于蒸、煮、炒、炖等具体做法纯属实现细节
                </p></li><li class="listitem"><p>
                    可以这么理解（闭包）：所谓包，指函数与其周围的环境变量捆绑打包；所谓闭，指这些变量是封闭的，只能为该函数所专用
                </p></li><li class="listitem"><p>
                    合理地使用闭包能使代码更加简洁清晰，散发出函数式特有的优雅气质
                </p></li><li class="listitem"><p>
                    换盘子有两种方式：一种是服务员主动换，一种是客人要求换。前者是轮询，后者是通知
                </p></li><li class="listitem"><p>
                    如果把待加工的菜看作数据，技法看作算法，将数据与算法分离，以算法为中心，那是什么范式？
                </p></li></ul></div></div><div class="section" title="？提问"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="question"></a>？提问</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>什么是闭包？为什么被称为闭包？它有什么作用？</p></li><li class="listitem"><p>规则引擎有何用处？</p></li><li class="listitem"><p>能否设想一个生活中的场景，把介绍的九种编程范式都用上？</p></li></ul></div></div><div class="section" title="：讲解"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="explaination"></a>：讲解</h2></div></div></div><p>
            叹号摘下眼镜，揉了揉眼：“范式再好，多了也难免有些审美疲劳。” 
        </p><p>
            逗号也搓着太阳穴：“这段时间脑子被灌得沉甸甸的。”
        </p><p>
            “彼此彼此！你们的脑袋老闹涝灾，我的喉咙老闹旱灾。”冒号说着，拿起矿泉水瓶一饮而尽。
        </p><p>
            大伙听着怪别扭的，这不是拐着弯说我们脑子进水了吗？
        </p><p>
            冒号清了清嗓子：“为尊重民意，也为避免消化不良，大家先轻松一下。下面我们来个<span class="strong"><strong>情景编程</strong></span>。”
        </p><p>
            “情景编程？没听说过，只听说过情景英语。”问号感到挺新鲜。
        </p><p>
            “都是学语言嘛，有何两样？”冒号轻描淡写，“让我们试着用生活中的实例将一些编程范式串联起来。前面提到，OOP可以看作管理一个服务型公司，现在以餐馆为例，你们每人设计一类对象及其提供的服务。”
        </p><p>
            问号来了兴致：“我先来吧。构造一个前台接待员，负责迎客、引座、送客。”
        </p><p>
            句号很是不满：“还真不客气，上来就把最漂亮的对象抢走了。”
        </p><p>
            台下一阵笑声。
        </p><p>
            “我来构建最常见的服务员。”逗号一捋袖子，一副准备开干的样子，“负责斟茶、写菜、上菜、换盘。”
        </p><p>
            “嗯，很熟练。”冒号一本正经。
        </p><p>
            句号实在得很：“我设计收银员，专管收帐、出具发票。”
        </p><p>
            引号颇为自豪：“我造一个技术含量最高的大厨，专门负责烹调。”
        </p><p>
            逗号不服：“你倒简单，那么高的技术含量，敢情炒肉和炖肉一个做法啊？”
        </p><p>
            引号自觉理亏：“那就负责蒸、煮、炒、炖吧。”
        </p><p>
            冒号为其辩护：“引号同学并没有错，可惜没能坚持。厨师只需提供一种服务：把纸上菜变成盘中菜，至于蒸、煮、炒、炖等具体做法纯属实现细节。”
        </p><p>
            叹号有点委屈：“唉，看来我只好做技术含量最低的厨工了，负责食品预加工、洗碗、打扫清洁。”
        </p><p>
            冒号将大家设计的类翻译成Java代码——
        </p><div class="informalexample"><pre class="programlisting">
// 前台接待员
Class Receptionist
{
    public void receive(Customer customer)  {…} // 迎客
    public void usher(Customer customer)    {…} // 引座
    public void send(Customer customer)	    {…} // 送客
}

// 服务员
Class Waiter
{
    public void pourTea(Customer customer)             {…} // 斟茶
    public List&lt;Order&gt; write(Customer customer)        {…} // 写菜
    public void serve(Customer customer, Course course){…} // 上菜
    public void exchangePlate(Customer customer)       {…} // 换盘
}

// 收银员
Class Cashier
{
    public void charge(Customer customer)	{…} // 收帐
    public void issueInvoice(Customer customer) {…} // 出具发票
}

// 厨师
Class Cook
{
    public Course cook(Order order)		{…} // 烹调
}

// 厨工
Class KitchenHand
{
    public void prepareFood()			{…} // 准备食品
    public void washDishes()			{…} // 洗碗
    public void clean()				{…} // 打扫清洁
}</pre></div><p>
            “你们造人，我来造物。”冒号构造了一个餐馆的类——
        </p><div class="informalexample"><pre class="programlisting">
// 餐馆
Class Restaurant
{
    // 每当有顾客来访，返回该顾客
    private Customer accept() {…}
 
    // 为指定顾客提供所有的餐馆服务
    private void serve(Customer customer) {…}

    // 餐馆服务
    public void service()
    {
        while (true) // 无限循环，假设餐馆7×24小时营业
        {
            final Customer customer;
            if ((customer = accept() ) != null) // 某顾客来访
            {
                serve(customer);  // 为该顾客提供服务
            }
        }
    }
}</pre></div><p>
            冒号解说道：“这里accept类似Socket的accept，属于堵塞呼叫，意味着此方法将堵塞进程直至收到新数据。为简单计，把一行顾客当作一个Customer。大家对此段代码有何看法？”
        </p><p>
            “没什么，很简单啊。”逗号说完补充一句，“关键是serve方法的实现。”
        </p><p>
            “这里我们明显用到了两个范式，<span class="emphasis"><em>对象式</em></span>和<span class="emphasis"><em>过程式</em></span>。”冒号提示道。
        </p><p>
            引号会意：“应该还需要并发式。serve如果与service在同一线程中运行，那么餐馆只有等服务完一个Customer后才能服务后面的，这显然是荒唐的。”
        </p><p>
            “对极了！”冒号将“serve(customer);”改写为——
        </p><div class="informalexample"><pre class="programlisting">
// serve(customer);  // 错误地使用单线程！
new Thread           // 构造一个线程
    (new Runnable()
    {
        public void run(){ Restaurant.this.serve(customer); }
    }).start();         // 启动该线程 </pre></div><p>
            冒号道：“这回serve在新线程中运行，不会耽误Restaurant服务下一位Customer了。”
        </p><p>
            问号眼尖：“我注意到声明customer时前面加上了关键字final，有必要吗？”
        </p><p>
            “如果不用线程，是不必要的。”冒号回应道，“我们在建造线程时用到了实现Runnable接口的<span class="term">匿名类</span>（anonymous class），它是涉及到<span class="emphasis"><em>局部变量</em></span>customer的<span class="term">内部类</span>（inner class），Java语法要求该局部变量必须是final类型。值得一提的是，这里不仅用到了<span class="emphasis"><em>并发式</em></span>，而且与<span class="emphasis"><em>函数式</em></span>也密切相关。”
        </p><p>
            “函数式？”逗号奇道。
        </p><p>
            “不错。”冒号坚定地点着头，“我们不是提过函数式中的函数是头等公民吗？也就是说，函数与其他基本数据类型一样，可以作为传递参数、返回值或与变量名绑定。<span class="term">闭包</span>（closure）便是这样一种函数，并且还能保留当初创建时周围的环境变量。以上匿名类本质上是函数serve的包装，经实例化后作为参数传入Thread的构造函数，并且记住了外部类的局部变量customer——这也是为什么它必须是final以保证不被重新赋值的原因。应该说这是一种OO化的闭包形式，预计在Java 7中它的用法会更简洁，这也意味着Java代码中将飘进更多的函数式风味。”
        </p><p>
            引号喃喃道：“以前只听说过数学里有个闭包的概念。”
        </p><p>
            冒号略加指点：“可以这么理解：所谓<span class="emphasis"><em>包</em></span>，指函数与其周围的环境变量捆绑打包；所谓<span class="emphasis"><em>闭</em></span>，指这些变量是封闭的，只能为该函数所专用。如果你真懂数学，就会发现它们本质上是相通的。”
        </p><p>
            叹号隐隐约约地觉得：“把函数与变量捆绑起来，怎么听起来像是OOP的做法啊？”
        </p><p>
            “嗯，的确相似。”冒号微颔，“不妨认为闭包就是封装了环境变量的<span class="emphasis"><em>隐形对象</em></span>的方法——通常是匿名方法。我们用一段JavaScript代码来加深印象——”
        </p><div class="informalexample"><pre class="programlisting">
/* 返回函数f的近似导函数 */
function derivative(f) 
{  // dx最好作为参数传入，放在此处主要是为了说明闭包的用法
    var dx = 0.00001;  // dx越小，近似度越高
    return function(x) { return (f(x + dx) - f(x)) / dx; };
} 

/* 返回一个数的平方数 */
function square(x) { return x * x; }

/* 返回一个数的双倍数  */
function double(x) { return 2 * x; }

/* 对任何一个不大的数值变量x（比如小于100），下列函数的返回值应该非常接近于零 */
function testSquareDerivative(x) { return derivative(square) (x) - double(x); } </pre></div><p>
            叹号感到有点头痛：“怎么跑出了微积分？大学学的那点高数早就还给老师了。”
        </p><p>
            冒号笑着安慰他：“还给老师没关系，我再借给你一点：平方函数的导数是双倍函数。因此，函数derivative(square)应该很接近函数double的作用效果。作为检验，testSquareDerivative能将任何一个不大的数映射到近似于零的数<a class="link" href="#note1"><sup>[1]</sup></a>。”
        </p><p>
            引号这下彻底明白了：“在求导函数derivative中，传入的参数f和返回值都是函数，这是函数作为头等公民的特征。其中返回的匿名函数就是闭包，它附着了两个环境变量：外层函数的传入参数f和局部变量dx。”
        </p><p>
            “完全正确！”冒号作出积极的肯定，“如果不是闭包，这两个环境变量在derivative返回后就会失去效用。由此可见，合理地使用闭包能使代码更加简洁清晰，散发出函数式特有的优雅气质。”
        </p><p>
            句号有些按捺不住编程的冲动，自告奋勇：“我来具体实现餐馆的serve方法吧。”
        </p><p>
            得到冒号的默许，句号在黑板上写下——
        </p><div class="informalexample"><pre class="programlisting">
private void serve(Customer customer)
{
    // 找一个空闲的接待员
    Receptionist receptionist = findReceptionist();
    receptionist.receive(customer);
    receptionist.usher(customer);
    // 找一个空闲的服务员
    Waiter waiter = findWaiter();
    waiter.pourTea(customer);
    List&lt;Order&gt; orders = waiter.write(customer);
    // 将菜单交给一位厨师
    Cook cook = waiter.pass(orders);
    for (Order order : orders) // 厨师照单做菜
    {
        Course course = cook.cook(order);
        // 找一个空闲的服务员
        waiter = findWaiter();
        // 服务员上菜
        waiter.serve(customer, course); 
        // 顾客开始享用
        customer.eat(course); 
    }

    // 顾客用餐完毕。。。
    // 找一个空闲的收银员
    Cashier cashier = findCashier();
    cashier.charge(customer);
    cashier.issueInvoice(customer);
    // 找一个空闲的接待员
    receptionist = findReceptionist();
    receptionist.send(customer);
} </pre></div><p>
            句号写毕又复查一遍，拍拍手上的粉笔灰，心满意足地走下台来。
        </p><p>
            叹号提意见：“我的厨工没派上用场，应该在厨师烹调前调用KitchenHand的prepareFood方法。”
        </p><p>
            问号挑出另外的毛病：“在for循环中，厨师、服务员和顾客的行为应该在不同的线程中，厨师不可能等服务员上完一道菜或顾客吃完一道菜后才做下一道。”
        </p><p>
            “可能更复杂呢！”逗号也来凑热闹，“一位顾客点的几样菜可能分别由几位厨师同时做，每位厨师都在不同的线程中工作。”
        </p><p>
            引号更严谨：“还应有一个后台线程，让服务员（Waiter）随时换盘（exchangePlate），让厨工（KitchenHand）随时洗盘（washDishes）和清洁（clean），这样所有服务人员提供的服务都用上了。”
        </p><p>
            句号倒抽凉气：“估不到漏洞这么多，并发式真是无处不在啊。”
        </p><p>
            冒号继续点拨：“换盘子有两种方式：一种是服务员主动换，一种是客人要求换。前者是轮询，后者是通知。”
        </p><p>
            “哦，<span class="emphasis"><em>事件驱动式</em></span>！”句号迅即反应过来，“客人是事件源，服务员是事件处理器，客人不定期地招手呼唤是在发表事件以通知服务员。客人与服务员是多对多的松耦合关系。”
        </p><p>
            冒号点点头，又指着引号：“刚才有人不满你的大厨职责过于简单，现在你来实现一下，也好显显技术含量。”
        </p><p>
            引号在台上摸了半天头，编出一段代码——
        </p><div class="informalexample"><pre class="programlisting">
Class Cook
{
    public Course cook(Order order) 
    {
        // 根据菜单查食谱
        Recipe recipe = lookupRecipe(order);
        // 找到食谱的烹调步骤
        List&lt;Instruction&gt; instructions = recipe.getInstructions();
        for (Instruction instruction : instructions)
        {
            follow(instruction); // 按食谱的指令操作
        }
    }
} </pre></div><p>
            “堂堂大厨原来是靠查食谱做菜的。”逗号揶揄道。
        </p><p>
            引号为难地说：“这不是在编程嘛，好端端的人脑，不得不去模拟电脑，完全搞倒了。”
        </p><p>
            “要设计会烹调的机器人，兴许还真得这样呢。”冒号笑道，“不过由于各种菜式组合繁多，如果每种菜都配菜谱未免太庞杂，如何精简呢？”
        </p><p>
            句号建议：“菜式成千上万，烹调技法相对少许多，不妨以技法为主线。”
        </p><p>
            “好主意！”冒号挑起大拇指，“如果把待加工的菜看作数据，技法看作算法，将数据与算法分离，以算法为中心，那是什么范式？”
        </p><p>
            “<span class="emphasis"><em>泛型式</em></span>！”大家异口同声。
        </p><p>
            “至此我们已涉及了过程式、对象式、并发式、函数式、事件驱动式和泛型式。”引号扳着手指算着，“还差逻辑式、元编程和切面式了。”
        </p><p>
            冒号把目光转向逗号：“写菜单并不容易，如果客人不直接点菜，你的服务员如何向他推荐？”
        </p><p>
            逗号答：“最简单的方法是报菜名，并一一询问客人。”
        </p><p>
            冒号皱眉：“这样你是简单了：一个迭代就完事，可客人也该发火了。”
        </p><p>
            逗号赶紧修正：“先询问客人的口味、忌讳等等，再向他建议一些菜式。”
        </p><p>
            “这还差不多。”冒号眉头舒展开来，“考虑到客人的口味、忌讳等各有不同，餐馆的菜单也随时可能变化，如果把这些都硬编码（hardcode），再加上层层叠叠的if-else语句，代码将成为懒婆娘的裹脚——又臭又长又难维护。”
        </p><p>
            引号提议：“可以把这些信息预先存入数据库，届时用SQL查询。”
        </p><p>
            “想法很好，只是有一点难度。”冒号提醒道， “这些信息并非简单的对应关系，包含一些逻辑推理，甚至需要一些模糊判断。”
        </p><p>
            句号一拍大腿：“前面不是提到领域特定语言DSL吗？将所有规则用自定义的DSL编写，再利用<span class="emphasis"><em>元编程</em></span>转换成C、Java之类的通用语言，不是很好吗？”
        </p><p>
            “棒极了！”冒号不吝赞词，“不过还有一种思路。我们可以搜集餐馆的菜式、顾客口味、忌讳以及各种菜与口味、忌讳之间的关系等等一系列事实和规则，用<span class="term">规则语言</span>（Rule Language）来描述，通过<span class="term">规则引擎</span>（Rule Engine）来导出符合顾客需求的菜肴。这种方式将业务规则与应用程序分离、将知识表示与逻辑实现分离，是SoC原理的一种应用，同时也是一种<span class="emphasis"><em>逻辑式</em></span>编程。”
        </p><p>
            问号关心地问：“这些规则引擎与Java程序兼容吗？”
        </p><p>
            冒号回答：“不少规则引擎用Java实现或专为Java平台设计，如Jess、Drools、JLisa、JRules等，Sun还发布了javax.rules API（JSR 94）以统一对各类引擎的访问接口。另外在.NET平台上也有业务规则引擎，Microsoft Business Rule Engine（MS BRE）和Windows Workflow Foundation （WF）中的WF Rules均提供了业务规则引擎。”
        </p><p>
            引号颇感意外：“既然是逻辑式编程，为什么不采用代表语言Prolog呢？”
        </p><p>
            冒号准备了一大段理由等着他：“刚才提到的规则引擎多是基于Rete算法<a class="link" href="#note2"><sup>[2]</sup></a>的，主要采用<span class="emphasis"><em>数据驱动</em></span>（data-driven）的<span class="term">正向推理</span>（forward chaining）法，而Prolog引擎采用<span class="emphasis"><em>目标驱动</em></span>（goal-driven）的<span class="term">逆向推理</span>（backward chaining）法。正向推理<span class="emphasis"><em>自底向上</em></span>，利用推理规则从已有的事实数据推出更多的数据，直到达成目标；逆向推理正相反，<span class="emphasis"><em>自顶向下</em></span>，从目标出发寻找满足结论的事实<a class="link" href="#note3"><sup>[3]</sup></a>。相比而言，正向推理适合针对不同输入作出不同反应，而逆向推理适合回答查询。现在是服务员根据客人的喜好提建议，当然用正向推理更合适。再说这类引擎与Java的集成更加方便，因此我们没有选择Prolog。”
        </p><p>
            讲到此处，每个人都意识到，只剩下最后一个范式了。
        </p><p>
            冒号提出一个新问题：“假如餐馆经理接到顾客投诉，反映服务人员态度不好，卫生状况也不理想，应该怎么办？”
        </p><p>
            问号抢先说：“首先我的接待员在迎客（receive）时要笑容可掬地对顾客说：‘欢迎光临！’，在送客（send）时要对顾客鞠躬：‘请慢走，欢迎下次再来’”
        </p><p>
            逗号接着说：“我的服务员在上完菜后应对客人说：‘请慢用’，句号的收银员也应加些礼貌用语，让人家高高兴兴地掏钱。”
        </p><p>
            句号补充道：“服务员在上菜（serve）前、厨师在烹饪（cook）前应洗手，厨工在洗碗（washDishes）后应对餐具消毒。”
        </p><p>
            冒号紧接着问：“如果餐馆对礼貌规范或卫生标准做修改，必然要牵扯不同类中的不同的方法，维护起来很不方便，怎样才能有效地解决这个问题呢？”
        </p><p>
            答案已经昭然若揭了。
        </p><p>
            冒号干脆自问自答：“不错，正是用<span class="emphasis"><em>切面式</em></span>编程。只要创立两个Aspect：Etiquette和Sanitation，分别负责礼貌规范和卫生标准方面的事务。一旦某一方面的要求发生变化，比如餐馆来了外宾，或者碰上非典或禽流感，只需在相应的Aspect模块中作调整：将礼貌用语换成英语或者提高卫生标准等等。如果采用runtime AOP，甚至还可在<span class="emphasis"><em>运行期</em></span>选择激活或禁用这些Aspect。”
        </p><p>
            下面开始有些骚动，大伙早已脑中满满而腹中空空，有点头重脚轻了。
        </p><p>
            冒号见状，遂发出激动人心的号召：“今天的课到此结束，让我们从虚拟的餐馆中走出，到真实的餐馆中去吧！”
        </p><p>
            众人齐声欢呼。
        </p></div><div class="section" title="，插语"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="note"></a>，插语</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p><a name="note1"></a>
                    若输入数过大，则需要设定更小的dx。此外，还可能产生计算溢出。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    Rete算法是一种高效的模式匹配算法，用于实现规则生成系统（production rule system）。文中提到的规则引擎除WF Rules外都是基于该算法的。
                </p></li><li class="listitem"><p><a name="note3"></a>
                    用形式逻辑的语言来说，正向推理顺着从前件（即if语句）到后件（即then语句）的方向，逆向推理顺着从后件到前件的方向。
                </p></li></ol></div></div><div class="section" title="。总结"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="summary"></a>。总结</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    闭包是一种能保留当初创建时的环境变量的函数。它通常以匿名的方式存在，多用于函数式编程中，能使代码更加简洁清晰。Java中的匿名类可以看作OO化的闭包形式。
                </p></li><li class="listitem"><p>
                    Java平台上的Jess、Drools、JLisa、JRules和.NET平台上的MS BRE、WF Rules都是规则引擎，主要基于正向推理。它们提供了逻辑式编程环境，能有效地将业务规则从应用程序中分离出来，提高了软件的灵活性和可维护性。
                </p></li><li class="listitem"><p>
                    每种编程范式都能在生活中找到它的应用，它们本来就是人类思维方式的投影。
                </p></li></ul></div></div><div class="section" title="“”参考"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="reference"></a>“”参考</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>
                    Wikipedia．Closure (computer science)．<a class="link" href="http://en.wikipedia.org/wiki/Closure_(computer_science)" target="_top">http://en.wikipedia.org/wiki/Closure_(computer_science)</a>
                </p></li><li class="listitem"><p>
                    Ernest Friedman-Hill．Jess 7.1p2 manual．<a class="link" href="http://www.jessrules.com/jess/docs/Jess71p2.pdf" target="_top">http://www.jessrules.com/jess/docs/Jess71p2.pdf</a>
                </p></li><li class="listitem"><p>
                    Mark Proctor等．Drools Documentation．<a class="link" href="http://downloads.jboss.com/drools/docs/4.0.7.19894.GA/html_single/index.html" target="_top">http://downloads.jboss.com/drools/docs/4.0.7.19894.GA/html_single/index.html</a>
                </p></li></ol></div></div></div>

<!-- below is edited manually -->
<strong><span style="font-family: 宋体">课后思考</span></strong>
<ul style="margin-top: 0cm; list-style-type: none">
    <li>
        04-01 通过本课的介绍，你对函数范式与逻辑范式是否有了进一步的认识？它们的思想能否应用在你所熟悉的过程式或对象式语言当中？
    </li>
    <li>
        04-02 同样一个问题用不同的语言来编程，代码可能会有极大的差异。你认为这种差异的主要根源是语言还是范式？
    </li>
    <li>
        04-03 认真研究本课中的编程范式汇总表，并补充新的内容，如各范式的关键词、理论基础、最佳实践、注意事项等等。
    </li>
    <li>
        04-04 掌握编程范式对语言学习和编程设计有何实际意义？
    </li>
</ul><p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F09%2F19%2Fcolon-class-4_4%2F&amp;title=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A74.4%EF%BC%9A%E6%83%85%E6%99%AF%E8%8C%83%E5%BC%8F" id="wpa2a_38">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2009年09月21日 -- <a href="http://blog.zhenghui.org/2009/09/21/colon-class-5_1/" title="冒号课堂§5.1：教学计划">冒号课堂§5.1：教学计划</a> (1)</li><li>2009年09月17日 -- <a href="http://blog.zhenghui.org/2009/09/17/colon-class-4_3/" title="冒号课堂§4.3：汇总范式">冒号课堂§4.3：汇总范式</a> (5)</li><li>2009年09月15日 -- <a href="http://blog.zhenghui.org/2009/09/15/colon-class-4_2/" title="冒号课堂§4.2：逻辑范式">冒号课堂§4.2：逻辑范式</a> (0)</li><li>2009年09月13日 -- <a href="http://blog.zhenghui.org/2009/09/13/colon-class-4_1/" title="冒号课堂§4.1：函数范式">冒号课堂§4.1：函数范式</a> (0)</li><li>2009年09月11日 -- <a href="http://blog.zhenghui.org/2009/09/11/colon-class-3_4/" title="冒号课堂§3.4：事件驱动">冒号课堂§3.4：事件驱动</a> (2)</li><li>2009年09月10日 -- <a href="http://blog.zhenghui.org/2009/09/10/colon-class-3_3/" title="冒号课堂§3.3：切面范式">冒号课堂§3.3：切面范式</a> (4)</li><li>2009年09月9日 -- <a href="http://blog.zhenghui.org/2009/09/09/colon-class-3_2/" title="冒号课堂§3.2：超级范式">冒号课堂§3.2：超级范式</a> (2)</li><li>2009年09月8日 -- <a href="http://blog.zhenghui.org/2009/09/08/colon-class-3_1/" title="冒号课堂§3.1：泛型范式">冒号课堂§3.1：泛型范式</a> (5)</li><li>2009年09月7日 -- <a href="http://blog.zhenghui.org/2009/09/07/colon-class-2_4/" title="冒号课堂§2.4：并发范式">冒号课堂§2.4：并发范式</a> (2)</li><li>2009年09月6日 -- <a href="http://blog.zhenghui.org/2009/09/06/colon-class-2_3/" title="冒号课堂§2.3：对象范式">冒号课堂§2.3：对象范式</a> (3)</li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/09/19/colon-class-4_4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>冒号课堂§4.3：汇总范式</title>
		<link>http://blog.zhenghui.org/2009/09/17/colon-class-4_3/</link>
		<comments>http://blog.zhenghui.org/2009/09/17/colon-class-4_3/#comments</comments>
		<pubDate>Thu, 17 Sep 2009 01:33:15 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[编程范式]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[设计模式]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=428</guid>
		<description><![CDATA[<b>汇总范式</b>——一张五味俱全的大烙饼（<em>总结编程范式</em>）<br/> • 设计模式好比组合套路，能在一些特定场合下克敌制胜；编程范式则好比武功门派，博大精深且自成体系<br/> • 一种编程范式之所以能独树一帜，关键在于它突破了原有的编程方式的某些限制，带来革命性的新思维和新方法，进一步解放了程序员的劳动力<br/> • 因其长而容己，因其短而容他，此万物之理也<br/> • 语言为形，范式为神。若能以神导形、以形传神，则看似平白无趣的程序也能写出诗画般的意境 [...]]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center"><span style="font-family: 宋体">冒号课堂</span></h1>
<strong><span style="font-size: 13pt; font-family: 宋体">第四课 重温范式(3)</span></strong>

<!-- below comes from generated html -->
<head><link rel="stylesheet" href="http://blog.zhenghui.org/css/colonclass.css" type="text/css"></head>

<div lang="zh-CN" class="article" title="汇总范式"><div class="titlepage"><div><div><h1 class="title"><a name="id599705"></a>4.3 汇总范式——一张五味俱全的大烙饼</h1></div><div><div class="author"><h3 class="author">郑晖</h3></div></div><div><div class="abstract" title="摘要"><p class="title"><b>摘要</b></p><p>总结编程范式</p></div></div></div><hr /></div><div class="toc"><p><b>目录</b></p><dl><dt><span class="section"><a href="#preview">！预览</a></span></dt><dt><span class="section"><a href="#question">？提问</a></span></dt><dt><span class="section"><a href="#explaination">：讲解</a></span></dt><dt><span class="section"><a href="#note">，插语</a></span></dt><dt><span class="section"><a href="#summary">。总结</a></span></dt><dt><span class="section"><a href="#reference">“”参考</a></span></dt></dl></div><div class="epigraph"><div class="literallayout"><p>形者神之质，神者形之用 </p></div><div class="attribution"><span>—<span class="attribution">《范缜•神灭论》</span></span></div></div><div class="section" title="！预览"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="preview"></a>！预览</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    设计模式好比组合套路，能在一些特定场合下克敌制胜；编程范式则好比武功门派，博大精深且自成体系
                </p></li><li class="listitem"><p>
                    一种编程范式之所以能独树一帜，关键在于它突破了原有的编程方式的某些限制，带来革命性的新思维和新方法，进一步解放了程序员的劳动力
                </p></li><li class="listitem"><p>
                    因其长而容己，因其短而容他，此万物之理也
                </p></li><li class="listitem"><p> 语言为形，范式为神。若能以神导形、以形传神，则看似平白无趣的程序也能写出诗画般的意境                    
                </p></li></ul></div></div><div class="section" title="？提问"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="question"></a>？提问</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>编程范式与设计模式有什么区别？</p></li><li class="listitem"><p>编程范式的核心价值是什么？</p></li><li class="listitem"><p>
                    总结前面介绍的编程范式，它们各自有哪些代表语言？核心概念和运行机制是什么？针对的问题和主要的目的是什么？实现原理是什么？常见的应用有哪些？有什么不足之处？
                </p></li></ul></div></div><div class="section" title="：讲解"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="explaination"></a>：讲解</h2></div></div></div><p>
            稍事休整后，大家重新团结在以冒号为中心的周围。
        </p><p>
            问号再度发问：“编程范式与设计模式都是一种抽象的软件思想，都有一套具体的实现方法。单从字面上看，‘编程’与‘设计’、‘范式’与‘模式’的区别似乎也不太大。它们究竟有什么不同呢？”
        </p><p>
            “这个问题有点意思。”冒号颔言，“设计模式一般针对某一特定场景的问题，而编程范式针对的是广泛得多的问题领域，通常有一整套的思想和理论体系，具有全局性、系统性和渗透性，这一点在五大重要范式中显得尤为突出。因此，编程范式更普适更抽象，涉及的深度和广度也是设计模式难以比拟的。”
        </p><p>
            引号不免有些疑问：“但事件驱动式不是也能作为设计模式吗？”
        </p><p>
            冒号解疑：“这倒并不矛盾。同样的思想用在<span class="emphasis"><em>整体系统</em></span>的结构设计上，则称为架构模式；用在<span class="emphasis"><em>局部模块</em></span>的细节实现上，则称为设计模式<a class="link" href="#note1"><sup>[1]</sup></a>；用在引导编程实践上，则称为编程范式。”
        </p><p>
            句号的武侠瘾又犯了：“设计模式好比组合套路，能在一些特定场合下克敌制胜；编程范式则好比武功门派，博大精深且自成体系。”
        </p><p>
            “很形象的比喻。”冒号赞赏道，“设计模式是遵循<span class="emphasis"><em>设计原则</em></span>的一些具体技巧，以保证代码的可维护性、可扩展性和可重用性为目的。它重在设计，对编程语言一般没有要求<a class="link" href="#note2"><sup>[2]</sup></a>。编程范式则不同，对编程语言往往有专门的要求。通常所说的某某范式的语言，即指该语言对该范式在语法上有明确充分的支持，不需要借助其他的范式或工具。事实上，语言本来就是围绕其所倡导的核心范式来设计的<a class="link" href="#note3"><sup>[3]</sup></a>。”
        </p><p>
            逗号询问：“如果一种语言不支持某种范式，那么还能用这种范式编程吗？”
        </p><p>
            “语言不直接支持范式，只是说明它不属于该范式的语言，但还是可能求助工具来应用该范式。比如元编程可以借助Yacc或ANTLR来完成，AOP可以借助一些库或框架来实现。”冒号道，“正是依靠语言和工具的支持，编程范式得以建立起一套独特而完善的抽象机制和方法体系，从而为所倡导的世界观与方法论奠定基石。”
        </p><p>
            叹号请求：“能不能帮我们理清一下思路，把学过的范式一并汇总比较？”
        </p><p>
            不一会儿，众人面前呈现出一张表格，地毯似的覆盖了整个投影屏（如表4-1所示）——
        </p><div class="table"><a name="id571450"></a><p class="title"><b>表4-1. 常见的编程范式</b></p><div class="table-contents"><table summary="常见的编程范式" border="1"><colgroup><col><col><col><col></colgroup><thead><tr><th>编程范式</th><th>核心概念</th><th>关键突破</th><th>主要目的</th></tr><tr><th>代表语言</th><th>运行机制</th><th>实现原理</th><th>常见应用</th></tr></thead><tbody><tr><td>
                            命令式/过程式（Imperative/Procedural）
                        </td><td>
                            命令/过程（Command /Procedure）
                        </td><td>突破单一主程序和非结构化程序的限制</td><td>模拟机器思维，实现自顶向下的模块设计</td></tr><tr><td>Fortran/Pascal/C</td><td>命令执行</td><td>引入逻辑控制和子程序</td><td>交互式、事件驱动型系统；数值计算等</td></tr><tr><td>
                            函数式/应用式（Functional/Applicative）
                        </td><td>
                            函数（Function）
                        </td><td>突破机器思维的限制</td><td>模拟数学思维，简化代码，减少副作用</td></tr><tr><td>Scheme/Haskell</td><td>表达式计算</td><td>引入高阶函数，将函数作为数据处理</td><td>微积分计算；数学逻辑；博弈等</td></tr><tr><td>
                            逻辑式（Logic）
                        </td><td>
                            断言（Predicate）
                        </td><td>突破逻辑与控制粘合的限制</td><td>专注逻辑分析，减少控制代码</td></tr><tr><td>Prolog/Mercury</td><td>逻辑推理</td><td>利用推理引擎在已知的事实和规则的基础上进行逻辑推断</td><td>
                            机器证明；专家系统；自然语言处理；语义网（semantic web）；决策分析；业务规则管理等
                        </td></tr><tr><td>
                            对象式（Object-Oriented）                            
                        </td><td>
                            对象（Object）                            
                        </td><td>突破数据与代码分离的限制</td><td>迎合人类认知模式，提高软件的易用性、重用性和可维护性</td></tr><tr><td>Smalltalk/Java</td><td>对象间信息交换</td><td>引入封装、继承和多态机制</td><td>大型复杂交互式系统等</td></tr><tr><td>
                            并发式/并行式（Concurrent/Parallel）
                        </td><td>
                            进程/线程（Process/Thread）                            
                        </td><td>突破串行的限制</td><td>充分利用资源、提高运行效率、提高软件的响应能力、保证公平竞争</td></tr><tr><td>Erlang/Oz</td><td>进程/线程间通讯与同步</td><td>引入并行的线程模块以及模块间的通讯与同步机制</td><td>图形用户界面；I/O处理；多任务系统如操作系统、网络服务器等；实时系统；嵌入式系统；计算密集型系统如科学计算、人工智能等</td></tr><tr><td>
                            泛型式（Generic）
                        </td><td>
                            算法（Algorithm）                            
                        </td><td>突破静态类型语言的限制</td><td>提高算法的普适性</td></tr><tr><td>Ada/Eiffel/C++</td><td> 算法实例化（多发生于编译期）</td><td>利用模板推迟类型指定</td><td>普适性算法如排序、搜索等；集合类容器等</td></tr><tr><td>
                            元编程（Metaprogramming）                            
                        </td><td>
                            元程序（Metaprogram）                            
                        </td><td>突破语言的常规语法限制</td><td>减少手工编码，提升语言级别</td></tr><tr><td>Lisp/Ruby/JavaScript</td><td>动态生成代码或自动修改执行指令</td><td>利用代码生成或语言内建的反射（reflection）、动态等机制，将程序语言作为数据来处理</td><td>自动代码生成；定义结构化配置文件；IDE；编译器；解释器；人工智能；模型驱动架构（MDA）；领域特定语言（DSL）等</td></tr><tr><td>
                            切面式（Aspect-Oriented）                            
                        </td><td>
                            切面（Aspect）                            
                        </td><td>突破横切关注点无法模块化的限制</td><td>实现横切关注点分离</td></tr><tr><td>AspectJ/AspectC++</td><td>在接入点处执行建议</td><td>通过编织（weaving）将附加行为嵌入主体程序</td><td>日志输出；代码跟踪；性能监控；异常处理；安全检查；事务管理等</td></tr><tr><td>
                            事件驱动（Event-Driven）                            
                        </td><td>
                            事件（Event）                            
                        </td><td>突破顺序、同步的流程限制</td><td>调用者与被调用者在代码和时间上双重解耦</td></tr><tr><td>C#/VB.NET</td><td>监听器收到事件通知后做出响应</td><td>引入控制反转和异步机制</td><td>图形用户界面；网络应用；服务器；操作系统；IoC框架；异步输入；DOM等</td></tr></tbody></table></div></div><br class="table-break"><p>
            叹号怔了怔，好似被一张巨大的烙饼给噎住了。
        </p><p>
            冒号并不急于讲解，欲以静制动。
        </p><p>
            果然，逗号沉不住气了，问道：“在第一栏的编程范式及其代表语言中，为什么并发式的代表语言没有Java和C#，只有Erlang和Oz？”
        </p><p>
            “Java和C#虽然在语法和核心库中为并发编程提供了不少支持，但真正将并发范式融入基本设计理念的语言还得数Erlang、Oz这些较为冷门的语言。”冒号解释，“类似地，比起Java、JavaScript等语言来，C#和VB.NET在语言设计上对事件驱动式编程给予了更多的关注<a class="link" href="#note4"><sup>[4]</sup></a>，因而更具代表性。”
        </p><p>
            问号发现：“第三栏‘关键突破’的提法很特别啊。”
        </p><p>
            冒号轻捶桌面以示强调：“一种编程范式之所以能独树一帜，关键在于它突破了原有的编程方式的某些限制，带来革命性的新思维和新方法，进一步解放了程序员的劳动力。这便是范式的<span class="strong"><strong>核心价值</strong></span>所在。”
        </p><p>
            引号如获至宝：“这张表格浓缩了范式的精华，既是对此前知识的总结，也是对今后编程的指导，实在太有用了！”
        </p><p>
            句号显得更为冷静：“有其长必有其短。我们了解了每种范式的长处，是不是还应该了解它们各自的短处？”
        </p><p>
            冒号开始对各个范式逐一数落：“过程式编程的数据与代码脱节，不方便维护；函数式和逻辑式的开发效率一般比过程式高，但运行效率和语言表现力则有所不如；对象式编程用于数学计算、符号处理等对象特征淡薄的领域，在心理上缺乏认知基础，在运行效率上不如纯过程式，在开发效率上不如函数式；并发式编程增加了代码的复杂度，加重了程序员的负担；泛型式编程影响了代码的可读性，过度使用模块还可能造成代码膨胀（code bloat）<a class="link" href="#note5"><sup>[5]</sup></a>；元编程过于强大，运用不当会超出程序员的控制，宜谨慎使用；切面式编程减少了程序的可预测性和可控性，同时给代码的跟踪调试带来一定困难，还可能造成性能上的损失；事件驱动式编程虽然也能用于同步的流程应用，但毕竟机制更复杂，没有普通的流程式编程那么自然易懂。”
        </p><p>
            叹号看上去有点泄气：“您可真够绝的，先把这些编程范式一个个捧到天上，又几杆子它们一个个打下云端。”
        </p><p>
            “<span class="strong"><strong>因其长而容己，因其短而容他</strong></span>，此万物之理也。”冒号忽然惜言如金，一番之乎者也地予以回应。
        </p><p>
            句号借用了一句俗话：“不怕有缺点，就怕没特点。”
        </p><p>
            冒号本欲多言，却恐众人食多伤胃，遂作结案陈词：“尽管只是管中窥豹，相信大家多少见识了编程范式的魅力之处。它们各擅胜场，有风格之别而无高下之分。作文绘画讲究形神兼备，编程也不例外。<span class="strong"><strong>语言为形，范式为神</strong></span>。若能以神导形、以形传神，则看似平白无趣的程序也能写出诗画般的意境。”
        </p><p>
            一席话说得众人皆觉虽不能至，然心向往之。
        </p></div><div class="section" title="，插语"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="note"></a>，插语</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p><a name="note1"></a>
                    因此设计模式有时被称为微架构（microarchitecture）模式。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    设计模式的应用范围主要集中于静态的OOP语言，但也不排斥动态的或非OOP的语言。
                </p></li><li class="listitem"><p><a name="note3"></a>
                    当然随着语言的演进，也可能支持新的范式。比如，C++、Java和C#一开始都不支持泛型编程，C#对函数范式的支持也是逐渐加大的。
                </p></li><li class="listitem"><p><a name="note4"></a>
                    C#和VB.NET专门为事件驱动式设计了event、delegate等关键字以及一些配套的便利机制。
                </p></li><li class="listitem"><p><a name="note5"></a>
                    这里的代码不是指程序员写的源代码，而是指编译器生成的代码。
                </p></li></ol></div></div><div class="section" title="。总结"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="summary"></a>。总结</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>
                    相比设计模式，编程范式针对的问题领域更广泛，提出的思想和方法更普适、更抽象、更系统。此外，设计模式重在设计，对语言和工具的要求不高，而编程范式需要建立一套抽象机制和方法体系，离不开语言或工具的支持。
                </p></li><li class="listitem"><p>
                    编程范式的核心价值在于：突破原有的编程方式的某些限制，带来新思维和新方法，从而进一步解放程序员的劳动力。
                </p></li><li class="listitem"><p>
                    正文中编程范式的汇总表格既是对此前知识的总结，也是对今后编程的指导。
                </p></li><li class="listitem"><p>
                    既要了解编程范式的长处，也要了解它们的短处。
                </p></li><li class="listitem"><p>
                    编程范式为神，编程语言为形，应以神导形、以形传神。
                </p></li></ul></div></div><div class="section" title="“”参考"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="reference"></a>“”参考</h2></div></div></div><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>
                    Elena Bolshakova．PROGRAMMING PARADIGMS IN COMPUTER SCIENCE EDUCATION．International Journal &#8220;Information Theories &amp; Applications&#8221;，2005，Vol.12：285-290
                </p></li><li class="listitem"><p>
                    Amnon H. Eden，Rick Kazman．Architecture, design, implementation．Proceedings of the 25th International Conference on Software engineering，2003：149–159
                </p></li></ol></div></div></div><p><a class="a2a_dd a2a_target addtoany_share_save" href="http://www.addtoany.com/share_save#url=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F09%2F17%2Fcolon-class-4_3%2F&amp;title=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A74.3%EF%BC%9A%E6%B1%87%E6%80%BB%E8%8C%83%E5%BC%8F" id="wpa2a_40">分享/保存</a></p><h3  class="related_post_title">相关文章</h3><ul class="related_post"><li>2009年09月13日 -- <a href="http://blog.zhenghui.org/2009/09/13/colon-class-4_1/" title="冒号课堂§4.1：函数范式">冒号课堂§4.1：函数范式</a> (0)</li><li>2009年08月31日 -- <a href="http://blog.zhenghui.org/2009/08/31/colon-class-1_5/" title="冒号课堂§1.5：开发技术">冒号课堂§1.5：开发技术</a> (3)</li><li>2009年08月29日 -- <a href="http://blog.zhenghui.org/2009/08/29/colon-class-1_4/" title="冒号课堂§1.4：初识范式">冒号课堂§1.4：初识范式</a> (2)</li><li>2009年09月27日 -- <a href="http://blog.zhenghui.org/2009/09/27/colon-class-5_4/" title="冒号课堂§5.4：语言误区">冒号课堂§5.4：语言误区</a> (0)</li><li>2009年09月21日 -- <a href="http://blog.zhenghui.org/2009/09/21/colon-class-5_1/" title="冒号课堂§5.1：教学计划">冒号课堂§5.1：教学计划</a> (1)</li><li>2009年09月19日 -- <a href="http://blog.zhenghui.org/2009/09/19/colon-class-4_4/" title="冒号课堂§4.4：情景范式">冒号课堂§4.4：情景范式</a> (0)</li><li>2009年09月15日 -- <a href="http://blog.zhenghui.org/2009/09/15/colon-class-4_2/" title="冒号课堂§4.2：逻辑范式">冒号课堂§4.2：逻辑范式</a> (0)</li><li>2009年09月11日 -- <a href="http://blog.zhenghui.org/2009/09/11/colon-class-3_4/" title="冒号课堂§3.4：事件驱动">冒号课堂§3.4：事件驱动</a> (2)</li><li>2009年09月10日 -- <a href="http://blog.zhenghui.org/2009/09/10/colon-class-3_3/" title="冒号课堂§3.3：切面范式">冒号课堂§3.3：切面范式</a> (4)</li><li>2009年09月9日 -- <a href="http://blog.zhenghui.org/2009/09/09/colon-class-3_2/" title="冒号课堂§3.2：超级范式">冒号课堂§3.2：超级范式</a> (2)</li></ul>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/09/17/colon-class-4_3/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>

