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

<channel>
	<title>冒号空间 &#187; 编程范式</title>
	<atom:link href="http://blog.zhenghui.org/tag/programming-paradigm/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.zhenghui.org</link>
	<description>自然、人类、机器</description>
	<lastBuildDate>Fri, 16 Jul 2010 18:33:48 +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>冒号课堂§5.1：教学计划</title>
		<link>http://blog.zhenghui.org/2009/09/21/colon-class-5_1/</link>
		<comments>http://blog.zhenghui.org/2009/09/21/colon-class-5_1/#comments</comments>
		<pubDate>Mon, 21 Sep 2009 02:39:08 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[程序员]]></category>
		<category><![CDATA[编程范式]]></category>
		<category><![CDATA[迭代学习法]]></category>

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

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=385</guid>
		<description><![CDATA[<b>切面范式</b>——多角度看问题（<em>切面式编程简谈</em>）<br/>
•	从宏观角度看，太阳底下没有新鲜事——AOP无非是SoC原理和DRY原则的一种应用<br/>
•	从微观角度看，太阳每天都是新的——AOP虽自OOP的土壤中长出，却脱离藩篱自成一体<br/>
•	抽象是前提，分解是方式，模块化是结果<br/>
•	在常人眼中复杂的牛体，庖丁经过抽象，已目无全牛，及至提刀分解，自是游刃有余。待牛如土委地，模块化即成<br/>
•	两条（抽象与分解的原则）：单一化，正交化。每个模块职责明确专一，模块之间相互独立，即高内聚低耦合<br/>
•	对程序员来说，英语也是一门计算机语言，而且是必修的语言<br/>
•	OOP只能沿着继承树的纵向方向重用，而AOP则弥补了OOP的不足，可以在横向方向重用<br/>
•	如果一个程序是一个管道系统，AOP就是在管道上钻一些孔，在每个孔中注入新的代码流]]></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="id523744"></a>3.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>
                    从宏观角度看，太阳底下没有新鲜事——AOP无非是SoC原理和DRY原则的一种应用
                </p></li><li class="listitem"><p>
                    从微观角度看，太阳每天都是新的——AOP虽自OOP的土壤中长出，却脱离藩篱自成一体
                </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只能沿着继承树的纵向方向重用，而AOP则弥补了OOP的不足，可以在横向方向重用
                </p></li><li class="listitem"><p>
                    如果一个程序是一个管道系统，AOP就是在管道上钻一些孔，在每个孔中注入新的代码流
                </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>什么是SoC和DRY？</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>实施AOP有哪些步骤？</p></li><li class="listitem"><p>为什么说AOP是OOP的一种补充？</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方兴未艾，AOP又开始崭露头角。AOP算是OOP的一种补充、一种分支还是一种超越？”
        </p><p>
            叹号故作捶胸顿足状：“OOP还没有完全吃透，又来了个什么AOP。”
        </p><p>
            “不同的人对新生事物采取不同的态度。”冒号王顾左右而言他，“追星族倾向于盲目追捧，唯恐落伍，他们信奉新潮的、流行的就是好的；守旧派倾向于本能抗拒，回避求新，他们认为经典的、传统的才是好的。”
        </p><p>
            引号和叹号互视一眼，不情愿地戴上了老冒派发的帽子。
        </p><p>
            冒号续道：“从宏观角度看，太阳底下没有新鲜事——AOP无非是SoC原理和DRY原则的一种应用；从微观角度看，太阳每天都是新的——AOP虽自OOP的土壤中长出，却脱离藩篱自成一体，并且嫁接到非OOP的领地，不仅在纯过程式语言、函数式语言、甚至逻辑式语言中得到发展，而且本身也具备了一定的声明式语言特征，成为一种新的软件模块化方式。”
        </p><p>
            问号举手：“什么是SoC和DRY？”
        </p><p>
            引号代答：“SoC就是Separation of Concerns，即关注点分离；DRY是Don’t Repeat Yourself，即尽量减少重复代码。”
        </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>
            “泛型式虽未引入新类型的模块，其核心也是抽象出算法后与数据分解。”冒号为其解围，“以此类推，切面式的AOP将程序抽象分解为切面。”
        </p><p>
            问号提问：“抽象与分解的原则是什么？”
        </p><p>
            冒号作了个V字：“两条：单一化，正交化。每个模块职责明确专一，模块之间相互独立，即<span class="emphasis"><em>高内聚低耦合</em></span>（high cohesion &amp; low coupling）<a class="link" href="#note1"><sup>[1]</sup></a>。此原则相当普适，是分析复杂事物的一种基本方法，在数学和物理中应用得尤为广泛，如质因式分解、正交分解、谱分解等等。”
        </p><p>
            逗号调皮地抬杠：“为什么称为正交化呢？斜交化不行吗？”
        </p><p>
            冒号呵呵一笑：“在数学中互为正交的两个向量在彼此方向上投影为零，意味着彼此独立、互不影响，斜交可不行。”
        </p><p>
            逗号吐了吐舌头。
        </p><p>
            “诚如前述，AOP以切面为模块。”冒号返回主题，“切面Aspect常直译为‘方面’，但它描述的是<span class="term">横切关注点</span>（cross-cutting concerns），故‘切面’更准确生动，而‘方面’则失之空泛呆板。何谓横切关注点？顾名思义，乃是<span class="strong"><strong>与程序的纵向主流执行方向横向正交的关注焦点</strong></span>。不妨回顾一下，无论是过程式的函数，还是对象式的方法，都包含了完整的执行代码。但有些代码<span class="emphasis"><em>横跨</em></span>多个模块，以片断的形式散落在各处，虽具有相似的逻辑，却无法用传统的方式提炼成模块，难以实现SoC与DRY。典型的例子如：在调用某些对象的方法、读写某些对象的域、抛出某些异常等等前后，需要用到统一的业务逻辑，诸如日志输出、代码跟踪、性能监控、异常处理、安全检查、事务管理等等。为解决此类问题，AOP应运而生。它将每类横切关注点封装到单独的Aspect模块中，将程序中的一些执行点与相应的代码绑定起来。单个的执行点称为<span class="term">接入点</span>（join point），例如：调用某个对象的方法前后；符合预先指定条件的接入点集合称为<span class="term">切入点</span>（pointcut），例如：所有以set为命名开头的方法；每段绑定的代码称为一个<span class="term">建议</span>（advice）。”
        </p><p>
            问号有点疑问：“接入点与切入点有何区别？”
        </p><p>
            冒号释疑：“望文生义，接入处是点，切入处是面，面由点组成。advice<span class="emphasis"><em>定义于</em></span>切入点上，<span class="emphasis"><em>执行于</em></span>接入点处。换言之，共享一段附加代码的接入点组成了一个切入点。切入点一般用条件表达式来描述，不仅有广泛性，还有<span class="emphasis"><em>预见性</em></span>——以后新增的代码如果含有满足切入点条件的接入点，advice中的代码便自动附着其上。这是AOP的威力所在，但有时也是麻烦所在。”
        </p><p>
            引号很较真：“好像一些书上把join point译作连接点，把advice译作通知。”
        </p><p>
            “误导，完全是误导！”冒号有些痛心疾首，“何谓join point？是advice中额外代码接入之处，join显为‘参加’、‘加入’之意。如果说翻作‘连接’ 只是因缺乏动感和方向性而不够贴切的话，将advice译作‘通知’则近乎荒谬了。advice是在原有程序流程中加入的额外流程，可理解为建议采取的措施，而‘通知’强调的是一种信息，难道是程序运行到join point的信息？抑或采取某种行动的信息？简直不知所云。”
        </p><p>
            顿了一会，冒号仍意犹未尽：“英文好的技术不好，技术好的英文不好，两者都好的不屑去翻译，导致市面上的译书虽汗牛充栋，然佳作寥寥。这里奉劝各位，如果真想成为优秀的程序员，一定要尽可能地读原文的书籍、文章和文档。事实上，凡是科学和艺术方面的专业人员，要想专业水平上一层台阶，都应读该专业权威经典的<span class="emphasis"><em>原文</em></span>。要知道，语言之间的天堑原本难以弥合，译者的专业水准、语言功底和严谨程度更是参差不齐。纵使万事俱备，一年半载后的译书便如隔夜的饭菜，虽刚出炉，已然不新鲜了。”
        </p><p>
            逗号抱怨：“英文虽读得懂，但太慢、太费劲了。”
        </p><p>
            “多读，读多了就习惯了。”冒号鼓励着，“对程序员来说，<span class="emphasis"><em>英语也是一门计算机语言</em></span>，而且是必修的语言。”
        </p><p>
            课堂上有这么一个规律，越是题外的话大家讨论得越欢。下面果然开始嘈杂起来，冒号也乐得乘机歇歇嘴。
        </p><p>
            等大伙渐渐把视线重新聚焦到讲台上，冒号才继续讲课：“从软件重用的角度看，可以这么理解AOP与OOP的关系：OOP只能沿着继承树的纵向方向重用，而AOP则弥补了OOP的不足，可以在横向方向重用。这算是回答了引号开始提出的问题：AOP不是OOP的分支，也不能说是超越了OOP，而是OOP的一种补充——尽管AOP并不局限用于OOP语言。”
        </p><p>
            问号的求知欲很强：“AOP实现的机理是什么？”
        </p><p>
            冒号回答：“如果一个程序是一个管道系统，AOP就是在管道上钻一些孔，在每个孔中注入新的代码流。因此AOP实现的关键是将advice的代码嵌入到主体程序之中，术语称<span class="term">编织</span>（weaving）。这是很自然的——将问题分解之后再合成，问题才得以还原。编织可分两种：一种是静态编织，通过修改源码或字节码（bytecode）在编译期（compile-time）、后编译期（post-compile）或加载期（load-time）嵌入代码——请注意，这里涉及到刚才提到的元编程和产生式编程；另一种是动态编织，通过代理（proxy）等技术在运行期（run-time）实现嵌入。具体的工具包括一些扩展性语言如AspectJ、AspectC++、Aspect#等和一些框架如AspectWerkz、Spring、Jboss AOP等。”
        </p><p>
            叹号搔着头：“听起来怪复杂的。”
        </p><p>
            引号倒不在乎：“这些机理是AOP的实现者需要操心的，使用者只需关心AOP是否好用，性能如何等等。”
        </p><p>
            “为了让你们有更直观的印象，我们借用光学原理来类比。”冒号用幻灯片展示了一幅图——
        </p><div class="figure"><a name="id524045"></a><p class="title"><b>图3-3. 切面式编程</b></p><div class="figure-contents"><div class="mediaobject"><img src="http://blog.zhenghui.org/img/colonclass/figure3-3.jpg" alt="切面式编程"></div></div></div><br class="figure-break"><p>
            “众所周知，白光经过三棱镜的折射而分解为七色光，是谓光的色散。再经过一个倒置的三棱镜，七色光又重新会聚为白光。”冒号简述中学的物理知识，“如果把一个复杂的系统看作<span class="emphasis"><em>复合色</em></span>的白光，经过第一个三棱镜——关注分离器，系统被分解为不同的切面，如同不同的<span class="emphasis"><em>单色</em></span>的彩光。这些切面经过第二个三棱镜——编织器，再度合成为原系统。”
        </p><p>
            叹号脸上的迷惘之色渐去：“这下清楚多了。”
        </p><p>
            句号积极发言：“从中看出，AOP的实施分三步：切面分解、切面实现和切面合成。其中第一步是在设计者的头脑中进行的，第三步是通过AOP的工具实现的，真正需要程序员编码的部分在第二步，即分别实现各切面的advice。”
        </p><p>
            冒号赶紧补漏：“你好像忽略了切面的另一要素pointcut，如果程序员不指明advice挂靠的切入点，系统如何知道该何时何处调用他编写的执行代码呢？”
        </p><p>
            句号的嘴张成O状：“是哦，我怎么把这茬给忘了？”
        </p><p>
            在AOP的议题结束前，冒号不忘指出：“与OOP一样，AOP在带来便利的同时，也增加了一定的复杂度和性能损耗。它们更适用于大中型程序，用在小型程序中则不啻牛刀杀鸡。”
        </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>
                    耦合（coupling）用来衡量模块之间的依赖程度，内聚（cohesion）用来衡量模块内在的关联强度。它们常用来作为软件质量的评判标准，耦合度宜低，内聚度宜高。
                </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>
                    SoC是Separation of concerns的缩写，指应将关注点分离；DRY是Don’t Repeat Yourself的缩写，指应尽量减少重复代码。
                </p></li><li class="listitem"><p>
                    抽象与分解是治愈代码紊乱、松散、重复的良方。
                </p></li><li class="listitem"><p>
                    抽象与分解的原则是单一化和正交化，以保障软件系统符合“高内聚、低耦合”的要求。
                </p></li><li class="listitem"><p>
                    横切关注点指与程序的纵向主流执行方向横向正交的关注焦点。
                </p></li><li class="listitem"><p>
                    接入点是附加行为——建议（advice）的执行点，切入点（pointcut）是指定的接入点（join point）集合，这些接入点共享一段插入代码。切入点与建议组成了切面（aspect），是模块化的横切关注点。
                </p></li><li class="listitem"><p>
                    编织是将附加的切面逻辑嵌入到主体应用程序之中的过程。编织分静态编织和动态编织两种。静态编织在编译期、后编译期或加载期嵌入代码，动态编织则在运行期嵌入。
                </p></li><li class="listitem"><p>
                    AOP的实施分三步：切面分解、切面实现和切面合成。
                </p></li><li class="listitem"><p>
                    OOP只能沿继承树的纵向方向重用，AOP可以沿横向方向重用。
                </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．Aspect-oriented programming．<a class="link" href="http://en.wikipedia.org/wiki/Aspect-oriented_programming" target="_top">http://en.wikipedia.org/wiki/Aspect-oriented_programming</a>
                </p></li><li class="listitem"><p>
                    Ramnivas Laddad ．I want my AOP! ．<a class="link" href="http://www.javaworld.com/javaworld/jw-01-2002/jw-0118-aspect.html" target="_top">http://www.javaworld.com/javaworld/jw-01-2002/jw-0118-aspect.html</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%2F10%2Fcolon-class-3_3%2F&amp;linkname=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A73.3%EF%BC%9A%E5%88%87%E9%9D%A2%E8%8C%83%E5%BC%8F">分享/保存</a>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/09/10/colon-class-3_3/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>冒号课堂§3.2：超级范式</title>
		<link>http://blog.zhenghui.org/2009/09/09/colon-class-3_2/</link>
		<comments>http://blog.zhenghui.org/2009/09/09/colon-class-3_2/#comments</comments>
		<pubDate>Wed, 09 Sep 2009 13:25:44 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[产生式编程]]></category>
		<category><![CDATA[元编程]]></category>
		<category><![CDATA[模板元编程]]></category>
		<category><![CDATA[编程范式]]></category>
		<category><![CDATA[语言导向式编程]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=377</guid>
		<description><![CDATA[<b>超级范式</b>——提升语言的级别（<em>元编程简谈</em>）<br/>
•	元编程作为超级范式的一个体现是，它能提升语言的级别<br/>
•	如果说OOP的关键在于构造对象的概念，那么LOP的关键在于构造语言的语法<br/>
•	离开IDE就无法编写、编译或调试的程序员，如同卸盔下马后便失去战斗力的武士，是残缺和孱弱的<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="id516098"></a>3.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>
                    如果说OOP的关键在于构造对象的概念，那么LOP的关键在于构造语言的语法
                </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="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>相比自编的元程序，用IDE自动生成的代码有什么缺陷？ </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>
            问号忽然想起一事，问道：“有一本名为《C++模版元编程》的书，既然提到了模板，想来也属于泛型编程吧？”
        </p><p>
            冒号答道：“<span class="term">模板元编程</span>即Template Metaprogramming，与泛型编程密切相关但自成一派，隶属于另一种编程范式——<span class="term">元编程</span>（Metaprogramming），简称MP。此处的前缀‘meta-’常译作‘元’，其实就是‘超级’、‘行而上’的意思。比如，<span class="term">元数据</span>（Metadata）是关于数据的数据，<span class="term">元对象</span>（Metaobject）是关于对象的对象，依此类推，元编程自然是关于程序的程序，或者说是编写、操纵程序的程序。”
        </p><p>
            叹号皱着眉：“听着有点绕。”
        </p><p>
            冒号投影出另一段代码——
        </p><div class="informalexample"><p><span class="emphasis"><em>C++（元编程）：</em></span></p><pre class="programlisting">
    template &lt;int N&gt;
    struct factorial 
    {
         enum { value = N * factorial&lt;N - 1&gt;::value };
    };

    template &lt;&gt;            // 特化（specialization）
    struct factorial&lt;0&gt;    // 递归中止
    {
         enum { value = 1 };
    };

    int main()
    {
        cout &lt;&lt; factorial&lt;5&gt;::value &lt;&lt; endl; // 等价于 cout &lt;&lt; 120 &lt;&lt; endl;
        return 0;
    }</pre></div><p>
            “以上用模板元编程实现了阶乘运算。”冒号讲解道，“与前面三种核心范式的阶乘实现有着根本的不同：这里阶乘的值是在编译时而非运行时计算出来的。换句话说，这段代码以模板形式通过编译器生成了新的代码，并在编译期间获得执行。”
        </p><p>
            叹号大惑不解：“这又说明什么呢？”
        </p><p>
            冒号并不直接回答：“假设你需要批量处理用户文档，其格式结构预先给定，但既不像CSV（逗号分隔）那么简单，也不像XML那么标准，并且用户随时可能改变格式标准，请问如何设计这段程序？”
        </p><p>
            叹号略一思索，便回答：“三大模块：阅读器读出输入文档，解析器按照格式标准去解析，处理器对解析结果进行处理。”
        </p><p>
             “显然关键在解析器，如果你是从头做起，那么问题至少有四。”冒号扳着指头数，“第一、费时写解析器代码；第二、费时调试解析器代码；第三、如果用户更改格式标准，你得重复做上两件事；第四、如果这段程序是大型程序的一部分，任何改动都可能意味着软件的重新编译、连接、测试、打包、部署等等。如果因为你的缘故公司不得不频频发布补丁包的话，你的饭碗恐怕是朝不保夕了。”
        </p><p>
            还是句号机灵：“既然谈到了元编程，一定是利用元编程，根据不同的格式标准自动生成相应的解析器代码。不过——此法虽一劳永逸，但难度似乎不小啊。” 
        </p><p>
            “思路对头！”冒号赞许道，“大家听说过Lex和Yacc吗？它们能根据格式标准生成相应的解析器代码。更妙的是，格式标准不限于静态数据，甚至可以含有动态指令！这意味着用户不仅能定义业务数据格式，还能定义业务流程。”
        </p><p>
             “这敢情好！”叹号兴奋地说。
        </p><p>
            “如果知道Lex和Yacc本来就是编写编译器和解释器的工具，你就不会惊讶于它们的强大了。顺带说一句，编译器本身就是元编程的典型范例——把高级语言转化为汇编语言或机器语言的程序，不就是能写程序的程序吗？”冒号引申开来，“更进一步地，我们可以定义自己的<span class="term">领域特定语言</span>DSL，更加灵活方便地处理客户逻辑。”
        </p><p>
            逗号有点糊涂了：“领域特定语言？就是前两堂课提到的非通用编程语言吧？怎么和元编程也扯上关系了？”
        </p><p>
            “不是扯上关系，而是它们之间本来就有着千丝万缕的联系。”冒号纠正着，“相比第三代的通用编程语言，领域特定语言由于其在应用范围上和语法上的限制而显得简单、针对性强，有时被成为‘小语言’（little language），也是一种<span class="term">特高级语言</span>（very high-level programming language ，简称VHLL），属于第四代编程语言。”
        </p><p>
            冒号说到此处，逗号猛地一拍脑门：“哦，我明白了。第四代语言最终需要编译为机器语言，而编译器就是元编程的应用。” 
        </p><p>
            “你只说对了一半。”冒号不疾不缓地说，“DSL一般不会一步到位地编译为第一代的机器语言或第二代的汇编语言，而是通过现成的<span class="term">编译器生成器</span>（compiler-compiler或compiler generator）首先转化为第三代的高级语言。这样不仅大大降低了难度，也方便了程序的调试。刚才提到的Yacc（<span class="strong"><strong>Y</strong></span>et <span class="strong"><strong>A</strong></span>nother <span class="strong"><strong>C</strong></span>ompiler <span class="strong"><strong>C</strong></span>ompiler）便是这样的工具，能为<span class="term">解析器</span>（parser）产生C程序，多用于Unix下的编程。更现代的工具如ANTLR (<span class="strong"><strong>AN</strong></span>other <span class="strong"><strong>T</strong></span>ool for <span class="strong"><strong>L</strong></span>anguage <span class="strong"><strong>R</strong></span>ecognition)，能生成C、C++、Java、C#、Python等多种语言的源程序。”
        </p><p>
            引号立刻联想到：“我记得框架Hiberate的必备库中就含有antlr.jar文件，与这个ANTLR有关吗？”
        </p><p>
             “说得正是！”冒号很满意学员完美的配合，“Hiberate中的HQL（Hibernate Query Language）是典型的DSL，需要通过ANTLR来解析。你们可以验证一下，在Hibernate的API中有org.hibernate.hql.antlr的package，但在其发布的源代码中相应的目录下却看不到一个Java源文件。却是为何？盖因此package中所有的源代码都是在ant build中自动生成的，这些非人工编辑的文件是不会放在版本控制中的。”
        </p><p>
            众人茅塞顿开。
        </p><p>
            句号想通了一个逻辑：“元编程作为超级范式的一个体现是，它能<span class="strong"><strong>提升语言的级别</strong></span>。比如，有了编译器的存在，汇编语言升级为第三代高级语言；同样借助Yacc、ANTLR之类的元编程工具，第三代语言可以升级为第四代的DSL语言。”
        </p><p>
            冒号并未就此止步：“将这一模式发挥到极致，便是更加激进的<span class="term">语言导向式编程</span><a class="link" href="#note1"><sup>[1]</sup></a>（Language-Oriented Programming，简称LOP）。这种编程范式的思路是：在建立一套DSL体系之后，直接用它们来编写软件，尽量不用通用语言。””
        </p><p>
            叹号莫明其妙：“想法近乎疯狂啊！放着好端端的通用语言不用，先造一套专用语言，这么做划算吗？”
        </p><p>
            “如果一个大型系统涉及的领域十分专业，包含的业务逻辑十分复杂，为其定制DSL或许会磨刀不误砍柴工。我们通过下面的两个图比较一下这种范式与主流编程范式的不同之处。”冒号映出新的投影——
        </p><div class="figure"><a name="id516344"></a><p class="title"><b>图3-1. 通用语言编程</b></p><div class="figure-contents"><div class="mediaobject"><img src="http://blog.zhenghui.org/img/colonclass/figure3-1.jpg" alt="通用语言编程"></div></div></div><br class="figure-break"><div class="figure"><a name="id516362"></a><p class="title"><b>图3-2. 专用语言编程</b></p><div class="figure-contents"><div class="mediaobject"><img src="http://blog.zhenghui.org/img/colonclass/figure3-2.jpg" alt="专用语言编程"></div></div></div><br class="figure-break"><p>
            “由于DSL比通用语言更简单、更抽象、更专业、更接近自然语言和声明式语言，开发效率显著提高，因此图中手工部分的时间相应减少。此外尤为关键的是，这种方式填补了专业程序员与业务分析员之间的鸿沟。要求一个非专业编程的业务分析员用DSL来开发固是勉为其难，但要做到读懂代码并审查其中的业务逻辑则已非难事。” 冒号细解个中要点，“如果说OOP的关键在于构造对象的<span class="emphasis"><em>概念</em></span>，那么LOP的关键在于构造语言的<span class="emphasis"><em>语法</em></span>。有人认为LOP是继OOP之后的下一个重要的编程范式，我们不妨拭目以待。”
        </p><p>
            句号整理了一下头绪：“能不能这么说：如果处理一些复杂、非标准格式的文档，可以考虑用元编程；如果整个业务逻辑复杂多变，可以考虑利用现有的DSL或创造新的DSL来处理业务，即所谓的语言导向式编程。”
        </p><p>
            “总结得不错，不过当特定格式的文档有了专门的解析器后，这种文档格式标准就可视为一种语言了，不是吗？这本质上就是DSL啊。”冒号出语点化。
        </p><p>
            句号顿时醒悟：“是啊，就像XML、HTML一样，能被程序认识的格式可不就是一种计算机语言嘛。”
        </p><p>
            冒号将话题延伸：“我们的想象力可以再狂野些，在文本DSL的基础上裹以图形界面，从而引进图形语言。如果再将部分业务逻辑开放给用户定制，那么你的客户会欣喜地发现，他们的经理只要点点鼠标就可以改变整个业务流程了，而这一切不仅不需要软件开发方或第三方的参与，连本公司的技术人员也免了。这时候倒是你的老板发愁了：你的设计太过完美，客户的后续开发费怕是赚不到啰。”
        </p><p>
            众人一乐。
        </p><p>
            问号继续发问：“还有其他元编程的应用吗？”
        </p><p>
            冒号随口举了几例：“元编程的例子比比皆是：许多IDE如Visual Studio、Delphi、Eclipse等均能通过向导、拖放控件等方式自动生成源码；UML建模工具将类图转换为代码；Servlet引擎将JSP转换为Java代码；包括Spring、Hibernate、XDoclet在内的许多框架和工具都能从配置文件、annotation/attribute等中产生代码。” 
        </p><p>
            引号仍不知足：“这些应用虽然典型，但都是些开发工具、框架引擎之类的基础软件，有没有平时编程就能用到的例子？”
        </p><p>
            “当然有！”冒号坚定地答复，“有时程序中会出现大量的重复代码，却囿于语法上的限制无法进一步抽象化和模块化。如果采用手工编写或者单纯拷贝的方法，既费时又易错，显为下策。有时可借助IDE内置的代码生成功能，但一方面局限性很大，另一方面无法<span class="emphasis"><em>自动化</em></span>和<span class="emphasis"><em>版本化</em></span>。”
        </p><p>
            问号插问：“什么叫版本化？”
        </p><p>
            冒号解释：“理想情况下，一个程序员对程序的贡献都应该保存在版本控制系统（version control system）中，以便跟踪、比较、改进、借鉴和再生成。在IDE下自动生成的代码本身可以被记录，但产生代码时的行为却不能被记录，几次简单的鼠标动作就能产生较大的代码差别，使得版本比较的意义大打折扣。顺便说一句，离开IDE就无法编写、编译或调试的程序员，如同卸盔下马后便失去战斗力的武士，是残缺和孱弱的。”
        </p><p>
            问号有些明白了：“这是因为鼠标行为本身在代码中是没有痕迹的。”
        </p><p>
            “不仅是鼠标行为，有些需要键盘交互的行为也是没有痕迹的。比如在命令行下用debugger来调试的行为无法被记录，也难以重复和自动化，只能作为权宜之策。相比之下，日志（logging）和<span class="term">单元测试</span>（unit test）具有明显的优势）<a class="link" href="#note2"><sup>[2]</sup></a>。”冒号答完，立马重返主题，“回到上面的问题，既然有重复的代码，不能从语法上提炼，不妨退一步从文字上提炼。我们可以利用AWK、Perl之类的擅长文字处理的脚本语言，当然也可以用Java、C等非脚本语言，再辅以XSLT之类的模板语言，自动生成重复代码。这样不仅灵活性强，而且生成代码的代码——也就是元程序代码可以被重用，元程序的数据来源也能版本化。”
        </p><p>
            句号深得要领：“就像Hibernate中的antlr包一样，真正的源码反而不在版本控制中了。一方面没有保存的必要——可以自动生成；另一方面没有比较的必要——元程序的数据来源的变化比实际源码的变化更简明、更直观。”
        </p><p>
            冒号继续推进：“另外，有时程序的结构需要动态改变，而C++、Java、C#等<span class="term">静态语言</span>是不允许动态变更类的成员或实现代码的，利用元编程便可突破这种限制。”
        </p><p>
            逗号恍然大悟：“原来元编程就是编写能自动生成源代码的程序。”
        </p><p>
            “也不尽然。”冒号马上修正道，“自动生成源代码的编程也属于另一种编程范式——<span class="term">产生式编程</span>（Generative Programming）<a class="link" href="#note3"><sup>[3]</sup></a>的范畴。区别在于后者更看重代码的生成，而元编程看重的是生成代码的可执行性。另外，除了在编译期间生成源代码的<span class="term">静态元编程</span>，还有能在运行期间修改程序的<span class="term">动态元编程</span>。从低级的汇编语言到一些高级的动态语言如Perl、Python、Ruby、JavaScript、Lisp、Prolog等均支持此类功能。比如许多脚本语言都提供eval函数，可以在运行时将字符串作为表达式来运算<a class="link" href="#note4"><sup>[4]</sup></a>。”
        </p><p>
            问号突然问道：“编写病毒算不算元编程？”
        </p><p>
            “编写一个只是删除或感染文件的病毒，不必用到元编程。但如果要开发一个能自我变异的智能病毒，那就需要元编程了。不过你要是把元编程用在这方面，可别说是我教的。”冒号开了个玩笑。
        </p><p>
            引号自言自语：“程序的程序，就是程序的平方。”
        </p><p>
            “也可以是程序的立方，四次方……理论上是无限次方。在传统的编程中，运算是动态的，但程序本身是静态的；在元编程中，二者都是动态的。元程序将程序作为数据来对待，能自我发现、自我赋权和自我升级，有着其他程序所不具备的<span class="emphasis"><em>自觉性</em></span>、<span class="emphasis"><em>自适应性</em></span>和<span class="emphasis"><em>智能性</em></span>，可以说是一种最高级的程序。它要求编程者超越常规的编程思维，在一种崭新的高度上理解编程。想象一下吧！”冒号激情勃发，“如果有一天机器人能自我学习、自我完善，甚至能生产新的机器人，实现‘<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>
                    Martin Ward最早提出此范式，见参考文献【1】。
                </p></li><li class="listitem"><p><a name="note2"></a>
                     虽然调试与日志和测试不是一码事，但合理的日志和单元测试能大量减少调试工作。
                </p></li><li class="listitem"><p><a name="note3"></a>
                    也译作“生成式编程”，属于自动编程（Automatic Programming）范畴。
                </p></li><li class="listitem"><p><a name="note4"></a>
                    考虑到eval过于广泛和强大，有些动态语言还提供其他更明确和更安全的元编程机制，如JavaScript可用字符串来构建Function，Ruby更是提供了define_method、instance_eval、 class_eval和module_eval等诸多元编程方法。
                </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>
                    元编程有诸多应用：许多开发工具、框架引擎之类的基础软件都有自动生成源代码的功能；创造DSL以便更高效地处理专门领域的业务；自动生成重复代码；动态改变程序的语句、函数、类等等。
                </p></li><li class="listitem"><p>
                    IDE下自动生成的代码通常局限性大且可读性差，小操作可能造成的源码上的大差异，削弱了版本控制的意义。用自编的无需人机交互的元程序来生成代码，只需将元程序的数据来源版本化，简明而直观。同时由于元程序可以随时修改，因此局限性小，更加灵活。
                </p></li><li class="listitem"><p>
                   	语言导向式编程（LOP）通过创建一套专用语言DSL来编写程序。相比通用语言，DSL更简单、更抽象、更专业、更接近自然语言和声明式语言、开发效率更高，同时有助于专业程序员与业务分析员之间的合作。
                </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>
                    Martin Ward．Language Oriented Programming．<a class="link" href="http://www.cse.dmu.ac.uk/~mward/martin/papers/middle-out-t.pdf" target="_top">http://www.cse.dmu.ac.uk/~mward/martin/papers/middle-out-t.pdf</a>
                </p></li><li class="listitem"><p>
                    Sergey Dmitriev．Language Oriented Programming: The Next Programming Paradigm ．<a class="link" href="http://www.onboard.jetbrains.com/is1/articles/04/10/lop/mps.pdf" target="_top">http://www.onboard.jetbrains.com/is1/articles/04/10/lop/mps.pdf</a>
                </p></li><li class="listitem"><p>
                    Wikipedia．Metaprogramming．<a class="link" href="http://en.wikipedia.org/wiki/Metaprogramming" target="_top">http://en.wikipedia.org/wiki/Metaprogramming</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%2F09%2Fcolon-class-3_2%2F&amp;linkname=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A73.2%EF%BC%9A%E8%B6%85%E7%BA%A7%E8%8C%83%E5%BC%8F">分享/保存</a>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/09/09/colon-class-3_2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>冒号课堂§3.1：泛型范式</title>
		<link>http://blog.zhenghui.org/2009/09/08/colon-class-3_1/</link>
		<comments>http://blog.zhenghui.org/2009/09/08/colon-class-3_1/#comments</comments>
		<pubDate>Tue, 08 Sep 2009 14:52:31 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[STL]]></category>
		<category><![CDATA[容器]]></category>
		<category><![CDATA[泛型编程]]></category>
		<category><![CDATA[算法]]></category>
		<category><![CDATA[编程范式]]></category>
		<category><![CDATA[迭代器]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=369</guid>
		<description><![CDATA[<b>泛型范式</b>——抽象你的算法（<em>泛型式编程简谈</em>）<br/>
•	算法串联数据，如脊贯肉；数据实化算法，如肉附脊<br/>
•	泛型编程是算法导向的，即以算法为起点和中心点，逐渐将其所涉及的概念内涵模糊化、外延扩大化，将其所涉及的运算抽象化、一般化，从而扩展算法的适用范围<br/>
•	思想是鸡，结论是蛋]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center;"><span style="font-family: 宋体;">冒号课堂</span></h1>
<strong><span style="font-size: 13pt; font-family: 宋体;">第三课 常用范式（1）</span></strong>
<p><strong><span style="font-family: 宋体;">课前导读</span></strong></p>
<p style="text-indent: 18pt;"><span style="font-family: 宋体;">这一课介绍了四个常用的编程范式：泛型式、元编程、切面式和事件驱动式。与上节课类似，这些介绍也都是入门性的，目的是让读者认识到范式的多样性与差异性。</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 18pt;"><span style="font-family: 宋体;">本课共分四节——</span></p>
<p style="text-indent: 18pt;"><em>1.</em><em><span style="font-family: 宋体;">泛型范式——抽象你的算法</span></em></p>
<p style="text-indent: 18pt;"><em>2.</em><em><span style="font-family: 宋体;">超级范式——提升语言的级别</span></em></p>
<p style="margin-left: 18pt;"><em>3.</em><em><span style="font-family: 宋体;">切面范式——多角度看问题</span></em></p>
<p style="margin-left: 18pt;"><em>4.</em><em><span style="font-family: 宋体;">事件驱动——有事我叫你，没事别烦我</span></em></p>

<!-- below comes from generated html -->
<head><link rel="stylesheet" href="http://blog.zhenghui.org/css/colonclass.css" type="text/css"></head>
 
<div lang="zh-CN" class="article" title="泛型范式"><div class="titlepage"><div><div><h1 class="title"><a name="id632473"></a>3.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></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>STL有哪些要素？各自有什么作用？</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>
            “有一种编程范式可以解决这个问题，它打破了不同数据类型之间的壁垒，让你的代码不再臃肿，这——就是<span class="term">泛型编程</span>。”冒号的语调和说辞不免令人联想到电视上的减肥广告，“Generic Programming，简称GP，其基本思想是：将算法与其作用的数据结构分离，并将后者尽可能泛化，最大限度地实现算法重用。这种泛化是基于<span class="term">模板</span>（template）的<span class="term">参数多态</span>（parametric polymorphism），相比OOP基于<span class="term">继承</span>（inheritance）的<span class="term">子类型多态</span>（subtyping polymorphism），不仅普适性更强，而且效率也更高。这不能不说是一种异数——我们知道，普适性往往是以效率为代价的。如果一定要找出代价的话，那就是其用法稍微复杂一些，可读性稍微差一些。GP最著名的代表是C++中的STL（Standard Template Library），其后亦为Java、C#、D等语言所吸纳<a class="link" href="#note1"><sup>[1]</sup></a>。此外，一些函数式语言如Ocaml、Standard ML、Haskell等也支持GP。”
        </p><p>
            冒号写下两段代码——
        </p><div class="informalexample"><p><span class="emphasis"><em>C++（泛型编程）：</em></span></p><pre class="programlisting">
    template &lt;typename T&gt;
    T max(T a, T b)       // 求出两个数中的较大者
    {
            return  (a &gt; b) ? a : b;
    }</pre><p><span class="emphasis"><em>C（宏定义）：</em></span></p><pre class="programlisting">
    #define max(a,b) ((a) &gt; (b) ? (a) : (b))
    </pre></div><p>
            “求两个数中的较大值是经常遇到的问题。”冒号解说着，“对于静态类型语言<a class="link" href="#note2"><sup>[2]</sup></a>来说，若参数类型不同，即使函数体相同也不能合为一体。如果语言不支持<span class="term">重载</span>（overload），还可能出现maxInt、maxLong、maxFloat、 maxDouble之类的函数名，冗赘而丑陋。尽管在C中可用<span class="term">宏定义</span>（macro definition）来实现，但无法保证类型安全，而C++模板则兼顾类型安全和代码重用，并且由于是在编译期间展开的，效率上也不损失。不止于此，C++支持运算符重载，除数值类型外，一切定义了‘&gt;’ 运算的数据类型均可调用max函数，真是一举N得，N趋向无穷大啊！”
        </p><p>
            冒号边说边比划，夸张的语气和手势逗得大家都笑了。
        </p><p>
            引号提出疑问：“Java的一切对象都是Object类型，只要将所有参数都换成Object类型，岂不也是一种泛化？”
        </p><p>
            冒号答道：“首先，基本类型如int、float等不是Object的子类，虽然Java 新增了自动装拆箱（autoboxing/unboxing）的功能，但要付出性能的代价。更重要的是，这将不可避免地需要类型的<span class="term">显式转换</span>（explicit conversion或cast），无法在编译期间施行严格的类型检查，由此丧失了静态类型语言的优势，为bug大开方便之门。这也是Java最终引入泛型的原因，虽然有些姗姗来迟。类似地，C/C++中的通用指针void *也有类型安全问题。”
        </p><p>
            句号发表他的看法：“泛型虽好，似乎只是某些局部才用到的技术，不具有前面几种范式的渗透性。”
        </p><p>
            冒号听罢不语，返身在黑板上写下几道题——
        </p><div class="informalexample"><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p>
                        从一个整数数组中随机抽取十个数，对其中的素数求和
                    </p></li><li class="listitem"><p>
                        将一个无序整数集中所有的完全平方数换成其平方根
                    </p></li><li class="listitem"><p>
                        从学生成绩表中，列出门门都及格且平均分在70分以上的学生名单
                    </p></li><li class="listitem"><p>
                        在一个着色二元树中，将所有的红色结点涂成蓝色
                    </p></li><li class="listitem"><p>
                        将一个字符串从倒数第三个字符开始反向拷贝到另一个字符串中
                    </p></li><li class="listitem"><p>
                        每从标准输入读取一个非数字的字符X，于标准输出打印“X不是数字字符”
                    </p></li></ol></div></div><p>
            句号暗忖，这有何难？不过是些常规题罢了。不料冒号的问题却出人意表：“请问它们之间有何共同之处？能否共享同一段代码？”
        </p><p>
            见众人缄默已久，冒号接着投影出一段代码——
        </p><div class="informalexample"><pre class="programlisting">
template &lt;class Iterator, class Act, class Test&gt;
void process(Iterator begin, Iterator end, Act act, Test test)
// 对容器中在给定范围内（即起于begin止于end）所有满足给定条件的元
//素（即test（元素）==true）进行处理（即act（元素））
{
    for ( ; begin != end; ++begin)    // 从头至尾遍历容器内元素
        if (test(*begin)) act(*begin); // 若当前元素满足条件，则对其采取行动
}</pre></div><p>
            “STL有三要素：算法（algorithm）、容器（container）和迭代器（iterator）。算法是一系列切实有效的步骤；容器是数据的集合，可理解为抽象的数组；迭代器是算法与容器之间的接口，可理解为抽象的指针或者游标。”冒号讲述道，“<span class="strong"><strong>算法串联数据，如脊贯肉；数据实化算法，如肉附脊</strong></span>。只有抽象出表面的数据，算法的脊梁才能显现。以上几题看似风马牛不相及，若运用泛型思维，便可发现它们的共性：对指定集合中满足指定条件的元素进行指定处理。用模板语言，寥寥数行即勾勒完毕。”
        </p><p>
            问号诧异道：“相比前面的max模板，这儿连元素的数据类型T都不见了？”
        </p><p>
            冒号回答：“元素被容器封装了。”
        </p><p>
            问号追问：“可连容器也看不到啊？”
        </p><p>
            冒号料有此问：“容器通过它的迭代器参与算法。”
        </p><p>
            句号豁然开朗：“通过模板，泛化了容器——可以是数组、列表、集合、映射、队列、栈、字符串等等；泛化了元素——可以是任何数据类型；泛化了处理方法和限定条件——可以是任何函数。”
        </p><p>
            冒号提醒道：“补上两点：这里的处理方法和限定条件不限于函数，还可以是<span class="term">函子</span>（functor）<a class="link" href="#note3"><sup>[3]</sup></a>——自带状态的函数对象；另外迭代器也被泛化了——可以从前往后移动，可以从后往前移动，可以来回移动，可以随机移动，可以按任意预先定义的规律移动。”
        </p><p>
            叹号由衷感叹：“果然强悍无比啊！”
        </p><p>
            逗号倒也心细：“最后一题中标准输入也算容器吗？”
        </p><p>
             “为什么不呢？只要一个对象配备了迭代器，它就可以作为容器来对待。I/O流上就有现成的迭代器，当然你也可以自行定制。索性我们来看看这道题的解法吧。”冒号给出了第六题的实现代码——
        </p><div class="informalexample"><pre class="programlisting">
#include &lt;iostream&gt;
#include “process.h” // 前述process所在的头文件

using namespace std;

// 判断字符是否为非数字字符
bool notDigit(char c)
{
    return (c &lt; '0') || (c &gt; '9');
}

// 打印非数字字符
void printNondigit(char c)
{
    cout &lt;&lt; c &lt;&lt; "不是数字字符" &lt;&lt; endl;
}

int main()
{
    process(istream_iterator&lt;char&gt;(cin), istream_iterator&lt;char&gt;(),
            printNondigit, notDigit);

    return 0;
}</pre></div><p>
            逗号打量了半天：“这里完全看不到I/O读取的过程，也看不到通常的迭代循环，简洁得难以置信。”
        </p><p>
            冒号补充道：“不光是代码简洁，它还让人摆脱了底层编码的细节，在更高更抽象的层次上进行编程设计。”
        </p><p>
            引号发觉：“开始谈起泛型编程时，您特别强调它对数据类型的抽象。现在看起来，它也能对函数进行抽象呢。”
        </p><p>
            “说得没错，条件是被抽象的函数或者方法具有相同的签名（signature）或接口（interface）。不过别忘了，在C和C++中的函数——准确地说是函数指针——也能作为数据类型。但不管怎样，这都表明泛型编程不仅能泛化<span class="emphasis"><em>概念</em></span>，还能泛化<span class="emphasis"><em>行为</em></span>。”冒号目光转向句号，“现在还有人认为泛型编程的渗透性不够强吗？”
        </p><p>
            句号腆然一笑。
        </p><p>
            “这些只是泛型编程的冰山一角。重要的是，我们不是在玩弄花哨的技巧，而是在用一种新的视角去审视问题。”冒号总结道，“泛型编程是<span class="emphasis"><em>算法导向的</em></span>（Algorithm-Oriented），即以算法为起点和中心点，逐渐将其所涉及的<span class="emphasis"><em>概念</em></span>（如数据结构、类）内涵模糊化、外延扩大化，将其所涉及的<span class="emphasis"><em>运算</em></span>（函数、方法、接口）抽象化、一般化，从而扩展算法的适用范围。这非常类似数学思维——当数学家证明完一个定理后，总会试图在保持核心思想的前提下，尽可能地放宽题设，增强结论，从而推广定理。外行人常以为数学定理最重要，其实数学思想才是数学的精髓。比如举世皆知的哥德巴赫猜想和费尔马大定理，人们在攻克它们的过程中产生的新思想、新理论、新方法，已远远超过了定理本身的意义。数学家们甚至不愿这些猜想被过早地解决，怕扼杀了会下金蛋的鸡。在他们眼里，<span class="strong"><strong>思想是鸡，结论是蛋</strong></span>。这也无怪乎STL会出自一位学数学的人之手了<a class="link" href="#note4"><sup>[4]</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>
                    但它们的实现机制却大不相同：C++和D采用类型模板（template），Java采用类型擦除（type erasure），C#采用类型具化（reification），相应的表现也有显著差异。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    静态类型语言在编译期间或运行之前施行类型检查（type checking）。后有详论。
                </p></li><li class="listitem"><p><a name="note3"></a>
                    又称function object，在C++中指重载了函数调用算符（operator()）的类，在Java和C#中可通过interface来实现。
                </p></li><li class="listitem"><p><a name="note4"></a>
                    STL的发明者Alexander Stepanov曾是莫斯科大学数学系的学生（1967-1972）。
                </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>
                    STL有三要素：算法、容器和和迭代器。算法是一系列可行的步骤；容器是数据的集合，是抽象化的数组；迭代器是算法与容器之间的接口，是抽象化的指针。算法串联数据，数据实化算法。
                </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 C++ Programming Language, Special ed.．Reading, MA：Addison-Wesley，2000．507-516，549-560
                </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%2F08%2Fcolon-class-3_1%2F&amp;linkname=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A73.1%EF%BC%9A%E6%B3%9B%E5%9E%8B%E8%8C%83%E5%BC%8F">分享/保存</a>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/09/08/colon-class-3_1/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>冒号课堂§2.4：并发范式</title>
		<link>http://blog.zhenghui.org/2009/09/07/colon-class-2_4/</link>
		<comments>http://blog.zhenghui.org/2009/09/07/colon-class-2_4/#comments</comments>
		<pubDate>Mon, 07 Sep 2009 05:14:57 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[函数式编程]]></category>
		<category><![CDATA[对象式编程]]></category>
		<category><![CDATA[并发式编程]]></category>
		<category><![CDATA[线程]]></category>
		<category><![CDATA[编程范式]]></category>
		<category><![CDATA[过程式编程]]></category>
		<category><![CDATA[进程]]></category>
		<category><![CDATA[逻辑式编程]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=361</guid>
		<description><![CDATA[<b>并发范式</b>——合作与竞争（<em>并发式编程简谈</em>）<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="id599705"></a>2.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></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>
            “不但有，而且有很多。”冒号喝了一口水，悠悠地说，“<span class="term">并发式编程</span>（concurrent programming）就是其中之一。”
        </p><p>
            叹号有些惊讶：“并发式编程也算一种范式？它似乎更像是提高运行效率的一种手段。” 
        </p><p>
            “大谬不然。”冒号摇摇头，“真正的并发式编程，绝不只是调用线程<a class="link" href="#note1"><sup>[1]</sup></a>API或使用synchronized、lock之类的关键字那么简单。从宏观的架构设计，到微观的数据结构、流程控制乃至算法，相比通常的串行式编程都可能发生变化。随着硬件性能和用户需求的双重提升，并发式编程已成为不可回避的主题。毫不夸张地说，并发式编程是继OOP之后又一场思想和技术上的革命。只是相比OOP，尽管年龄相仿，但语言上不够支持，标准上不够统一，理论上不够完善，因而这场革命更具<span class="emphasis"><em>破坏性</em></span>和<span class="emphasis"><em>建设性</em></span>。现在我们来看一个例子，比较两种烧水泡茶的方案。”
        </p><p>
            说着冒号在黑板上写下——
        </p><div class="informalexample"><p>方案一：洗茶杯；放茶叶；灌水壶；烧水；水开后泡茶。</p><p>方案二：灌水壶；在烧水的同时，洗茶杯；放茶叶；水开后泡茶。</p></div><p>
            引号见多识广：“我记得这好像是运筹学中的例子，显然方案二更佳。从编程的角度来看，方案一是串行式编程，方案二是并发式编程——烧水的线程与洗茶杯放茶叶的线程是同时进行的。”
        </p><p>
            “如果方案一也用并发式编程呢？”冒号追问。
        </p><p>
            引号一愣，随即道：“必须先洗茶杯后放茶叶，洗茶杯放茶叶的同时也没法烧水，至于泡茶，更得等水开之后了。”
        </p><p>
            句号明白了冒号的用意：“这就是说，单凭并发式编程并不能保证提高程序性能，还必须在程序设计上下功夫。” 
        </p><p>
            问号继续深究：“即便如此，如果硬件不支持，并发式编程也未必能提高效率啊。比如在一台单处理器的主机上，多个线程只能是模拟的、逻辑上的并行，而非真正物理上的并行。”
        </p><p>
            “这话有一定道理。”冒号有限度地表示赞同，“但设计者应该未雨绸缪，总不能等到有了多处理器才想到并发式编程吧？再者，没有多处理器，是不是可以利用多台单处理器的主机同时运算呢？退一步讲，即使在一台单处理器的情况下，并发式编程有时也是必不可少的。它能保证不同用户、不同程序之间的公平竞争，这对多用户、多任务的系统而言尤为重要。此外，采用并发式编程同样可能提高性能。最典型的一个例子：当一个线程因为等待某种资源而被堵塞时，可以切换到其他线程而不至让CPU闲置。更重要的一点是，难道除了缩短程序运行时间外，并发式编程就没有别的好处吗？譬如打开一个网页，你是希望浏览器边下载边显示网页呢，还是下载完成后再显示？”
        </p><p>
            “当然是边下载边显示啦。” 叹号毫不犹豫地说，“如果每个网页都是下载完成后再显示，那还不把人给憋坏了！”
        </p><p>
            逗号加了一句：“浏览器加载文字和加载图像也应分开在不同的线程，如果用户对文字部分不感兴趣，不等图像显示就可直接关闭网页了。”
        </p><p>
            “如果网页被提前关闭，那也是假用户之手变相缩短了程序运行时间。”冒号接过话来，“还有一个常见的例子，包括浏览器在内的许多应用软件在运行中都会显示一个进程条。该进程条的更新需要一个单独的线程，从效率上看它起的作用是负面的，但大大提高了用户体验，是软件人性化的表现。再举一例，媒体播放器一般都会提供暂停、快进、倒退、快放、慢放等按钮，如果不采用多线程，它们将形同虚设。此处并发式编程的作用在于提高了软件的响应能力，也改善了用户体验——有谁愿意驾驶一辆启动后不能刹车、不能倒车、不能变速、油尽方停的汽车呢？”
        </p><p>
            “岂止是不愿意，简直是恐怖！”叹号加重了语气。
        </p><p>
            “对于操作系统、各种实时系统和诸如数据库、网络服务器等基于服务的系统来说，在响应用户请求的时间上和资源的合理分配上有着极高的要求，并发式编程更是不可或缺。”句号接着补充。
        </p><p>
            问号紧接着又问：“并发式编程还有其他用处吗？”
        </p><p>
            冒号应道：“不同编程范式采用不同的视角和方法来设计和开发软件。并发式编程<span class="emphasis"><em>以进程为导向</em></span>（Process-Oriented）、以任务为中心将系统模块化。我们都知道，模块化在编程中是个好东西。”
        </p><p>
            引号心存疑惑：“过程式中引入了函数模块，对象式中引入了对象模块，但并发式似乎没有<span class="emphasis"><em>在语法上</em></span>引入新型模块，比如C和Java中的线程其实不过是函数或方法的包装而已。”
        </p><p>
            “我猜你的意思是：在定义一个线程之前，其主函数已经模块化了，不能把功劳记在并发式编程上，对吧？”冒号笑问，“你不能孤立静止地看待每个模块，还要考虑到模块之间的相互关联和作用。相比串行式，并发式在模块之间引入了新的通讯和控制方式。也就是说，原先的一些模块的定义和划分一定是建立在线程机制的基础上的。如果失去线程的支持，它们的合理性自然会打上问号，说不定整体设计都会受到牵连。这也体现了编程范式的<span class="emphasis"><em>渗透性和全局影响力</em></span>。”
        </p><p>
            教室上空弥漫的疑云一消而散。
        </p><p>
            冒号进一步指出：“除了用户主观上的需求和硬件客观上的可能外，并发式显得格外重要的另一深层原因是：许多程序是现实世界的模拟，而我们生活的世界不折不扣是并发式的。从某种意义上看，并发式的模拟比对象式的模拟更贴近世界。”
        </p><p>
            引号再次提问：“并发式编程在设计上有什么要求？”
        </p><p>
            “并发式编程<span class="strong"><strong>以资源共享与竞争为主线</strong></span>——又是对当今世界形势的一个逼真模拟。这意味着程序设计将围绕进程的划分与调度、进程之间的通讯与同步<a class="link" href="#note2"><sup>[2]</sup></a>等等来展开。合理的并发式设计需要诸多方面的权衡考量。”冒号说着，放出一段幻灯片——
        </p><div class="informalexample"><div class="itemizedlist"><ul class="itemizedlist" type="disc"><li class="listitem"><p>软件易于重用、维护、测试</p></li><li class="listitem"><p>公平有效地利用资源，优化程序性能如增大吞吐量、减少响应时间、提高效率等</p></li><li class="listitem"><p>保障进程安全，防止<span class="term">竞态条件</span>（race condition）</p></li><li class="listitem"><p>保持进程活性，避免死锁、饥饿、活锁、资源枯竭等</p></li><li class="listitem"><p>减少锁开销、上下文切换等带来的性能损失</p></li><li class="listitem"><p>妥善处理多进程在算法、调试等方面带来的复杂性</p></li></ul></div></div><p>
            叹号蹙眉：“并发式编程好是好，就是太复杂。”
        </p><p>
            “天下没有免费的午餐。有所得，必有所失。” 冒号淡淡地说，“并发式编程当然不容易，但也并非难以掌握。最重要的是，作为一个程序员，你不得不面对它。即使你不直接用并发式编程，你依赖的代码和依赖你的代码也可能用到；即使现在没有用并发式，将来也可能用到。如果采取避而不理的鸵鸟政策，早晚会被人点中你的死穴。虽然目前不会对并发式编程作更深入的探讨，但希望能引起你们足够的重视。”
        </p><p>
            句号谈及他的感受：“相比OOP在语言上得到的支持，并发式的支持力度好像很不够。”
        </p><p>
            冒号点头称是：“这是由并发式的复杂性和成熟度决定的。另外，在究竟是在语言级别上支持并发、还是交由操作系统处理的问题上，仁者见仁，智者见智。Ada、Java和C#等选择前者，在语法上对并发式编程有一定的支持；C和C++等选择后者，除了关键字volatile外，主要靠库函数支持。专门为并发式而设计的语言大多仅限于学术研究而非商业应用，Erlang语言<a class="link" href="#note3"><sup>[3]</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>为主题，一个以<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="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><div class="informalexample"><p>过程式：以过程为模块的君主体系，模块之间互相授命与听命</p><p>函数式：以函数为模块的数学体系，模块之间互相替换与合成</p><p>逻辑式：以断言为模块的逻辑体系，模块之间互相归纳与演绎</p><p>对象式：以对象为模块的民主体系，模块之间互相交流与服务</p><p>并发式：以进程为模块的生产体系，模块之间互相竞争与合作</p></div><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>
                    并发式编程中以进程（process）或线程（thread）为基本单位。尽管它们有很大的差异，但为简明起见，这里不加区分地交替使用两个术语。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    <span class="term">同步</span>（synchronization）只在采用<span class="term">共享内存</span>（shared memory）的并发模型中需要，在采用<span class="term">消息传递</span>（message passing）的并发模型中并不需要。本文主要以前一种并发模型为讨论对象，它也是大多数语言或库（包括C、C++、Java、C#等）所支持的模型。
                </p></li><li class="listitem"><p><a name="note3"></a>
                    Erlang是由爱立信开发的一种通用编程语言，支持函数式和并发式，采用消息传递的并发模型。
                </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"><div class="table"><a name="id640460"></a><p class="title">五大重要范式对比：</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></thead><tbody><tr><td>过程式</td><td>君主体系</td><td>过程</td><td>授命与听命</td></tr><tr><td>函数式</td><td>数学体系</td><td>函数</td><td>替换与合成</td></tr><tr><td>逻辑式</td><td>逻辑体系</td><td>断言</td><td>归纳与演绎</td></tr><tr><td>对象式</td><td>民主体系</td><td>对象</td><td>交流与服务</td></tr><tr><td>并发式</td><td>生产体系</td><td>进程</td><td>竞争与合作</td></tr></tbody></table></div></div><br class="table-break"></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>
                    Abraham Silberschatz，Peter Galvin．Operating System Concepts，5ed.．Reading, MA：Addison-Wesley，1998．155-235
                </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>02-01 在你所掌握的语言当中，有哪些是命令式的？哪些是声明式的？其中，命令式语言里有哪些声明式的特征？声明式的语言里有哪些命令式的特征？它们各自有哪些优点和缺点？</li>
    <li>02-02 你编写的程序符合结构化编程的原则吗？</li>
    <li>02-03 你接触过函数式语言和逻辑式语言吗？如果没有，试着阅读相关的入门书籍，体会它们与过程式迥然不同的风味。（函数式语言推荐Haskell和 Scheme，逻辑式语言推荐Prolog）</li>
    <li>02-04 相比纯过程式的编程语言（如C语言），你认为OOP语言主要有哪些优势？又有哪些劣势？</li>
    <li>02-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%2F09%2F07%2Fcolon-class-2_4%2F&amp;linkname=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A72.4%EF%BC%9A%E5%B9%B6%E5%8F%91%E8%8C%83%E5%BC%8F">分享/保存</a>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/09/07/colon-class-2_4/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>冒号课堂§2.3：对象范式</title>
		<link>http://blog.zhenghui.org/2009/09/06/colon-class-2_3/</link>
		<comments>http://blog.zhenghui.org/2009/09/06/colon-class-2_3/#comments</comments>
		<pubDate>Sat, 05 Sep 2009 17:26:50 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[OOP]]></category>
		<category><![CDATA[易用性]]></category>
		<category><![CDATA[编程范式]]></category>
		<category><![CDATA[过程式编程]]></category>
		<category><![CDATA[重用性]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=353</guid>
		<description><![CDATA[<b>对象范式</b>——民主制社会的编程法则（<em>对象式编程简谈</em>）<br/>
•	如果把整个流程看作一颗倒长的大树，过程式编程自树根向下，逐渐分支，直到每片树叶，类似数学证明中的分析法，即执果索因的逆推法；OOP则从每片树叶开始，逐渐合并，直到树根，类似数学证明中的综合法，即执因索果的正推法<br/>
•	与其说OOP更具重用性，不如说更具易用性<br/>
•	函数是被动的实体，对象是主动的实体<br/>
•	过程式程序的世界是君主制的；OO程序的世界是民主制的<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="id622049"></a>2.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>
                    如果把整个流程看作一颗倒长的大树，过程式编程自树根向下，逐渐分支，直到每片树叶，类似数学证明中的分析法，即执果索因的逆推法；OOP则从每片树叶开始，逐渐合并，直到树根，类似数学证明中的综合法，即执因索果的正推法
                </p></li><li class="listitem"><p>
                    与其说OOP更具重用性，不如说更具易用性
                </p></li><li class="listitem"><p>
                    函数是被动的实体，对象是主动的实体
                </p></li><li class="listitem"><p>
                    过程式程序的世界是君主制的；OO程序的世界是民主制的
                </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>OOP是一种特殊的命令式吗？</p></li><li class="listitem"><p>OOP的基本思想是什么？</p></li><li class="listitem"><p>OOP到底好在哪里？</p></li><li class="listitem"><p>OOP将要一统天下吗？</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>
            “面向对象？”冒号咕哝着，“姑且称之为OO或对象式吧，既不标新立异，也不以讹传讹。在回答你的问题之前，请先回答我的：什么是OOP？”
        </p><p>
            引号应答如流：“<span class="term">OOP</span>（Object-Oriented programming）是一种计算机编程模式，它以对象作为问题空间的基本元素，利用对象和对象间的相互作用来设计程序。所谓对象，是实际问题中实体的抽象，具有一定的属性和功能。OOP的三个基本特征是：封装性、继承性和多态性。所谓封装性就是——”
        </p><p>
            冒号作了个暂停的手势：“OOP的的基本特征相信大家早就耳熟能详了，那么根据你刚才的定义，能否得出OOP一定是命令式的结论？”
        </p><p>
            引号歪头想了一阵，答道：“从定义上好像并不能得出，难道C++、Java、C#不是命令式的吗？”
        </p><p>
            冒号回答：“当然是，但这不妨碍Clos成为OO版的Lisp，而Prolog也有不少融入OO特征的扩充，如Visual Prolog、Logtalk等。OOP虽然是在命令式的基础上发展起来的，但其核心思想可泛化为：以数据为中心组织逻辑，将系统视为相互作用的对象集合，并利用继承与多态来增强可维护性、可扩展性和可重用性。这种思想也能应用到函数式和逻辑式中，只不过对象的方法从命令式中的过程分别换成函数式中的函数和逻辑式中的断言罢了。大致说来，命令式、函数式和逻辑式互相平行，而OOP与它们正交。”
        </p><p>
            问号提问：“OOP已经成为一种潮流，上堂课列举的十二种流行语言中只有C不是OO的，这是否意味着OOP将要一统天下？”
        </p><p>
            “严格说来，VB（VB.NET除外）和JavaScript也不是OO的，只是<span class="term">基于对象的</span>（Object Based）<a class="link" href="#note1"><sup>[1]</sup></a>。” 冒号纠正道，“至于OOP是否会一统天下，答案是否定的。首先，与能独当一面的三类最基本的范式不同，纯粹的OOP是不存在的<a class="link" href="#note2"><sup>[2]</sup></a>，必须结合其他范式；其次，世上没有包治百病的万灵丹方，OOP也不例外。用软件业的行话来说：没有银弹（No silver bullet）<a class="link" href="#note3"><sup>[3]</sup></a>。OOP最适用于大型复杂的、交互式的、尤其是与现实世界密切相关的系统，但在小型应用、数学计算、符号处理等方面并无优势。需要指出的是，语言和范式的流行，与大公司支持和商业推动是密切相关的。有人说OOP其实是MOP（Money-Oriented Programming），即以金钱为导向的。虽有过激之嫌，但有经验的股民都知道，有主力运作的股票总是涨得快一些的。当然OOP能流行，自有独到之处，谁能说说它到底好在哪里？”
        </p><p>
            逗号抢答：“OOP能提高软件的可维护性、可扩展性和可重用性。”
        </p><p>
            冒号反问：“为什么过程式编程的可维护性、可扩展性和可重用性就差呢？”
        </p><p>
            感到来者不善，逗号有点发虚：“因为OOP具有信息隐藏、继承和多态的特征。”
        </p><p>
            冒号并不买帐：“首先，将可维护性、可扩展性和可重用性与OOP划等号，是只见树木，不见森林——那是所有范式和语言的共同目标。其次，以C语言为例，信息隐藏可用关键字static来实现；继承可用<span class="term">合成</span>（composition）来代替；多态可以利用函数指针来实现。更何况这些只是手段而非目的，只要设计合理，C程序同样具有可维护性、可扩展性和可重用性，性能效率还更优越。即使在OOP日益风行的今天，C的占有率始终稳踞前列，许多大型复杂软件如操作系统、数据库等仍以C为主，这足以证明其仍堪大用。”
        </p><p>
            见逗号有些理屈词穷，冒号语气放缓：“请不要误解，我并非OOP的反对者，相反今后还要重点讨论它。但我希望大家少一点照本宣科和人云亦云，多一点独立思考，甚至不妨标新立异。”
        </p><p>
            稍作停顿，冒号继续发问：“过程式编程与OOP在设计理念上有什么区别？”
        </p><p>
             “过程式编程的理念是以过程为中心，自顶向下、逐步求精<a class="link" href="#note4"><sup>[4]</sup></a>。”引号一出口就自感有些“照本宣科”，见冒号正用鼓励的目光看着他，这才继续说下去，“OOP则正相反，以数据为中心，自底向上、逐步合并。”
        </p><p>
            冒号首肯道：“如果把整个流程看作一颗倒长的大树，过程式编程自树根向下，逐渐分支，直到每片树叶，类似数学证明中的分析法，即<span class="emphasis"><em>执果索因</em></span>的逆推法；OOP则从每片树叶开始，逐渐合并，直到树根，类似数学证明中的综合法，即<span class="emphasis"><em>执因索果</em></span>的正推法。”
        </p><p>
            句号心领神会：“倘若把树根看成主函数，离树根越近，离用户需求也越近。如果用过程式编程，由于是逆推法，树干改变容易导致树枝相应改变，因此一旦用户需求发生变化，可能会从树根波及到树枝甚至树叶，维护起来殊为不易。相反OOP从树叶开始设计，离用户需求较远，抽象程度较高，受波及的程度较小，因此更易维护和重用。”
        </p><p>
            冒号拊掌赞道：“好极了！”
        </p><p>
            问号不解：“您刚才不还说C程序同样具有可重用性吗？”
        </p><p>
            冒号微微一笑：“数学中分析法与综合法往往是结合起来使用的，过程式编程与OOP也是如此，只不过各有偏重罢了。句号的一番话虽不无道理，但也授OOP的反对者以口实：OOP鼓吹的可重用性来自‘自底向上’的设计模式，而这种模式并非OOP的专利。其实软件设计的最重要的并不是编程语言，甚至也不是编程范式，而是<span class="strong"><strong>抽象思维</strong></span>。关于这一点，我们今后还会详细阐述。”
        </p><p>
            叹号不甘寂寞，插言道：“OOP以对象为基本模块单位，而对象是现实中具体事物和抽象概念的模拟，这使得编程设计更自然更人性化。”
        </p><p>
            “深合吾意！”冒号挥动着右手，“尽管OOP最大的卖点是其高度的可重用性，相比其他范式却并不具明显优势。但它更接近人类的认知模式，编程者更容易也更乐于用这种方式编程，这是它深入人心的一个重要原因。比较一下两种用法：<code class="code">牛.吃（草）</code>与<code class="code">吃（牛，草）</code>，哪种更接近人类思维？”
        </p><p>
            有人在底下嘀咕：“如果把牛换成狗，那么一个是狗吃屎，一个是吃狗屎。”
        </p><p>
            全班捧腹。
        </p><p>
            冒号也忍不住笑了：“OOP人性化的另一表现是其接口简洁易记。看看Win32 API、Unix API等之类操作系统接口或OCI之类的数据库接口，函数的参数动辄七八个乃至上十个，函数名和数据结构成员也多冗长晦涩，既难记又易错。相比之下，相应的Java的API显然平易近人得多。”
        </p><p>
            问号刨根问底：“为什么C的API不能象Java的那么简洁呢？”
        </p><p>
            冒号释疑：“单纯这么比较其实对C并不公平，因为Java的API虽然简洁易用，但功能上与相应C的API并不等同，换句话说，Java把<span class="emphasis"><em>接口粗粒度化</em></span>了。”
        </p><p>
             “接口粗粒度化？”引号质疑道，“就是把一些函数包装起来吧？我们也可以用C将操作系统、数据库之类的API再包装一下。”
        </p><p>
            “事实上许多软件公司都曾这样做过。”冒号颔首作答，“但C函数不像Java对象，本身没有状态，只有依靠参数传递或外部变量来维持相关函数之间的联系，包装后的接口肯定不如Java简洁，但应该比Java高效。说白了，OOP就是将相关的函数用数据粘合，重新包装后再贴上对象的标签。从这种角度上看，<span class="strong"><strong>与其说OOP更具重用性，不如说更具易用性</strong></span>。”
        </p><p>
            叹号狐疑道：“OOP并不更具重用性？这可是它的金字招牌啊！”
        </p><p>
            冒号冷哼一声：“不要被金字招牌晃晕了眼，我来问你：是收音机、电视机之类的电器产品更具重用性呢，还是电阻、电容之类的电器元件更具重用性？”
        </p><p>
             “当然是电器元件啦。”叹号冲口而出。
        </p><p>
            冒号因势利导：“每个电器元件具备单一的功能，正如过程式中的函数；每个电器产品是对多个相互关联的电器元件的封装，正如OOP中的对象。同样的电器元件可用于不同的电器产品，具有高度的可重用性，而电器产品重用性低，但易用性高。<a class="link" href="#note5"><sup>[5]</sup></a>”
        </p><p>
            众人犹自将信将疑。
        </p><p>
            “对一个没有独立思考习惯的人来讲，与其说他认同一个理论，倒不如说他认同该理论倡导者的权威。而在他仰视权威的同时，也把自己的思想交托给了权威。”冒号颇具犬儒之风，“你们可以怀疑我的观点，但绝不可放弃自己的思考。请注意，这就是我在第一堂课提到的精神——<span class="strong"><strong>批判精神</strong></span>。”
        </p><p>
            冒号这时停了下来，与在座的每位逐一对视。他仿佛想通过目光把这种精神注入到每个人的身上，就像武侠小说中通过手掌将内功传输给他人一样。并不是每个人都能理解冒号的用心，但都或多或少地感受到一种异样的气氛。
        </p><p>
            “关于OOP今天就谈到这里。”冒号恢复了常态，“请不要奇怪为何如此流行的编程范式我却一带而过，那是因为你们对它相对比较熟悉，而我们这一轮只是在作热身运动，以后再作专项训练。在结束之前，我们引进一个新视点：过程式编程的模块以函数为单位，OOP的模块以对象为单位，二者的区别是：函数是被动的实体，对象是主动的实体。<span class="strong"><strong>过程式程序的世界是君主制的</strong></span>，主函数是国王，其他函数是臣民，等级分明，所有臣民在听命于上级的同时也对下级发号施令，最终为国王服务；<span class="strong"><strong>OO程序的世界是民主制的</strong></span>，所有对象都是独立而平等的公民，有权利保护自己的财产和隐私并向他人寻求服务，同时有义务为他人提供承诺的服务，公民之间通过信息交流来协作完成各种任务。更进一步地，<span class="strong"><strong>封装使得公民拥有个体身份</strong></span>，需要对自己负责；<span class="strong"><strong>继承使得公民拥有家庭身份</strong></span>，需要对家庭负责；<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>
                    所谓基于对象的，有两种不同的涵义。一种指“限制版”的OOP，即具备对象概念，但不具备OOP的一些其他特征，如继承或多态等。Visual Basic（不包括Visual Basic .NET）正属于此类。另一种指基于原型的（prototype-based），或者说基于实例的（instance-based），而不像通常OOP是基于类的（class-based）。JavaScript、NewtonScript、MOO等语言即属此类。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    这里所谓“纯粹的OOP”并非指一般意义上的“pure OOP”（即所谓的“一切都是对象”），而指单纯的、不含其他范式的OOP。
                </p></li><li class="listitem"><p><a name="note3"></a>
                    出自Fred Brooks的著名文章《no silver bullet》（参见文献【2】）。他认为没有一项技术或管理方法的发展能保证，在十年内让软件的生产力、可靠性或简洁性等方面提高一个数量级。常用来泛指没有一项软件技术或方法是万能的。
                </p></li><li class="listitem"><p><a name="note4"></a>
                    更准确地说，这是前文提到的结构化编程思想。
                </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>
                    OOP大多是命令式的，但也有函数式的和逻辑式的OO语言。
                </p></li><li class="listitem"><p>
                    OOP的核心思想可以归纳为：以数据为中心组织逻辑，将系统视为相互作用的对象集合，并利用继承与多态来增强可维护性、可扩展性和可重用性。
                </p></li><li class="listitem"><p>
                    OOP既不能脱离其他范式，也绝非适用于一切应用。
                </p></li><li class="listitem"><p>
                    可维护性、可扩展性和可重用性是所有范式和语言的共同目标，并非OOP所独有。
                </p></li><li class="listitem"><p>
                    与其说OOP更具重用性，不如说更具易用性。
                </p></li><li class="listitem"><p>
                    过程式编程以过程为中心，自顶向下，逐步求精。
                </p></li><li class="listitem"><p>
                    对象式编程以数据为中心，自底向上，逐步合并。
                </p></li><li class="listitem"><p>
                    过程式程序的世界是君主制的，OO程序的世界是民主制的。
                </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．Object-oriented programming．<a class="link" href="http://en.wikipedia.org/wiki/Object-oriented_programming" target="_top">http://en.wikipedia.org/wiki/Object-oriented_programming</a>
                </p></li><li class="listitem"><p>
                    Frederick Brooks．The Mythical Man-month．Boston：AddisonWesley，1995．179-203
                </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%2F06%2Fcolon-class-2_3%2F&amp;linkname=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A72.3%EF%BC%9A%E5%AF%B9%E8%B1%A1%E8%8C%83%E5%BC%8F">分享/保存</a>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/09/06/colon-class-2_3/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>冒号课堂§2.2：声明范式</title>
		<link>http://blog.zhenghui.org/2009/09/04/colon-class-2_2/</link>
		<comments>http://blog.zhenghui.org/2009/09/04/colon-class-2_2/#comments</comments>
		<pubDate>Fri, 04 Sep 2009 15:48:57 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[函数式编程]]></category>
		<category><![CDATA[命令式编程]]></category>
		<category><![CDATA[声明式编程]]></category>
		<category><![CDATA[编程范式]]></category>
		<category><![CDATA[逻辑式编程]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=341</guid>
		<description><![CDATA[<b>声明范式</b>——目标决定行动（<em>声明式编程简谈</em>）<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="id671200"></a>2.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">Archimedes</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></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></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="term">声明式编程</span>（declarative programming）。顾名思义，声明式编程由若干<span class="term">规范</span>（specification）的声明组成的，即一系列陈述句：‘已知这，求解那’，强调‘做什么’而非‘怎么做’。声明式编程是人脑思维方式的抽象，即利用数理逻辑或既定规范对已知条件进行推理或运算。”
        </p><p>
            问号询问：“声明式产生的背景是什么呢？”
        </p><p>
            “声明式编程发轫于人工智能的研究，主要包括<span class="term">函数式编程</span>（functional programming，简称FP）和<span class="term">逻辑式编程</span>（logic programming，简称LP）。其中，函数式编程将计算描述为数学函数的求值，而逻辑式编程通过提供一系列事实和规则来推导或论证结论。其实支持它们的语言出现得并不比命令式的晚多少——最早的函数式语言Lisp（<span class="strong"><strong>LIS</strong></span>t <span class="strong"><strong>P</strong></span>rocessor）已有半个世纪的历史，最早之一的逻辑式语言Prolog（<span class="strong"><strong>PRO</strong></span>gramming in <span class="strong"><strong>LOG</strong></span>ic）也与C同龄。只是由于大多数更多地用于学术研究而非商业应用，颇有些‘养在深闺人未识’啊。”冒号有些惋惜，“起源的不同决定了这两大类范式代表着迥然不同的编程理念和风格：<span class="strong"><strong>命令式编程是行动导向（Action-Oriented）的</strong></span>，因而<span class="strong"><strong>算法是显性而目标是隐性的</strong></span>；<span class="strong"><strong>声明式编程是目标驱动（Goal-Driven）的</strong></span>，因而<span class="strong"><strong>目标是显性而算法是隐性的</strong></span>。为便于说明，我们分别用三种代表性的语言来实现阶乘（factorial）运算。”
        </p><p>
            冒号在黑板上打出投影——
        </p><div class="example"><a name="id671308"></a><p class="title"><b>阶乘的三种实现：</b></p><div class="example-contents"><p><span class="emphasis"><em>C（命令式）</em></span>——</p><pre class="programlisting">
    int factorial(int n) 
    {
        int f = 1;
        for (; n &gt; 0; --n) f *= n;
        return f;
    }</pre><p><span class="emphasis"><em>Lisp（函数式）</em></span>——</p><pre class="programlisting">
    (defun factorial(n)
      (if (= n 0)
        1                               //  若n等于0，则n!等于1
        (* n (factorial(- n 1)))))      //  否则n!等于n* (n-1)</pre><p><span class="emphasis"><em>Prolog（逻辑式）</em></span>——</p><pre class="programlisting">
    // 0! 等于1
    factorial(0,1).
    // 若M等于N-1且 M!等于Fm且F等于N*Fm，则N! 等于F
    factorial(N,F) :-   M is N-1, factorial(M,Fm), F is N * Fm. </pre></div></div><br class="example-break"><p>
            冒号提问：“撇开语法细节，大家说说以上三段代码区别在哪里？”
        </p><p>
            句号沉思片刻，答道：“C明确给出了阶乘的迭代算法，而Lisp仅描述了阶乘的递归定义，Prolog则陈述了两个关于阶乘的断言。”
        </p><p>
            冒号很满意：“一针见血！第二个问题：你们更习惯哪一种思维方式？”
        </p><p>
            逗号不加思索：“当然是第一种！”
        </p><p>
            冒号微笑着说：“这证明你至少是受过一定训练的程序员。大家回想一下，当你们初学编程时，是否习惯这种思维方式？”
        </p><p>
            叹号沉吟道：“好像不太习惯<code class="code">i = i + 1</code>之类的语句。”
        </p><p> “对！”冒号的一嗓子吓了众人一跳，“我们最早接触的变量是代数方程中的x、y、z等，本质上是<span class="emphasis"><em>抽象化的符号</em></span>，变量值是该符号在给定约束条件下的允许值。而命令式编程中的变量本质上是<span class="emphasis"><em>抽象化的内存</em></span>，变量值是该内存的储存内容。通俗地说，前者好比<span class="emphasis"><em>姓名</em></span>，所指之人是固定的；后者好比<span class="emphasis"><em>住址</em></span>，所住之人是变化的。此外，等号在代数中是一种约束，而在许多命令式语言中则表示赋值。因此<code class="code">i = i + 1</code>可以在命令式编程中出现，但绝不可能在数学推理中出现<a class="link" href="#note1"><sup>[1]</sup></a>——除非在反证法中。”
        </p><p>
            叹号又道：“现在回头再看代数，反倒有些不习惯了。”
        </p><p>
            “这就是思维的<span class="emphasis"><em>定势效应</em></span>。”冒号感慨道，“声明式编程让我们重回数学思维：函数式编程类似代数中的表达式变换和计算，逻辑式编程则类似数理逻辑推理。其中的变量也如数学中的一样，是抽象符号而非内存地址，因此没有赋值运算，不会产生变量被改写的<span class="emphasis"><em>副作用</em></span>（side-effect），也不存在内存分配和释放的问题。这既简化了代码，也减少了调试——不妨想一想，有多少bug是由于某个变量被意外改写或内存管理不慎而造成的？”
        </p><p>
            问号问道：“声明式语言与命令式语言看来是两个世界的产物，它们是否有相通之处？”
        </p><p>
            冒号答道：“首先，所有高级语言都建立于低级语言之上，最终转化为机器语言，声明式语言也不例外。其次，声明式语言与命令式语言并非泾渭分明，而是互相交叉渗透的。一些‘非纯粹’ 的声明式语言也提供变量赋值和流程控制，而一些命令式语言也在逐渐发展，通过利用其他程序或增加新的语言特征来实现声明式编程。总的说来，在命令式语言中融入声明式的元素应当是一种趋势。尤其是函数式，它的一些特征已经在许多命令式语言中得到了支持。比较而言，声明式编程重目标、轻过程，专注问题的分析和表达而不致陷入算法的迷宫，其代码也更加简洁清晰、易于修改和维护。从这种意义上说，声明式语言天然地就比命令式语言更高级。上节课提到的前三代计算机语言基本上都是命令式的，而后两代基本上都是声明式的，由此可见一斑。”
        </p><p>
            句号一拍脑袋：“命令式是模拟电脑的，声明式是模拟人脑的，人脑当然比电脑高级啦。”
        </p><p>
            冒号另有佐证：“早在命令式语言引入函数从而进化为过程式语言时，就已经开始向声明式过渡了。何以见得？比方说调用一个函数的语句：<code class="code">doWhat()</code>，这不正是在声明‘what to do’吗？至于‘how to do’，即函数的具体实现细节，则不劳调用者费心。这种声明式的风格，提高了语言的抽象能力和开发效率，促成了语言的升级。”
        </p><p>
            逗号仍然有些疑惑：“既然声明式编程有这么多好处，为什么命令式语言不仅占大多数，而且流行程度也不减呢？”
        </p><p>
             冒号回答：“编程语言的流行程度与其擅长的领域关系密切。声明式语言——尤其是函数式语言和逻辑式语言——擅长基于数理逻辑的应用，如人工智能、符号处理、数据库、编译器等，对基于业务逻辑的、尤其是交互式或事件驱动型的应用就不那么得心应手了。而大多数软件是面向用户的，交互性强、多为事件驱动、业务逻辑千差万别，显然命令式语言在此更有用武之地。”
        </p><p>
            大家频频颔首。
        </p><p>
            “值得指出的是，声明式编程并不仅仅局限于函数式和逻辑式。”冒号旋即补充道，“比方说，C#中的attribute、Java中的annotation和XDoclet库等采用的也是具有声明式特征的<span class="term">属性导向式编程</span>（Attribute-Oriented Programming，简称@OP）。再比如，Prograph<a class="link" href="#note2"><sup>[2]</sup></a> 、SISAL<a class="link" href="#note3"><sup>[3]</sup></a>等<span class="term">数据流语言</span>（dataflow language）采用的<span class="term">数据流式编程</span>（Dataflow Programming）与函数式编程有不少共同点，同样属于声明式的范畴。还有一些语言如Oz、CHIP等支持与逻辑式编程相交的<span class="term">约束式编程</span>（Constraint Programming）<a class="link" href="#note4"><sup>[4]</sup></a>。此外，大家熟悉的数据库语言SQL，<span class="term">样式语言</span>XSLT、CSS，<span class="term">标记语言</span>HTML、XML、SVG，<span class="term">规范语言</span>IDL（Interface Description Language）等等都是声明式的。算上它们，声明式语言所占的比例也是非常可观的。此前之所以没有提及，一方面，不少声明式语言采用的范式并没有专门的名称；另一方面，这些语言大多是<span class="term">领域特定语言</span>，并且不少并非<span class="term">图灵完备的</span>，有的连运算都没有。毕竟，目前我们的重点还是放在<span class="term">通用编程语言</span>上。”
        </p><p>
            问号突然想到了什么，指着投影问：“这里用Lisp实现阶乘的方法不也可以用在C上吗？”
        </p><p>
            冒号点点头，写下一段代码——
        </p><pre class="programlisting">
int factorial(int n) 
{
    return n == 0 ? 1 : n * factorial(n - 1);
}</pre><p>
            “这是C的递归实现。”冒号娓娓道来，“除了细微的语法差别外，二者的确很相似，这说明用命令式语言也可以讲出声明式的味道。实际上，命令式语言提倡迭代而不鼓励递归，早期的Fortran 甚至都不支持递归。一则迭代比递归更符合命令式的思维模式，因为前者贴近机器语言而后者贴近数学语言；二则除<span class="term">尾递归</span>（tail recursion）<a class="link" href="#note5"><sup>[5]</sup></a>外，一般递归比迭代的开销（overhead）大。相反，声明式语言提倡递归而不支持迭代<a class="link" href="#note6"><sup>[6]</sup></a>。就语法而言，它不允许迭代中的循环变量；就视角而言，迭代着眼微观过程而递归着眼宏观规律。”
        </p><p>
            叹号轻叹：“原来貌似普通的迭代和递归有那么多道道！”
        </p><p>
            “任何语言都难脱命令式或声明式的窠臼。事实上，凡是非命令式的编程都可归为声明式编程。因此，命令式、函数式和逻辑式是最核心的三种范式。为清楚起见，我们用一幅图来表示它们之间的关系。”说罢，冒号换了一个幻灯片——
        </p><div class="figure"><a name="id671551"></a><p class="title"><b>图2-4. 编程范式的简单分类</b></p><div class="figure-contents"><div class="mediaobject"><img src="http://blog.zhenghui.org/img/colonclass/figure2-4.jpg" alt="编程范式的简单分类"></div></div></div><br class="figure-break"><p>
            末了，冒号归纳道：“归根结底，<span class="strong"><strong>编程是寻求一种机制，将指定的输入转化为指定的输出</strong></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>，输入是<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>，编程就是设计一系列命题，通过逻辑推理以完成证明。绘成表格如下（如表2-1所示）——” 
        </p><div class="table"><a name="id671610"></a><p class="title"><b>表2-1. 三种核心编程范式的比较</b></p><div class="table-contents"><table summary="三种核心编程范式的比较" border="1"><colgroup><col><col><col><col><col><col></colgroup><thead><tr><th>范式</th><th>程序</th><th>输入</th><th>输出</th><th>程序设计</th><th>程序运行</th></tr></thead><tbody><tr><td>命令式</td><td>自动机</td><td>初始状态</td><td>最终状态</td><td>设计指令</td><td>命令执行</td></tr><tr><td>函数式</td><td>数学函数</td><td>自变量</td><td>因变量</td><td>设计函数</td><td>表达式变换</td></tr><tr><td>逻辑式</td><td>逻辑证明</td><td>题设</td><td>结论</td><td>设计命题</td><td>逻辑推理</td></tr></tbody></table></div></div><br class="table-break"><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>
                    数学中应该用类似<span class="mathphrase">i<sub>n+1</sub> = i<sub>n</sub> + 1</span>的表示法。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    Prograph即<span class="strong"><strong>Pro</strong></span>gramming in <span class="strong"><strong>Graph</strong></span>ics，是一种可视化的、对象导向（OO）的数据流语言，它用图表（diagram）来取代文本编码。
                </p></li><li class="listitem"><p><a name="note3"></a>
                    SISAL即<span class="strong"><strong>S</strong></span>treams and <span class="strong"><strong>I</strong></span>teration in a <span class="strong"><strong>S</strong></span>ingle <span class="strong"><strong>A</strong></span>ssignment <span class="strong"><strong>L</strong></span>anguage，是一种函数式数据流语言，擅长并行科学计算。
                </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>
                    有些声明式语言（例如Lisp的一个变种Scheme）虽然支持迭代，但一般是用尾递归来实现的。从本质上说，这只是一种语法上的甜头（syntactic sugar）。它与普通迭代的区别在于：前者的循环变量是重新绑定（rebind）的，而后者的循环变量是重复赋值（reassign）的。
                </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 class="simpara"> 三种核心编程范式采用如下不同的机制——</p><p class="simpara">命令式：自动机机制，通过设计指令完成从初始态到最终态的转变。</p><p class="simpara">函数式：数学变换机制，通过设计函数完成从自变量到因变量的计算。</p><p class="simpara">逻辑式：逻辑证明机制，通过逻辑推理完成从题设到结论的证明。</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>
                    Ravi Sethi．Programming Languages: Concepts &amp; Constructs(英文版第2版)．北京：机械工业出版社，2002．301-340,423-470
                </p></li><li class="listitem"><p>
                    Wikipedia．Declarative programming．<a class="link" href="http://en.wikipedia.org/wiki/Declarative_programming" target="_top">http://en.wikipedia.org/wiki/Declarative_programming</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%2F04%2Fcolon-class-2_2%2F&amp;linkname=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A72.2%EF%BC%9A%E5%A3%B0%E6%98%8E%E8%8C%83%E5%BC%8F">分享/保存</a>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/09/04/colon-class-2_2/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>冒号课堂§2.1：命令范式</title>
		<link>http://blog.zhenghui.org/2009/09/03/colon-class-2_1/</link>
		<comments>http://blog.zhenghui.org/2009/09/03/colon-class-2_1/#comments</comments>
		<pubDate>Thu, 03 Sep 2009 08:08:52 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[命令式编程]]></category>
		<category><![CDATA[流程图]]></category>
		<category><![CDATA[结构化编程]]></category>
		<category><![CDATA[编程范式]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=322</guid>
		<description><![CDATA[<b>命令范式</b>——一切行动听指挥（<em>命令式编程简谈</em>）<br/>
•	（命令式编程）其世界观是：程序是由若干行动指令组成的有序列表；其方法论是：用变量来储存数据，用语句来执行指令<br/>
•	（结构化编程）在微观上，主张循规守矩；在宏观上，主张分而治之<br/>
•	一个按结构化编程思想设计的流程图，每个模块大小适中、模块之间关系简明、模块内部线路清晰，单从视觉上就会给人一种美感]]></description>
			<content:encoded><![CDATA[<h1 style="text-align: center;"><span style="font-family: 宋体;">冒号课堂</span></h1>
<strong><span style="font-size: 13pt; font-family: 宋体;">第二课 重要范式（1）</span></strong>

<p><strong><span style="font-family: 宋体;">课前导读</span></strong></p>
<p style="text-indent: 18pt;"><span style="font-family: 宋体;">这一课讨论了五个最重要的编程范式：命令式、函数式、逻辑式、对象式和并发式。这些只是入门性的介绍，读者领会其精神即可，细节上不必过于在意。</span></p>
<p style="margin: 0cm 0cm 0pt; text-indent: 18pt;"><span style="font-family: 宋体;">本课共分四节——</span></p>
<p style="text-indent: 18pt;"><em>1.</em><em><span style="font-family: 宋体;">命令范式——一切行动听指挥</span></em></p>
<p style="text-indent: 18pt;"><em>2.</em><em><span style="font-family: 宋体;">声明范式——目标决定行动</span></em></p>
<p style="margin-left: 18pt;"><em>3.</em><em><span style="font-family: 宋体;">对象范式——民主制社会的编程法则</span></em></p>
<p style="margin-left: 18pt;"><em>4.</em><em><span style="font-family: 宋体;">并发范式——合作与竞争</span></em></p>

<!-- below comes from generated html -->
<head><link rel="stylesheet" href="http://blog.zhenghui.org/css/colonclass.css" type="text/css"></head>
        
        <div lang="zh-CN" class="article" title="命令范式"><div class="titlepage"><div><div><h1 class="title"><a name="id519232"></a>2.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></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>
            第二课伊始，冒号开门见山：“首先介绍一下最原始也是我们最熟悉的编程范式：<span class="term">命令式编程</span>（imperative programming）。用命令式编写的程序由命令序列组成，即一系列祈使句：‘先做这，再做那’，强调‘怎么做’。更学术点说，命令式编程是电脑——准确地讲，是<span class="term">冯•诺伊曼机</span>（von Neumann machine）——运行机制的抽象，即依序从内存中获取指令和数据然后去执行。从范式的角度看，<span class="strong"><strong>其世界观是：程序是由若干行动指令组成的有序列表；其方法论是：用变量来储存数据，用语句来执行指令</strong></span>。”
        </p><p>
            逗号小声嘟囔着：“我们用的编程语言不都是命令式的吗？”
        </p><p>
            “应该说绝大多数语言是命令式的，但非命令式的语言也是存在的。关于后者，我们稍后再议。” 冒号纠正道，“这也在情理之中。语言的演化是渐进的，大多数语言追根溯源是汇编语言的升级，而作为与机器语言一一对应的汇编语言自然是命令式的，因而这种范式最为传统和普及。”
        </p><p>
            不料问号竟穷追不舍：“为什么机器语言就一定得是命令式呢？”
        </p><p>
            “我很欣赏你这种打破砂锅问到底的精神，不过你可以问到底，我却不敢保证能答到底哦。”冒号有些逗趣地说。
        </p><p>
            问号略带腼腆地笑了。
        </p><p>
            “从理论上而言，完全可以有非命令式的机器语言存在，前提是计算机采用了特殊的硬件实现，比如非冯•诺伊曼结构的<span class="term">数据流机</span>（dataflow machine）和<span class="term">归约机</span>（reduction machine）。但这类计算机并未流行于市，相应的机器语言自然罕见了<a class="link" href="#note1"><sup>[1]</sup></a>。”冒号还是给出了理由。
        </p><p>
            引号问：“命令式编程与人们常说的过程式编程是一回事吗？”
        </p><p>
            “严格说来，<span class="term">过程式编程</span>（procedural programming）是指引入了<span class="term">过程</span>（procedure）、<span class="term">函数</span>（function）或<span class="term">子程序</span>（subroutine/subprogram）的命令式编程。但由于现代的命令式语言均具备此特征，故二者往往不加区分。”冒号回应道。
        </p><p>
            句号认为：“由于常用的语言基本上都是命令式的，其思想也与计算机的运行机制相符，一切对我们来说，似乎都是自然而然的事。”
        </p><p>
            “单纯的命令式思想的确很朴素，毋庸赘述。” 冒号承认，“但有必要提一下<span class="term">结构化编程</span>（structured programming或简称SP），它是在过程式编程的基础上发展起来的。其本质是一种编程原则，提倡代码应具有清晰的逻辑结构，以保证程序易于读写、测试、维护和优化。可别小瞧它，在上世纪六十年代首次爆发的软件危机中，它曾起着中流砥柱的作用，就像后来的OOP一样。Pascal正是遵循结构化编程原则而设计的一种教学语言，当年也是风光无限。为直观起见，我们用图来表现程序的结构化特征——”
        </p><div class="figure"><a name="id519336"></a><p class="title"><b>图2-1. 非严格的结构化程序框图</b></p><div class="figure-contents"><div class="mediaobject"><img src="http://blog.zhenghui.org/img/colonclass/figure2-1.jpg" alt="非严格的结构化程序框图"></div></div></div><br class="figure-break"><p>
            “这是一个<span class="term">流程图</span>（flowchart），或称<span class="term">程序框图</span>，它描述了一个简单程序：用户从标准输入中键入算术表达式，程序打印出结果，如此循环往复，直到用户输入字符‘q’为止。”冒号解释道，“若以纯粹的结构化编程的标准来衡量，该流程图并未达标。”
        </p><p>
            叹号有些惊讶：“这个图不是再清晰不过了吗？”
        </p><p>
            “根据结构化定理（structured program theorem）<a class="link" href="#note2"><sup>[2]</sup></a>，任何程序都可用顺序（concatenation）、选择（selection）和循环（repetition）等三种基本控制结构来表示。”冒号画了几幅图——
        </p><div class="figure"><a name="id519382"></a><p class="title"><b>图2-2. 三种基本控制结构：顺序、选择和循环</b></p><div class="figure-contents"><div class="mediaobject"><img src="http://blog.zhenghui.org/img/colonclass/figure2-2.jpg" alt="三种基本控制结构：顺序、选择和循环"></div></div></div><br class="figure-break"><p>
            冒号指点着黑板：“结构化编程就是在三种基本结构的基础上进行嵌套组合。如果将每个基本结构看作基本电器元件，编程就是将这些元件组装成复杂的电路。请注意，所有基本元件都满足‘单入口、单出口’（single entry, single exit，简称SESE）的原则，这使得电路井井有条，不会串线缠绕。”
        </p><p>
            引号已经看出：“上面的流程图无法用基本结构来组合，所以不是标准的结构化程序。”
        </p><p>
            冒号提出要求：“你能改造一下吗？”
        </p><p>
            引号走上讲台，在前图的基础上作了些许调整——
        </p><div class="figure"><a name="id519414"></a><p class="title"><b>图2-3. 严格的结构化程序框图</b></p><div class="figure-contents"><div class="mediaobject"><img src="http://blog.zhenghui.org/img/colonclass/figure2-3.jpg" alt="严格的结构化程序框图"></div></div></div><br class="figure-break"><p>
            “嗯，不错。这下流程图可以拆解为基本结构了。”冒号表示认可，“二者的区别在于前者利用了break语句，在循环途中退出，后者通过引入quit标志（flag）保证了循环的完整性。”
        </p><p>
            逗号的脸上写下一个大大的问号：“难道连break语句都不能用吗？”
        </p><p>
            “break语句只是在循环体内部的跳转，合理地使用它能简化代码，不致影响整体结构，大可不必拘泥于教条。但goto语句可以跳到程序过程中的任一点，造成<span class="emphasis"><em>静态程序</em></span>（static program）与<span class="emphasis"><em>动态进程</em></span>（dynamic process）之间的差异，影响程序可读性，是要竭力避免的<a class="link" href="#note3"><sup>[3]</sup></a>。”冒号如是说道，“结构化编程的思想包括两方面。在微观上，主张循规守矩，采用顺序、选择和循环三种逻辑结构，摒弃或限制goto语句<a class="link" href="#note4"><sup>[4]</sup></a>，以避免杂乱无章的代码。在宏观上，主张分而治之（divide and conquer），采用‘自顶向下（top-down）’ <a class="link" href="#note5"><sup>[5]</sup></a> 的设计，通过<span class="emphasis"><em>模块化</em></span>将一个较为复杂的系统分解为若干相对简单的子系统，每个子系统可以独立地进一步分解，直到容易编码实现为止。这两方面是互为因果、互为保障的——由基本结构拼装而成的系统一定是模块清晰、层次分明的；反之，系统逐步分解到最后，一定会演化成基本结构。”
        </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>
                    数据流机支持数据流式语言，归约机支持函数式语言。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    见参考文献【1】。
                </p></li><li class="listitem"><p><a name="note3"></a>
                    1967年，E.W. Dijkstra 在其著名论文“Goto statement considered harmful”中指出goto语句的危害性，主张废除这种用法。
                </p></li><li class="listitem"><p><a name="note4"></a>
                    1974年，Donald Knuth在论文“Structured Programming with Goto Statements”中认为goto语句仍有其合理性，应该限制而不是完全摒弃。
                </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>
                    结构化编程是过程式编程的一种原则，其主要思想是：提倡在宏观上采用‘自顶向下’的设计，微观上采用顺序、选择和循环的逻辑结构，摒弃或限制goto语句，以保证程序结构清晰、易于调试和维护。
                </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>
                    Corrado Böhm，Giuseppe Jacopini．Flow Diagrams, Turing Machines and Languages with Only Two Formation Rules． Communications of the ACM，1966，9(5)：366–371
                </p></li><li class="listitem"><p>
                    Ravi Sethi．Programming Languages: Concepts &amp; Constructs, 2<sup>nd</sup> Ed.．Reading, MA：Addison Wesley，1996．59-77
                </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%2F03%2Fcolon-class-2_1%2F&amp;linkname=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A72.1%EF%BC%9A%E5%91%BD%E4%BB%A4%E8%8C%83%E5%BC%8F">分享/保存</a>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/09/03/colon-class-2_1/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>冒号课堂§1.5：开发技术</title>
		<link>http://blog.zhenghui.org/2009/08/31/colon-class-1_5/</link>
		<comments>http://blog.zhenghui.org/2009/08/31/colon-class-1_5/#comments</comments>
		<pubDate>Mon, 31 Aug 2009 02:58:29 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[工具包]]></category>
		<category><![CDATA[开发技术]]></category>
		<category><![CDATA[惯用法]]></category>
		<category><![CDATA[架构]]></category>
		<category><![CDATA[框架]]></category>
		<category><![CDATA[编程范式]]></category>
		<category><![CDATA[设计模式]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=308</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: 宋体">第一课 开班导言(5)</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="id627012"></a>1.5 开发技术——实用还是时髦？</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>
            冒号说道：“一种是具体而实用的，最好能立马解决学习和工作中的问题；一种是时髦而花哨的，管它有用没用，不学点心里就是不踏实。”
        </p><p>
            众人虽觉此话有些尖刻，细想起来也有几分道理，但老冒明知而不为，不走群众路线，偏去扯什么劳什子的范式——当然，直接谈OOP倒是不错的。
        </p><p>
            “<span class="strong"><strong>自以为懂的未必真的懂，自以为不懂的未必真的不懂</strong></span>。” 冒号玩起了玄学，“有些概念和技术即使背得烂熟，甚至用得烂熟，那也不代表真正掌握；有些概念和技术看起来很新奇，却不过是新瓶装旧酒。”
        </p><p>
            叹号颇不服气：“用得烂熟都不算掌握，难不成只有发明概念和技术才算掌握？”
        </p><p>
            “哈哈，那倒不必。”冒号笑道，“用得烂熟不等于用得恰到好处，能解决问题不等于没有后顾之忧。”
        </p><p>
            逗号问道：“那掌握的标准是什么？”
        </p><p>
             “许多应聘者喜欢在简历中言必称精通某某语言、某某技术云云，大多不必面试即知其大言炎炎——倘若真的精通，他自当应聘更高的职位。”冒号有感而发却又似不着边际，“任何概念和技术都不是孤立的，如果不能在纵向的时间和横向的联系中找准坐标，便似那群摸象的盲人，各执一端却又自以为是。”
        </p><p>
            众人心想，老冒虽言辞旦旦却有凿空之嫌，一节课下来，天马行空的扯了不少，真刀真枪的一个也无，该不是只会纸上谈兵吧？
        </p><p>
            句号紧扣主题：“您为何选择谈编程范式，而不是框架、设计模式还有架构呢？难道它们真如您所说只是时髦而花哨的东西吗？”
        </p><p>
            “我可没这么说。”冒号矢口否认，“但在弄清一样东西存在的意义之前就随众跟风，早晚会跟丢的。我先问问你们：什么是<span class="term">框架</span>（framework）？它与一般的<span class="term">库</span>（library）和<span class="term">工具包</span>（toolkit）有何不同？”
        </p><p>
            引号应答：“框架就是一组协同工作的类，它们为特定类型的软件构筑了一个可重用的设计。与库和工具包不同之处在于前者侧重<span class="emphasis"><em>设计重用</em></span>而后两者侧重<span class="emphasis"><em>代码重用</em></span>。”
        </p><p>
            “嗯，有点标准答案的味道。”冒号夸道，“如果吹毛求疵的话，框架并不限于OOP，可以是协同工作的<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>在特定领域的整体设计上不必重新发明轮子；库和工具包的意义在于使<span class="emphasis"><em>开发者</em></span>摆脱底层编码，专注特定问题和业务逻辑。”
        </p><p>
            问号提出问题：“框架与库和工具包看起来很相似——都是一些代码集合，都提供一些API（应用编程接口），是什么使得它们不同呢？”
        </p><p>
            “问得好！”冒号赞言，“框架与工具包最大的差别在截然相反的设计理念上：<span class="strong"><strong>库和工具包是为程序员带来自由的，框架是为程序员带来约束的</strong></span>。具体地说，库和工具包是为程序员提供武器装备的，框架则利用<span class="emphasis"><em>控制反转</em></span>（IoC）<a class="link" href="#note1"><sup>[1]</sup></a>机制实现对各模块的统一调度从而剥夺了程序员对全局的掌控权，使他们成为手执编程武器、随时听候调遣的士兵。”
        </p><p>
            叹号苦着脸：“程序员原来就是一小卒子啊！”
        </p><p>
             “哪个将军不是从小卒做起的？”冒号反问道，“不错，框架是在语言的语法规则之外施加于程序员的又一层枷锁，但没有规矩不成方圆。正如行军打仗，讲究排兵布阵，程序员就是那兵，框架就是那阵。”
        </p><p>
            句号说：“可不可以这么理解，框架就是一些人——也就是框架设计者，把一个软件开发中最甜的部分啃掉了，剩下部分留给下面的人？”
        </p><p>
             “从某种意义上说，是这样。”冒号点点头。
        </p><p>
            逗号很不甘心：“我就想啃最甜的部分。”
        </p><p>
             “当心别把牙给崩掉。”冒号笑道，“不是打击你，首先你还没那本事；其次即使你有本事也未必有机会；最后即使有本事也有机会，重新设计框架也未必是好的选择。就说大名鼎鼎的Struts吧，哪怕你设计出比它更高明的框架也难以被采用，因为前者早已成为Java平台上网络开发的事实（de facto）标准，公司很容易从市场上招到懂Struts的程序员，不必培训即可上手，成本低见效快。过去许多公司都有自己的网络框架，但最后大多都放弃了，并不是因为Struts更优秀，而是因为它更普及。毕竟大多数软件开发是以金钱而不是技术为中心的。”
        </p><p>
            问号提议：“您能谈谈设计模式和架构吗？”
        </p><p>
            冒号侃侃而谈：“与框架与库和工具包不同，<span class="term">设计模式</span>（design pattern）和<span class="term">架构</span>（architecture）不是<span class="emphasis"><em>软件产品</em></span>，而是<span class="emphasis"><em>软件思想</em></span>。<span class="strong"><strong>设计模式是软件的战术思想，架构是软件的战略决策</strong></span>。设计模式是针对某些经常出现的问题而提出的行之有效的设计解决方案，它侧重<span class="emphasis"><em>思想重用</em></span>，因此比框架更抽象、更普适，但多限于局部解决方案，没有框架的整体性。与之相似的还有<span class="term">惯用法</span>（idiom），也是针对常发问题的解决方案，但偏重实现而非设计，与实现语言密切相关，是一种更底层更具体的编程技巧。至于架构，一般指一个软件系统的最高层次的整体结构和规划，一个架构可能包含多个框架，而一个框架可能包含多个设计模式。”
        </p><p>
            引号愈发疑惑：“这些不是都很重要吗？”
        </p><p>
            “当然都很重要。不过——”冒号话锋一转，“在没有打好基础前，架构只是空中楼阁，因此不可能现在谈它。至于框架，不同的应用领域有不同的框架，如表现层的Struts、业务层的Spring、持久层的Hibernate等等，即使相同领域的框架也有多个选择，更不用说不同的语言框架还不一样，从何谈起？再说框架其实一点也不高深，完全可以无师自通，关键是领会思想，多学习多实践。说到设计模式，一共就那么几十个，一本‘四人帮’（GoF）<a class="link" href="#note2"><sup>[2]</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="strong"><strong>语感</strong></span>的重视和培养，导致学生只会‘中式英语’。同样道理，一个惯用C语言编程的人也许很快就能写一些C++程序，但如果他只注重C++的<span class="emphasis"><em>语法</em></span>而不注重培养OOP的<span class="emphasis"><em>语感</em></span>，那么写出的程序一定是‘C式C++’。与其如此，倒不如直接用C呢。”
        </p><p>
            句号悟道：“您是想告诉我们，<span class="strong"><strong>学习编程范式能增强编程语言的语感</strong></span>？”
        </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>
                    控制反转（Inversion of Control）是一种软件设计原则。与通常的用户代码调用可重用的库（library）代码不同，IoC倒转了控制流方向：由库代码调用用户代码。有人将此比作好莱坞法则：“不要打电话给我们，我们会打给你的”。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    设计模式最经典书籍《Design Patterns: Elements of Reusable Object-Oriented Software》的四位作者常被称为GoF或Gang of Four。
                </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>
                    库和工具包侧重代码重用，框架侧重设计重用。库和工具包从微观上解决具体问题，是为程序员带来自由的；框架从宏观上控制软件整体的结构和流程，是为程序员带来约束的。框架是通过控制反转（IoC）机制反客为主的。
                </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．26-28
                </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>01-01 作为一个软件开发者，你现在处于哪个阶段？你未来的目标是什么？</li>
    <li>01-02 传统的学习方式的弊端在哪里？你是否有切肤之痛？</li>
    <li>01-03 你认为一个优秀的程序员需要具备什么素质和精神？</li>
    <li>01-04 你了解哪些计算机语言？你对一门语言的取舍与喜恶的根据是什么？</li>
    <li>01-05 你认为计算机语言未来的发展方向是什么？</li>
    <li>01-06 你能否在编程中感受到自己的激情和灵性？</li>
    <li>01-07 你了解哪些框架？它们主要解决了哪些问题？应用范围是什么？实现的机理是什么？</li>
    <li>01-08 你了解哪些设计模式？它们为什么能成其为模式？</li>
    <li>01-09 你熟悉的语言中有哪些惯用法？</li>
    <li>01-10 你对编程范式是如何理解的？学习它的意义何在？</li>
</ul><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F08%2F31%2Fcolon-class-1_5%2F&amp;linkname=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A71.5%EF%BC%9A%E5%BC%80%E5%8F%91%E6%8A%80%E6%9C%AF">分享/保存</a>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/08/31/colon-class-1_5/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>冒号课堂§1.4：初识范式</title>
		<link>http://blog.zhenghui.org/2009/08/29/colon-class-1_4/</link>
		<comments>http://blog.zhenghui.org/2009/08/29/colon-class-1_4/#comments</comments>
		<pubDate>Sat, 29 Aug 2009 14:29:27 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[对象导向]]></category>
		<category><![CDATA[编程范式]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[面向对象]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=303</guid>
		<description><![CDATA[<b>初识范式</b>——程序王国中的世界观与方法论（<em>初步引入编程范式</em>）<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="id599705"></a>1.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></ul></div><div class="itemizedlist"><ul class="itemizedlist" type="disc"><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>
            逗号不以为然：“最流行的不就那么几个：Java、C++、C#还有VB吗？”
        </p><p>
            不意此言遭到冒号连珠炮似的反问：“可你知道它们为什么会流行吗？是不是学会这几样就是一个合格的程序员了？它们会不会变得不那么流行，甚至被其他语言取代？如果不会，为什么？如果会，又怎么办？”
        </p><p>
            逗号赧然语塞。
        </p><p>
            冒号口气放缓：“掌握一门语言的语法、工具和技巧固然重要，但那只相当于学会一门兵器的招法，更重要的当然是心法。招法重形，心法重意。<span class="strong"><strong>得形而忘意，无异舍本逐末；得意而忘形，方能游刃有余</strong></span>。下面要谈的就是一种心法：编程范式。”
        </p><p>
            问号不解：“编程范式？听上去很学究，那是什么东东？”
        </p><p>
            冒号续道：“范式译自英文的paradigm，也有译作典范、范型、范例的。所谓<span class="term">编程范式</span>（programming paradigm），指的是计算机编程的基本风格或典范模式。借用哲学的术语，如果说每个编程者都在创造虚拟世界，那么编程范式就是他们置身其中自觉不自觉采用的世界观和方法论。”
        </p><p>
            叹号吸口气：“好抽象哦！”
        </p><p>
            句号心中一动：“您是说我们都是虚拟世界的创造者，都在创造自己的黑客帝国？”
        </p><p>
            大家不禁莞尔。
        </p><p>
            冒号动情地说：“难道不是吗？只不过帝国有大小之分、优劣之别罢了。当你编程之时，便进入到自己创造的世界之中。这是你的世界，只有注入你的想象力、创造力和激情，它才有勃勃生机。你编写的岂止是代码，分明还有乐曲；你敲击的岂止是键盘，分明还有琴键；你运行的岂止是程序，分明还有世界。当优美的旋律奏起，整个世界都随之翩然起舞，一种莫可名状的满足是否会充溢你的全身？”
        </p><p>
            大家都被冒号诗化的语言感染了，没想到编程也可以如此感性。
        </p><p>
            良久，引号试探地问：“面向对象编程就是一种编程范式吧？”
        </p><p>
            冒号点头：“不错，它是时下最流行的一种编程范式。顺便说一句，‘面向对象’ 译自Object-Oriented，但‘面向’二字令人费解。据说有本书叫‘面向对象方法’，比别的计算机书都畅销，知道为什么吗？不少同学把它当成恋爱指南买走了。”
        </p><p>
            全班笑倒。
        </p><p>
            冒号认真地说：“将Object-Oriented译成‘对象导向’<a class="link" href="#note1"><sup>[1]</sup></a>，虽然稍嫌拗口，但更贴切。并非刻意要咬文嚼字，这关系到对编程范式的理解。我们知道，编程是为了解决问题，而解决问题可以有多种视角和思路，其中普适且行之有效的模式被归结为范式。由于着眼点和思维方式的不同，相应的范式自然各有侧重和倾向，因此一些范式常用‘oriented’来描述。换言之，每种范式都引导人们带着某种的倾向去分析问题、解决问题，这不就是‘导向’吗？而‘面向’ 的宾语往往是预先确定的目标，如面向世界、面向未来、面向用户、面向问题等等。此外，‘面向’强调<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>
            全班再倒。
        </p><p>
            句号得意地解释：“按梦中情人的标准去找对象，具体目标未定但选择倾向已定，这就是一种导向，而且是对象导向。找到之后再约会，不就面向对象了吗？”
        </p><p>
            众人称绝。
        </p><p>
            “我们是来谈编程范式的，不是来谈对象的。” 冒号一脸的道貌岸然，“编程范式是抽象的，必须通过具体的编程语言来体现。它代表的世界观往往体现在语言的<span class="strong"><strong>核心概念</strong></span>中，代表的方法论往往体现在语言的<span class="strong"><strong>表达机制</strong></span>中。一种范式可以在不同的语言中实现，一种语言也可以同时支持多种范式。任何语言在设计时都会倾向某些范式、同时回避某些范式，由此形成了不同的语法特征和语言风格。”
        </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>
                    港澳台地区将其译为“物件导向”。即使单从字面上翻译，oriented是“以&#8230;为方向的；以&#8230;为目的的；导向的；定向的”的意思，也比译为“面向”更合适。
                </p></li><li class="listitem"><p><a name="note2"></a>
                    作为类比，经济学中的“market-oriented”译为“市场导向（或取向）的”的远多于译为“面向市场的”。
                </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>
                    编程范式是计算机编程中的基本风格和典范模式，是编程者在其所创造的虚拟世界中自觉不自觉采用的世界观和方法论。每种范式都引导人们带着其特有的倾向和思路去分析和解决问题。OOP就是一种编程范式。
                </p></li><li class="listitem"><p>
                    Object-Oriented多译作“面向对象”，不如“对象导向”贴切。
                </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>
                    Wikipedia．Programming paradigm．<a class="link" href="http://en.wikipedia.org/wiki/Programming_paradigm" target="_top">http://en.wikipedia.org/wiki/Programming_paradigm</a>
                </p></li><li class="listitem"><p>
                    Stephen H. Kaisler．SOFTWARE PARADIGMS．New Jersey：Wiley，2005．21-22
                </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%2F08%2F29%2Fcolon-class-1_4%2F&amp;linkname=%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%C2%A71.4%EF%BC%9A%E5%88%9D%E8%AF%86%E8%8C%83%E5%BC%8F">分享/保存</a>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/08/29/colon-class-1_4/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>《冒号课堂》目录</title>
		<link>http://blog.zhenghui.org/2009/08/21/contents-of-colonclass/</link>
		<comments>http://blog.zhenghui.org/2009/08/21/contents-of-colonclass/#comments</comments>
		<pubDate>Fri, 21 Aug 2009 15:34:27 +0000</pubDate>
		<dc:creator>hui</dc:creator>
				<category><![CDATA[冒号课堂]]></category>
		<category><![CDATA[OOP]]></category>
		<category><![CDATA[目录]]></category>
		<category><![CDATA[程序员]]></category>
		<category><![CDATA[编程范式]]></category>
		<category><![CDATA[软件设计]]></category>

		<guid isPermaLink="false">http://blog.zhenghui.org/?p=4</guid>
		<description><![CDATA[《冒号课堂》一书的目录]]></description>
			<content:encoded><![CDATA[<h3 style="text-align: left;"><span style="font-family: 宋体;">
</span></h3>
<ul style="margin-top: 0cm; list-style-type: none;">
	<li style="font-size: 14pt; color: #3366ff;">上篇：<strong>编程范式与编程语言</strong></li>
	<li style="font-size: 14pt; color: #3366ff;"><strong>
</strong></li>
	<li><strong>第一课 开班导言</strong></li>
	<li><a href="http://blog.zhenghui.org/2009/08/24/colon-class-1_1/">§1.1：开班发言——<em>程序员的四层境界</em></a></li>
	<li><a href="http://blog.zhenghui.org/2009/08/26/colon-class-1_2/">§1.2：首轮提问——<em>什么语言好？</em></a></li>
	<li><a href="http://blog.zhenghui.org/2009/08/27/colon-class-1_3/">§1.3：语言选择——<em>合适的就是好的</em></a></li>
	<li><a href="http://blog.zhenghui.org/2009/08/29/colon-class-1_4/">§1.4：初识范式——<em>程序王国中的世界观与方法论</em></a></li>
	<li><a href="http://blog.zhenghui.org/2009/08/31/colon-class-1_5/">§1.5：开发技术——<em>实用还是时髦？</em></a></li>
	<li><em>
</em></li>
	<li><strong>第二课 重要范式</strong></li>
	<li><a href="http://blog.zhenghui.org/2009/09/03/colon-class-2_1/">§2.1：命令范式——<em>一切行动听指挥</em></a></li>
	<li><a href="http://blog.zhenghui.org/2009/09/04/colon-class-2_2/">§2.2：声明范式——<em>目标决定行动</em></a></li>
	<li><a href="http://blog.zhenghui.org/2009/09/06/colon-class-2_3/">§2.3：对象范式——<em>民主制社会的编程法则</em></a></li>
	<li><a href="http://blog.zhenghui.org/2009/09/07/colon-class-2_4/">§2.4：并发范式——<em>合作与竞争</em></a></li>
	<li><em>
</em></li>
	<li><strong>第三课 常用范式</strong></li>
	<li><a href="http://blog.zhenghui.org/2009/09/08/colon-class-3_1/">§3.1：泛型范式——<em>抽象你的算法</em></a></li>
	<li><a href="http://blog.zhenghui.org/2009/09/09/colon-class-3_2/">§3.2：超级范式——<em>提升语言的级别</em></a></li>
	<li><a href="http://blog.zhenghui.org/2009/09/10/colon-class-3_3/">§3.3：切面范式——<em>多角度看问题</em></a></li>
	<li><a href="http://blog.zhenghui.org/2009/09/11/colon-class-3_4/">§3.4：事件驱动——<em>有事我叫你，没事别烦我</em></a></li>
	<li><em>
</em></li>
	<li><strong>第四课 重温范式</strong></li>
	<li><a href="http://blog.zhenghui.org/2009/09/13/colon-class-4_1/">§4.1：函数范式——<em>精巧的数学思维</em></a></li>
	<li><a href="http://blog.zhenghui.org/2009/09/15/colon-class-4_2/">§4.2：逻辑范式——<em>当算法失去了控制</em></a></li>
	<li><a href="http://blog.zhenghui.org/2009/09/17/colon-class-4_3/">§4.3：汇总范式——<em>一张五味俱全的大烙饼</em></a></li>
	<li><a href="http://blog.zhenghui.org/2009/09/19/colon-class-4_4/">§4.4：情景范式——<em>餐馆里的编程范式</em></a></li>
	<li><em>
</em></li>
	<li><strong>第五课 语言小谈</strong></li>
	<li><a href="http://blog.zhenghui.org/2009/09/21/colon-class-5_1/">§5.1：教学计划——<em>接下来的故事</em></a></li>
	<li><a href="http://blog.zhenghui.org/2009/09/23/colon-class-5_2/">§5.2：数据类型——<em>规则与变通</em></a></li>
	<li><a href="http://blog.zhenghui.org/2009/09/25/colon-class-5_3/">§5.3：动态语言——<em>披着彩衣飞舞的脚本语言</em></a></li>
	<li><a href="http://blog.zhenghui.org/2009/09/27/colon-class-5_4/">§5.4：语言误区——<em>语言的宗教情结</em></a></li>
	<li><em>
</em></li>
	<li><strong>第六课 语言简评</strong></li>
	<li><a href="http://blog.zhenghui.org/2009/09/29/colon-class-6_1/">§6.1：系统语言——<em>权力的双刃剑</em></a></li>
	<li><a href="http://blog.zhenghui.org/2009/10/03/colon-class-6_2/">§6.2：平台语言——<em>先搭台后唱戏</em></a></li>
	<li><a href="http://blog.zhenghui.org/2009/10/08/colon-class-6_3/">§6.3：前台语言——<em>视觉与交互的艺术</em></a></li>
	<li><a href="http://blog.zhenghui.org/2009/10/10/colon-class-6_4/">§6.4：后台脚本——<em>敏捷开发的利器</em></a></li>
	<li><em>
</em></li>
</ul>
<ul style="margin-top: 0cm; list-style-type: none;">
	<li style="font-size: 14pt; color: #3366ff;">下篇：<strong>抽象机制与对象范式</strong></li>
	<li style="font-size: 14pt; color: #3366ff;"><strong>
</strong></li>
	<li><strong>第七课 抽象封装</strong></li>
	<li>§7.1：抽象思维——<em>减法和除法的学问</em></li>
	<li>§7.2：数据抽象——<em>“做什么”重于“怎么做”</em></li>
	<li>§7.3：封装隐藏——<em>包装的讲究</em></li>
	<li><em>
</em></li>
	<li><strong>第八课 抽象接口</strong></li>
	<li>§8.1：软件应变——<em>随需而变，适者生存</em></li>
	<li>§8.2：访问控制——<em>代码的多级管理</em></li>
	<li>§8.3：接口服务——<em>讲诚信与守规矩</em></li>
	<li><em>
</em></li>
	<li><strong>第九课 继承机制</strong></li>
	<li>§9.1：继承关系——<em>继承财富，更要继承责任</em></li>
	<li>§9.2：慎用继承——<em>以谨慎之心对待权力</em></li>
	<li><em>
</em></li>
	<li><strong>第十课 多态机制</strong></li>
	<li><a href="http://blog.zhenghui.org/2009/10/20/colon-class-10_1/">§10.1：多态类型——<em>静中之动</em></a></li>
	<li><a href="http://blog.zhenghui.org/2009/10/24/colon-class-10_2/">§10.2：抽象类型——<em>实中之虚</em></a></li>
	<li><em>
</em></li>
	<li><strong>第十一课 值与引用</strong></li>
	<li>§11.1：语法类型——<em>体用之分</em></li>
	<li>§11.2：语义类型——<em>阴阳之道</em></li>
	<li><em>
</em></li>
	<li><strong>第十二课 设计原则</strong></li>
	<li>§12.1：间接原则——<em>柔胜于刚，曲胜于直</em></li>
	<li>§12.2：依赖原则——<em>有求皆苦，无欲则刚</em></li>
	<li>§12.3：内聚原则——<em>不是一家人，不进一家门</em></li>
	<li>§12.4：保变原则——<em>与魔鬼打交道的艺术</em></li>
	<li><em>
</em></li>
	<li><strong>第十三课 设计模式</strong></li>
	<li>§13.1：创建模式——<em>不要问我从哪里来</em></li>
	<li>§13.2：结构模式——<em>建筑的技巧</em></li>
	<li>§13.3：行为模式——<em>君子之交淡如水</em></li>
	<li>§13.4：闭班小结——<em>软件无形，编程有道</em></li>
</ul><a class="a2a_dd addtoany_share_save" href="http://www.addtoany.com/share_save?linkurl=http%3A%2F%2Fblog.zhenghui.org%2F2009%2F08%2F21%2Fcontents-of-colonclass%2F&amp;linkname=%E3%80%8A%E5%86%92%E5%8F%B7%E8%AF%BE%E5%A0%82%E3%80%8B%E7%9B%AE%E5%BD%95">分享/保存</a>]]></content:encoded>
			<wfw:commentRss>http://blog.zhenghui.org/2009/08/21/contents-of-colonclass/feed/</wfw:commentRss>
		<slash:comments>20</slash:comments>
		</item>
	</channel>
</rss>
