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

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

