<?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>冒号空间</title>
	<atom:link href="http://blog.zhenghui.org/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.zhenghui.org</link>
	<description>自然、人类、机器</description>
	<lastBuildDate>Wed, 10 Mar 2010 03:50:14 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>《冒号课堂》在台湾上市</title>
		<link>http://blog.zhenghui.org/2010/03/09/colonclass-sales-tw/</link>
		<comments>http://blog.zhenghui.org/2010/03/09/colonclass-sales-tw/#comments</comments>
		<pubDate>Tue, 09 Mar 2010 02:14:53 +0000</pubDate>
		<dc:creator>hui zheng</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="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>&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>
<a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zhenghui.org%2F2010%2F03%2F09%2Fcolonclass-sales-tw%2F&amp;linkname=%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">分享/保存</a>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2010/03/09/colonclass-sales-tw/feed/</wfw:commentRss>
		<slash:comments>0</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 zheng</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>
<a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zhenghui.org%2F2010%2F01%2F01%2Fcolonclass-bbs-openning%2F&amp;linkname=%E3%80%8A%E5%86%92%E5%8F%B7%E8%AE%BA%E5%9D%9B%E3%80%8B%E5%BC%80%E6%94%BE">分享/保存</a>]]></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 zheng</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>鉴于 <a href="http://bbs.zhenghui.org/">冒号论坛</a>已经开放，本帖已成<em>deprecated</em> API，<em>不建议</em>读者继续回复此帖。</p><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F11%2F07%2Fcolonclass-suggestions%2F&amp;linkname=%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">分享/保存</a>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/11/07/colonclass-suggestions/feed/</wfw:commentRss>
		<slash:comments>53</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 zheng</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;Times New Roman&amp;quot;,&amp;quot;Bitstream Charter&amp;quot;,Times,serif;">网站主页：</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;Times New Roman&amp;quot;,&amp;quot;Bitstream Charter&amp;quot;,Times,serif;">豆瓣：<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;Times New Roman&amp;quot;,&amp;quot;Bitstream Charter&amp;quot;,Times,serif;">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;Times New Roman&amp;quot;,&amp;quot;Bitstream Charter&amp;quot;,Times,serif;"><br /></span></span></p>
<p><strong>网上书店</strong>：</p>
<p><span style="color: #000000;"><span style="font-family: Georgia,&amp;quot;Times New Roman&amp;quot;,&amp;quot;Bitstream Charter&amp;quot;,Times,serif;">互动网：<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;Times New Roman&amp;quot;,&amp;quot;Bitstream Charter&amp;quot;,Times,serif;">当当网：<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;Times New Roman&amp;quot;,&amp;quot;Bitstream Charter&amp;quot;,Times,serif;">卓越网：<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;Times New Roman&amp;quot;,&amp;quot;Bitstream Charter&amp;quot;,Times,serif;">北发图书网：<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;Times New Roman&amp;quot;,&amp;quot;Bitstream Charter&amp;quot;,Times,serif;">新华书店.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;Times New Roman&amp;quot;,&amp;quot;Bitstream Charter&amp;quot;,Times,serif;">金桥书城：<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;Times New Roman&amp;quot;,&amp;quot;Bitstream Charter&amp;quot;,Times,serif;">电子工业出版社：<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;Times New Roman&amp;quot;,&amp;quot;Bitstream Charter&amp;quot;,Times,serif;">中国图书网：<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;Times New Roman&amp;quot;,&amp;quot;Bitstream Charter&amp;quot;,Times,serif;">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 border="0" cellspacing="1" cellpadding="1" width="100%">
<tbody>
<tr>
<td height="25" valign="bottom" bgcolor="#fbf9f5"><strong>字数：</strong>570千字</td>
<td width="21%" height="25" valign="bottom" bgcolor="#fbf9f5"></td>
<td width="36%" valign="bottom" bgcolor="#fbf9f5"><strong>定价：</strong>￥65.00元</td>
</tr>
<tr>
<td align="center" valign="top"></td>
<td width="19%" height="25" valign="bottom" bgcolor="#fbf9f5"><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 height="25" valign="bottom" bgcolor="#fbf9f5"><strong>开本：</strong>16</td>
</tr>
</tbody></table>
</div>
<a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F10%2F26%2Fcolonclass-sales%2F&amp;linkname=%E3%80%8A%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%E3%80%8B%E4%B8%8A%E5%B8%82">分享/保存</a>]]></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 zheng</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><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F10%2F24%2Fcolon-class-10_2%2F&amp;linkname=%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">分享/保存</a>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/10/24/colon-class-10_2/feed/</wfw:commentRss>
		<slash:comments>5</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 zheng</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>
<strong><span style="font-size: 13pt; font-family: 宋体;">第十课 多态机制（1）</span></strong>

<strong> </strong>

<strong><span style="font-family: 宋体;">课前导读</span></strong>
<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 -->
<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="id525234"></a>10.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><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"><b>图10-1. Authenticator的UML类图（模板方法模式）</b></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"><b>图10-2. Authenticator的UML类图（策略模式）</b></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><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F10%2F20%2Fcolon-class-10_1%2F&amp;linkname=%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">分享/保存</a>]]></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 zheng</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><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F10%2F16%2Fpreface-of-colonclass%2F&amp;linkname=%E3%80%8A%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%E3%80%8B%E8%87%AA%E5%BA%8F">分享/保存</a>]]></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 zheng</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><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F10%2F10%2Fcolon-class-6_4%2F&amp;linkname=%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">分享/保存</a>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/10/10/colon-class-6_4/feed/</wfw:commentRss>
		<slash:comments>2</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 zheng</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><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F10%2F08%2Fcolon-class-6_3%2F&amp;linkname=%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">分享/保存</a>]]></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 zheng</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><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F10%2F03%2Fcolon-class-6_2%2F&amp;linkname=%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">分享/保存</a>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/10/03/colon-class-6_2/feed/</wfw:commentRss>
		<slash:comments>1</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 zheng</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>

<strong> </strong>

<strong><span style="font-family: 宋体;">课前导读</span></strong>
<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><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F09%2F29%2Fcolon-class-6_1%2F&amp;linkname=%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">分享/保存</a>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/09/29/colon-class-6_1/feed/</wfw:commentRss>
		<slash:comments>2</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 zheng</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><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F09%2F27%2Fcolon-class-5_4%2F&amp;linkname=%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">分享/保存</a>]]></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 zheng</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><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F09%2F25%2Fcolon-class-5_3%2F&amp;linkname=%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">分享/保存</a>]]></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 zheng</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><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F09%2F23%2Fcolon-class-5_2%2F&amp;linkname=%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">分享/保存</a>]]></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 zheng</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>

<strong> </strong>

<strong><span style="font-family: 宋体;">课前导读</span></strong>
<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><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F09%2F21%2Fcolon-class-5_1%2F&amp;linkname=%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">分享/保存</a>]]></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 zheng</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><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F09%2F19%2Fcolon-class-4_4%2F&amp;linkname=%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">分享/保存</a>]]></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 zheng</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><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F09%2F17%2Fcolon-class-4_3%2F&amp;linkname=%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">分享/保存</a>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/09/17/colon-class-4_3/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>冒号课堂§4.2：逻辑范式</title>
		<link>http://blog.zhenghui.org/2009/09/15/colon-class-4_2/</link>
		<comments>http://blog.zhenghui.org/2009/09/15/colon-class-4_2/#comments</comments>
		<pubDate>Tue, 15 Sep 2009 05:00:43 +0000</pubDate>
		<dc:creator>hui zheng</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[Prolog]]></category>
		<category><![CDATA[控制]]></category>
		<category><![CDATA[算法]]></category>
		<category><![CDATA[编程范式]]></category>
		<category><![CDATA[逻辑]]></category>
		<category><![CDATA[逻辑式编程]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=421</guid>
		<description><![CDATA[<b>逻辑范式</b>——当算法失去了控制（<em>再谈逻辑式编程</em>）<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="id599676"></a>4.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>
                    评价代码的复杂度，长短只是一个因素。程序员不是打字员，花在思考上的时间和精力远远超过花在键盘上
                </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>
            “那我们就用逻辑式语言Prolog再实现一次quicksort吧。”冒号说着将幻灯片翻页——
        </p><div class="informalexample"><pre class="programlisting">
/*快速排序法的Prolog实现 */
/* 定义划分法 */
partition(_,[],[],[]).                             /* 划分递归终点 */
partition(Pivot,[X|Rest],[X|Small],Big) :- 
X &lt; Pivot, partition(Pivot,Rest,Small,Big).        /* 比基准小的归入Small */
partition(Pivot,[X|Rest],Small,[X|Big]) :- 
X &gt;= Pivot, partition(Pivot,Rest,Small,Big).       /* 比基准大的归入Big */

/* 定义排序法 */
qsort([],[]).                                      /* 排序递归终点 */
qsort([Pivot|Rest],Sorted) :- 
partition(Pivot,Rest,Small,Big),                   /* 按基准划分子列 */
      qsort(Small,SortedSmall),                    /* 对前面的子列递归 */
      qsort(Big,SortedBig),                        /* 对后面的子列递归 */
      append(SortedSmall,[Pivot|SortedBig],Sorted)./* 子列合并 */ </pre></div><p>
            逗号挠挠头：“看不太懂哦，好在我记住了您的一句话：容忍无知。我忍了！”
        </p><p>
            大伙都乐了。
        </p><p>
            “本节课的焦点不是语言而是范式，因此对Prolog代码不详加解说。我只简单地说三点：首先，Prolog代码是由一系列事实（fact）、规则（rule）和查询（query）语句组成的<a class="link" href="#note1"><sup>[1]</sup></a>。其次，与大多数语言不同的是，大写字母或下划线开头的标识符是变量，其他的是常量或函数。请注意，这不是约定俗成，而是语法规定。最后，符号‘:-’等价于if；逗号‘,’等价于and。比如，我们可以用Prolog来表达一个断言：如果一个人未婚且为男士，那么他就是一光棍。”冒号转身在黑板上写下——
        </p><div class="informalexample"><pre class="programlisting">
/* X is bachelor if X is unmarried and male*/
bachelor (X) :- unmarried(X) , male(X). </pre></div><p>
            听见下面一阵嘀咕声，冒号忽地闪过一个念头：这个例子该不会触动了满足条件的某位同学的心事吧？顿了一会，继续说道：“逻辑式实现的排序虽不比函数式更简洁，但比起过程式来还是绰绰有余的。毕竟同属声明式，省去了不少有关变量赋值、迭代和流程控制方面的代码。我们再看一个更加典型的范例。”
        </p><p>
            黑板上出现了一幅树状图形——
        </p><div class="figure"><a name="id597056"></a><p class="title"><b>图4-1. 家谱</b></p><div class="figure-contents"><div class="mediaobject"><img src="http://blog.zhenghui.org/img/colonclass/figure4-1.jpg" alt="家谱"></div></div></div><br class="figure-break"><p>
             冒号简作说明：“这是一个三代家谱图。已知每人的性别和父辈，要求判断任意两人之间的关系。我们先用Java来试一试——”
        </p><div class="informalexample"><pre class="programlisting">
class Person
{
    private Person parent;
    private boolean isMale;

    public Person(Person parent, boolean isMale)
    {
        this.isMale = isMale;
        this.parent = parent;
    }

    private boolean isSibling(Person other)
    {
        return parent == other.parent &amp;&amp; parent != null &amp;&amp; this != other;
    }

    public String getRelation(Person other)
    {
        if (other == null || this == other) return null;

        if (parent == other) return isMale ? "son" : "daughter";

        if (other.parent == this) return isMale ? "father" : "mother";

        if (parent == null) // this是老祖宗
        {
            if (other.parent == null) return null;

            if (other.parent.parent == this) return isMale ? "grandfather" : "grandmother";

            return null;
        }

        if (other.parent == null) // other是老祖宗
        {
            if (parent.parent == other) return isMale ? "grandson" : "granddaughter";

            return null;
        }

        // 非直系
        if (isSibling(other)) return isMale ? "brother" : "sister";

        if (parent.isSibling(other.parent)) return "cousin";

        if (parent.isSibling(other)) return isMale ? "nephew" : "niece";

        if (isSibling(other.parent)) return isMale ? "uncle" : "aunt";

        return null;
    }

    public static void main(String[] args)
    {
        Person a = new Person(null, true);
        Person b = new Person(a, true);
        Person c = new Person(a, true);
        Person d = new Person(a, false);
        Person e = new Person(b, false);
        Person f = new Person(b, true);
        Person g = new Person(c, false);
        Person h = new Person(d, true);
        Person i = new Person(d, false);
        Person j = new Person(d, true);
        // 以下省略。。。
     }
}</pre></div><p>
            “这段代码很平凡，毋需多言。再来看看逻辑式语言的做法。”冒号不愿过多地纠缠于细节，随即又换成了Prolog代码——
        </p><div class="informalexample"><pre class="programlisting">
/* 规则 */
/* 上下两代直系关系 */
father(X,Y)        :- parent(X,Y), male(X).
mother(X,Y)        :- parent(X,Y), female(X).
child(X,Y)         :- parent(Y,X).
son(X,Y)           :- parent(Y,X), male(X).
daughter(X,Y)      :- parent(Y,X), female(X).

/* 祖孙关系 */
grandparent(X,Y)   :- parent(X,Z), parent(Z,Y).
grandfather(X,Y)   :- grandparent(X,Y), male(X).
grandmother(X,Y)   :- grandparent(X,Y), female(X).
grandchild(X,Y)    :- grandparent(Y,X).
grandson(X,Y)      :- grandparent(Y,X), male(X).
granddaughter(X,Y) :- grandparent(Y,X), female(X).

/* 平辈关系 */
/* 若X与Y有相同的父辈Z，且X不是Y，则X与Y是同胞*/
sibling(X,Y)       :- parent(Z,X), parent(Z,Y), X\==Y. 
brother(X,Y)       :- sibling(X,Y), male(X).
sister(X,Y)        :- sibling(X,Y), female(X).
cousin(X,Y)        :- parent(Z,X), parent(W,Y), sibling(Z,W).

/* 上下两代旁系关系 */
uncle(X,Y)         :- parent(Z,Y), brother(X,Z).
aunt(X,Y)          :- parent(Z,Y), sister(X,Z).
nephew(X,Y)        :- parent(Z,X), sibling(Z,Y), male(X).
niece(X,Y)         :- parent(Z,X), sibling(Z,Y), female(X).

/* 定义一个普适关系relation，方便查询 */
relation(R, X, Y)       :-  relations(Rs), member(R,Rs), Q =..[R,X,Y], call(Q).

/* 事实 */
/* 关系列表 */
relations([parent,father,mother,son,daughter,grandparent,grandfather,
grandmother,grandchild,grandson,granddaughter,
                sibling,brother,sister,cousin,uncle,aunt,nephew,niece]).

parent(a,b). parent(a,c). parent(a,d).
parent(b,e). parent(b,f).
parent(c,g).
parent(d,h). parent(d,i). parent(d,j).

male(a).
male(b).
male(c).
female (d).
female (e).
male(f).
female (g).
male(h).
female (i).
male(j). </pre></div><p>
            叹号没有看出名堂：“Prolog代码并不比Java代码简短多少啊。”
        </p><p>
            “评价代码的复杂度，长短只是一个因素。程序员不是打字员，花在思考上的时间和精力远远超过花在键盘上。”冒号指出，“就拿此例来说，Java代码虽然并不复杂，但有不少的选择分支语句，次序很重要。稍有不慎，就会出现逻辑错误。另外如果我们把关系分得更细致些，比如区分叔伯舅、姑姨婶、堂兄表妹等；再加入姻亲关系，比如姑嫂婆媳、妯娌连襟等。这时你再来改写这段代码试试？”
        </p><p>
            引号听得头皮有些发麻：“那一定需要不少重重嵌套的if-else语句了。”
        </p><p>
            问号提出的问题更让人头痛：“如果我们不限于三代，再加上曾孙女、曾叔父之类的关系呢？”
        </p><p>
            逗号联想到一则笑话：“话说一对父子与一对母女联姻，作父亲的娶了那位女儿，作儿子的娶了那位母亲。本来关系已经够颠倒错乱了，雪上加霜的是这两对夫妇又各自有了子女，那位父亲终于精神崩溃了。”
        </p><p>
            大家哄笑着：这下彻底乱套啰。
        </p><p>
            “前面的Java代码之所以没有嵌套，得益于及时退出的一些return语句。如果考虑到超过三代的关系以及多重交叉的关系，许多语句都得改写。可见上述代码是多么地脆弱！” 冒号就棍打腿，“再看Prolog代码，如果要求更细的血亲关系、增加姻亲关系或三代以上的关系，只需引入新的规则和事实即可，不会影响原有代码。下面列出几个示范语句——”
        </p><div class="informalexample"><pre class="programlisting">
/* 规则 */
/* 配偶原则 */
father(X,Y)          :- spouse(Z,X), mother(Z,Y).
mother(X,Y)        :- spouse(Z,X), father(Z,Y).
husband(X,Y)      :- spouse(X,Y), male(X).
wife(X,Y)            :- spouse(X,Y), female(X).

/* 父系的堂、姑兄弟姐妹 */
paternal_cousin(X,Y) :- father(Z,X), father(W,Y), sibling(Z,W).
/* 母系的舅、姨兄弟姐妹 */
maternal_cousin(X,Y) :- mother(Z,X), mother(W,Y), sibling(Z,W).

/* 姻亲关系 */
father_in_law(X,Y) :- spouse(Y,Z), father(X,Z).
mother_in_law(X,Y) :- spouse(Y,Z), mother(X,Z).
son_in_law(X,Y)    :- spouse(X,Z), daughter(Z,Y).
daughter_in_law(X,Y) :- spouse(X,Z), son(Z,Y).

/* 曾祖孙关系 */
great_grandparent(X,Y) :- grandparent(Z,Y), parent(X,Z).
great_grandchild(X,Y)  :- grandchild(Z,Y), child(X,Z).

/* 事实 */
/* 新引入的关系 */
relations([husband,wife, paternal_cousin,maternal_cousin,
father_in_law,mother_in_law,son_in_law,daughter_in_law,
great_grandparent,great_grandchild]).

parent(pa,a).
spouse(a,as).
spouse(ds,d).
spouse(cs,c). </pre></div><p>
            句号方悟其妙：“这样的代码既无层层嵌套，也无次序分别。比起过程式，编写轻松得多，程序的可维护性和可扩展性也更高。”
        </p><p>
             “此外另有妙处。逻辑式与过程式和函数式的一个不同之处是，它没有明显的输入、输出之分。上面的程序不仅可以用来判断任意二人之间的关系，还能倒过来通过关系来找人。”冒号板书了几行字——
        </p><div class="informalexample"><p>输入查询：relation(R,a,ds)          /* a与ds的关系是什么？ */</p><p>输出结果：R=father_in_law</p><p>输入查询：great_grandparent (pa,X)  /* pa是谁的曾祖？*/</p><p>输出结果：X=e;X=f;X=g; X=h; X=i; X=j;</p></div><p>
            引号义务作翻译：“这告诉我们两件事：a与ds是翁婿关系，pa有曾孙e、f、g、h、i和j。”
        </p><p>
            “逻辑式语言着眼于关系而非函数，对付这类问题正是它的拿手好戏。”冒号声音逐渐高亢，“大家应该都听说过等式‘算法+数据结构=程序’吧？这是Pascal设计者Niklaus Wirth的一本著作的书名，它刻画了过程式尤其是结构化编程的思想。后来Robert Kowalski进一步提出：<span class="strong"><strong>算法=逻辑+控制</strong></span>。其中逻辑是算法的核心，控制主要用于改进算法的效率。在逻辑式编程中，程序员只需表达<span class="emphasis"><em>逻辑</em></span>，而<span class="emphasis"><em>控制</em></span>交给编程语言的解释器或编译器去管理。”
        </p><p>
            “所以程序员的负担大大减轻了。”问号接口道，“逻辑式编程听起来真是不错，但不知Prolog程序能否与Java程序对接呢？”
        </p><p>
            冒号回答：“任何程序之间的对接都是可能的，只是不同的对接方式在复杂度和效率上有所差异而已。除了通过程序之间的通讯（如socket）或可执行文件的直接调用外，Prolog与C、C++、Java、C#、VB、Perl、JavaScript等多种语言之间，还能借助工具进行源代码转换<a class="link" href="#note2"><sup>[2]</sup></a>或通过双向编程接口互嵌代码。具体到Java，一方面可以通过JNI （Java Native Interface）与Prolog引擎相连<a class="link" href="#note3"><sup>[3]</sup></a>，另一方面可以利用Prolog引擎的Java实现来完成JVM上的集成<a class="link" href="#note4"><sup>[4]</sup></a>。”
        </p><p>
            句号请求：“能否总结一下逻辑式编程的优缺点？”
        </p><p>
            冒号欣然应允：“由于逻辑式编程模拟人类的逻辑思维，故而在机器证明、专家系统、自然语言处理、博弈等人工智能领域如鱼得水，同时在非学术领域的知识管理、智能决策分析等方面也能大显身手。同为声明式，它与函数式一样比命令式更简洁、更抽象、更少副作用，运用得当能大大提高生产效率，还能用于<span class="term">快速原型</span>（rapid prototyping）开发。但缺点是运行效率偏低，可掌控性较差，与常规的过程式思维差异较大，更适合<span class="emphasis"><em>基于规则</em></span>（rule-based）而不是<span class="emphasis"><em>基于状态</em></span>（state-based）的应用<a class="link" href="#note5"><sup>[5]</sup></a> 。此外，相对而言逻辑式语言还不够成熟和完善。” 
        </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>
                    用数学逻辑的话来说，事实与规则是公理，查询就是待证的定理。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    如Prolog Café和P#能分别将Prolog代码转化为Java代码和C#代码。
                </p></li><li class="listitem"><p><a name="note3"></a>
                    比如JPL通过JNI与Prolog FLI （Foreign Language Interface）将Java与SWI-Prolog桥接起来。
                </p></li><li class="listitem"><p><a name="note4"></a>
                    比如JIProlog（Java Internet Prolog）是一个用Java实现的Prolog解释器，为Java和Prolog提供双向API。类似的还有JLog等。
                </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>
                    Michael Lee Scott．Programming Language Pragmatics．San Francisco：Morgan Kaufmann，2000．620-650
                </p></li><li class="listitem"><p>
                    Robert A. Kowalski．Algorithm = Logic + Control．Communications of the ACM，1979，22(7)：424-436
                </p></li></ol></div></div></div><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F09%2F15%2Fcolon-class-4_2%2F&amp;linkname=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A74.2%EF%BC%9A%E9%80%BB%E8%BE%91%E8%8C%83%E5%BC%8F">分享/保存</a>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/09/15/colon-class-4_2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>冒号课堂§4.1：函数范式</title>
		<link>http://blog.zhenghui.org/2009/09/13/colon-class-4_1/</link>
		<comments>http://blog.zhenghui.org/2009/09/13/colon-class-4_1/#comments</comments>
		<pubDate>Sun, 13 Sep 2009 12:50:36 +0000</pubDate>
		<dc:creator>hui zheng</dc:creator>
				<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=401</guid>
		<description><![CDATA[<b>函数范式</b>——精巧的数学思维（<em>再谈函数式编程</em>）<br/>
•	单靠记忆只能触及知识之表，单靠练习只能深入知识之里，唯有培养方能渗透知识之根<br/>
•	学会适度地容忍无知<br/>
•	不仅需要强调钻劲和深度的“钉子精神”，还需要强调磨功和广度的“刨子精神”<br/>
•	编程语言的语法、语义等都是从编程范式的树根衍生而出的枝叶，把握了这种脉络和节奏，代码才会如音乐舞蹈般韵律有致<br/>
•	每种范式擅长的问题领域不尽相同，只有博闻广识，方可扬长避短，程序才会如行云流水般流畅自然<br/>
•	程序员更习惯机器风格的过程式思维和现实风格的OOP思维，不容易接纳数学风格的函数式思维]]></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>

<strong> </strong>

<strong><span style="font-family: 宋体;">课前导读</span></strong>
<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="id637341"></a>4.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><li class="listitem"><p>
                    每种范式擅长的问题领域不尽相同，只有博闻广识，方可扬长避短，程序才会如行云流水般流畅自然
                </p></li><li class="listitem"><p>
                    程序员更习惯机器风格的过程式思维和现实风格的OOP思维，不容易接纳数学风格的函数式思维
                </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>相比过程式和OOP，函数式编程的弱点是什么？</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>
            “你说得没错，我就是那个不近情理的导游。”冒号哈哈一笑，“类似时下流行的欧洲N国M日游，大部分人的收获就是一堆照片和日渐模糊的记忆。不出多日，如果不看标注，八成连照片上的背景是在法国还是在意大利都分不清了。”
        </p><p>
            逗号颇有同感：“差不多，目前我的收获就是一堆幻灯片和似懂非懂的概念。”
        </p><p>
            冒号料有此果：“这一点也不奇怪。别说几天游一个国家，单一个罗马，没有一个月是不可能深入了解的。至于编程范式，单一个OOP，没有两年以上的实践和思考，是难以真正领会其精髓的。”
        </p><p>
            叹号深表怀疑：“OOP需要两年以上才能领会？！”
        </p><p>
            “那还得看你是否有足够的勤奋和悟性。”冒号加强了语气，“前面说过，<span class="strong"><strong>单靠记忆只能触及知识之表，单靠练习只能深入知识之里，唯有培养方能渗透知识之根</strong></span>。编程范式正处知识的根部，你们又怎能奢望只听几堂课即豁然贯通呢？”
        </p><p>
            引号表达自己的感受：“虽然学了不少东西，但也存了不少疑惑，搁在心里有点不舒服。”
        </p><p>
            “我明白你的意思。凡事追根究底是一种良好的学习习惯，也是一种可贵的学习精神。” 冒号表示理解和肯定，“但学习如打仗，除了要有直线式的纵深攻击，还要有曲线式的迂回包抄。回顾我们中学的课堂，往往是每引入一个概念或理论，便围绕其作深入的学习和反复的练习。在此过程中的种种疑惑，随着学习的深入都会烟消云散。这样稳扎稳打、层层推进，学得扎实，心里也踏实。但这种方法并不总是最好的，尤其在面临动态的、开放的知识体系时，难免左支右绌。为此，我们必须学会<span class="strong"><strong>适度地容忍无知</strong></span>。请注意，容忍无知不是放任无知，而是一种学习的技巧，让无知成为求知的动力而不是障碍。容忍无知能使我们既不沮丧气馁，也不急于求成。在学习时不妨略过一些细节或难点，先概览全貌以获取感性认识，然后在逐步积累中升华为理性认识。要而言之，我们不仅需要强调钻劲和深度的‘<span class="strong"><strong>钉子精神</strong></span>’，还需要强调磨功和广度的‘<span class="strong"><strong>刨子精神</strong></span>’。我一口气兜售这么多编程范式，就是为了刺激大家求知欲，同时为大家进行第一道刨磨。”
        </p><p>
            引号得到一些安慰：“看来今后我们还会故地重游的。”
        </p><p>
            “不仅会重游，而且会‘深度游’。” 冒号肯定地说，“此番我们一路行色匆匆，若能感受到途中景色带来的感官冲击，便算是不枉此行了。其实，把编程范式类比旅游景点并不十分准确，或许比作当地的风俗文化更确切些。”
        </p><p>
            句号立刻会意：“景点是具体的，背后的风俗文化是抽象的；编程语言是具体的，背后的编程范式是抽象的。”
        </p><p>
            “此乃其一。”冒号右手伸出食指，“其二，如果不了解景点的历史文化和风俗人情，仅仅满足于表面的奇观异景，一切很快便会淡忘。比如说，你若不了解圣经文化、不了解文艺复兴史，则欧洲之行至多只是视觉的盛宴，而非文化的洗礼，收获将是有限的，印象将是肤浅的。同样，如果你不了解编程范式，那么眼中的编程语言只是语法、语义、核心库、规范等组成的集合，写出的代码虽能编译、能工作，却会显得生硬、别扭。就像中式英语，语法正确、表达也正确，可就是不正宗、不地道。其症结我们在第一节课中已经提过了，即所谓的<span class="strong"><strong>语感缺失</strong></span>。”
        </p><p>
            问号实话实说：“可我还是不明白编程范式如何提高我们的编程语感。”
        </p><p>
            “那就让我们再说说范式吧。”冒号并不着急，“范式可以粗略理解为模范、模型、模式、风格、流派等等。软件中的范式除了编程范式外，还有架构范式<a class="link" href="#note1"><sup>[1]</sup></a>、数据库范式<a class="link" href="#note2"><sup>[2]</sup></a>等。比如，对象导向式（Object-Oriented）可以应用于编程、架构和数据库中，分别成为OOP（Object-Oriented Programming）、OOA（Object-Oriented Architecture）和OODB（object-oriented database）；类似地，事件驱动式（Event-Driven）可以是一种编程范式，可以是一种架构模型，也可以是一种设计模式。总之，每种范式都代表着<span class="strong"><strong>一套独特而有效的解决问题的思想和方法</strong></span>。掌握范式对编程语感的提高至少有两层作用：首先，编程语言的语法、语义等都是从编程范式的树根衍生而出的枝叶，把握了这种脉络和节奏，代码才会如音乐舞蹈般韵律有致；其次，每种范式擅长的问题领域不尽相同，只有博闻广识，方可扬长避短，程序才会如行云流水般流畅自然。”
        </p><p>
            逗号添油加醋：“武功练至化境，一定是博采众长，就像杨过融合了东邪、西毒、南帝、北丐、中神通等各派武功，才能随心所欲地打出黯然销魂掌来。”
        </p><p>
            提起武侠人物，众人俱是逸兴遄飞，哪能体会到半点黯然消魂之伤？
        </p><p>
            冒号道：“天下之理，殊途同归。我们停止玄玄之论，用实例来说明吧。谁来介绍一下快速排序法（quicksort）？”
        </p><p>
            众人飞舞的思绪渐渐收敛，终于由引号作答：“快速排序法的思想是：在列表中找一个基准元素，将所有小于它的元素划归一个子列，置于其前；将所有大于等于它的元素划归另一子列，置于其后。然后递归地对前后两个子列作同样处理，直至最终。”
        </p><p>
            “很好，让我们用Java来实现一下该算法。”冒号显示出一段代码——
        </p><div class="informalexample"><pre class="programlisting">
/** 快速排序法的Java实现 */
public class Sorter
{
    public static &lt;T extends Comparable&lt;? super T&gt;&gt; void qsort(T[] list)
    {
        qsort(list, 0, list.length - 1);
    }

    private static &lt;T extends Comparable&lt;? super T&gt;&gt; void qsort(T[] list, int low, int high)
    {
        if (low &gt;= high) return;

        int i = low - 1, j = high + 1;
        T pivot = list[low]; // 基准元素

        for ( ; ; )
        {
            do { ++i; } while (list[i].compareTo(pivot) &lt; 0);
            do { --j; } while (list[j].compareTo(pivot) &gt; 0);

            if (i &gt;= j) break;

            // 交换
            T tmp = list[i]; list[i] = list[j]; list[j] = tmp;
        }

        // 找到分割点j，递归
        qsort(list, low, j);
        qsort(list, j + 1, high);
    }
}</pre></div><p>
            “请问这里用到了哪些编程范式？”冒号提问。
        </p><p>
            叹号心想，有何难哉？遂答：“既然是用Java实现的，自然少不了OOP。同时为了使算法更具普适性，还用到了泛型编程。”
        </p><p>
            “你好像忘记了最重要的过程式，反倒是OOP的色彩极淡。”冒号显然不满意他的答案。
        </p><p>
            叹号不解：“不是说Java是100%的OOP语言吗？”
        </p><p>
            冒号颇为不屑：“不要轻信这种浮浅之论。且不说Java的<span class="term">基本类型</span>（primitive type）不属于<span class="term">类</span>（class），本就不是100%的OOP，即使是100%的OOP，那与过程式也不矛盾啊。此例中的Sorter类连一个<span class="term">实例成员</span>（instance member）也没有，唯一与OOP沾边的是作为interface的Comparable，在C中也可用函数指针代替。如果不考虑泛型式的特征，本例无论用Java还是用C，并没有本质差别。事实上，对于这类纯算法的问题，OOP范式本无太多用武之地。换句话说，quicksort虽然是通过以OOP著称的Java来实现的，但用的主要还是过程式的思想和方法。”
        </p><p>
            问号赶紧问道：“还能用其他范式来实现吗？”
        </p><p>
            此问正合冒号之意：“我们改用纯函数式语言Haskell来试试——”
        </p><div class="informalexample"><pre class="programlisting">
-- 快速排序法的Haskell实现 
qsort :: (Ord a) =&gt; [a] -&gt; [a]  --函数声明
qsort[] = []                    --递归终点 
qsort(pivot : rest) = qsort[x| x &lt;- rest, x &lt; pivot]         --对前面的子列递归
                          ++ [pivot]
                          ++ qsort[x| x &lt;- rest, x &gt;= pivot] --对后面的子列递归</pre></div><p>
            叹号几不能信：“竟然可以如此精炼？”
        </p><p>
            “上面的Java代码很难再精简了，但与Haskell代码相比还是太冗长了。后者省去了所有的赋值、迭代和流程控制，只有单纯的递归，反映了典型的函数式特征。”冒号解说着，“鉴于你们对Haskell不太熟悉，我稍微解释一下。第一步，声明函数类型<a class="link" href="#note3"><sup>[3]</sup></a>：同类型列表之间的变换，其中Ord可类比Java中的Comparable，以保证列表元素之间能进行比较；第二步，声明递归终点：空列排序后仍是空列；第三步，描述递归原则：基准元素pivot与剩余子列rest进行排序后的列表，正是将小于基准的子列和超过基准的子列分别排序，中间插入基准元素后的结果。”
        </p><p>
            句号思有所得，不禁喜形于色：“我明白了，这两段代码生动地反映了命令式编程与声明式编程之间的差别：前者需要指定计算的过程，后者只需指定计算的原则。一个着重微观的细节，一个着重宏观的方向，自有繁简之别。”
        </p><p>
            冒号亦有所慰：“非常好！类似的话我以前也说过，但你们自己说的才是真正的收获啊。我们还提过，过程式与函数式的差别同时也是机器思维与数学思维的差别。不妨对比Haskell表达式与数学中的集合表达式，它们是多么地相近！”
        </p><p>
            黑板上出现两行式子——
        </p><div class="informalexample"><pre class="programlisting">
数学表达式：   {x| x ∈ rest, x &lt; pivot} 
Haskell表达式：[x| x &lt;- rest, x &lt; pivot] </pre></div><p>
            逗号仔细打量着：“嗯，的确像，跟哥俩似的，连符号&lt;-都是仿照集合属于符（∈）的。”
        </p><p>
             “还有另一种表达方法。”冒号又添加了一行——
        </p><div class="informalexample"><pre class="programlisting">
Haskell表达式2 ：(filter (&lt; pivot) rest) </pre></div><p>
            “虽然与前一表达式的简洁度相差无几，但可读性更强。filter即是过滤，将列表rest中的元素进行筛选，条件是小于基准元素。”冒号讲解道。
        </p><p>
            问号略感迷惑：“（&lt; pivot）的用法看起来有点怪异。”
        </p><p>
            “它是一个函数，也是filter的第一个参数，用来判断第二个参数rest的元素是否合格，即 &lt; pivot。这体现了函数式的一个重要特征：函数是头等公民（first-class citizen）——可作为传递参数、可作为表达式的值、可嵌入数据结构、也可与某变量绑定，与普通的基本数据类型毫无二致。这类函数更数学化的叫法是<span class="term">高阶函数</span>（higher-order function），它是函数式编程<span class="emphasis"><em>简洁而强大</em></span>的重要根源。”冒号细加解释，“大家还记得上节课谈到的回调函数吧？callback无非是将函数作为参数来传递，本质上是将<span class="emphasis"><em>代码</em></span>当<span class="emphasis"><em>数据</em></span>来使用，回调机制的巨大威力均拜此高级用法所赐。”
        </p><p>
            众人又一段经脉被打通了。
        </p><p>
            引号提出一个很实际的问题：“函数式编程的确很酷，可Java并不支持。如果采用Haskell之类的函数式语言，会不会带来系统集成上的困难？”
        </p><p>
            冒号打消了他的疑虑：“Java平台下已经集成了不少的支持函数式编程的语言，如JRuby、Jython、Groovy、Scala等，甚至Haskell在JVM下也有相应的Jaskell。其中，Groovy与Java的结合最为自然。此外，C#3.0引入了lambda表达式，也能方便地支持函数式。我们看一下它们是如何实现quicksort的——”
        </p><div class="informalexample"><pre class="programlisting">
/** 快速排序法的Groovy实现 */
def qsort(list) {
    if (list.size() &lt;= 1) return list

    def pivot = list[0]
    return (qsort(list.findAll{x -&gt; x &lt; pivot})
          +             list.findAll{x -&gt; x == pivot}
          +    qsort(list.findAll{x -&gt; x &gt; pivot}))
} </pre></div><p/><div class="informalexample"><pre class="programlisting">
/** 快速排序法的C#3.0实现 */
IEnumerable&lt;T&gt; qsort&lt;T&gt;(IEnumerable&lt;T&gt; list) where T : IComparable&lt;T&gt;
{
    if (list.Count() &lt;= 1) return list;

    var pivot = list.First();
    return qsort(list.Where(x =&gt; x.CompareTo(pivot) &lt; 0))
                .Concat(list.Where(x =&gt; x.CompareTo(pivot) == 0))
                .Concat(qsort(list.Where(x =&gt; x.CompareTo(pivot) &gt; 0)));
} </pre></div><p>
            “以上两种方法如出一辙，虽然比Haskell的代码略长了些，并且还带着过程式的烙印，但总体思想还是函数式的。”冒号紧扣本质，“函数式还有一个重要特征：无副作用或尽量减少副作用<a class="link" href="#note4"><sup>[4]</sup></a>。所谓无副作用，是指一个函数在被调用前后保持程序的状态不变。无副作用的函数不会改变非局部变量的值，不会改变传入的参数，也没有I/O操作。”
        </p><p>
            逗号脱口而出：“什么状态都不变，那这样的函数有什么用？”
        </p><p>
            冒号不以为奇：“你的这种想法源自根深蒂固的命令式思维。我们曾把命令式程序比作状态自动机，其运行过程就是在不断地修改机器的状态。而函数式程序则是进行表达式变换，一般不会改变变量的值。其实函数式并非完全不改变内存，只不过改变的是<span class="term">栈内存</span>（stack）罢了。换言之，无副作用函数的作用关键在于其估值结果，按过程式的说法是返回值。刚才的quicksort不正是如此吗？”
        </p><p>
            逗号如梦初醒。
        </p><p>
            冒号旋即补充：“当然，在涉及流程控制、顺序操作、状态维护、I/O运算等问题时，没有副作用是很不方便的。函数式语言一般也会提供变通的方式，比如Haskell、Ruby、Python、Scala等语言支持的monad便是这样一种机制。”
        </p><p>
            问号仍有疑问：“药物最好没有副作用，函数没有副作用的好处是什么？”
        </p><p>
            冒号嘴一咧：“好处太多了。首先，没有副作用的函数易于重构、调试和单元测试。其次，代码有效性与函数顺序无关，方便并发处理和优化处理。举个简单的例子，计算两个函数的乘积：f(x) * g(y)。由于无副作用，f(x) 和g(y)的估值过程是独立的，估值顺序也不重要，因此理论上可以对二者并行计算。另外，还可利用<span class="term">惰性求值</span>（lazy evaluation）<a class="link" href="#note5"><sup>[5]</sup></a>：如果算出f(x)为零，那么不用计算g(y)便可知乘积为零了。”
        </p><p>
            叹号忍不住赞叹：“听起来真不错！”
        </p><p>
            冒号突然扭头写下一行字，然后提问：“请问这个unix命令表示什么意思？”
        </p><div class="informalexample"><pre class="programlisting">
grep the BigFile.txt | head </pre></div><p>
            引号回答：“这是要打印出文件BigFile.txt中包含字符串‘the’的十行文字。”
        </p><p>
            冒号点头：“没错，这是unix<span class="term">管道</span>（pipe）的用法。它由两个命令组成：grep和head，前者的输出是后者的输入。一个有趣的事实是，后者不用等到前者执行完毕才启动。更有趣的是，只要后者获取了足够的数据，前者便会停止执行。故而当grep在给定文件中找到含有给定字符串的十行文字后，即可功成身退，因为那已是head的全部所需。假如没有管道机制，那就只能这样——”
        </p><p>
            
        </p><div class="informalexample"><pre class="programlisting">
grep the BigFile.txt &gt; tmpfile; head tmpfile </pre></div><p>
            “这样不仅产生了多余的临时文件，而且效率大大降低。这与惰性求值有关吗？”句号隐隐明白了冒号的用意。
        </p><p>
            “类似地，通常要计算f(g(x))的值，需要计算完g(x)后才能将所得值代入函数f。有了惰性求值机制，g(x)的计算完全由函数f的需求来驱动，避免做无用功。此乃其惰性之所在。”
        </p><p>
            逗号挑起大拇指：“这个懒偷得真聪明！”
        </p><p>
            “惰性求值不仅能节省有限的时间，还能超越无限的时间——g(x)甚至可以永不退出，从而可能产生无穷的输出结果集供函数f使用。这可是在过程式编程中想都不敢想的事啊。”冒号言犹未尽，“最后，没有副作用的函数是<span class="term">引用透明的</span>（referential transparency），即一个表达式随时可以用它的值来替换<a class="link" href="#note6"><sup>[6]</sup></a>，正如数学中的函数一样，保证了数学思维的贯彻和运用。”
        </p><p>
            引号自感获益颇丰：“前面介绍范式时，觉得函数式最为神秘。现在总算有了些感性认识了。”
        </p><p>
            冒号道出缘由：“函数式编程不仅有许多独特的概念和方法，还有很深的数学背景——<span class="term">λ-演算</span>（lambda calculus）。如果一开始便倾囊相授，你们必会望而却步，我岂不是打草惊蛇了？”
        </p><p>
            众人始觉：老冒狡猾狡猾的，原来在诱敌深入啊。
        </p><p>
            “尽管函数式有这么多优点，运算能力从理论上比诸过程式也毫不逊色<a class="link" href="#note7"><sup>[7]</sup></a>，但一直没有成为主流范式。”冒号话锋一转，“细究之，至少有两方面的原因：主观上，程序员更习惯<span class="emphasis"><em>机器风格</em></span>的过程式思维和<span class="emphasis"><em>现实风格</em></span>的OOP思维，不容易接纳<span class="emphasis"><em>数学风格</em></span>的函数式思维；客观上，函数式语言在表现力和运行效率<a class="link" href="#note8"><sup>[8]</sup></a>等方面与过程式和OOP语言也有一定的差距。饶是如此，支持它的语言还是越来越多，其简洁精巧的特性也为越来越多的人所青睐。它的整体应用虽然主要集中于数学计算、人工智能等领域，但局部应用早已遍地开花了。”
        </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>
                    如OOA（Object-Oriented Architecture），COA（Component-Oriented Architecture），SOA（Service-Oriented Architecture）、EDA（Event-Driven Architecture）等。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    如关系数据库（relational database）、对象导向式数据库（object-oriented database）等。
                </p></li><li class="listitem"><p><a name="note3"></a>
                    这一步可省略，但出于对代码的清晰度以及性能、调试等方面的考虑，最好保留。
                </p></li><li class="listitem"><p><a name="note4"></a>
                    没有副作用的函数式语言被称为纯函数式（purely functional），如Haskell和SISAL；有副作用的被称为非纯函数式（impurely functional），如Lisp和ML。不过Haskell等语言也可通过monad来实现包括I/O在内的副作用。
                </p></li><li class="listitem"><p><a name="note5"></a>
                    惰性求值又称为延迟求值（delayed evaluation）或非严格求值（non-strict evaluation）。参考文献【3】对惰性求值和高阶函数的意义作了深入的阐述。
                </p></li><li class="listitem"><p><a name="note6"></a>
                    例如，如果square是一个平方函数，那么square(3)总可用9来代替。这是因为函数square的无副作用性保证了相同的输入（此处是3）一定有相同的输出（此处是9）。
                </p></li><li class="listitem"><p><a name="note7"></a>
                    λ-演算被证明是图灵完备的。
                </p></li><li class="listitem"><p><a name="note8"></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>
                    在函数式编程中，函数是程序的核心，是头等公民，一般没有或很少副作用，同时没有显式的内存管理。
                </p></li><li class="listitem"><p>
                    由于函数式编程中的高阶函数与基本数据类型平起平坐，故可将代码作数据用，这是程序既简洁又强大的原因之一。回调机制采用的正是函数式风格。
                </p></li><li class="listitem"><p>
                    无副作用的函数容易重构、调试和测试，便于并发和优化处理，并能贯彻和运用数学思维。
                </p></li><li class="listitem"><p>
                    惰性求值是需求驱动的，可以避免不必要的等待和计算。
                </p></li><li class="listitem"><p>
                    相比过程式和OOP，函数式编程思想过于数学化和抽象化，语言的表现力和运行效率也有所不足。
                </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>
                    Michael Lee Scott．Programming Language Pragmatics．San Francisco：Morgan Kaufmann，2000．589-620
                </p></li><li class="listitem"><p>
                    Stephen H. Kaisler．SOFTWARE PARADIGMS．New Jersey：Wiley，2005．23-24
                </p></li><li class="listitem"><p>
                    John Hughes．Why Functional Programming Matters．The Computer Journal，1989，32(2)：98-107
                </p></li></ol></div></div></div><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F09%2F13%2Fcolon-class-4_1%2F&amp;linkname=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A74.1%EF%BC%9A%E5%87%BD%E6%95%B0%E8%8C%83%E5%BC%8F">分享/保存</a>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/09/13/colon-class-4_1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>冒号课堂§3.4：事件驱动</title>
		<link>http://blog.zhenghui.org/2009/09/11/colon-class-3_4/</link>
		<comments>http://blog.zhenghui.org/2009/09/11/colon-class-3_4/#comments</comments>
		<pubDate>Fri, 11 Sep 2009 12:37:48 +0000</pubDate>
		<dc:creator>hui zheng</dc:creator>
				<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=393</guid>
		<description><![CDATA[<b>事件驱动</b>——有事我叫你，没事别烦我（<em>事件驱动式编程简谈</em>）<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: 宋体">第三课 常用范式(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="id578923"></a>3.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><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>异步过程特点和作用是什么？</p></li><li class="listitem"><p>事件驱动式编程最重要的特征是什么？它们是如何实现的？</p></li><li class="listitem"><p>事件驱动式与观察者模式、MVC架构有何关系？</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>
            “第一种方式睡不踏实，不得已而为之。敢用第二种方式的人多半没心没肺，估计IT人都达不到那种境界。只要有同伴在身旁，我想大家都会选第三种方式的。”句号的回答获得一致认同。
        </p><p>
            冒号续问：“好，抛开第二种方式不谈，为什么第三种要比第一种优越呢？”
        </p><p>
            句号回答：“犯困者既要打盹又要警戒，必然苦不堪言。如果把警戒的任务委托同伴，两人分工合作，自然愉快得多。”
        </p><p>
            冒号再问：“他们是如何合作的呢？”
        </p><p>
            “放哨者一旦发现有情况，立即通知犯困者采取行动——睁眼坐直，作认真听讲状。”句号说得是绘声绘色。
        </p><p>
            除了两位当事人略显尴尬外，其他人均乐不可支。
        </p><p>
            眼见时机成熟，冒号不再兜圈：“采用警觉式者<span class="emphasis"><em>主动去</em></span><span class="strong"><strong>轮询</strong></span>（polling），行为取决于自身的观察判断，是<span class="emphasis"><em>流程驱动</em></span>的，符合常规的<span class="term">流程驱动式编程</span>（Flow-Driven Programming）的模式。采用托付式者<span class="emphasis"><em>被动等</em></span><span class="strong"><strong>通知</strong></span>（notification），行为取决于外来的突发事件，是<span class="emphasis"><em>事件驱动</em></span>的，符合<span class="term">事件驱动式编程</span>（Event-Driven　Programming，简称EDP）的模式。下面我们就来说说这种编程范式。”
        </p><p>
            逗号瓮声瓮气道：“没想到打瞌睡打出了个范式。”
        </p><p>
            冒号瞥了他一眼，继续说下去：“为完成一样事，既可以采用流程驱动式，也可以采用事件驱动式。这样的例子在生活中可谓俯拾即是，刚才逗号同学为大家现场示范了一个，谁还能举出其他范例？”
        </p><p>
            叹号抢先举例：“与客户打交道，推销员主动打电话或登门拜访，他的工作是流程驱动的；接线员坐等电话，他的工作是事件驱动的。”
        </p><p>
            问号也说：“同样是交通工具，公共汽车主要是流程驱动的，它的路线已预先设定；出租车主要是事件驱动的，它的路线基本上由随机搭载的乘客所决定。” 
        </p><p>
            引号以个人经验作例：“购买喜爱的杂志可以选择频繁光顾报刊亭，也可以选择一次性订阅。浏览关注的新闻网站或博客，可以直接访问站点，也可以订阅相应的RSS。主动检查所关心的内容是否更新是流程驱动的，用订阅的方式是事件驱动的。”
        </p><p>
            句号回到本行：“Windows下的许多工作既可以在DOS下用批处理程序实现，也可以在图形界面下完成。前者不需人工干预，显然是流程驱动的；后者毫无疑问是事件驱动的。”
        </p><p>
            “看来你们对这种范式很熟悉嘛。不过，它原理虽简单，威力却无穷。看似一招，实则暗藏百式，甚可幻化千招。个中精妙之处，断非一时可以尽述。”冒号不知不觉中又走进了武侠的世界。
        </p><p>
            众人听了，暗疑老冒有些言过其实。
        </p><p>
            冒号正式入题：“首当其冲的问题是：何谓事件？通俗地说，它是已经发生的某种令人关注的事情。在软件中，它一般表现为一个程序的某些信息状态上的变化。基于事件驱动的系统一般提供两类的<span class="term">内建事件</span>（built-in event）：一类是<span class="term">底层事件</span>（low-level event）或称<span class="term">原生事件</span>（native event），在用户图形界面（GUI）系统中这类事件直接由鼠标、键盘等硬件设备触发；一类是<span class="term">语义事件</span>（semantic event），一般代表用户的行为逻辑，是若干底层事件的组合。比如鼠标拖放（drag-and-drop）多表示移动被拖放的对象，由鼠标按下、鼠标移动和鼠标释放三个底层事件组成。”
        </p><p>
            问号推想：“编程人员应该还能创造新的事件类型吧？”
        </p><p>
            “那是当然。”冒号点点头，“还有一类<span class="term">用户自定义事件</span>（user-defined event）。它们可以是在原有的内建事件的基础上进行的包装，也可以是纯粹的<span class="term">虚拟事件</span>（virtual event）。除此之外，编程者不但能定义事件，还能产生事件。虽然大部分事件是由外界激发的<span class="term">自然事件</span>（natural event），但有时程序员需要主动激发一些事件，比如模拟用户鼠标点击或键盘输入等，这类事件被称为<span class="term">合成事件</span>（synthetic event）<a class="link" href="#note1"><sup>[1]</sup></a>。这些都进一步丰富完善了事件体系和事件机制，使得事件驱动式编程更具渗透性。”
        </p><p>
            叹号嘟哝了一句：“看来这里边还有点名堂。”
        </p><p>
            “名堂多着呢！”冒号回应，“事件固然是事件驱动式编程的核心概念，但一个编程范式的独特之处绝不仅仅是一些概念，更重要的是建立于这些概念之上的思维模式。为了了解这种范式与众不同的特点，我们先看看如何利用win32的API在windows下创建一个简单的窗口——”
        </p><div class="informalexample"><pre class="programlisting">
/** 一个win32窗口程序 */
…WinMain(...) // windows应用程序的主函数
{
    // 第一步——注册窗口类别
    ...;
    windowClass.lpfnWndProc = WndProc; // 指定该类窗口的回调函数
    windowClass.lpszClassName = windowClassName; // 指定该类窗口的名字
    RegisterClassEx(&amp;windowClass);

    //第二步——创建一个上述类别的窗口
    CreateWindowEx(…, windowClassName, ...);
    …;

    //  第三步——消息循环
    while (GetMessage(&amp;msg, NULL, 0, 0)  &gt; 0) // 获取消息
    {
        TranslateMessage(&amp;msg); // 翻译键盘消息
        DispatchMessage(&amp;msg);  // 分派消息
    }
}

// 第四步——窗口过程（处理消息）
…WndProc(…, msg,...)
{
    switch (msg)
    {
        case WM_SIZE:   …;   // 用户改变窗口尺寸
        case WM_MOVE: …; // 用户移动窗口
        case WM_CLOSE: …; // 用户关闭窗口
        …;
    }
}</pre></div><p>
            “没有选用Java、Visual C++、C#、VB或者Delphi来实现窗口，是因为它们高度的封装和强大的IDE掩盖了部分事件机制。如果你们对win32 API不太熟悉，没有关系。为了减少语言和API上的障碍，同时突出重点，这里最大限度地省略了次要的过程和参数等，仅保留脉络主干。”冒号解释，“从中看出到，创建一个能响应用户操作的win32窗口共分四步：注册窗口类别、创建窗口、消息循环和窗口过程。”
        </p><p>
            问号对概念很敏感：“消息与事件是一回事吗？”
        </p><p>
            “严格说来它们不是一回事，但如果你不想深究，不加区分也无大碍。概略地说，消息是Windows内部最基本的通讯方式，事件需要通过消息来传递，是消息的主要来源。每当用户触发一个事件，如移动鼠标或敲击键盘，系统都会将其转化为消息并放入相应程序的消息队列（message queue）中<a class="link" href="#note2"><sup>[2]</sup></a>。”冒号解答着，“明白了这一点，上面的代码就不难理解了——在<span class="emphasis"><em>消息循环</em></span>中，程序通过GetMessage不断地从消息队列中获取消息，经过TranslateMessage预处理后再通过DispatchMessage将消息送交<span class="emphasis"><em>窗口过程</em></span>WndProc处理。”
        </p><p>
            逗号琢磨了一会，不解地问：“窗口过程应该是在分派消息时被调用的，但我怎么想不出DispatchMessage是如何联系到WndProc的？”
        </p><p>
            冒号为其解惑：“DispatchMessage的消息参数含有事发窗口的<span class="term">句柄</span>（handle），从而可以得到窗口过程WndProc<a class="link" href="#note3"><sup>[3]</sup></a>。至于窗口与窗口过程之间是如何建立联系的，回看前面两步就一目了然了：当初在创建窗口时指明了窗口类别名windowClassName，而窗口类别windowClass又绑定了窗口过程。”
        </p><p>
            叹号有点纳闷：“干嘛要绕这么大的弯子，直接调用WndProc不就得了？”
        </p><p>
            “对于这个简单的程序来说，的确区别不大。但假如再增添其他菜单、按钮、文本框之类的控件，每个控件都可绑定自己的窗口过程，那么到底该调用哪个才对呢？”冒号反问。
        </p><p>
            叹号虽有所悟，但仍有心结：“总觉得窗口过程的用法有些怪怪的。”
        </p><p>
            冒号一敲桌案：“没错！怪就怪在编程者自己写了一个应用层的函数，却不直接调用它，而是通过库函数<span class="emphasis"><em>间接调用</em></span>。这类函数有个专用名称：<span class="term">回调函数</span>（callback）。”
        </p><p>
            引号忍不住插话：“回调函数我知道，在C和C++中就是函数指针嘛。”
        </p><p>
            “确切地说，函数指针是C和C++用来实现callback的一种方式。此外，抽象类（abstract class）、接口（interface）、C++中的泛型函子（generic functor）和C#中的委托（delegate）都可实现callback。我们先图解一下回调机制。”冒号调出一张图示——
        </p><div class="figure"><a name="id619557"></a><p class="title"><b>图3-4. 普通函数与回调函数的对比</b></p><div class="figure-contents"><div class="mediaobject"><img src="http://blog.zhenghui.org/img/colonclass/figure3-4.jpg" alt="普通函数与回调函数的对比"></div></div></div><br class="figure-break"><p>
            “如果我们把系统划分为两层<a class="link" href="#note4"><sup>[4]</sup></a>：低层的函数库和高层的应用程序。同样作为主函数的辅助函数，左图中的普通函数直接被主函数调用，然而右图中的回调函数却是通过库函数间接被主函数调用的。”冒号的手影在幻灯下上下翻飞。
        </p><p>
            句号点出要害：“一般都是高层代码调用低层代码，callback反其道而行之，因此显得与众不同。”
        </p><p>
             “所言极是。一方面，在软件模块分层中，低层模块为高层模块提供服务，并且不能依赖高层模块，以保证其可重用性；另一方面，通常被调者（callee）为调用者（caller）提供服务，调用者依赖被调者。两相结合，决定了低层模块多为被调者，高层模块多为调用者。但这种惯例并不总是合适的——低层模块为了追求更强的普适性和可扩展性，有时也有调用高层模块的需求，于是便邀callback前来相助。我们看一个简单的例子。”冒号写下一段Java代码——
        </p><div class="informalexample"><pre class="programlisting">
String[] strings = {"Please", "sort", "the", "strings", "in", "REVERSE", "order"};
Arrays.sort(strings, new Comparator&lt;String&gt;() {
    public int compare(String a, String b){ return -a.compareToIgnoreCase(b); }
    });</pre></div><p>
            引号很快读懂了代码：“这是将字符串组不区分大小写地逆序排列。其中Comparator的匿名类实现了callback，因为它的方法compare是在类库中被调用的。”
        </p><p>
            “此处callback的好处是显而易见的——它使得Arrays.sort不再局限于自然排序，允许用户自行定制排序规则，大大提高了算法的重用性。”冒号说着将幻灯片又翻到前页，“回头再看win32窗口程序的例子，其中第三步消息循环那段代码不依赖应用程序代码，完全可以提炼出来作为library的一部分。事实上，在Visual C++里这段代码就‘下放’到MFC类库中去了。假设窗口过程由应用程序直接调用，那么消息循环中的代码将不再具有独立性，无法作为公因子分解出来。”
        </p><p>
            叹号块垒顿消，畅然无比：“终于搞清那个怪异的窗口过程了！每个窗口在创建时就携带了一个callback，以后每当系统侦查到事件，都能轻易地从事发窗口身上找到它的callback，然后调用它以响应事件。”
        </p><p>
             “这等于将侦查事件与响应事件两项任务进行了正交分解，降低了软件的耦合度和复杂度。”句号言犹未尽，又加了一句，“就像刚才，引号负责侦查事件——警戒，逗号负责响应事件——警醒。想法很好，可惜配合不够默契，还是给人逮住了。”
        </p><p>
            逗、引二人大窘，余者大笑。
        </p><p>
            “仔细比较，以上两个callback的用法还是稍有不同的。在字符串组排序中，callback在作为参数传入低层的函数后，很快就在该函数体中被调用；在窗口程序中，callback则先被储存起来，至于何时被调用完全是未定之数。用一句话概括：前者属<span class="emphasis"><em>同步</em></span>（synchronous）回调，后者属<span class="emphasis"><em>异步</em></span>（asynchronous）回调。它们都<span class="strong"><strong>使调用者不再依赖被调者</strong></span>，将二者<span class="strong"><strong>从代码上解耦</strong></span>，异步调用更将二者<span class="strong"><strong>从时间上解耦</strong></span>。”冒号显示出一副新图—— 
        </p><div class="figure"><a name="id619643"></a><p class="title"><b>图3-5. 异步回调</b></p><div class="figure-contents"><div class="mediaobject"><img src="http://blog.zhenghui.org/img/colonclass/figure3-5.jpg" alt="异步回调"></div></div></div><br class="figure-break"><p>
            “图中处于低层的软件平台是在win32 API的基础上的改进。不仅把主循环从应用程序中<span class="emphasis"><em>沉淀</em></span>下来，而且将储存callback的过程封装在一个注册函数中，使得应用程序代码变得更简洁、健壮。同时我们看到，整个流程的控制权已经从应用程序的主程序转移到底层平台的主循环中，符合好莱坞原则。”冒号。
        </p><p>
            逗号好奇地问：“什么是好莱坞原则？”
        </p><p>
             “don&#8217;t call us, we&#8217;ll call you.”冒号难得甩出一句洋文，“我很想画蛇添足地在末尾加上单词‘back’，这样更容易理解callback的含义：‘call you back’。此话的背景大约是这样的：一个艺人要想演出，需与好莱坞的经纪公司联系。由于幻想一朝成名的人太多，经纪人总是牛气十足，他们的口头禅是：‘别打电话给我们，留下你的电话，有活干我们会打给你的’。”
        </p><p>
            引号认真地解析：“好莱坞经纪公司相当于一个背后运作的软件平台，艺人相当于一个callback，‘留下你的电话’就是注册callback，‘我们会打给你的’就是异步调用callback。”
        </p><p>
            冒号接着补充：“‘别打电话给我们’意味着经纪公司处于主导地位，艺人们处于受控状态，这便是<span class="term">控制反转</span>（Inversion of Control，简称IoC）。”
        </p><p>
            问号听着耳熟：“控制反转？第一课谈到框架时似乎提到过。”
        </p><p>
             “没错，正是它！”冒号谈兴愈浓，“一般library中用到callback只是局部的控制反转，而framework将IoC机制用到全局。程序员牺牲了对应用程序流程的主导权，换来的是更简洁的代码和更高的生产效率。如果将编程譬比命题作文，不用framework的程序是一张可以自由写作的白纸，library是作文素材库；采用framework的程序是一篇成型的作文，作者只需填写空白的词语和段落即可。”
        </p><p>
            叹号为之一叹：“唉，编程序变成了做填空题，真没劲！ ”
        </p><p>
             “那你就多努力，争取以后出填空题吧。”冒号笑着鼓励他，“控制反转不仅增强了framework在代码和设计上的重用性，还极大地提高了framework的可扩展性。这是因为framework的内部运转机制虽是封闭的，但也开放了不少与外部相连的扩展接口点，类似插件（plugin）体系。如下图所示——”
        </p><div class="figure"><a name="id619702"></a><p class="title"><b>图3-6. 框架的IoC机制</b></p><div class="figure-contents"><div class="mediaobject"><img src="http://blog.zhenghui.org/img/colonclass/figure3-6.jpg" alt="框架的IoC机制"></div></div></div><br class="figure-break"><p>
            引号联想到另一个名词：“我知道有个依赖反转，与控制反转是一回事吗？”
        </p><p>
            冒号简答：“虽然不少人把它们看成同义词，但<span class="term">依赖反转原则</span>（Dependency-Inversion Principle，简称DIP）更加具体——高层模块不应依赖低层模块，它们都应依赖抽象；抽象不应依赖细节，细节应依赖抽象。经常相提并论的还有<span class="term">依赖注射</span>（Dependency Injection，简称DI）——动态地为一个软件组件提供外部依赖。由于时间关系，它们之间的区别容后再叙。有一点可以看出，它们的<span class="strong"><strong>主题是控制与依赖，目的是解耦，方法是反转，而实现这一切的关键是抽象接口</strong></span>。”
        </p><p>
            “为什么说是抽象接口而不是前面所说的回调函数？”打过瞌睡的逗号现在似乎变得特别清醒。
        </p><p>
            冒号予以说明：“回调函数的提法较为古老，多出现于过程式编程，抽象接口是更现代、更OO的说法。另外从字面上看，‘回调’强调的是<span class="emphasis"><em>行为方式</em></span>——低层反调高层，而‘抽象接口’强调的是<span class="emphasis"><em>实现方式</em></span>——正是由于接口具有抽象性，低层才能在调用它时无需虑及高层的具体细节，从而实现控制反转。”
        </p><p>
            众人细细品味着冒号的这番话。
        </p><p>
            问号忽然惊觉：“我们是不是跑题了？本来是谈事件驱动式编程的，结果从callback谈到控制反转，再到框架，现在又说起了抽象接口。”
        </p><p>
            “事物是普遍联系的嘛。”冒号扯了句哲学套话，“不谙熟callback和IoC机制，就不可能真正领会事件驱动式编程的精髓。不过，也该回到中心主题了。我们通过win32 API用四步实现了一个简单的窗口程序，与事件直接相关的有三步：实现<span class="term">事件处理器</span>（event handler）或<span class="term">事件监听器</span>（event listener）；注册事件处理器；实现<span class="term">事件循环</span>（event loop）。具体上，事件处理器负责处理事件，经注册方能在事发时收到通知；事件循环负责侦查事件、预处理事件、管理事件队列和分派事件等，无事时默默等待，有事时立即响应，生命不息工作不止。在整个事件机制中，主循环好比心脏，事件处理器好比大脑，是最重要的两类模块。”
        </p><p>
            句号指出：“在支持事件驱动的开发环境中，主循环是现成的。许多IDE的图形编辑器在程序员点击控件后，还能自动生成事件处理器的骨架代码，连注册的步骤也免除了。”
        </p><p>
            冒号提醒他：“并不是总有这样的好事，要知道事件驱动式并不局限于GUI应用，支持事件驱动的开发环境也未必唾手可得。程序员有时必须自行设计整个事件系统，他需要决定：采用事件驱动式是否合适？如果合适，如何设计事件机制？其中包括事件定义、事件触发、事件侦查、事件转化、事件合并、事件调度、事件传播、事件处理、事件连带（event cascade）<a class="link" href="#note5"><sup>[5]</sup></a>等等一系列问题。”
        </p><p>
            逗号扮着苦相说：“我的脑袋就是一个事件监听器，在听到要面临这么多的事件后，迅速作出反应——大了一圈。”
        </p><p>
            众皆弯腰捧腹。
        </p><p>
            “脑袋能变大是件好事啊，说明它伸缩性强，相信用它来编的程序也是一样。”冒号打着哈哈，“事件驱动式的程序可伸缩性就很强，知道为什么吗？”
        </p><p>
            叹号随口说道：“不是因为利用回调函数实现了控制反转吗？”
        </p><p>
            “非也非也。”冒号文绉绉地说，“软件的<span class="term">可伸缩性</span>（scalability）一般指从容应对工作量增长的能力，常与性能（performance）等指标一并被考量。而控制反转的主要作用是降低模块之间的依赖性，从而降低模块的耦合度和复杂度，提高软件的可重用性、柔韧性和可扩展性，但对可伸缩性并无太大帮助。我们已经看到，控制反转导致了事件驱动式编程的<span class="strong"><strong>被动性（passivity）</strong></span>。此外，事件驱动式还具有<span class="strong"><strong>异步性（asynchrony）</strong></span>的特征，这是由事件的不可预测性与随机性决定的。如果一个应用中存在一些该类特质的因素，比如频繁出现<span class="term">堵塞呼叫</span>（blocking call），不妨考虑将其包装为事件。”
        </p><p>
            问号打岔道：“什么是堵塞呼叫？”
        </p><p>
            冒号作了个比方：“在高速公路上一辆车突然出故障停在路途，急调维修人员。如果现场修理，在修好之前所在车道是堵塞的，后面车辆无法通行。类似地，在程序中一些函数需要等待某些数据而不能立即返回<a class="link" href="#note6"><sup>[6]</sup></a>，从而堵塞整个进程。”
        </p><p>
            引号道出常识：“显然更可取的修车做法是：先把车拖到路边，修完后向其他车辆发出信号，以便重回车道。”
        </p><p>
            冒号趁热打铁：“同理，我们可以让堵塞呼叫暂时脱离主进程，事成之后再利用事件机制申请重返原进程。相比第一种同步流程式的方案，这种异步事件式将连续的进程中<span class="emphasis"><em>独立且耗时</em></span>的部分抽取出来，从而减少随机因素造成的资源浪费，提高系统的性能和可伸缩性。”
        </p><p>
            问号听得仔细：“为什么抽取的部分是‘独立且耗时’，而不是‘随机且耗时’？”
        </p><p>
            “问得好！”冒号很欣赏他严谨的学风，“再拿修车来说，第二种方案之所以可行有两方面原因：一是修车耗时，二是修车独立。所谓独立又有两层含义：与车道独立——修车时不必占用车道；与后车独立——后面车辆不必恭候该车。如果一分钟内能修好，或者路边没有足够空位，再或者后面车辆是故障车的随行车，那么拖车方案均不成立。大家可以自己类比堵塞呼叫的情形，我就不再饶舌了。总之，<span class="strong"><strong>独立是异步的前提，耗时是异步的理由</strong></span>。至于随机嘛，只是副产品，一个独立且耗时的子过程，通常结束时间也是不可预期的。”
        </p><p>
            眼见天色已晚，冒号赶忙换上最后一页幻灯片——
        </p><div class="figure"><a name="id619872"></a><p class="title"><b>图3-7. 事件驱动式模型</b></p><div class="figure-contents"><div class="mediaobject"><img src="http://blog.zhenghui.org/img/colonclass/figure3-7.jpg" alt="事件驱动式模型"></div></div></div><br class="figure-break"><p>
            “上图为一个典型的事件驱动式模型。事件处理器事先在关注的<span class="term">事件源</span>上注册，后者不定期地发表<span class="term">事件对象</span>，经过<span class="term">事件管理器</span>的转化（translate）、合并（coalesce）、排队（enqueue）、分派（dispatch）等集中处理后，事件处理器接收到事件并对其进行相应处理。请注意事件处理器随时可以注册或注销事件源，意味着二者之间的关系是<span class="emphasis"><em>动态</em></span>建立和解除的。”冒号在幻灯屏上指指点点，“通过事件机制，事件源与事件处理器之间建立了<span class="emphasis"><em>松耦合</em></span>的<span class="emphasis"><em>多对多关系</em></span>：一个事件源可以有多个处理器，一个处理器可以监听多个事件源。再换个角度，把事件处理器视为服务方，事件源视为客户方，便是一个client-server模式。每个服务方与其客户方之间的会话（session）是异步的，即在处理完一个客户的请求后不必等待下一请求，随时可切换（switch）到对其他客户的服务。更有甚者，事件处理器也能产生事件，实现处理器接口的事件源也能处理事件，它们可以角色换位，于是又演化为peer-to-peer模式。”
        </p><p>
            叹号抱怨：“有点眼花缭乱了。”
        </p><p>
            为湿润枯燥的理论，冒号再次举例：“你们不是很喜欢在QQ上聊天吗？QQ服务器是事件管理器，每个聊天者既是事件源又是事件处理器，这正是事件驱动式的P2P模式啊<a class="link" href="#note7"><sup>[7]</sup></a>。此外，聊天时不等对方回答，就可与另一网友交谈，这就是<span class="emphasis"><em>会话切换</em></span>带来的异步效果。不过同样是聊天，改用电话就稍有不同了。”
        </p><p>
            冒号扫了 众人一眼，果见有人皱起了眉头。
        </p><p>
            “当你正用座机通话时，手机响了。你会怎么做？”冒号提示。
        </p><p>
            逗号本能地回答：“要么挂掉电话再接手机，要么让打手机的人迟些打来。”
        </p><p>
            句号听出了门道：“这说明电话的通话过程是同步而非异步的，原因是打电话双方的交流是连贯的、非堵塞式的（non-blocking），与QQ聊天正好相反。”
        </p><p>
            冒号点头称许。
        </p><p>
            虽然早已过了下课时间，引号仍是好学不倦：“我觉得观察者模式与事件驱动式很像啊。”
        </p><p>
            “你开始不是还举了订阅杂志和RSS的例子吗？<span class="term">发行/订阅模式</span>（publish-subscribe pattern）<a class="link" href="#note8"><sup>[8]</sup></a>正是<span class="term">观察者模式</span>（observer pattern）的别名，一方面可看作<span class="strong"><strong>简化或退化的事件驱动式</strong></span>，另一方面可看作<span class="strong"><strong>事件驱动式的核心思想</strong></span>。该模式省略了事件管理器部分，由事件源直接调用事件处理器的接口。这样更加简明易用，但威力有所削弱，缺少事件管理、事件连带等机制。著名的MVC（Model-View-Controller）架构正是它在架构设计上的一个应用<a class="link" href="#note9"><sup>[9]</sup></a>。”冒号长舒了一口气，准备收工，“事件驱动式的应用极广，变化极多，还涉及到框架、设计模式、架构、以及其他的编程范式，本身也可作为一种架构模型。今天我们仅仅是蜻蜓点水，更深入更具体的内容只能留后探讨了。时候不早，你们也该饿了，赶快回家吧！范式可不能当饭吃哦。”
        </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>
                    许多基于事件驱动的系统都提供了createEvent之类的API，授权编程者自行产生事件。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    更准确地说，Windows先把所有的硬件事件存入系统消息队列（system message queue），然后再放入应用程序消息队列（application message queue）。
                </p></li><li class="listitem"><p><a name="note3"></a>
                    比如可以这样从msg中得到窗口过程： (WNDPROC)GetWindowLong(msg.hwnd, GWL_WNDPROC)。
                </p></li><li class="listitem"><p><a name="note4"></a>
                    后面的论述同样适用于其他形式的软件分层结构。
                </p></li><li class="listitem"><p><a name="note5"></a>
                    指事件处理器在处理过程中又产生新的事件，从而再次触发事件处理器。
                </p></li><li class="listitem"><p><a name="note6"></a>
                    比如套接字（socket）中的accept函数。
                </p></li><li class="listitem"><p><a name="note7"></a>
                    真正的P2P网络是不需要中心服务器的，此处P2P指聊天双方是不分主客的对等关系。
                </p></li><li class="listitem"><p><a name="note8"></a>
                    有人将发行-订阅模式视为事件驱动设计的同义词，这是有道理的：在实际生活中，处于出版商与订阅者之间的邮局可作为事件管理器。
                </p></li><li class="listitem"><p><a name="note9"></a>
                    MVC也可作为一种设计模式，同样是观察者模式的应用。
                </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>
                    Callback指能作为参数传递的函数或代码，它允许低层模块调用高层模块，使调用者与被调者从代码上解耦。异步callback在传入后并不立即被调用，使调用者与被调者从时间上解耦。
                </p></li><li class="listitem"><p>
                    控制反转一般通过callback来实现，其目的是降低模块之间的依赖性，从而降低模块的耦合度和复杂度。
                </p></li><li class="listitem"><p>
                    在框架设计中，控制反转增强了软件的可重用性、柔韧性和可扩展性，减少了用户的负担，简化了用户的代码。
                </p></li><li class="listitem"><p>
                    控制反转、依赖反转原则和依赖注射是近义词，它们的主题是控制与依赖，目的是解耦，方法是反转，而实现这一切的关键是抽象接口（包括函数指针、抽象类、接口、C++中的泛型函子和C#中的委托）。
                </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>
                    观察者模式又名发行/订阅模式，既是事件驱动式的简化，也是事件驱动式的核心思想。MVC架构是观察者模式在架构设计上的一个应用。
                </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．Event-driven programming．<a class="link" href="http://en.wikipedia.org/wiki/Event-driven" target="_top">http://en.wikipedia.org/wiki/Event-driven</a>
                </p></li><li class="listitem"><p>
                    Wikipedia．Callback (computer science)．<a class="link" href="http://en.wikipedia.org/wiki/Callback_(computer_science)" target="_top">http://en.wikipedia.org/wiki/Callback_(computer_science)</a>
                </p></li><li class="listitem"><p>
                    Charles Petzold．Programming Windows, 5<sup>th</sup> ed.．Redmond：Microsoft Press，1999．41-70
                </p></li><li class="listitem"><p>
                    Robert C. Martin．Agile Software Development: Principles, Patterns, and Practices（影印版）．北京：中国电力出版社，2003．127-134
                </p></li><li class="listitem"><p>
                    Martin Fowler．Inversion of Control Containers and the Dependency Injection pattern．<a class="link" href="http://martinfowler.com/articles/injection.html" target="_top">http://martinfowler.com/articles/injection.html</a>
                </p></li><li class="listitem"><p>
                    Erich Gamma，Richard Helm，Ralph Johnson，John Vlissides．Design Patterns: Elements of Reusable Object-Oriented Software．Boston：Addison-Wesley，1994．293-299
                </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>03-01 了解C++中的STL、Java中的 Collections Framework和C#中的Collection Classes。</li>
    <li>03-02 了解C++、Java和C#中的泛型机制，比较它们之间的异同以及各自在集合（collection）中的应用。</li>
    <li>03-03 当你成功构想地并实现了一个算法，是否考虑过利用泛型编程来扩大其适用范围以提高其重用性？</li>
    <li>03-04 当你发觉几个模块中有类似的算法，是否考虑过利用泛型思想进行重构？</li>
    <li>03-05 当你发觉程序中有大量类似的代码，是否考虑过用产生式编程来自动生成它们？</li>
    <li>03-06 试着利用编译器生成器（如ANTLR）自定义一种DSL，并用它来解决问题。</li>
    <li>03-07 你采用过AOP吗？它有哪些优缺点？</li>
    <li>03-08 如何合理地抽象出系统的横切关注点？</li>
    <li>03-09 请对比流程驱动式编程与事件驱动式编程之间的差异，它们各自适合哪些应用？</li>
    <li>03-10 你编写的代码是否有足够的灵活性和可扩展性？能否利用控制反转原理？</li>
    <li>03-11 你在程序中是如何处理堵塞呼叫的？是否考虑过引入异步机制？</li>
</ul><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F09%2F11%2Fcolon-class-3_4%2F&amp;linkname=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A73.4%EF%BC%9A%E4%BA%8B%E4%BB%B6%E9%A9%B1%E5%8A%A8">分享/保存</a>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/09/11/colon-class-3_4/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
