<?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/"
	xmlns:series="http://unfoldingneurons.com/"
	>

<channel>
	<title>朱文昊 Albert Zhu &#187; 软件工程</title>
	<atom:link href="http://zhuwenhao.com/category/%e6%8a%80%e6%9c%af/%e8%bd%af%e4%bb%b6%e5%b7%a5%e7%a8%8b/feed/" rel="self" type="application/rss+xml" />
	<link>http://zhuwenhao.com</link>
	<description>朱文昊的中文博客－－专注技术，向往自由</description>
	<lastBuildDate>Sun, 22 Jan 2012 13:47:56 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>一些软件设计的原则</title>
		<link>http://zhuwenhao.com/771/%e6%8a%80%e6%9c%af/%e8%bd%af%e4%bb%b6%e5%b7%a5%e7%a8%8b/%e8%ae%be%e8%ae%a1/%e4%b8%80%e4%ba%9b%e8%bd%af%e4%bb%b6%e8%ae%be%e8%ae%a1%e7%9a%84%e5%8e%9f%e5%88%99/</link>
		<comments>http://zhuwenhao.com/771/%e6%8a%80%e6%9c%af/%e8%bd%af%e4%bb%b6%e5%b7%a5%e7%a8%8b/%e8%ae%be%e8%ae%a1/%e4%b8%80%e4%ba%9b%e8%bd%af%e4%bb%b6%e8%ae%be%e8%ae%a1%e7%9a%84%e5%8e%9f%e5%88%99/#comments</comments>
		<pubDate>Mon, 25 Apr 2011 06:27:18 +0000</pubDate>
		<dc:creator>朱文昊 Albert Zhu</dc:creator>
				<category><![CDATA[设计]]></category>

		<guid isPermaLink="false">http://zhuwenhao.com/?p=771</guid>
		<description><![CDATA[本文是专题：程序员修炼之路中的第6篇，共6篇转载自 http://coolshell.cn/articles/4535.html
作者  [...]]]></description>
			<content:encoded><![CDATA[<p>转载自 http://coolshell.cn/articles/4535.html</p>
<p>作者 陈皓</p>
<p>以前本站向大家介绍过一些软件开发的原则，比如<a title="优质代码的十诫" rel="bookmark" href="http://coolshell.cn/articles/1007.html" target="_blank">优质代码的十诫</a>和<a title="Unix传奇(下篇)" href="http://coolshell.cn/articles/2324.html" target="_blank">Unix传奇(下篇)</a>中所以说的UNIX的设计原则。相信大家从中能够从中学了解到一些设计原理方面的知识，正如我在《<a title="再谈“我是怎么招聘程序员的”（上）" href="http://coolshell.cn/articles/4506.html" target="_blank">再谈“我是怎么招聘程序”</a>》中所说的，一个好的程序员通常由其操作技能、知识水平，经验层力和能力四个方面组成。在这里想和大家说说设计中的一些原则，我认为这些东西属于长期经验总结出来的知识。这些原则，每一个程序员都应该了解。但是请不要教条主义，在使用的时候还是要多多考虑实际情况。其实，<strong>下面这些原则，不单单只是软件开发，可以推广到其它生产活动中，甚至我们的生活中</strong>。</p>
<h4>Don’t Repeat Yourself (DRY)</h4>
<p>DRY  是一个最简单的法则，也是最容易被理解的。但它也可能是最难被应用的（因为要做到这样，我们需要在泛型设计上做相当的努力，这并不是一件容易的事）。它意 味着，当我们在两个或多个地方的时候发现一些相似的代码的时候，我们需要把他们的共性抽象出来形一个唯一的新方法，并且改变现有的地方的代码让他们以一些 合适的参数调用这个新的方法。</p>
<p><strong>参考</strong>：<a title="http://en.wikipedia.org/wiki/Don%27t_repeat_yourself" rel="nofollow" href="http://en.wikipedia.org/wiki/Don%27t_repeat_yourself">http://en.wikipedia.org/wiki/Don%27t_repeat_yourself</a></p>
<h4>Keep It Simple, Stupid (KISS)</h4>
<p>KISS原则在设计上可能最被推崇的，在家装设计，界面设计 ，操作设计上，复杂的东西越来越被众人所BS了，而简单的东西越来越被人所认可，比如<a title="UI的恶梦" href="http://coolshell.cn/articles/1907.html" target="_blank">这些UI的设计</a>和我们<a title="为什么中国的网页设计那么烂？" href="http://coolshell.cn/articles/3605.html" target="_blank">中国网页</a>（尤其是<a title="微软用新浪来当反面教材" href="http://coolshell.cn/articles/3872.html" target="_blank">新浪的网页</a>） 者是负面的例子。“宜家”（IKEA）简约、效率的家居设计、生产思路；“微软”（Microsoft）“所见即所得”的理念；“谷歌”（Google) 简约、直接的商业风格，无一例外的遵循了“kiss”原则，也正是“kiss”原则，成就了这些看似神奇的商业经典。而苹果公司的iPhone/iPad 将这个原则实践到了极至。</p>
<p>&nbsp;</p>
<p>把一个事情搞复杂是一件简单的事，但要把一个复杂的事变简单，这是一件复杂的事。</p>
<p><strong>参考</strong>：<a title="http://en.wikipedia.org/wiki/KISS_principle" rel="nofollow" href="http://en.wikipedia.org/wiki/KISS_principle">http://en.wikipedia.org/wiki/KISS_principle</a></p>
<h4>Program to an interface, not an implementation</h4>
<p>这是设计模式中最根本的哲学，注重接口，而不是实现，依赖接口，而不是实现。接口是抽象是稳定的，实现则是多种多样的。以后面我们会面向对象的SOLID原则中会提到我们的依赖倒置原则，就是这个原则的的另一种样子。还有一条原则叫 <strong>Composition over inheritance</strong>（喜欢组合而不是继承），这两条是那23个经典设计模式中的设计原则。</p>
<h4>Command-Query Separation (CQS)  – 命令-查询分离原则</h4>
<ul>
<li>查询：当一个方法返回一个值来回应一个问题的时候，它就具有查询的性质；</li>
<li>命令：当一个方法要改变对象的状态的时候，它就具有命令的性质；</li>
</ul>
<p>通常，一个方法可能是纯的Command模式或者是纯的Query模式，或者是两者的混合体。在设计接口时，如果可能，应该尽量使接口单一化，保证 方法的行为严格的是命令或者是查询，这样查询方法不会改变对象的状态，没有副作用，而会改变对象的状态的方法不可能有返回值。也就是说：如果我们要问一个 问题，那么就不应该影响到它的答案。实际应用，要视具体情况而定，语义的清晰性和使用的简单性之间需要权衡。将Command和Query功能合并入一个 方法，方便了客户的使用，但是，降低了清晰性，而且，可能不便于基于断言的程序设计并且需要一个变量来保存查询结果。</p>
<p>在系统设计中，很多系统也是以这样原则设计的，查询的功能和命令功能的系统分离，这样有则于系统性能，也有利于系统的安全性。</p>
<p><strong>参考</strong>：<a title="http://en.wikipedia.org/wiki/Command-query_separation" rel="nofollow" href="http://en.wikipedia.org/wiki/Command-query_separation">http://en.wikipedia.org/wiki/Command-query_separation</a></p>
<h4>You Ain’t Gonna Need It (YAGNI)</h4>
<p>这个原则简而言之为——只考虑和设计必须的功能，避免过度设计。只实现目前需要的功能，在以后您需要更多功能时，可以再进行添加。</p>
<ul>
<li>如无必要，勿增复杂性。</li>
<li>软件开发先是一场沟通博弈。</li>
</ul>
<p>以前本站有一篇关于<a title="代码重构的一个示例" href="http://coolshell.cn/articles/3005.html" target="_blank">过度重构的文章</a>，这个示例就是这个原则的反例。而，WebSphere的设计者就<a href="http://www.bbc.co.uk/news/business-11944966" target="_blank">表示过他过度设计了这个产品</a>。我们的程序员或是架构师在设计系统的时候，会考虑很多扩展性的东西，导致在架构与设计方面使用了大量折衷，最后导致项目失败。这是个令人感到讽刺的教训，因为本来希望尽可能延长项目的生命周期，结果反而缩短了生命周期。</p>
<p><strong>参考</strong>：<a title="http://en.wikipedia.org/wiki/You_Ain%27t_Gonna_Need_It" rel="nofollow" href="http://en.wikipedia.org/wiki/You_Ain%27t_Gonna_Need_It" target="_blank">http://en.wikipedia.org/wiki/You_Ain%27t_Gonna_Need_It</a></p>
<h4><span id="more-771"></span>Law of Demeter – 迪米特法则</h4>
<p>迪米特法则(Law of Demeter)，又称“最少知识原则”（Principle of Least  Knowledge），其来源于1987年荷兰大学的一个叫做Demeter的项目。Craig Larman把Law of  Demeter又称作“不要和陌生人说话”。在《程序员修炼之道》中讲LoD的那一章叫作“解耦合与迪米特法则”。关于迪米特法则有一些很形象的比喻：</p>
<ul>
<li>如果你想让你的狗跑的话，你会对狗狗说还是对四条狗腿说？</li>
<li>如果你去店里买东西，你会把钱交给店员，还是会把钱包交给店员让他自己拿？</li>
</ul>
<p>和狗的四肢说话？让店员自己从钱包里拿钱？这听起来有点荒唐，不过在我们的代码里这几乎是见怪不怪的事情了。</p>
<p>对于LoD，正式的表述如下：</p>
<blockquote><p>对于对象 ‘O’ 中一个方法’M&#8217;，M应该只能够访问以下对象中的方法：</p>
<ol>
<li>对象O；</li>
<li>与O直接相关的Component Object；</li>
<li>由方法M创建或者实例化的对象；</li>
<li>作为方法M的参数的对象。</li>
</ol>
</blockquote>
<p>在《Clean Code》一书中，有一段Apache framework中的一段违反了LoD的代码：</p>
<p>final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();</p>
<p>这么长的一串对其它对象的细节，以及细节的细节，细节的细节的细节……的调用，增加了耦合，使得代码结构复杂、僵化，难以扩展和维护。</p>
<p>在《重构》一书中的代码的环味道中有一种叫做“Feature Envy”(依恋情结），形象的描述了一种违反了LoC的情况。Feature  Envy就是说一个对象对其它对象的内容更有兴趣，也就是说老是羡慕别的对象的成员、结构或者功能，大老远的调用人家的东西。这样的结构显然是不合理的。 我们的程序应该写得比较“害羞”。不能像前面例子中的那个不把自己当外人的店员一样，拿过客人的钱包自己把钱拿出来。“害羞”的程序只和自己最近的朋友交 谈。这种情况下应该调整程序的结构，让那个对象自己拥有它羡慕的feature，或者使用合理的设计模式（例如Facade和Mediator）。</p>
<p><strong>参考</strong>：<a title="http://en.wikipedia.org/wiki/Principle_of_Least_Knowledge" rel="nofollow" href="http://en.wikipedia.org/wiki/Principle_of_Least_Knowledge">http://en.wikipedia.org/wiki/Principle_of_Least_Knowledge</a></p>
<h4>面向对象的S.O.L.I.D 原则</h4>
<p>一般来说这是面向对象的五大设计原则，但是，我觉得这些原则可适用于所有的软件开发。</p>
<p><strong>Single Responsibility Principle (SRP) – 职责单一原则</strong></p>
<p>关于单一职责原则，其核心的思想是：<strong>一个类，只做一件事，并把这件事做好，其只有一个引起它变化的原因</strong>。单一职 责原则可以看作是低耦合、高内聚在面向对象原则上的引申，将职责定义为引起变化的原因，以提高内聚性来减少引起变化的原因。职责过多，可能引起它变化的原 因就越多，这将导致职责依赖，相互之间就产生影响，从而极大的损伤其内聚性和耦合度。单一职责，通常意味着单一的功能，因此不要为一个模块实现过多的功能 点，以保证实体只有一个引起它变化的原因。</p>
<ul>
<li>Unix/Linux是这一原则的完美体现者。各个程序都独立负责一个单一的事。</li>
<li>Windows是这一原则的反面示例。几乎所有的程序都交织耦合在一起。</li>
</ul>
<p><strong>Open/Closed Principle (OCP) – 开闭原则</strong></p>
<p>关于开发封闭原则，其核心的思想是：模块是可扩展的，而不可修改的。也就是说，<strong>对扩展是开放的，而对修改是封闭的</strong>。</p>
<ul>
<li>对扩展开放，意味着有新的需求或变化时，可以对现有代码进行扩展，以适应新的情况。</li>
<li>对修改封闭，意味着类一旦设计完成，就可以独立完成其工作，而不要对类进行任何修改。</li>
</ul>
<p>对于面向对象来说，需要你依赖抽象，而不是实现，23个经典设计模式中的“策略模式”就是这个实现。对于非面向对象编程，一些API需要你传入一个 你可以扩展的函数，比如我们的C  语言的qsort()允许你提供一个“比较器”，STL中的容器类的内存分配，ACE中的多线程的各种锁。对于软件方面，浏览器的各种插件属于这个原则的 实践。</p>
<p><strong>Liskov substitution principle (LSP) – 里氏代换原则</strong></p>
<p>软件工程大师Robert C. Martin把里氏代换原则最终简化为一句话：“Subtypes must be substitutable  for their base  types”。也就是，子类必须能够替换成它们的基类。即：子类应该可以替换任何基类能够出现的地方，并且经过替换以后，代码还能正常工作。另外，不应该 在代码中出现if/else之类对子类类型进行判断的条件。里氏替换原则LSP是使代码符合开闭原则的一个重要保证。正是由于子类型的可替换性才使得父类 型的模块在无需修改的情况下就可以扩展。</p>
<p>这么说来，似乎有点教条化，我非常建议大家看看这个原则个两个最经典的案例——“正方形不是长方形”和“鸵鸟不是鸟”。通过这两个案例，你会明白 《墨子 小取》中说的  ——“娣，美人也，爱娣，非爱美人也….盗，人也；恶盗，非恶人也。”——妹妹虽然是美人，但喜欢妹妹并不代表喜欢美人。盗贼是人，但讨厌盗贼也并不代表 就讨厌人类。<strong>这个原则让你考虑的不是语义上对象的间的关系，而是实际需求的环境</strong>。</p>
<p>在很多情况下，在设计初期我们类之间的关系不是很明确，LSP则给了我们一个判断和设计类之间关系的基准：需不需要继承，以及怎样设计继承关系。</p>
<p><strong>Interface Segregation Principle (ISP) – 接口隔离原则</strong></p>
<p>接口隔离原则意思是把功能实现在接口中，而不是类中，使用多个专门的接口比使用单一的总接口要好。</p>
<p>举个例子，我们对电脑有不同的使用方式，比如：写作，通讯，看电影，打游戏，上网，编程，计算，数据等，如果我们把这些功能都声明在电脑的抽类里 面，那么，我们的上网本，PC机，服务器，笔记本的实现类都要实现所有的这些接口，这就显得太复杂了。所以，我们可以把其这些功能接口隔离开来，比如：工 作学习接口，编程开发接口，上网娱乐接口，计算和数据服务接口，这样，我们的不同功能的电脑就可以有所选择地继承这些接口。</p>
<p>这个原则可以提升我们“搭积木式”的软件开发。对于设计来说，Java中的各种Event Listener和Adapter，对于软件开发来说，不同的用户权限有不同的功能，不同的版本有不同的功能，都是这个原则的应用。</p>
<p><strong>Dependency Inversion Principle (DIP) – 依赖倒置原则</strong></p>
<p>高层模块不应该依赖于低层模块的实现，而是依赖于高层抽象。</p>
<p>举个例子，墙面的开关不应该依赖于电灯的开关实现，而是应该依赖于一个抽象的开关的标准接口，这样，当我们扩展程序的时候，我们的开关同样可以控制 其它不同的灯，甚至不同的电器。也就是说，电灯和其它电器继承并实现我们的标准开关接口，而我们的开关产商就可不需要关于其要控制什么样的设备，只需要关 心那个标准的开关标准。这就是依赖倒置原则。</p>
<p>这就好像浏览器并不依赖于后面的web服务器，其只依赖于HTTP协议。这个原则实在是太重要了，社会的分工化，标准化都是这个设计原则的体现。</p>
<p><strong>参考</strong>：<a href="http://en.wikipedia.org/wiki/Solid_%28object-oriented_design%29">http://en.wikipedia.org/wiki/Solid_(object-oriented_design)</a></p>
<h4>Common Closure Principle（CCP）– 共同封闭原则</h4>
<p>一个包中所有的类应该对同一种类型的变化关闭。一个变化影响一个包，便影响了包中所有的类。一个更简短的说法是：一起修改的类，应该组合在一起（同 一个包里）。如果必须修改应用程序里的代码，我们希望所有的修改都发生在一个包里（修改关闭），而不是遍布在很多包里。CCP原则就是把因为某个同样的原 因而需要修改的所有类组合进一个包里。如果2个类从物理上或者从概念上联系得非常紧密，它们通常一起发生改变，那么它们应该属于同一个包。</p>
<p>CCP延伸了开闭原则（OCP）的“关闭”概念，当因为某个原因需要修改时，把需要修改的范围限制在一个最小范围内的包里。</p>
<p><strong>参考</strong>：<a href="http://c2.com/cgi/wiki?CommonClosurePrinciple">http://c2.com/cgi/wiki?CommonClosurePrinciple</a></p>
<h4>Common Reuse Principle (CRP) – 共同重用原则</h4>
<p>包的所有类被一起重用。如果你重用了其中的一个类，就重用全部。换个说法是，没有被一起重用的类不应该被组合在一起。CRP原则帮助我们决定哪些类 应该被放到同一个包里。依赖一个包就是依赖这个包所包含的一切。当一个包发生了改变，并发布新的版本，使用这个包的所有用户都必须在新的包环境下验证他们 的工作，即使被他们使用的部分没有发生任何改变。因为如果包中包含有未被使用的类，即使用户不关心该类是否改变，但用户还是不得不升级该包并对原来的功能 加以重新测试。</p>
<p>CCP则让系统的维护者受益。CCP让包尽可能大（CCP原则加入功能相关的类），CRP则让包尽可能小（CRP原则剔除不使用的类）。它们的出发点不一样，但不相互冲突。</p>
<p><strong>参考</strong>：<a href="http://c2.com/cgi/wiki?CommonReusePrinciple">http://c2.com/cgi/wiki?CommonReusePrinciple</a></p>
<h4>Hollywood Principle – 好莱坞原则</h4>
<p>好莱坞原则就是一句话——“don’t call us, we’ll call  you.”。意思是，好莱坞的经纪人们不希望你去联系他们，而是他们会在需要的时候来联系你。也就是说，所有的组件都是被动的，所有的组件初始化和调用都 由容器负责。组件处在一个容器当中，由容器负责管理。</p>
<p>简单的来讲，就是由容器控制程序之间的关系，而非传统实现中，由程序代码直接操控。这也就是所谓“控制反转”的概念所在：</p>
<ol>
<li>不创建对象，而是描述创建对象的方式。</li>
<li>在代码中，对象与服务没有直接联系，而是容器负责将这些联系在一起。</li>
</ol>
<p>控制权由应用代码中转到了外部容器，控制权的转移，是所谓反转。</p>
<p>好莱坞原则就是IoC（Inversion of Control）或DI（Dependency Injection  ）的基础原则。这个原则很像依赖倒置原则，依赖接口，而不是实例，但是这个原则要解决的是怎么把这个实例传入调用类中？你可能把其声明成成员，你可以通过 构造函数，你可以通过函数参数。但是 IoC可以让你通过配置文件，一个由Service Container  读取的配置文件来产生实际配置的类。但是程序也有可能变得不易读了，程序的性能也有可能还会下降。</p>
<p><strong>参考</strong>：</p>
<ul>
<li><a href="http://en.wikipedia.org/wiki/Hollywood_Principle">http://en.wikipedia.org/wiki/Hollywood_Principle</a></li>
<li><a href="http://en.wikipedia.org/wiki/Inversion_of_Control">http://en.wikipedia.org/wiki/Inversion_of_Control</a></li>
</ul>
<h4>High Cohesion &amp; Low/Loose coupling &amp; – 高内聚， 低耦合</h4>
<p>这个原则是UNIX操作系统设计的经典原则，把模块间的耦合降到最低，而努力让一个模块做到精益求精。</p>
<ul>
<li>内聚：一个模块内各个元素彼此结合的紧密程度</li>
<li>耦合：一个软件结构内不同模块之间互连程度的度量</li>
</ul>
<p>内聚意味着重用和独立，耦合意味着多米诺效应牵一发动全身。</p>
<p><strong>参考</strong>：</p>
<ul>
<li><a title="http://en.wikipedia.org/wiki/Coupling_(computer_science)" rel="nofollow" href="http://en.wikipedia.org/wiki/Coupling_%28computer_science%29">http://en.wikipedia.org/wiki/Coupling_(computer_science)</a></li>
<li><a title="http://en.wikipedia.org/wiki/Cohesion_(computer_science)" rel="nofollow" href="http://en.wikipedia.org/wiki/Cohesion_%28computer_science%29">http://en.wikipedia.org/wiki/Cohesion_(computer_science)</a></li>
</ul>
<h4>Convention over Configuration（CoC）– 惯例优于配置原则</h4>
<p>简单点说，就是将一些公认的配置方式和信息作为内部缺省的规则来使用。例如，Hibernate的映射文件，如果约定字段名和类属性一致的话，基本 上就可以不要这个配置文件了。你的应用只需要指定不convention的信息即可，从而减少了大量convention而又不得不花时间和精力啰里啰嗦 的东东。配置文件很多时候相当的影响开发效率。</p>
<p>Rails 中很少有配置文件（但不是没有，数据库连接就是一个配置文件），Rails 的fans号称期开发效率是 java 开发的 10  倍，估计就是这个原因。Maven也使用了CoC原则，当你执行mvn  -compile命令的时候，不需要指源文件放在什么地方，而编译以后的class文件放置在什么地方也没有指定，这就是CoC原则。</p>
<p><strong>参考</strong>：<a title="http://en.wikipedia.org/wiki/Convention_over_Configuration" rel="nofollow" href="http://en.wikipedia.org/wiki/Convention_over_Configuration">http://en.wikipedia.org/wiki/Convention_over_Configuration</a></p>
<h4>Separation of Concerns (SoC) – 关注点分离</h4>
<p>SoC  是计算机科学中最重要的努力目标之一。这个原则，就是在软件开发中，通过各种手段，将问题的各个关注点分开。如果一个问题能分解为独立且较小的问题，就是 相对较易解决的。问题太过于复杂，要解决问题需要关注的点太多，而程序员的能力是有限的，不能同时关注于问题的各个方面。正如程序员的记忆力相对于计算机 知识来说那么有限一样，程序员解决问题的能力相对于要解决的问题的复杂性也是一样的非常有限。在我们分析问题的时候，如果我们把所有的东西混在一起讨论， 那么就只会有一个结果——乱。</p>
<p>我记得在上一家公司有一个项目，讨论就讨论了1年多，项目本来不复杂，但是没有使用SoC，全部的东西混为一谈，再加上一堆程序员注入了各种不同的观点和想法，整个项目一下子就失控了。最后，本来一个1年的项目做了3年。</p>
<p>实现关注点分离的方法主要有两种，一种是标准化，另一种是抽象与包装。标准化就是制定一套标准，让使用者都遵守它，将人们的行为统一起来，这样使用 标准的人就不用担心别人会有很多种不同的实现，使自己的程序不能和别人的配合。Java  EE就是一个标准的大集合。每个开发者只需要关注于标准本身和他所在做的事情就行了。就像是开发镙丝钉的人只专注于开发镙丝钉就行了，而不用关注镙帽是怎 么生产的，反正镙帽和镙丝钉按标来就一定能合得上。不断地把程序的某些部分抽像差包装起来，也是实现关注点分离的好方法。一旦一个函数被抽像出来并实现 了，那么使用函数的人就不用关心这个函数是如何实现的，同样的，一旦一个类被抽像并实现了，类的使用者也不用再关注于这个类的内部是如何实现的。诸如组 件，分层，面向服务，等等这些概念都是在不同的层次上做抽像和包装，以使得使用者不用关心它的内部实现细节。</p>
<p>说白了还是“高内聚，低耦合”。</p>
<p><strong>参考</strong>：<a href="http://sulong.me/archives/99">http://sulong.me/archives/99</a></p>
<h4>Design by Contract (DbC) – 契约式设计</h4>
<p>DbC的核心思想是对软件系统中的元素之间相互合作以及“责任”与“义务”的比喻。这种比喻从商业活动中“客户”与“供应商”达成“契约”而得来。例如：</p>
<ul>
<li>供应商必须提供某种产品（责任），并且他有权期望客户已经付款（权利）。</li>
<li>客户必须付款（责任），并且有权得到产品（权利）。</li>
<li>契约双方必须履行那些对所有契约都有效的责任，如法律和规定等。</li>
</ul>
<p>同样的，如果在程序设计中一个模块提供了某种功能，那么它要：</p>
<ul>
<li>期望所有调用它的客户模块都保证一定的进入条件：这就是模块的先验条件（客户的义务和供应商的权利，这样它就不用去处理不满足先验条件的情况）。</li>
<li>保证退出时给出特定的属性：这就是模块的后验条件——（供应商的义务，显然也是客户的权利）。</li>
<li>在进入时假定，并在退出时保持一些特定的属性：不变式。</li>
</ul>
<p>契约就是这些权利和义务的正式形式。我们可以用“三个问题”来总结DbC，并且作为设计者要经常问：</p>
<ul>
<li>它期望的是什么？</li>
<li>它要保证的是什么？</li>
<li>它要保持的是什么？</li>
</ul>
<p>根据Bertrand  Meyer氏提出的DBC概念的描述，对于类的一个方法，都有一个前提条件以及一个后续条件，前提条件说明方法接受什么样的参数数据等，只有前提条件得到 满足时，这个方法才能被调用；同时后续条件用来说明这个方法完成时的状态，如果一个方法的执行会导致这个方法的后续条件不成立，那么这个方法也不应该正常 返回。</p>
<p>现在把前提条件以及后续条件应用到继承子类中，子类方法应该满足：</p>
<ol>
<li>前提条件不强于基类．</li>
<li>后续条件不弱于基类．</li>
</ol>
<p>换句话说，通过基类的接口调用一个对象时，用户只知道基类前提条件以及后续条件。因此继承类不得要求用户提供比基类方法要求的更强的前提条件，亦 即，继承类方法必须接受任何基类方法能接受的任何条件（参数）。同样，继承类必须顺从基类的所有后续条件，亦即，继承类方法的行为和输出不得违反由基类建 立起来的任何约束，不能让用户对继承类方法的输出感到困惑。</p>
<p>这样，我们就有了基于契约的LSP，基于契约的LSP是LSP的一种强化。</p>
<p><strong>参考</strong>：<a href="http://en.wikipedia.org/wiki/Design_by_contract">http://en.wikipedia.org/wiki/Design_by_contract</a></p>
<h4>Acyclic Dependencies Principle (ADP) – 无环依赖原则</h4>
<p>包之间的依赖结构必须是一个直接的无环图形，也就是说，在依赖结构中不允许出现环（循环依赖）。如果包的依赖形成了环状结构，怎么样打破这种循环依 赖呢？有2种方法可以打破这种循环依赖关系：第一种方法是创建新的包，如果A、B、C形成环路依赖，那么把这些共同类抽出来放在一个新的包D里。这样就把 C依赖A变成了C依赖D以及A依赖D，从而打破了循环依赖关系。第二种方法是使用DIP（依赖倒置原则）和ISP（接口分隔原则）设计原则。</p>
<p>无环依赖原则（ADP）为我们解决包之间的关系耦合问题。在设计模块时，不能有循环依赖。</p>
<p><strong>参考</strong>：<a href="http://c2.com/cgi/wiki?AcyclicDependenciesPrinciple">http://c2.com/cgi/wiki?AcyclicDependenciesPrinciple</a></p>
<h4>————————————————————————————</h4>
<p>上面这些原则可能有些学院派，也可能太为理论，我在这里说的也比较模糊和简单，这里只是给大家一个概貌，如果想要了解更多的东西，大家可以多google一下。</p>
<p>不过这些原则看上去都不难，但是要用好却并不那么容易。要能把这些原则用得好用得精，而不教条，我的经验如下：（我以为这是一个理论到应用的过程）</p>
<ol>
<li>你可以先粗浅或是表面地知道这些原则。</li>
<li>但不要急着马上就使用。</li>
<li>在工作学习中观察和总结别人或自己的设计。</li>
<li>再回过头来了回顾一下这些原则，相信你会有一些自己的心得。</li>
<li>有适度地去实践一下。</li>
<li>Goto第 3步。</li>
</ol>
<p>我相信可能还会有其实一些原则，欢迎大家提供。</p>
<p>（全文完）</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://zhuwenhao.com/771/%e6%8a%80%e6%9c%af/%e8%bd%af%e4%bb%b6%e5%b7%a5%e7%a8%8b/%e8%ae%be%e8%ae%a1/%e4%b8%80%e4%ba%9b%e8%bd%af%e4%bb%b6%e8%ae%be%e8%ae%a1%e7%9a%84%e5%8e%9f%e5%88%99/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<series:name><![CDATA[程序员修炼之路]]></series:name>
	</item>
		<item>
		<title>C语言嵌入式系统编程修炼之二:软件架构篇</title>
		<link>http://zhuwenhao.com/411/%e6%8a%80%e6%9c%af/%e8%bd%af%e4%bb%b6%e5%b7%a5%e7%a8%8b/%e8%ae%be%e8%ae%a1/c%e8%af%ad%e8%a8%80%e5%b5%8c%e5%85%a5%e5%bc%8f%e7%b3%bb%e7%bb%9f%e7%bc%96%e7%a8%8b%e4%bf%ae%e7%82%bc%e4%b9%8b%e4%ba%8c%e8%bd%af%e4%bb%b6%e6%9e%b6%e6%9e%84%e7%af%87/</link>
		<comments>http://zhuwenhao.com/411/%e6%8a%80%e6%9c%af/%e8%bd%af%e4%bb%b6%e5%b7%a5%e7%a8%8b/%e8%ae%be%e8%ae%a1/c%e8%af%ad%e8%a8%80%e5%b5%8c%e5%85%a5%e5%bc%8f%e7%b3%bb%e7%bb%9f%e7%bc%96%e7%a8%8b%e4%bf%ae%e7%82%bc%e4%b9%8b%e4%ba%8c%e8%bd%af%e4%bb%b6%e6%9e%b6%e6%9e%84%e7%af%87/#comments</comments>
		<pubDate>Sat, 12 Jun 2010 06:19:06 +0000</pubDate>
		<dc:creator>朱文昊 Albert Zhu</dc:creator>
				<category><![CDATA[设计]]></category>

		<guid isPermaLink="false">http://zhuwenhao.com/?p=411</guid>
		<description><![CDATA[作者：宋宝华　来源：天极网
朱文昊按语: 最近读到的一篇文章,收藏并作为一篇程序员修炼之路专辑中的一篇文章.
模块划分
 
模块划分的”划”是规划的意思，意指怎样合理的将一个很大的软件划分为一系列功能独立的部分合作完成系统的需求。C语言作为一种结构化的程序设计语言，在模块的划分上主要依据功能（依功能进行划分在面向对象设计中成为一个错误，牛顿定律遇到了相对论），C语言模块化程序设计需理解如下概念：
（1） 模块即是一个.c文件和一个.h文件的结合，头文件(.h)中是对于该模块接口的声明；
（2） 某模块提供给其它模块调用的外部函数及数据需在.h中文件中冠以extern关键字声明；
（3）  [...]]]></description>
			<content:encoded><![CDATA[<p>作者：宋宝华　来源：天极网</p>
<p>朱文昊按语: 最近读到的一篇文章,收藏并作为一篇程序员修炼之路专辑中的一篇文章.</p>
<p><strong>模块划分</strong></p>
<p><strong> </strong></p>
<p>模块划分的”划”是规划的意思，意指怎样合理的将一个很大的软件划分为一系列功能独立的部分合作完成系统的需求。C语言作为一种结构化的程序设计语言，在模块的划分上主要依据功能（依功能进行划分在面向对象设计中成为一个错误，牛顿定律遇到了相对论），C语言模块化程序设计需理解如下概念：</p>
<p>（1） 模块即是一个.c文件和一个.h文件的结合，头文件(.h)中是对于该模块接口的声明；</p>
<p>（2） 某模块提供给其它模块调用的外部函数及数据需在.h中文件中冠以extern关键字声明；</p>
<p>（3） 模块内的函数和全局变量需在.c文件开头冠以static关键字声明；</p>
<p>（4） 永远不要在.h文件中定义变量！定义变量和声明变量的区别在于定义会产生内存分配的操作，是汇编阶段的概念；而声明则只是告诉包含该声明的模块在连接阶段从其它模块寻找外部函数和变量。如：</p>
<table border="1" width="90%" align="center" bgcolor="#dadacf" bordercolor="#ffcc66">
<tbody>
<tr>
<td>/*module1.h*/<br />
int a = 5; /* 在模块1的.h文件中定义int a */</p>
<p>/*module1 .c*/<br />
#include “module1.h” /* 在模块1中包含模块1的.h文件 */</p>
<p>/*module2 .c*/<br />
#include “module1.h” /* 在模块2中包含模块1的.h文件 */</p>
<p>/*module3 .c*/<br />
#include “module1.h” /* 在模块3中包含模块1的.h文件 */</td>
</tr>
</tbody>
</table>
<p>以上程序的结果是在模块1、2、3中都定义了整型变量a，a在不同的模块中对应不同的地址单元，这个世界上从来不需要这样的程序。正确的做法是：</p>
<table border="1" width="90%" align="center" bgcolor="#dadacf" bordercolor="#ffcc66">
<tbody>
<tr>
<td>/*module1.h*/<br />
extern int a; /* 在模块1的.h文件中声明int a */</p>
<p>/*module1 .c*/<br />
#include “module1.h” /* 在模块1中包含模块1的.h文件 */<br />
int a = 5; /* 在模块1的.c文件中定义int a */</p>
<p>/*module2 .c*/<br />
#include “module1.h” /* 在模块2中包含模块1的.h文件 */</p>
<p>/*module3 .c*/<br />
#include “module1.h” /* 在模块3中包含模块1的.h文件 */</td>
</tr>
</tbody>
</table>
<p>这样如果模块1、2、3操作a的话，对应的是同一片内存单元。</p>
<p>一个嵌入式系统通常包括两类模块：</p>
<p>（1）硬件驱动模块，一种特定硬件对应一个模块；</p>
<p>（2）软件功能模块，其模块的划分应满足低偶合、高内聚的要求。</p>
<p><strong>多任务还是单任务</strong></p>
<p>所谓”单任务系统”是指该系统不能支持多任务并发操作，宏观串行地执行一个任务。而多任务系统则可以宏观并行（微观上可能串行）地”同时”执行多个任务。</p>
<p>多任务的并发执行通常依赖于一个多任务操作系统（OS），多任务OS的核心是系统调度器，它使用任务控制块（TCB）来管理任务调度功能。TCB包括任务的当前状态、优先级、要等待的事件或资源、任务程序码的起始地址、初始堆栈指针等信息。调度器在任务被激活时，要用到这些信息。此外，TCB还被用来存放任务的”上下文”（context)。任务的上下文就是当一个执行中的任务被停止时，所要保存的所有信息。通常，上下文就是计算机当前的状态，也即各个寄存器的内容。当发生任务切换时，当前运行的任务的上下文被存入TCB，并将要被执行的任务的上下文从它的TCB中取出，放入各个寄存器中。</p>
<p>嵌入式多任务OS的典型例子有Vxworks、ucLinux等。嵌入式OS并非遥不可及的神坛之物，我们可以用不到1000行代码实现一个针对80186处理器的功能最简单的OS内核，作者正准备进行此项工作，希望能将心得贡献给大家。</p>
<p>究竟选择多任务还是单任务方式，依赖于软件的体系是否庞大。例如，绝大多数手机程序都是多任务的，但也有一些小灵通的协议栈是单任务的，没有操作系统，它们的主程序轮流调用各个软件模块的处理程序，模拟多任务环境。</p>
<p><span id="more-411"></span></p>
<p><strong>单任务程序典型架构</strong></p>
<p><strong> </strong></p>
<p>（1）从CPU复位时的指定地址开始执行；</p>
<p>（2）跳转至汇编代码startup处执行；</p>
<p>（3）跳转至用户主程序main执行，在main中完成：</p>
<p>a.初试化各硬件设备；</p>
<p>b.初始化各软件模块；</p>
<p>c.进入死循环（无限循环），调用各模块的处理函数</p>
<p>用户主程序和各模块的处理函数都以C语言完成。用户主程序最后都进入了一个死循环，其首选方案是：</p>
<table border="1" width="90%" align="center" bgcolor="#dadacf" bordercolor="#ffcc66">
<tbody>
<tr>
<td>while(1)<br />
{<br />
}</td>
</tr>
</tbody>
</table>
<p>有的程序员这样写：</p>
<table border="1" width="90%" align="center" bgcolor="#dadacf" bordercolor="#ffcc66">
<tbody>
<tr>
<td>for(;;)<br />
{<br />
}</td>
</tr>
</tbody>
</table>
<p>这个语法没有确切表达代码的含义，我们从for(;;)看不出什么，只有弄明白for(;;)在C语言中意味着无条件循环才明白其意。</p>
<p>下面是几个”著名”的死循环：</p>
<p>（1）操作系统是死循环；</p>
<p>（2）WIN32程序是死循环；</p>
<p>（3）嵌入式系统软件是死循环；</p>
<p>（4）多线程程序的线程处理函数是死循环。</p>
<p>你可能会辩驳，大声说：”凡事都不是绝对的，2、3、4都可以不是死循环”。Yes，you are right，但是你得不到鲜花和掌声。实际上，这是一个没有太大意义的牛角尖，因为这个世界从来不需要一个处理完几个消息就喊着要OS杀死它的WIN32程序，不需要一个刚开始RUN就自行了断的嵌入式系统，不需要莫名其妙启动一个做一点事就干掉自己的线程。有时候，过于严谨制造的不是便利而是麻烦。君不见，五层的TCP/IP协议栈超越严谨的ISO/OSI七层协议栈大行其道成为事实上的标准？</p>
<p>经常有网友讨论：</p>
<table border="1" width="90%" align="center" bgcolor="#dadacf" bordercolor="#ffcc66">
<tbody>
<tr>
<td>printf(“%d,%d”,++i,i++); /* 输出是什么？*/<br />
c = a+++b; /* c=? */</td>
</tr>
</tbody>
</table>
<p>等类似问题。面对这些问题，我们只能发出由衷的感慨：世界上还有很多有意义的事情等着我们去消化摄入的食物。</p>
<p>实际上，嵌入式系统要运行到世界末日。</p>
<p><strong>中断服务程序</strong></p>
<p>中断是嵌入式系统中重要的组成部分，但是在标准C中不包含中断。许多编译开发商在标准C上增加了对中断的支持，提供新的关键字用于标示中断服务程序(ISR)，类似于__interrupt、#program interrupt等。当一个函数被定义为ISR的时候，编译器会自动为该函数增加中断服务程序所需要的中断现场入栈和出栈代码。</p>
<p>中断服务程序需要满足如下要求：</p>
<p>(1)不能返回值；</p>
<p>(2)不能向ISR传递参数；</p>
<p>(3) ISR应该尽可能的短小精悍；</p>
<p>(4) printf(char * lpFormatString,…)函数会带来重入和性能问题，不能在ISR中采用。</p>
<p>在某项目的开发中，我们设计了一个队列，在中断服务程序中，只是将中断类型添加入该队列中，在主程序的死循环中不断扫描中断队列是否有中断，有则取出队列中的第一个中断类型，进行相应处理。</p>
<table border="1" width="90%" align="center" bgcolor="#dadacf" bordercolor="#ffcc66">
<tbody>
<tr>
<td>/* 存放中断的队列 */<br />
typedef struct tagIntQueue<br />
{<br />
int intType; /* 中断类型 */<br />
struct tagIntQueue *next;<br />
}IntQueue;</p>
<p>IntQueue lpIntQueueHead;</p>
<p>__interrupt ISRexample ()<br />
{<br />
int intType;<br />
intType = GetSystemType();<br />
QueueAddTail(lpIntQueueHead, intType)；/* 在队列尾加入新的中断 */<br />
}</td>
</tr>
</tbody>
</table>
<p>在主程序循环中判断是否有中断：</p>
<table border="1" width="90%" align="center" bgcolor="#dadacf" bordercolor="#ffcc66">
<tbody>
<tr>
<td>While(1)<br />
{<br />
If( !IsIntQueueEmpty() )<br />
{<br />
intType = GetFirstInt();<br />
switch(intType) /* 是不是很象WIN32程序的消息解析函数? */<br />
{<br />
/* 对，我们的中断类型解析很类似于消息驱动 */<br />
case xxx: /* 我们称其为”中断驱动”吧？ */<br />
…<br />
break;<br />
case xxx:<br />
…<br />
break;<br />
…<br />
}<br />
}<br />
}</td>
</tr>
</tbody>
</table>
<p>按上述方法设计的中断服务程序很小，实际的工作都交由主程序执行了。</p>
<p><strong>硬件驱动模块</strong></p>
<p><strong> </strong></p>
<p>一个硬件驱动模块通常应包括如下函数：</p>
<p>（1）中断服务程序ISR</p>
<p>（2）硬件初始化</p>
<p>a.修改寄存器，设置硬件参数（如UART应设置其波特率，AD/DA设备应设置其采样速率等）；</p>
<p>b.将中断服务程序入口地址写入中断向量表：</p>
<table border="1" width="90%" align="center" bgcolor="#dadacf" bordercolor="#ffcc66">
<tbody>
<tr>
<td>/* 设置中断向量表 */<br />
m_myPtr = make_far_pointer(0l); /* 返回void far型指针void far * */<br />
m_myPtr += ITYPE_UART; /* ITYPE_UART： uart中断服务程序 */<br />
/* 相对于中断向量表首地址的偏移 */<br />
*m_myPtr = &amp;UART _Isr; /* UART _Isr：UART的中断服务程序 */</td>
</tr>
</tbody>
</table>
<p>（3）设置CPU针对该硬件的控制线</p>
<p>a.如果控制线可作PIO（可编程I/O）和控制信号用，则设置CPU内部对应寄存器使其作为控制信号；</p>
<p>b.设置CPU内部的针对该设备的中断屏蔽位，设置中断方式（电平触发还是边缘触发）。</p>
<p>（4）提供一系列针对该设备的操作接口函数。例如，对于LCD，其驱动模块应提供绘制像素、画线、绘制矩阵、显示字符点阵等函数；而对于实时钟，其驱动模块则需提供获取时间、设置时间等函数。</p>
<p><strong>C的面向对象化</strong></p>
<p>在面向对象的语言里面，出现了类的概念。类是对特定数据的特定操作的集合体。类包含了两个范畴：数据和操作。而C语言中的struct仅仅是数据的集合，我们可以利用函数指针将struct模拟为一个包含数据和操作的”类”。下面的C程序模拟了一个最简单的”类”：</p>
<table border="1" width="90%" align="center" bgcolor="#dadacf" bordercolor="#ffcc66">
<tbody>
<tr>
<td>#ifndef C_Class<br />
#define C_Class struct<br />
#endif<br />
C_Class A<br />
{<br />
C_Class A *A_this; /* this指针 */<br />
void (*Foo)(C_Class A *A_this); /* 行为：函数指针 */<br />
int a; /* 数据 */<br />
int b;<br />
};</td>
</tr>
</tbody>
</table>
<p>我们可以利用C语言模拟出面向对象的三个特性：封装、继承和多态，但是更多的时候，我们只是需要将数据与行为封装以解决软件结构混乱的问题。C模拟面向对象思想的目的不在于模拟行为本身，而在于解决某些情况下使用C语言编程时程序整体框架结构分散、数据和函数脱节的问题。我们在后续章节会看到这样的例子。</p>
<p><strong>总结</strong></p>
<p>本篇介绍了嵌入式系统编程软件架构方面的知识，主要包括模块划分、多任务还是单任务选取、单任务程序典型架构、中断服务程序、硬件驱动模块设计等，从宏观上给出了一个嵌入式系统软件所包含的主要元素。</p>
<p>请记住：软件结构是软件的灵魂！结构混乱的程序面目可憎，调试、测试、维护、升级都极度困难。</p>
]]></content:encoded>
			<wfw:commentRss>http://zhuwenhao.com/411/%e6%8a%80%e6%9c%af/%e8%bd%af%e4%bb%b6%e5%b7%a5%e7%a8%8b/%e8%ae%be%e8%ae%a1/c%e8%af%ad%e8%a8%80%e5%b5%8c%e5%85%a5%e5%bc%8f%e7%b3%bb%e7%bb%9f%e7%bc%96%e7%a8%8b%e4%bf%ae%e7%82%bc%e4%b9%8b%e4%ba%8c%e8%bd%af%e4%bb%b6%e6%9e%b6%e6%9e%84%e7%af%87/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<series:name><![CDATA[程序员修炼之路]]></series:name>
	</item>
		<item>
		<title>程序员修炼之路－C语言</title>
		<link>http://zhuwenhao.com/375/%e6%8a%80%e6%9c%af/%e7%a8%8b%e5%ba%8f%e8%ae%be%e8%ae%a1%e8%af%ad%e8%a8%80/c-%e8%af%ad%e8%a8%80/%e7%a8%8b%e5%ba%8f%e5%91%98%e4%bf%ae%e7%82%bc%e4%b9%8b%e8%b7%af%ef%bc%8dc%e8%af%ad%e8%a8%80/</link>
		<comments>http://zhuwenhao.com/375/%e6%8a%80%e6%9c%af/%e7%a8%8b%e5%ba%8f%e8%ae%be%e8%ae%a1%e8%af%ad%e8%a8%80/c-%e8%af%ad%e8%a8%80/%e7%a8%8b%e5%ba%8f%e5%91%98%e4%bf%ae%e7%82%bc%e4%b9%8b%e8%b7%af%ef%bc%8dc%e8%af%ad%e8%a8%80/#comments</comments>
		<pubDate>Sun, 09 May 2010 12:50:59 +0000</pubDate>
		<dc:creator>朱文昊 Albert Zhu</dc:creator>
				<category><![CDATA[C 语言]]></category>
		<category><![CDATA[构造]]></category>

		<guid isPermaLink="false">http://zhuwenhao.com/?p=375</guid>
		<description><![CDATA[在程序员修炼之路这个系列里面，转载过几篇他人的文章。最近有同学问我如何深入学习C语言和职业规划的问题，让我决心自己动手总结一些观点，和朋友共勉。于是就有了这篇同名文章。
要想成为一名合格的C语言程序员，读什么样的书是一个首先碰到的基本问题。我的品位是，读计算机方面的著作，一定要读国外人写的经典级别的书。回忆我的往事，在中学时候看了一点BASIC基础，学会了盲打，会用了Windows  [...]]]></description>
			<content:encoded><![CDATA[<p>在程序员修炼之路这个系列里面，转载过几篇他人的文章。最近有同学问我如何深入学习C语言和职业规划的问题，让我决心自己动手总结一些观点，和朋友共勉。于是就有了这篇同名文章。</p>
<p>要想成为一名合格的C语言程序员，读什么样的书是一个首先碰到的基本问题。我的品位是，读计算机方面的著作，一定要读国外人写的经典级别的书。回忆我的往事，在中学时候看了一点BASIC基础，学会了盲打，会用了Windows 3X和95，这些就是我在读大学前全部的计算机基础知识。在大学第一年的寒假，回家的火车上，我没有买到座位票，于是只好站着回家。在这十八个小时的旅途中，我阅读了大约1/2的《C程序设计语言》，对，就是那本Kernighan和Richie合著的薄薄的书。不过惭愧的是，我当时的英语很差，读的当然是东南大学徐宝文翻译的第一版。徐先生的翻译很好，所以我才能顺利读下来。有人可能觉得奇怪，没有什么基础的情况下，如何能读完这么一本书？我的感受是，当要学习一种全新的东西，读书不能奢望全理解，勇敢的看下去，看完它，和作者的第一次沟通才能完成。</p>
<p>这第一次沟通，奠定了我的C语言基础知识，也决定了我今后在C语言程序员、系统软件设计、嵌入式系统设计等方面的职业脉络。读了第一本C语言经典之后，应该就可以编写一些和书中例程差不多的小程序了。接下来需要阅读的经典有：《C专家编程》(Expert C Programming &#8212; Deep C Secrets)、《C陷阱与缺陷》(C Traps and Pitfalls)、《C和指针》(Pointers on C)、《C语言核心技术》(C in a Nutshell)、《代码大全》(Code Complete)。读完了这些书，基本上就可以号称是C语言程序员了。</p>
<p>其中《C和指针》我接触的比较晚，非常的遗憾。当我读了《C和指针》，那种相见恨晚的感觉，难于言表。《C专家编程》《C陷阱与缺陷》这两本书，作者处的时代很久远了。如果在现代PC程序设计领域，相关问题可能很少遇到。但是对C语言程序员而言，还是要继续列为必读书目，因为那些晦涩的问题，还是会不停的重现在嵌入式系统的硬件和编译环境里。《代码大全》结合一定的工作经验来读，会有更深的感触。</p>
<p>学习C语言的路还没有结束，真的要理解C语言，你就要了解“语言”，读一读《程学设计语言》(Programming Language-Michael L. Scott)吧。这本研究生和本科课程通用的教材，会让你对C语言的了解上升一个层次，不，一个数量级。</p>
<p>过了这个界线，C语言的学习就该依据职业规划来细分道路了。我只能根据自己的经验谈谈。</p>
<p>首先，学会用Linux操作体系或者其他类似的＊nix系统，因为这些系统是面向程序员的操作系统，如果你真的是一个程序员，在＊nix你会感到更舒服。会用Gcc也是必须的。</p>
<p>其次，读一下Intel出版的《多核程序设计》。</p>
<p>(本文未完成，请期待更新)</p>
]]></content:encoded>
			<wfw:commentRss>http://zhuwenhao.com/375/%e6%8a%80%e6%9c%af/%e7%a8%8b%e5%ba%8f%e8%ae%be%e8%ae%a1%e8%af%ad%e8%a8%80/c-%e8%af%ad%e8%a8%80/%e7%a8%8b%e5%ba%8f%e5%91%98%e4%bf%ae%e7%82%bc%e4%b9%8b%e8%b7%af%ef%bc%8dc%e8%af%ad%e8%a8%80/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<series:name><![CDATA[程序员修炼之路]]></series:name>
	</item>
		<item>
		<title>程序员的十层楼</title>
		<link>http://zhuwenhao.com/372/%e6%8a%80%e6%9c%af/%e8%bd%af%e4%bb%b6%e5%b7%a5%e7%a8%8b/%e7%a8%8b%e5%ba%8f%e5%91%98%e7%9a%84%e5%8d%81%e5%b1%82%e6%a5%bc/</link>
		<comments>http://zhuwenhao.com/372/%e6%8a%80%e6%9c%af/%e8%bd%af%e4%bb%b6%e5%b7%a5%e7%a8%8b/%e7%a8%8b%e5%ba%8f%e5%91%98%e7%9a%84%e5%8d%81%e5%b1%82%e6%a5%bc/#comments</comments>
		<pubDate>Sun, 09 May 2010 11:25:11 +0000</pubDate>
		<dc:creator>朱文昊 Albert Zhu</dc:creator>
				<category><![CDATA[软件工程]]></category>

		<guid isPermaLink="false">http://zhuwenhao.com/?p=372</guid>
		<description><![CDATA[第1层  菜鸟第1层楼属于地板层，迈进这层楼的门槛是很低的。基本上懂计算机的基本操作，了解计算机专业的一些基础知识，掌握一门基本的编程语言如C/C++，或者Java，或者JavaScript，&#8230;，均可入门迈进这层。
在这层上，中国有着绝对的优势，除了从计算机专业毕业的众多人数外，还有大量的通信、自动化、数学等相关专业的人士进入这一行，此外还有众多的其他专业转行的人士，人数绝对比西方多出甚多。并且还有一个优势就是我们这层人员的平均智商比西方肯定高。
没有多少人愿意一辈子做菜鸟，因为做”菜鸟”的滋味实在是不咋的，整天被老大们吆喝着去装装机器，搭建一下测试环境，或者对照着别人写好的测试用例做一些黑盒 [...]]]></description>
			<content:encoded><![CDATA[<p>第1层  菜鸟第1层楼属于地板层，迈进这层楼的门槛是很低的。基本上懂计算机的基本操作，了解计算机专业的一些基础知识，掌握一门基本的编程语言如C/C++，或者Java，或者JavaScript，&#8230;，均可入门迈进这层。<br />
在这层上，中国有着绝对的优势，除了从计算机专业毕业的众多人数外，还有大量的通信、自动化、数学等相关专业的人士进入这一行，此外还有众多的其他专业转行的人士，人数绝对比西方多出甚多。并且还有一个优势就是我们这层人员的平均智商比西方肯定高。<br />
没有多少人愿意一辈子做菜鸟，因为做”菜鸟”的滋味实在是不咋的，整天被老大们吆喝着去装装机器，搭建一下测试环境，或者对照着别人写好的测试用例做一些黑盒测试，好一点的可以被安排去写一点测试代码。当然如果运气”好”的话，碰到了国内的一些作坊式的公司，也有机会去写一些正式的代码。<br />
所以，菜鸟们总是在努力学习，希望爬更高的一层楼去。</p>
<p>第2层 大虾从第1层爬到第2层相对容易一些，以C/C++程序员为例，只要熟练掌握C/C++编程语言，掌握C标准库和常用的各种数据结构算法，掌握STL的基本实现和使用方法，掌握多线程编程基础知识，掌握一种开发环境，再对各种操作系统的API都去使用一下，搞网络编程的当然对socket编程要好好掌握一下，然后再学习一些面向对象的设计知识和设计模式等，学习一些测试、软件工程和质量控制的基本知识，大部分人经过2～3年的努力，都可以爬到第2层，晋升为”大虾”。<br />
中国的”大虾”数量和”菜鸟”数量估计不会少多少，所以这层上仍然远领先于西方。<br />
大虾们通常还是有些自知之明，知道自己只能实现一些简单的功能，做不了大的东西，有时候还会遇到一些疑难问题给卡住，所以他们对那些大牛级的人物通常是非常崇拜的，国外的如Robert C. Martin、Linus Torvalds，国内的如求伯君、王志东等通常是他们崇拜的对象。其中的有些人希望有一天也能达到这些大牛级人物的水平，所以他们继续往楼上爬去。</p>
<p>第3层 牛人由于”大虾”们经常被一些疑难问题给卡住，所以有了”大虾”们只好继续学习，他们需要将原来所学的知识进一步熟练掌握，比如以熟练掌握C++编程语言为例，除了学一些基础性的C++书籍如《C++ Primer》，《Effective C++》，《Think in C++》，《Exception C++》等之外，更重要的是需要了解C++编译器的原理和实现机制，了解操作系统中的内部机制如内存管理、进程和线程的管理机制，了解处理器的基础知识和代码优化的方法，此外还需要更深入地学习更多的数据结构与算法，掌握更深入的测试和调试知识以及质量管理和控制方法，对各种设计方法有更好的理解等。<br />
学习上面说的这些知识不是一挥而就的，不看个三五十本书并掌握它是做不到的。以数据结构算法来说，至少要看个5～10本这方面的著作；以软件设计来说，光懂结构化设计、面向对象设计和一些设计模式是不够的，还要了解软件架构设计、交互设计、面向方面的设计、面向使用的设计、面向数据结构算法的设计、情感化设计等，否则是很难进到这个楼层的。<br />
当然除了上面说的知识外，大虾们还需要去学习各种经验和技巧。当然这点难不倒他们，现在出版的书籍众多，网络上的技术文章更是不胜数，然后再去各种专业论坛里泡一泡，把这些书籍和文章中的各种经验、技能、技巧掌握下来，再去学习一些知名的开源项目如Apache或Linux操作系统的源代码实现等。此时对付一般的疑难问题通常都不在话下，菜鸟和大虾们会觉得你很”牛”，你也就爬到了第3层，晋升为”牛人”了。<br />
看了上面所讲的要求，可能有些大虾要晕过去了，成为牛人要学这么多东西啊！要求是不是太高了？其实要求一点也不高，这么点东西都掌握不了的话，怎么能让别人觉得你”牛”呢？<br />
需要提一下的是，进入多核时代后，从第2层爬到第3层增加了一道多核编程的门槛。当然要迈过这道门槛并不难，已经有很多前辈高人迈进了这道门槛，只要循着他们的足迹前进就可以了。想迈进这道门槛者不妨去学习一下TBB开源项目的源代码(链接：http://www.threadingbuildingblocks.org/)，然后上Intel的博客（<a href="http://software.intel.com/zh-cn/blogs/category/multicore/" target="_blank">http://software.intel.com/zh-cn/blogs/</a>）和多核论坛（<a href="http://forum.csdn.net/Intel/IntelMulti-core/" target="_blank">http://forum.csdn.net/Intel/IntelMulti-core/</a>）去看看相关文章，再买上几本相关的书籍学习一下。<br />
在国内， 一旦成为”牛人”，通常可以到许多知名的公司里去，运气好者可以挂上一个架构师的头衔，甚至挂上一个”首席架构师”或者”首席xx学家”的头衔也不足为奇。有不少爬到这层的人就以为到了楼顶了，可以眼睛往天上看了，开始目空一切起来，以为自己什么都可以做了，什么都懂了，经常在网络上乱砸板砖是这个群体的最好写照。由此也看出，国内的牛人数量仍然众多，远多于西方的牛人数量，在这层上仍然是领先的。<br />
也有不少谦虚的”牛人”，知道自己现在还不到半桶水阶段。他们深知爬楼的游戏就像猴子上树一样，往下看是笑脸，往上看是屁股。为了多看笑脸，少看屁股，他们并没有在此停步不前，而是继续寻找到更上一层的楼梯，以便继续往上爬。</p>
<p>第4层 大牛从第3层爬到第4层可不像上面说过的那几层一样容易，要成为大牛的话，你必须要能做牛人们做不了的事情，解决牛人们解决不了问题。比如牛人们通常都不懂写操作系统，不会写编译器，不懂得TCP/IP协议的底层实现，如果你有能力将其中的任何一个实现得象模象样的话，那么你就从牛人升级为”大牛”了。<br />
当然，由于各个专业领域的差别，这里举操作系统、编译器、TCP/IP协议只是作为例子，并不代表成为”大牛”一定需要掌握这些知识，以时下热门的多核编程来说，如果你能比牛人们更深入地掌握其中的各种思想原理，能更加自如的运用，并有能力去实现一个象开源项目TBB库一样的东西，也可以成为”大牛”，又或者你能写出一个类似Apache一样的服务器，或者写出一个数据库，都可以成为”大牛”。<br />
要成为”大牛”并不是一件简单的事情，需要付出比牛人们多得多的努力，一般来说，至少要看过200~400本左右的专业书籍并好好掌握它，除此之外，还得经常关注网络和期刊杂志上的各种最新信息。<br />
当”牛人”晋升为”大牛”，让”牛人们”发现有比他们更牛的人时，对”牛人”们的心灵的震撼是可想而知的。由于牛人们的数量庞大，并且牛人对大虾和菜鸟阶层有言传身教的影响，所以大牛们通常能获得非常高的社会知名度，几乎可以用”引无数菜鸟、大虾、牛人竞折腰”来形容，看看前面提过的Linus Torvalds等大牛，应该知道此言不虚。<br />
虽然成为”大牛”的条件看起来似乎很高似的，但是这层楼并不是很难爬的一层，只要通过一定的努力，素质不是很差，还是有许多”牛人”可以爬到这一层的。由此可知，”大牛”这个楼层的人数其实并不像想像的那么少，例如比尔·盖茨之类的人好像也是属于这一层的。<br />
由于”大牛”这层的人数不少，所以也很难统计除到底是中国的”大牛”数量多还是西方的大牛数量多？我估计应该是个旗鼓相当的数量，或者中国的”大牛”们会更多一些。<br />
看到这里，可能会有很多人会以为我在这里说瞎话，Linus Torvalds写出了著名的Linux操作系统，我国并没有人写出过类似的东西啊，我国的”大牛”怎么能和西方的比呢? 不知大家注意到没有，Linus Torvalds只是写出了一个”象模象样”的操作系统雏形，Linux后来真正发展成闻名全球的开源操作系统期间，完全是因为许多支持开源的商业公司如IBM等，派出了许多比Linus Torvalds更高楼层的幕后英雄在里面把它开发出来的。<br />
可能有些菜鸟认为Linus Torvalds是程序员中的上帝，不妨说个小故事：<br />
Linus，Richard Stallman和Don Knuth（高德纳）一同参加一个会议。<br />
Linus 说：”上帝说我创造了世界上最优秀的操作系统。”<br />
Richard Stallman自然不甘示弱地说：”上帝说我创造了世界上最好用的编译器。”<br />
Don Knuth一脸疑惑的说：”等等，等等，我什么时候说过这些话？”<br />
由此可以看出，Linus Torvalds的技术水平并不像想像中那么高，只是”牛人”和”大虾”觉得”大牛”比他们更牛吧了。在我国，有一些当时还处于”大虾”层的人物，也能写出介绍如何写操作系统的书，并且书写得非常出色，而且写出了一个有那么一点点象模象样的操作系统来。我想中国的”大牛”们是不会比西方差的，之所以没有人写出类似的商业产品来，完全是社会环境的原因，并不是技术能力达不到的原因。<br />
“大牛”们之所以成为大牛，主要的原因是因为把”牛人”给盖了下去，并不是他们自己觉得如何牛。也许有很多菜鸟、大虾甚至牛人觉得”大牛”这层已经到顶了，但大多数”大牛”估计应该是有自知之明的，他们知道自己现在还没有爬到半山腰，也就勉强能算个半桶水的水平，其中有些爬到这层没有累趴下，仍然能量充沛，并且又有志者，还是会继续往更上一层楼爬的。<br />
看到这里，也许有些菜鸟、大虾、牛人想不明白了，还有比”大牛”们更高的楼层，那会是什么样的楼层？下面就来看看第5层楼的奥妙。<br />
<span id="more-372"></span><br />
第5层 专家当大牛们真正动手做一个操作系统或者类似的其他软件时，他们就会发现自己的基本功仍然有很多的不足。以内存管理为例，如果直接抄袭Linux或者其他开源操作系统的内存管理算法，会被人看不起的，如果自动动手实现一个内存管理算法，他会发现现在有关内存管理方法的算法数量众多，自己并没有全部学过和实践过，不知道到底该用那种内存管理算法。<br />
看到这里，可能有些人已经明白第5层楼的奥妙了，那就是需要做基础研究，当然在计算机里，最重要的就是”计算”二字，程序员要做基础研究，主要的内容就是研究非数值”计算”。<br />
非数值计算可是一个非常庞大的领域，不仅时下热门的”多核计算”与”云计算”属于非数值计算范畴，就是软件需求、设计、测试、调试、评估、质量控制、软件工程等本质上也属于非数值计算的范畴，甚至芯片硬件设计也同样牵涉到非数值计算。如果你还没有真正领悟”计算”二字的含义，那么你就没有机会进到这层楼来。<br />
可能有人仍然没有明白为什么比尔·盖茨被划在了大牛层，没有进到这层来。虽然比尔·盖茨大学未毕业，学历不够，但是家有藏书2万余册，进入软件这个行业比绝大部分人都早，撇开他的商业才能不谈，即使只看他的技术水平，也可以算得上是学富五车，顶上几个普通的计算机软件博士之和是没有问题的，比起Linus Torvalds之类的”大牛”们应该技高一筹才对，怎么还进不了这层楼呢？<br />
非常遗憾的是，从Windows操作系统的实现来看，其对计算的理解是很肤浅的，如果把Google对计算方面的理解比做大学生，比尔·盖茨只能算做一个初中生，所以比尔·盖茨永远只能做个大牛人，成不了”专家”。<br />
看到这里，也许国内的大牛们要高兴起来了，原来比尔·盖茨也只和我等在同一个层次，只要再升一层就可以超越比尔·盖茨了。不过爬到这层可没有从”牛人”升为”大牛”那么简单，人家比尔·盖茨都家有2万多册书，让你看个500~1000本以上的专业书籍并掌握好它应该要求不高吧。当然，这并不是主要的条件，更重要的是，需要到专业的学术站点去学习了，到ACM，IEEE，Elsevier，SpringerLink，SIAM等地方去下载论文应该成为你的定期功课，使用Google搜索引擎中的学术搜索更是应该成为你的日常必修课。此外，你还得经常关注是否有与你研究相关的开源项目冒出来，例如当听到有TBB这样针对多核的开源项目时，你应该第一时间到Google里输入”TBB”搜索一下，将其源代码下载下来好好研究一番，这样也许你的一只脚已经快迈进了这层楼的门槛。<br />
当你象我上面说的那样去做了以后，随着时间的推移，总会有某天，你发现，在很多小的领域里，你已经学不到什么新东西了，所有最新出来的研究成果你几乎都知道。此时你会发现你比在做”牛人”和”大牛”时的水平不知高出了多少，但是你一点也”牛”不起来，因为你学的知识和思想都是别人提出来的，你自己并没有多少自己的知识和思想分享给别人，所以你还得继续往楼上爬才行。<br />
我不知道国内的”专家”到底有多少，不过有一点可以肯定的是，如果把那些专门蒙大家的”砖家”也算上的话，我们的砖家比西方的要多得多。</p>
<p>第6层 学者<br />
当”专家”们想继续往上一层楼爬时，他们几乎一眼就可以看到楼梯的入口，不过令他们吃惊的是，楼梯入口处竖了一道高高的门槛，上面写着”创新”二字。不幸的是，大多数人在爬到第5层楼时已经体能消耗过度，无力翻过这道门槛。<br />
有少数体能充足者，可以轻易翻越这道门槛，但是并不意味着体力消耗过度者就无法翻越，因为你只是暂时还没有掌握恢复体能的方法而已，当掌握了恢复体能的方法，将体能恢复后，你就可以轻易地翻越这道门槛了。<br />
怎么才能将体能恢复呢？我们的老祖宗”孔子”早就教导过我们”温故而知新”，在英文里，研究的单词是”research”，其前缀”re”和”search”分别是什么意思不用我解释吧。或许有些人觉得”温故而知新”和”research”有些抽象，不好理解，我再给打个简单的比方，比如你在爬一座高山，爬了半天，中途体力不支，怎么恢复体力呢？自然是休息一下，重新进食一些食物，体力很快就可以得到恢复。<br />
由此可知，对体能消耗过度者，休息＋重新进食通常是恢复体能的最佳选择。可惜的是，国内的老板们并不懂得这点，他们的公司里不仅连正常国家规定的休息时间都不给足，有些公司甚至有员工”过劳死”出现。所以国内能翻越”创新”这道门槛的人是”少之又少”，和西方比起来估计是数量级的差别。<br />
再说说重新进食的问题，这个重新进食是有讲究的，需要进食一些基础性易消化的简单食物，不能进食山珍海味级的复杂食物，否则很难快速吸收。以查找为例，并不是去天天盯着那些复杂的查找结构和算法进行研究，你需要做的是将二分查找、哈希查找、普通二叉树查找等基础性的知识好好地复习几遍。<br />
以哈希查找为例，首先你需要去将各种冲突解决方法如链式结构、二次哈希等编写一遍，再试试不同种类的哈希函数，然后还需要试试在硬盘中如何实现哈希查找，并考虑数据从硬盘读到内存后，如何组织硬盘中的数据才能快速地在内存中构建出哈希表来，&#8230;，这样你可能需要将一个哈希表写上十几个不同的版本，并比较各个版本的性能、功能方面的区别和适用范围。<br />
总之，对任何一种简单的东西，你需要考虑各种各样的需求，以需求来驱动研究。最后你将各种最基础性的查找结构和算法都了然于胸后，或许某天你再看其他更复杂的查找算法，或者你在散步时，脑袋里灵光一现，突然间就发现了更好的方法，也就从专家晋升为”学者”了。<br />
学者所做的事情，通常都是在前人的基础上，进行一些小的优化和改进，例如别人发明了链式基数排序的方法，你第1个发现使用一定的方法，可以用数组替代链表进行基数排序，性能还能得到进一步提高。<br />
由于学者需要的只是一些小的优化改进，因此中国还是有一定数量的学者。不过和国外的数量比起来，估计少了一个数量级而已。<br />
也许有人会觉得现在中国许多公司申请专利的数量达到甚至超过西方发达国家了，我们的学者数量应该不会比他们少多少。因此，有必要把专利和这里说的创新的区别解释一下。<br />
所谓专利者，只要是以前没有的，新的东西，都可以申请专利；甚至是以前有的东西，你把他用到了一个新的领域的产品里去，也可以申请专利。比如你在房子里造一个水泥柱子，只要以前没有人就这件事申请专利，那么你就可以申请专利，并且下次你把水泥柱子挪一个位置，又可以申请一个新的专利；或者你在一个柜子上打上几个孔，下次又把孔的位置改一改，&#8230;，均可申请专利。<br />
这层楼里所说的创新，是指学术层面的创新，是基础研究方面的创新，和专利的概念是完全不同的，难度也是完全不同的。你即使申请了一万个象那种打孔一类的专利，加起来也够不到这层楼里的一个创新。<br />
当你爬到第6层楼时，你也许会有一种突破极限的快感，因为你终于把那道高高的写着”创新”二字的门槛给翻过去了，实现了”0&#8243;的突破。这时，你也许有一种”独上高楼，欲望尽天涯路”的感觉，但是很快你会发现看到的都是比较近的路，远处的路根本看不清楚。如果你还有足够的体力的话，你会想爬到更高一层的楼层去。</p>
<p>第7层 大师<br />
从第6层楼爬到第7层楼，并没有多少捷径可走，主要看你有没有足够的能量。你如果能象Hoare一样设计出一个快速排序的算法；或者象Eugene W. Myers一样设计出了一个用编辑图的最短路径模型来解决diff问题的算法；或者象M.J.D. Powell一样提出了一个能够处理非线性规划问题的SQP方法；或者你发现基于比较的排序算法，它的复杂度下界为O(NLogN)；或者你发现用栈可以将递归的算法变成非递归的；或者你设计出一个红黑树或者AVL树之类的查找结构；或者你设计出一个象C++或Java一样的语言；或者你发明了UML；&#8230;，你就爬到了第7层，晋升为”大师”了。<br />
上面举的这些例子中，其中有些人站的楼层比这层高，这里只是为了形象说明而举例他们的某个成就。从上面列出的一些大师的贡献可以看出，成为大师必须要有较大的贡献。首先解决问题必须是比较重要的，其次你要比前辈们在某方面有一个较大的提高，或者你解决的是一个全新的以前没有解决过的问题；最重要的是，主要的思路和方法必须是你自己提供的，不再是在别人的思路基础上进行的优化和改进。<br />
看了上面这些要求，如果能量不够的话，你也许会觉得有些困难，所以不是每个人都能成为”大师”的。中国软件业里能称得上是”大师”的人，用屈指可数来形容，估计是绰绰有余。值得一提得是，国外的”大师”就象我们的”大牛”一样满天飞的多。<br />
我把我猜测本国有可能进到这层楼的大师列一下，以起个抛砖引玉的作用。汉王的”手写识别”技术由于是完全保密的，不知道它里面用了什么思想，原创思想占的比重有多少，因此不知道该把它划到这层楼还是更高一层楼去。原山东大学王小云教授破解DES和MD5算法时，用到的方法不知道是不是完全原创的，如果是的话也可进到这层楼来。<br />
陈景润虽然没有彻底解决哥德巴赫猜想，但他在解决问题时所用的方法是创新的，因此也可以进到这层楼来。当然，如果能彻底解决哥德巴赫猜想，那么可以算到更高的楼层去。<br />
求伯君和王志东等大牛们，他们在做WPS和表格处理之类的软件时，不知是否有较大的原创算法在里面，如果有的话就算我错把他们划到了大牛层。由于所学有限，不知道国内还有那些人能够得上”大师”的级别，或许有少量做研究的教授、院士们，可以达到这个级别，有知道的不妨回个帖子晾一晾。<br />
鉴于”大师”这个称号的光环效应，相信有不少人梦想着成为”大师”。或许你看了前面举的一些大师的例子，你会觉得要成为大师非常困难。不妨说一下，现在有一条通往”大师”之路的捷径打开了，那就是多核计算领域，有大量的处女地等待大家去挖掘。<br />
以前在单核时代开发的各种算法，现在都需要改写成并行的。数据结构与算法、图像处理、数值计算、操作系统、编译器、测试调试等各个领域，都存在大量的机会，可以让你进到这层楼来，甚至有可能让你进到更高一层楼去。</p>
<p>第8层 科学家<br />
科学家向来都是一个神圣的称号，因此我把他放在了“大师”之上。要成为科学家，你的贡献必须超越大师，不妨随便举一些例子。<br />
如果你象Dijkstra一样设计了ALGOL语言，提出了程序设计的三种基本结构：顺序、选择、循环，那么你可以爬到第8层楼来。顺便说一下，即使抛开这个成果，Dijkstra凭他的PV操作和信号量概念的提出，同样可以进到这层楼。<br />
如果你象Don Knuth一样，是数据结构与算法这门学科的重要奠基者，你也可以进到这层楼来。当然，数据结构和算法这门学科不是某个人开创的，是许多大师和科学家集体开创的。<br />
如果你象巴科斯一样发明了Fortran语言，并提出了巴科斯范式，对高级程序语言的发展起了重要作用，你也可以进到这层楼来。<br />
或者你象Ken Thompson、Dennis Ritchie一样发明了Unix操作系统和功能强大、高效、灵活、表达力强的C语言，对操作系统理论和高级编程语言均作出重大贡献，那么你也可以进到这层楼来。<br />
或者你有Frederick P. Brooks一样机会，可以去领导开发IBM的大型计算机System/360和OS/360操作系统，并在失败后反思总结，写出《人月神话》，对软件工程作出里程碑式的贡献，你也可以进到这层来。<br />
或者你提出了面向对象设计的基本思想，或者你设计了互联网的TCP/IP协议，或者你象Steven A.Cook一样奠定NP完全性的理论基础，或者你象Frances Allen一样专注于并行计算来实现编译技术，在编译优化理论和技术取得基础性的成就，…，均可进入这层。<br />
当然，如果你发明了C++语言或者Java语言，你进不到这层来，因为你用到的主要思想都是这层楼中的科学家提出的，你自己并没有没有多少原创思想在里面。<br />
看了上面列出的科学家的成就，你会发现，要成为“科学家”，通常要开创一门分支学科，或者是这个分支学科的奠基者，或者在某个分支学科里作出里程碑式的重大贡献。如果做不到这些的话，那么你能象Andrew C. Yao（姚期智）一样在对计算理论的多个方向如伪随机数生成，密码学与通信复杂度等各个方向上作出重要贡献，成为集大成者，也可以进入这层楼。<br />
成为“科学家”后，如果你有幸象Dijkstra一样，出现在一个非常重视科学的国度。当你去世时，你家乡满城的人都会自动地去为你送葬。不过如果不幸生错地方的话，能不挨“板砖”估计就算万幸了。<br />
从上面随便举的一些例子中，你可能能猜到，西方科学家的数量是非常多的，于是你会想中国应该也有少量的科学家吧？我可以很负责任地告诉你一个不幸的结果，中国本土产生的科学家的数量为0。目前在国内，软件领域的唯一的科学家就是上面提过的姚期智，还是国外请回来的，并不是本土产生的。<br />
可能你不同意我说的本土科学家数量为0的结论，因为你经常看到有许多公司里都有所谓“首席XX科学家”的头衔。我想说的是，这些所谓的“首席XX科学家”都是远远够不到这层楼的级别的，有些人的水平估计也就是一个“牛人”或“大牛”的级别，好一点的最多也就一个“学者”的级别。尤其是那些被称作“首席经X学家”的，基本上可以把称号改为“首席坑大家”。<br />
虽然我国没有人能爬到这层楼上来，但是西方国家仍然有许多人爬到了比这层更高的楼上。如果要问我们比西方落后多少？那么可以简单地回答为：“落后了三层楼”。下面就来看看我们做梦都没有到过的更高一层楼的秘密。</p>
<p>第9层 大科学家<br />
进入这层楼的门槛通常需要一些运气，比如某天有个苹果砸到你头上时，你碰巧发现了万有引力，那么你可以进到这层楼来。当然，万有引力几百年前就被人发现了，如果你现在到处嚷嚷着说你发现了万有引力，恐怕马上会有人打110，然后警察会把你送到不正常人类的聚集地去。因此，这里举万有引力的例子，只是说你要有类似的成就才能进到这层楼来。<br />
牛顿发现万有引力定律开创了经典物理运动力学这门学科，如果你也能开创一门大的学科，那么你就从科学家晋升为“大科学家”。比如爱因斯坦创建了相对论，从一个小职员变成了大科学家。当然大科学家可远不止这两人，数学界里比物理学界更是多得多，如欧几里得创建了平面几何，笛卡尔开创解析几何，还有欧拉、高斯、莱布尼茨等数不清的人物，跟计算相关的大科学家则有图灵等人。<br />
从上面列出的一些大科学家可以发现，他们的成就不仅是开创了一个大的学科，更重要的是他们的成就上升到了“公理”的层面。发现公理通常是需要一点运气的，如果你的运气不够好的话，另外还有一个笨办法也可以进到这层楼来，那就是成为集大成者。例如冯·诺伊曼，对数学的所有分支都非常了解，许多领域都有较大的贡献，即使撇开他对计算机的开创贡献，成为大科学家照样绰绰有余。<br />
当然，程序员们最关心的是自己有没有机会变成大科学家。既然计算机这门大学科的开创性成果早就被冯·诺伊曼、图灵等人摘走了，那么程序员们是不是没有机会变成大科学家了呢？我们的古人说得好：“江山代有才人出，各领风骚数百年”，现在在计算机这门学科下面诞生了许多非常重要的大的分支，所以你还是有足够的机会进到这层楼的。<br />
如果你能够彻底解决自然语言理解（机器翻译）这门学科中的核心问题， 或者你在人工智能或者机器视觉（图像识别）方面有突破性的发现，那么你同样可以轻易地晋升为“大科学家”。这样当某天你老了去世时，或许那天国人已经觉醒，你也能享受到如Dijkstra一样的待遇，有满城甚至全国的人去为你送葬。<br />
现在还剩下另外一个大家感兴趣的问题没有讨论，那就是这层中已经出现了牛顿、爱因斯坦、高斯等我们平常人都认为是顶级的科学家，是不是这层已经是楼顶了呢？相信还记得本文标题的人应该知道现在仅仅是第9层，还有第10层没有到达呢。可能不少人现在要感到困惑了，难道还有人站在比牛顿、爱因斯坦、高斯等人更高的楼层上？<br />
这个世界上确实存在可以用一只手的手指数得清的那么几个人，他们爬到了第10层楼上。因此，第10层楼不是虚构的，而是确实存在的。如果对此有疑惑或者认为我在胡诌一番的话，那么不妨继续往下看下去，窥一下第10层楼的秘密。<br />
由于第十层比较有争议性，而且我觉得第十层的说法是本文最大的败笔，故不予转载。</p>
]]></content:encoded>
			<wfw:commentRss>http://zhuwenhao.com/372/%e6%8a%80%e6%9c%af/%e8%bd%af%e4%bb%b6%e5%b7%a5%e7%a8%8b/%e7%a8%8b%e5%ba%8f%e5%91%98%e7%9a%84%e5%8d%81%e5%b1%82%e6%a5%bc/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<series:name><![CDATA[程序员修炼之路]]></series:name>
	</item>
		<item>
		<title>程序员晋升架构师的十项必备技能</title>
		<link>http://zhuwenhao.com/367/%e6%8a%80%e6%9c%af/%e8%bd%af%e4%bb%b6%e5%b7%a5%e7%a8%8b/%e7%a8%8b%e5%ba%8f%e5%91%98%e6%99%8b%e5%8d%87%e6%9e%b6%e6%9e%84%e5%b8%88%e7%9a%84%e5%8d%81%e9%a1%b9%e5%bf%85%e5%a4%87%e6%8a%80%e8%83%bd/</link>
		<comments>http://zhuwenhao.com/367/%e6%8a%80%e6%9c%af/%e8%bd%af%e4%bb%b6%e5%b7%a5%e7%a8%8b/%e7%a8%8b%e5%ba%8f%e5%91%98%e6%99%8b%e5%8d%87%e6%9e%b6%e6%9e%84%e5%b8%88%e7%9a%84%e5%8d%81%e9%a1%b9%e5%bf%85%e5%a4%87%e6%8a%80%e8%83%bd/#comments</comments>
		<pubDate>Fri, 07 May 2010 02:22:02 +0000</pubDate>
		<dc:creator>朱文昊 Albert Zhu</dc:creator>
				<category><![CDATA[软件工程]]></category>

		<guid isPermaLink="false">http://zhuwenhao.com/?p=367</guid>
		<description><![CDATA[    不是每一个程序员都能够成为一个架构师——这是开发界广为流传的一个论调。架构师群体往往对这个言论表示默许，这不得不令广大入门不久的程序员们怀疑架构师们是不是隐藏了什么武林秘籍。程序员要修炼什么武功才能晋升为一个架构师？
    1、卓越的程序员
    Fred  [...]]]></description>
			<content:encoded><![CDATA[<p>    不是每一个程序员都能够成为一个架构师——这是开发界广为流传的一个论调。架构师群体往往对这个言论表示默许，这不得不令广大入门不久的程序员们怀疑架构师们是不是隐藏了什么武林秘籍。程序员要修炼什么武功才能晋升为一个架构师？</p>
<p>    1、卓越的程序员</p>
<p>    Fred George先生说：“不编程的架构师的职业生涯是短暂的”。他说这句话的背景主要是针对有些架构师的设计与实现有断层的问题而言的，因为如果架构师不去实践，只是想当然的认为“没问题，这个想法能实现”，那么对于项目的落实而言是个很大的隐患。支付宝架构师冯大辉也表示过，架构师是一个比较“虚”的岗位，主要的问题都在“落地”的过程中。</p>
<p>    2、抽象思维</p>
<p>    很多优秀的架构师们都一致的表示，逻辑思维和抽象思维能力是一个架构师最重要的素质。eBay的Randy Shoup先生称拥有条理清晰的逻辑思维能力的人“就像稀有动物那样难找”。Fred George则表示“驾驭概念的技能，在我看来是每一个人最高的潜力”，并表示自己不太介意这样一个苗子在其他方面的技能和经验的匮乏，因为在他看来除了思维之外的其他因素都是可以培养的。</p>
<p>    3、技术前瞻性</p>
<p>    有人谈到技术高手与架构师的区别就在于，架构师不光是着眼于现在，不仅仅局限于开发细节，比如如何调用，如何并发等等。而是跳出三界外，考虑一下面向未来问题和潜在风险的应对之道。</p>
<p>    4、问题解决大师</p>
<p>    只是，架构师之所以为架构师，是在于他们在面对庞大系统之时，仍然能够敏锐的发现其底层之真实。这不仅需要此哲学层面的“内功”，还需要架构师具有多领域知识和经验的积淀。</p>
<p>    5、多领域知识</p>
<p>    架构师身为一名技术领袖，需要通过发散知识的光芒来统御开发团队的。如果只是对本行业知识做到烂熟于心，那还仅仅是一名熟练工的水平。要想晋升更高的层次，还需要跳出“只缘身在此山中”的困惑。</p>
<p>    6、沟通能力</p>
<p>    做到人性化的沟通，需要我们在平时就进行培养。写出大部头的架构书，有的时候并没有用VISIO画出的简单架构图好理解。人对图形理解远远大于对文字的理解，直观简单的UML图可以极大的方便程序员理解架构师的意图。</p>
<p>    7、内力</p>
<p>    很多人理解的内力就是开发技术，包括语言的掌握、对框架的掌握、数据库管理能力、安全管理能力等等。但是我们看到，架构更多的内力体现在对技术的综合运用上，光会编程的程序员，最多就能做到高级程序员，也就是技术实现上的高手。</p>
<p>    8、权衡取舍</p>
<p>    Fred George先生提出了“短期滥用”的说法，即在系统能够承受的范围内做出一些妥协。在ATAM方法中，分析的思路是基于“情景”的：你需要提出各种可能的情景，然后来证明在每一个用户使用场景中，系统的哪一些内容是必要的、不可丢弃的——从而确定哪些部分是暂时可以不予考虑的。</p>
<p>    9、管控能力</p>
<p>    架构师在管理和控制的能力上，需要有自己独到的见解，而不是简单的认为这是项目经理或者财务部门的事情。身为技术专家的架构师，随不需要处理那些烦杂的日常管理。奇虎架构师李钊在一次接受采访时道出过架构师们的心声，技术人才转向管理就是莫大的浪费。对，如果架构师只是一味的去进行项目管理，那就和其他市场人员没有任何区别了。在这里架构师所需要的管理与控制，其实是从技术的角度，对一些问题的控制，特别是开发过程中的监控，而不是普通意义上的纯粹管理。</p>
<p>    10、艺术气质</p>
<p>    一个优美的系统则是可以像有机的生命一样成长的，这是因为从系统开始架构的那一刻起，架构师就考虑到这个系统以后将会面临的挑战，为系统的成长预留好空间。项目经理经常会对这位架构师提出的看似理想化的要求不置可否——项目经理只想着能够尽快以比较低的成本实现客户的需求，然而这些充满艺术美感的想法其实是打造健康——因而优美——的系统的根本因素。（完）</p>
]]></content:encoded>
			<wfw:commentRss>http://zhuwenhao.com/367/%e6%8a%80%e6%9c%af/%e8%bd%af%e4%bb%b6%e5%b7%a5%e7%a8%8b/%e7%a8%8b%e5%ba%8f%e5%91%98%e6%99%8b%e5%8d%87%e6%9e%b6%e6%9e%84%e5%b8%88%e7%9a%84%e5%8d%81%e9%a1%b9%e5%bf%85%e5%a4%87%e6%8a%80%e8%83%bd/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<series:name><![CDATA[程序员修炼之路]]></series:name>
	</item>
	</channel>
</rss>

