<?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>面向对象 | 酷 壳 - CoolShell</title>
	<atom:link href="https://coolshell.cn/tag/%e9%9d%a2%e5%90%91%e5%af%b9%e8%b1%a1/feed" rel="self" type="application/rss+xml" />
	<link>https://coolshell.cn</link>
	<description>享受编程和技术所带来的快乐 - Coding Your Ambition</description>
	<lastBuildDate>Thu, 16 Dec 2021 04:47:06 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.2</generator>
	<item>
		<title>一些软件设计的原则</title>
		<link>https://coolshell.cn/articles/4535.html</link>
					<comments>https://coolshell.cn/articles/4535.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Mon, 25 Apr 2011 00:24:18 +0000</pubDate>
				<category><![CDATA[程序设计]]></category>
		<category><![CDATA[Coding]]></category>
		<category><![CDATA[Design]]></category>
		<category><![CDATA[Object-Oriented]]></category>
		<category><![CDATA[Unix]]></category>
		<category><![CDATA[面向对象]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=4535</guid>

					<description><![CDATA[<p>以前本站向大家介绍过一些软件开发的原则，比如优质代码的十诫和Unix传奇(下篇)中所以说的UNIX的设计原则。相信大家从中能够从中学了解到一些设计原理方面的知识...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/4535.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/4535.html">一些软件设计的原则</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>以前本站向大家介绍过一些软件开发的原则，比如<a title="优质代码的十诫" href="https://coolshell.cn/articles/1007.html" target="_blank" rel="bookmark noopener">优质代码的十诫</a>和<a title="Unix传奇(下篇)" href="https://coolshell.cn/articles/2324.html" target="_blank" rel="noopener">Unix传奇(下篇)</a>中所以说的UNIX的设计原则。相信大家从中能够从中学了解到一些设计原理方面的知识，正如我在《<a title="再谈“我是怎么招聘程序员的”（上）" href="https://coolshell.cn/articles/4506.html" target="_blank" rel="noopener">再谈“我是怎么招聘程序”</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" href="http://en.wikipedia.org/wiki/Don%27t_repeat_yourself" rel="nofollow">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="https://coolshell.cn/articles/1907.html" target="_blank" rel="noopener">这些UI的设计</a>和我们<a title="为什么中国的网页设计那么烂？" href="https://coolshell.cn/articles/3605.html" target="_blank" rel="noopener">中国网页</a>（尤其是<a title="微软用新浪来当反面教材" href="https://coolshell.cn/articles/3872.html" target="_blank" rel="noopener">新浪的网页</a>）者是负面的例子。“宜家”（IKEA）简约、效率的家居设计、生产思路；“微软”（Microsoft）“所见即所得”的理念；“谷歌”（Google)简约、直接的商业风格，无一例外的遵循了“kiss”原则，也正是“kiss”原则，成就了这些看似神奇的商业经典。而苹果公司的iPhone/iPad将这个原则实践到了极至。</p>
<p><span id="more-4535"></span></p>
<p>把一个事情搞复杂是一件简单的事，但要把一个复杂的事变简单，这是一件复杂的事。</p>
<p><strong>参考</strong>：<a title="http://en.wikipedia.org/wiki/KISS_principle" href="http://en.wikipedia.org/wiki/KISS_principle" rel="nofollow">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" href="http://en.wikipedia.org/wiki/Command-query_separation" rel="nofollow">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="https://coolshell.cn/articles/3005.html" target="_blank" rel="noopener">过度重构的文章</a>，这个示例就是这个原则的反例。而，WebSphere的设计者就<a href="http://www.bbc.co.uk/news/business-11944966" target="_blank" rel="noopener">表示过他过度设计了这个产品</a>。我们的程序员或是架构师在设计系统的时候，会考虑很多扩展性的东西，导致在架构与设计方面使用了大量折衷，最后导致项目失败。这是个令人感到讽刺的教训，因为本来希望尽可能延长项目的生命周期，结果反而缩短了生命周期。</p>
<p><strong>参考</strong>：<a title="http://en.wikipedia.org/wiki/You_Ain%27t_Gonna_Need_It" href="http://en.wikipedia.org/wiki/You_Ain%27t_Gonna_Need_It" target="_blank" rel="nofollow noopener">http://en.wikipedia.org/wiki/You_Ain%27t_Gonna_Need_It</a></p>
<h4>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>对于对象 &#8216;O&#8217; 中一个方法&#8217;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 style="padding-left: 30px;">final String outputDir = ctxt.getOptions().getScratchDir().getAbsolutePath();</p>
<p>这么长的一串对其它对象的细节，以及细节的细节，细节的细节的细节&#8230;&#8230;的调用，增加了耦合，使得代码结构复杂、僵化，难以扩展和维护。</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" href="http://en.wikipedia.org/wiki/Principle_of_Least_Knowledge" rel="nofollow">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_(object-oriented_design)">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&#8217;t call us, we&#8217;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)" href="http://en.wikipedia.org/wiki/Coupling_%28computer_science%29" rel="nofollow">http://en.wikipedia.org/wiki/Coupling_(computer_science)</a></li>
<li><a title="http://en.wikipedia.org/wiki/Cohesion_(computer_science)" href="http://en.wikipedia.org/wiki/Cohesion_%28computer_science%29" rel="nofollow">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" href="http://en.wikipedia.org/wiki/Convention_over_Configuration" rel="nofollow">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 align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" id="wp_rp_first"><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/8961.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/01/kiss-150x150.png" alt="从面向对象的设计模式看软件设计" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8961.html" class="wp_rp_title">从面向对象的设计模式看软件设计</a></li><li ><a href="https://coolshell.cn/articles/18024.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2017/07/api-design-300x278-2-150x150.jpg" alt="API设计原则 &#8211; Qt官网的设计实践总结" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18024.html" class="wp_rp_title">API设计原则 &#8211; Qt官网的设计实践总结</a></li><li ><a href="https://coolshell.cn/articles/9949.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2013/07/inverted-bookshelf_thumb-150x150.jpg" alt="IoC/DIP其实是一种管理思想" width="150" height="150" /></a><a href="https://coolshell.cn/articles/9949.html" class="wp_rp_title">IoC/DIP其实是一种管理思想</a></li><li ><a href="https://coolshell.cn/articles/8745.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg" alt="如此理解面向对象编程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8745.html" class="wp_rp_title">如此理解面向对象编程</a></li><li ><a href="https://coolshell.cn/articles/7236.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2012/05/Bannière-Unix-linux-150x150.jpg" alt="用Unix的设计思想来应对多变的需求" width="150" height="150" /></a><a href="https://coolshell.cn/articles/7236.html" class="wp_rp_title">用Unix的设计思想来应对多变的需求</a></li><li ><a href="https://coolshell.cn/articles/5444.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg" alt="千万不要把 bool 设计成函数参数" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5444.html" class="wp_rp_title">千万不要把 bool 设计成函数参数</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/4535.html">一些软件设计的原则</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/4535.html/feed</wfw:commentRss>
			<slash:comments>69</slash:comments>
		
		
			</item>
		<item>
		<title>面向对象是个骗局？！</title>
		<link>https://coolshell.cn/articles/3036.html</link>
					<comments>https://coolshell.cn/articles/3036.html#comments</comments>
		
		<dc:creator><![CDATA[陈皓]]></dc:creator>
		<pubDate>Wed, 29 Sep 2010 00:37:54 +0000</pubDate>
				<category><![CDATA[C/C++语言]]></category>
		<category><![CDATA[技术读物]]></category>
		<category><![CDATA[编程语言]]></category>
		<category><![CDATA[轶事趣闻]]></category>
		<category><![CDATA[Alexander Stepanov]]></category>
		<category><![CDATA[Bjarne Stroustrup]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[Object-Oriented]]></category>
		<category><![CDATA[OOP]]></category>
		<category><![CDATA[STL]]></category>
		<category><![CDATA[面向对象]]></category>
		<guid isPermaLink="false">http://coolshell.cn/?p=3036</guid>

					<description><![CDATA[<p>今天在网上看到网页叫“Object Orientation Isa Hoax”——面向对象是一个骗局，标题很有煽动性（注：该网站上还有一个网页叫Object O...</p>
<p class="read-more"><a class="btn btn-default" href="https://coolshell.cn/articles/3036.html"> Read More<span class="screen-reader-text">  Read More</span></a></p>
The post <a href="https://coolshell.cn/articles/3036.html">面向对象是个骗局？！</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></description>
										<content:encoded><![CDATA[<p><script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3415450859608158"
     crossorigin="anonymous"></script>今天在网上看到网页叫“<a href="http://c2.com/cgi/wiki?ObjectOrientationIsaHoax" target="_blank">Object Orientation Isa Hoax</a>”——面向对象是一个骗局，标题很有煽动性（注：该网站上还有一个网页叫<a href="http://c2.com/cgi/wiki?ObjectOrientationIsDead" target="_blank">Object Orientation Is Dead</a>），好吧，打开看看上面有些 什么，发现这个网页是在收集一些关于“面向对象的反动言论”，没想到的是，很多言论出自很多大师之口。比如：Alexander Stepanov和Bjarne Stroustrup。这些言论挺有意思的，所以，我摘两段在下面：</p>
<p>第一段是Alexander Stepanov的（不要告诉我你不知道这个人，STL之父，关于他的故事，可以到<a href="http://www.techcn.com.cn/index.php?doc-view-131345.html" target="_blank">这里看看</a>）。他N年前作过一段采访，<a href="http://www.stlport.org/resources/StepanovUSA.html" target="_blank">原文在这里</a>（我非常建议大家去读一下这篇采访，相当过瘾），<a href="http://dev.csdn.net/htmls/11/11440.html" target="_blank">译文在这里</a>（不过有地方把原意都译反了，我重译了一下），其中有一个问答被上述的那个面向对象反动言论的网页收录了：</p>
<figure style="width: 225px" class="wp-caption alignright"><img decoding="async" loading="lazy" title="Alexander Stepanov" src="http://www.techcn.com.cn/uploads/200906/s_1244557971yFeOfA84.jpg" alt="" width="225" height="300" /><figcaption class="wp-caption-text">Alexander Stepanov</figcaption></figure>
<blockquote><p><strong>Question</strong>:<br />
I think STL and Generic Programming mark a definite departure from the common C++ programming style, which I find is almost completely derived from SmallTalk. Do you agree?</p>
<p><strong>提问</strong>：<br />
我认为STL和泛型编程标志着非同一般的C++编程风格，而一般C++风格几乎完全是从SmallTalk派生过来的。你同意吗？</p>
<p><strong>Answer</strong>:<br />
Yes. STL is not object oriented. I think that object orientedness is almost as much of a hoax as Artificial Intelligence. I have yet to see an interesting piece of code that comes from these OO people. In a sense, I am unfair to AI: I learned a lot of stuff from the MIT AI Lab crowd, they have done some really fundamental work: Bill Gosper&#8217;s Hakmem is one of the best things for a programmer to read. AI might not have had a serious foundation, but it produced Gosper and Stallman (Emacs), Moses (Macsyma) and Sussman (Scheme, together with Guy Steele). I find OOP technically unsound. It attempts to decompose the world in terms of interfaces that vary on a single type. To deal with the real problems you need multisorted algebras &#8211; families of interfaces that span multiple types. I find OOP philosophically unsound. It claims that everything is an object. Even if it is true it is not very interesting &#8211; saying that everything is an object is saying nothing at all. I find OOP methodologically wrong. It starts with classes. It is as if mathematicians would start with axioms. You do not start with axioms &#8211; you start with proofs. Only when you have found a bunch of related proofs, can you come up with axioms. You end with axioms. The same thing is true in programming: you have to start with interesting algorithms. Only when you understand them well, can you come up with an interface that will let them work.</p>
<p><strong>回答：</strong><br />
是的。STL不是面向对象的。我认为面向对象和人工智能差不多，都是个骗局。我至今仍然没有从那些OO编程的人那里看到一丁点有意思的代码。从某种意义上来说，我这么说对人工智能（AI）并不公平：因为我听说过很多MIT（麻省理工大） AI实验室里一帮人搞出来的东西，而且他们的确直正干了一些基础性的工作：Bill Gosper的Hakmem是程序员最好的读物之一。AI或许没有一个实实在在的基础，但它造就了Gosper和Stallman（Emacs）， Moses（Macsyma）和Sussman（Scheme， 和Guy Steele一起）。</p>
<ul>
<li>我发现OOP在技术上是荒谬的，它企图把事物按照不同单个类型的接口来解构，为了处理实际问题，你需要多种代数方法——横跨多种类型的接口族；</li>
<li>我发现OOP在哲学上是荒谬的，它声称一切都是对象。即使这是真的也不是很有趣——因为说一切都是对象跟什么都没说一样；</li>
<li>我发现OOP的方法论是错误的，它从类开始，就好像数学应该从从公理开始一样。其实你不会是从公理开始的，而是从证明开始。直到你找到了一大堆相关证据后你才能归纳出公理，然后以公理结束。在程序设计方面存在着同样的事实：你要从有趣的算法开始。只有很好地理解了算法，你才有可能提炼出接口以让其工作。</li>
</ul>
</blockquote>
<p><span style="color: #ffffff;">&lt;&#8212;&#8212;&#8212;&gt;</span></p>
<p>下面，我们再来看C++的发明者Bjarne Stroustrup，在1998年IEEE采访时的一段话（<a href="http://www2.research.att.com/~bs/ieee_interview.html" target="_blank">全篇见这里</a>），下面是其中的几段话：（我的翻译如下）</p>
<p><span id="more-3036"></span></p>
<figure style="width: 200px" class="wp-caption alignright"><img decoding="async" loading="lazy" title="Bjarne Stroustrup" src="http://www.techcn.com.cn/uploads/200906/1244559516ywHaeEXL.png" alt="" width="200" height="245" /><figcaption class="wp-caption-text">Bjarne Stroustrup</figcaption></figure>
<blockquote><p>So what is OO? Certainly not every good program is object-oriented, and not every object-oriented program is good. If this were so, &#8220;object-oriented&#8221; would simply be a synonym for &#8220;good,&#8221; and the concept would be a vacuous buzzword of little help when you need to make practical decisions. I tend to equate OOP with heavy use of class hierarchies and virtual functions (called methods in some languages). This definition is historically accurate because class hierarchies and virtual functions together with their accompanying design philosophy were what distinguished Simula from the other languages of its time. In fact, it is this aspect of Simula&#8217;s legacy that Smalltalk has most heavily emphasized.</p>
<p>那么，什么是OO面向对象？当然，不会是所有的程序都是面向对象的，而且，也不是所有的面向对象程序就是好的。如果面向对象是好的，那么“Object-Oriented”应该成为“Good”的同义词，并且，OO概念只会成为一个假大空的口号，在你需要做出实际决定时只可能帮你那么一丁点。我倾向于把OOP等价于大量使用继承类和虚函数（某些语言的调用方法）。从历史上来说，这个定义是精确的，因为，在那个时候，只有类的继承和虚函数一起存在的设计哲学，才能把Simula和其它语言分别开来。事实上，Smalltalk相当地强调着这种Simula的遗留问题。</p>
<p>Defining OO as based on the use of class hierarchies and virtual functions is also practical in that it provides some guidance as to where OO is likely to be successful. You look for concepts that have a hierarchical ordering, for variants of a concept that can share an implementation, and for objects that can be manipulated through a common interface without being of exactly the same type. Given a few examples and a bit of experience, this can be the basis for a very powerful approach to design.</p>
<p>用继承类和虚函数来定义OO在实际上可以让很多OO指导性的东西更能成功一些。在解决问题时，寻找的那些有层级次序的对象，以应对不同对象也可以重用同一个实现，并且对象可以被某个共同的接口来操作而不需要完全相同的类型。在你了解了一些示例和拥有了一些经验后，OO可以成为Design的一个强有力的基础。</p>
<p>However, not every concept naturally and usefully fits into a hierarchy, not every relationship among concepts is hierarchical, and not every problem is best approached with a primary focus on objects. For example, some problems really are primarily algorithmic. Consequently, a general-purpose programming language should support a variety of ways of thinking and a variety of programming styles. This variety results from the diversity of problems to be solved and the many ways of solving them. C++ supports a variety of programming styles and is therefore more appropriately called a multiparadigm, rather than an object-oriented, language (assuming you need a fancy label).</p>
<p>然而，并不是每一个对象都自然地有效地适合继承，并不是每一个对象间的关系都是继承，也并不是每一个问题的最佳解决途径需要主要地通过对象。例如，很多问题主要是算法问题（译注：如业务逻辑，数据流等）。我们知道，一个一般性的编程语言都应该有能力支持不同的思路和不同的编程风格。这样，对于问题的多样性，我们可以使用许许多多不同的的方法去解决他们，这就产生了很多的不同解法。C++支持编程风格的多样性，因此，C++叫做“多范式  multi-paradigm”会更合适一些，而不是一个面向对象语言。</p></blockquote>
<p><span style="color: #ffffff;">&lt;&#8212;&#8212;&#8212;&gt;</span></p>
<p>我个人在看过这些言论后，我先不管“面向对象是不是一个骗局”，不过从某种角度上来看的确是有些问题的，C++、OO、XML、SOA、网格计算等等诸如此类的东西的确被挂上了神圣的光坏。这些东西出来的时候总是只有一种赞美的声音。无论好坏，只有一种声音总是令人恐怖的，无论好坏，有不同的声音总是好的，每当这个社会或是我们的IT界大张旗鼓地鼓吹或是信仰某些东西，却没有任何一点不同意见的时候，我就会感到一种莫名的恐慌。我知道，这是我们从小受到的那种“非黑即白”的价值观教育所致，事物要么全是好的，要么全是不好的。其实任何事物都是有好有不好的，C++，敏捷开发，CMMi，OO，设计模式，重构，等等等等，他们都有好的也有不好的，关键看你怎么来使用（如之前的《<a title="代码重构的一个示例" href="https://coolshell.cn/articles/3005.html" target="_blank">代码重构的一个示例</a>》）。这个世界只有适合不适合的东西，不会出现放之四海皆准的东西，也不可能出现一种可以解决所有问题的东西，如果有，那么这种东西必然是一种宗教性质的用来洗脑的东西。</p>
<p>所以，每当在我身边看到或听到那些只有一种声音有如“电视购物”或是“新闻联播”之类的宣传或是鼓动的时候，我就感到很一种莫名的反感…… 不多说了，还是交给大家来评价吧。我仅以此篇文章献给那些OO-Oriented，Design Pattern-Oriented，Agile-Oriented，Process-Oriented，等等有着宗教信仰一般的人和事。<!--



<p align="center"><a href= target=_blank><img decoding="async" src=""></a></p>





<p align="center"><img decoding="async" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.weixin.jpg"> <img decoding="async" loading="lazy" src="https://coolshell.cn/wp-content/uploads/2020/03/coolshell.mini_.jpg" width="300" height="300"> <br />关注CoolShell微信公众账号和微信小程序</p>

 

--></p>
<div style="margin-top: 15px; font-size: 16px;color: #cc0000;">
<p align="center"><strong>（转载本站文章请注明作者和出处 <a href="https://coolshell.cn/">酷 壳 &#8211; CoolShell</a> ，请勿用于任何商业用途）</strong></p>
</div>

<div class="wp_rp_wrap  wp_rp_vertical_m" ><div class="wp_rp_content"><h3 class="related_post_title">相关文章</h3><ul class="related_post wp_rp"><li ><a href="https://coolshell.cn/articles/8745.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/8.jpg" alt="如此理解面向对象编程" width="150" height="150" /></a><a href="https://coolshell.cn/articles/8745.html" class="wp_rp_title">如此理解面向对象编程</a></li><li ><a href="https://coolshell.cn/articles/5202.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/14.jpg" alt="对象的消息模型" width="150" height="150" /></a><a href="https://coolshell.cn/articles/5202.html" class="wp_rp_title">对象的消息模型</a></li><li ><a href="https://coolshell.cn/articles/4535.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/1.jpg" alt="一些软件设计的原则" width="150" height="150" /></a><a href="https://coolshell.cn/articles/4535.html" class="wp_rp_title">一些软件设计的原则</a></li><li ><a href="https://coolshell.cn/articles/12199.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/plugins/wordpress-23-related-posts-plugin/static/thumbs/22.jpg" alt="C++ STL string的Copy-On-Write技术" width="150" height="150" /></a><a href="https://coolshell.cn/articles/12199.html" class="wp_rp_title">C++ STL string的Copy-On-Write技术</a></li><li ><a href="https://coolshell.cn/articles/20845.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2020/03/rust-social-wide-150x150.jpg" alt="Rust语言的编程范式" width="150" height="150" /></a><a href="https://coolshell.cn/articles/20845.html" class="wp_rp_title">Rust语言的编程范式</a></li><li ><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_thumbnail"><img src="https://coolshell.cn/wp-content/uploads/2018/05/300x262-150x150.jpg" alt="程序员练级攻略（2018)  与我的专栏" width="150" height="150" /></a><a href="https://coolshell.cn/articles/18360.html" class="wp_rp_title">程序员练级攻略（2018)  与我的专栏</a></li></ul></div></div>The post <a href="https://coolshell.cn/articles/3036.html">面向对象是个骗局？！</a> first appeared on <a href="https://coolshell.cn">酷 壳 - CoolShell</a>.]]></content:encoded>
					
					<wfw:commentRss>https://coolshell.cn/articles/3036.html/feed</wfw:commentRss>
			<slash:comments>79</slash:comments>
		
		
			</item>
	</channel>
</rss>
