<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>整洁代码 on 冯威的博客</title><link>https://fwhyy.com/tags/%E6%95%B4%E6%B4%81%E4%BB%A3%E7%A0%81/</link><description>Recent content in 整洁代码 on 冯威的博客</description><generator>Hugo</generator><language>zh-CN</language><lastBuildDate>Wed, 16 May 2012 00:00:00 +0800</lastBuildDate><atom:link href="https://fwhyy.com/tags/%E6%95%B4%E6%B4%81%E4%BB%A3%E7%A0%81/atom.xml" rel="self" type="application/rss+xml"/><item><title>《代码整洁之道》读书笔记(下)</title><link>https://fwhyy.com/2012/05/clear-code-reading-notes-two/</link><pubDate>Wed, 16 May 2012 00:00:00 +0800</pubDate><guid>https://fwhyy.com/2012/05/clear-code-reading-notes-two/</guid><description>&lt;p&gt;全书一共400多页，一共17章，第十三章讲并发，并且在附录A中有对并发的补充，第十四到十六章是一些Java代码的案例，第十七章相当于一个总结。本次写读书笔记主要涵盖前十二章的内容，由于篇幅分为上下两篇。本篇为下，个别章节因为能力有限，没有完全弄懂，就先空着了。&lt;/p&gt;
&lt;h2 id="第七章-错误处理"&gt;第七章 错误处理&lt;/h2&gt;
&lt;p&gt;错误处理简单来说就是当软件出现错误时还能正常工作。错误处理很重要，但不能打乱的原本的代码逻辑。&lt;/p&gt;
&lt;p&gt;使用异常处理而非返回码，底层往上抛，最上层集中处理。这点在第三章函数中也提到过，之所以看到有的地方是使用错误返回码，是因为早期的一些语言没有异常处理机制。现在的语言基本都有异常处理机制。&lt;/p&gt;
&lt;p&gt;异常的信息应该足够充分（包含出错的位置以及原因）。&lt;/p&gt;
&lt;p&gt;不要在catch块中去实现业务逻辑，就是说当出现异常的时候一定要抛出，而不要改变状态或是做其他一些操作，这样会留下很多陷进。&lt;/p&gt;
&lt;p&gt;底层的方法不要返回Null值，否则在调用时会添加很多的判断，可以抛出异常或返回特例对象，特例对象是指返回一个函数返回值类型的空对象。&lt;/p&gt;
&lt;p&gt;不要传递Null值。&lt;/p&gt;
&lt;h2 id="第八章-边界"&gt;第八章 边界&lt;/h2&gt;
&lt;p&gt;无&lt;/p&gt;
&lt;h2 id="第九章-单元测试"&gt;第九章 单元测试&lt;/h2&gt;
&lt;p&gt;我工作以来所经历的公司中都很少使用单元测试，以致于我现在对单元测试这方面还不是特别熟悉，只是在自己的个人项目中写过一些单元测试的代码。在DotNet平台下可以使用VS自带的单元测试功能或是NUnit。&lt;/p&gt;
&lt;p&gt;现在有一种编程的方法叫TDD（测试驱动开发），意思是先写单元测试，然后写对应的代码，通过修改调试让写的代码通过单元测试。使用TDD，会使测试覆盖所有的代码，测试代码和生产代码的比例有可能会达到1:1 ，所以也会带来成本的问题。TDD三定律：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在编写不能通过的单元测试前，不可以编写生产代码；&lt;/li&gt;
&lt;li&gt;只有编写刚好无法通过的单元测试，不能编译也算不通过；&lt;/li&gt;
&lt;li&gt;只可编写刚好足以通过当前失败测试的生产代码。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;测试代码和生产代码一样的重要，也需要保持整洁。测试代码要随着生产代码的修改而修改，否则只会产生大量无用的测试代码，而且也会给生产代码的修改带来风险。&lt;/p&gt;
&lt;p&gt;单元测试的好处：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;有了测试不用担心对代码的修改；&lt;/li&gt;
&lt;li&gt;有了测试可以毫无顾虑的去改进架构和设计；&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;整洁测试的三要素 ：可读性、可读性、可读性，测试中的可读性甚至比生成代码中的可读性还要重要。要做到高可读性，测试代码需要满足：明确、简洁和足够的表达力。&lt;/p&gt;
&lt;p&gt;每个测试都可以拆分为三个环节：构造测试数据、操作测试数据、检验操作是否达到预期结果。&lt;/p&gt;
&lt;p&gt;双重标准：指的是在测试环境中和生产环境中有些条件不必完全一致。生产环境中有时要考虑内存、cpu等性能问题，而在测试环境中不必做这些限制。&lt;/p&gt;
&lt;p&gt;每个测试一个断言，不必完全纠结，但单个测试断言数应该最小化。&lt;/p&gt;
&lt;p&gt;每个测试函数只测试一个概念，还是单一职责的问题。&lt;/p&gt;
&lt;p&gt;整洁的测试代码要满足“FIRST”原则&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;快速（Fast）：测试运行应该够快，如果运行很慢就不会想频繁去运行它，就会导致不能及时发现问题。&lt;/li&gt;
&lt;li&gt;独立（Independent）：测试应相互独立，某个测试不应成为下个测试的设定条件，应该可以单独运行每个测试。测试如果相互依赖，会导致一连串的测试失败，查找问题比较困难。&lt;/li&gt;
&lt;li&gt;可重复（Repeatable）：可以在任何环境中重复通过。&lt;/li&gt;
&lt;li&gt;自足验证（Self-Validating）：测试应该有布尔值输出，无论是成功还是失败。&lt;/li&gt;
&lt;li&gt;及时（Timely）测试应及时编写，单元测试应该在使其通过的生产代码编写之前编写。按这种要求及时TDD开发了，但在实际工作中并不是总能遇到TDD开发的团队。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="第十章-类"&gt;第十章 类&lt;/h2&gt;
&lt;p&gt;类通常由变量、属性和方法组成。按照书中所讲的Java的约定，类应该由一组变量开始，如果有静态公共常量，应该放在前面，然后是私有静态变量和私有实体变量。公共函数跟在变量之后，一些供公共函数调用的私有工具函数在公共函数之后。&lt;/p&gt;
&lt;p&gt;和函数一样，类也应该要尽可能的短小。但和函数不同不是以代码行数来权衡，而是以职责。如果无法准确的为某个类命名，则有可能是该类的职责过多。&lt;/p&gt;
&lt;p&gt;单一职责原则（SRP）：类或模块应该有且只有一条加以修改的理由。&lt;/p&gt;
&lt;p&gt;在实际的工作中很多开发人员往往不会思考这么多，他们只想着让代码可以工作就可以了，所以经常出现几千行的大类。系统应该是有许多短小的类而不是少量巨大的类组成。每个小类有单一的职责，只有一个修改的原因，所有这些小类在一起协同工作完成系统的功能。&lt;/p&gt;
&lt;p&gt;高内聚：如果一个类中每个变量都被每个方法所使用，则该类有最大的内聚性。&lt;/p&gt;
&lt;p&gt;保持内聚性得到许多短小的类。&lt;/p&gt;
&lt;p&gt;上面说到每个类应该有单一职责，就是说类之间应该低耦合，但类的内部应该是高内聚的。如果一个类中的每个变量被每个类使用，则该类具有最大的内聚性。内聚性高说明变量和方法相互关联形成一个逻辑整体。&lt;/p&gt;
&lt;p&gt;重构函数也会促使类职责的分离，看下面场景：&lt;/p&gt;
&lt;p&gt;将一个有许多变量的大函数拆分成小函数，如果拆出来的代码使用了其中4个变量，那么就会将这4个变量作为参数传入到小函数中，如果使用的变量越多，就意味着小函数的参数越多，这时可以讲这4个变量升级成实体变量，小函数就不需要参数了，可以直接使用这些变量。这样就使内聚性变低，因为类中有很多的变量只为少数的函数服务，这时就可以将这些变量和函数拆分出来，单独成类。&lt;/p&gt;
&lt;p&gt;开放闭合原则（OCP）&lt;/p&gt;
&lt;p&gt;依赖倒置原则（DIP）&lt;/p&gt;
&lt;h2 id="第十一章-系统"&gt;第十一章 系统&lt;/h2&gt;
&lt;p&gt;这一章主要讲的是怎样在较高的层级–系统层级来保持整洁。&lt;/p&gt;
&lt;p&gt;将系统构造和使用分开：软件系统应该将启始过程和启始过程之后的运行时逻辑分离开，在启动过程中构建应用对象，也会存在相互缠结的依赖关系。&lt;/p&gt;
&lt;p&gt;本章提到的一些技术概念，下面提供一些参考学习链接：&lt;/p&gt;
&lt;h2 id="工厂模式"&gt;工厂模式&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://blog.fwhyy.com/2009/11/design-patterns-notes-3-abstract-factory-pattern/"&gt;http://blog.fwhyy.com/2009/11/design-patterns-notes-3-abstract-factory-pattern/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;依赖注入（DI）&lt;/p&gt;
&lt;p&gt;控制反转（IOC）&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.cnblogs.com/leoo2sk/archive/2009/06/17/1504693.html"&gt;http://www.cnblogs.com/leoo2sk/archive/2009/06/17/1504693.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;扩容：不可能一开始就把系统做对，实现好当前客户的需求，然后重构，扩容来实现新的客户需求。&lt;/p&gt;
&lt;p&gt;软件系统与物理系统可以类比。他们的架构都可以递增式增长，只要我们持续将关注面恰当的切分。&lt;/p&gt;
&lt;p&gt;AOP&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.cnblogs.com/zhugenqiang/archive/2008/07/27/1252761.html"&gt;http://www.cnblogs.com/zhugenqiang/archive/2008/07/27/1252761.html&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="第十二章-迭进"&gt;第十二章 迭进&lt;/h2&gt;
&lt;p&gt;Kent Beck关于简单设计的四条规则&lt;/p&gt;
&lt;h3 id="1-运行所有测试"&gt;1 运行所有测试&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;不可测试的系统是不可验证的，不可验证就不应该部署；&lt;/li&gt;
&lt;li&gt;只要系统是可测试的，就会导向保持类的短小和单一的职责；&lt;/li&gt;
&lt;li&gt;紧耦合的代码是难以编写测试的，所以编写测试越多，就会导致使用依赖注入、面向接口编程等来使代码解耦。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-不可重复"&gt;2 不可重复&lt;/h3&gt;
&lt;h3 id="3-表达程序员的意图"&gt;3 表达程序员的意图&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;选好的变量名、函数名和类名；&lt;/li&gt;
&lt;li&gt;要记住，下一个阅读你代码的很可能就是你自己；&lt;/li&gt;
&lt;li&gt;添加适当的注释。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="4-尽可能减少类和方法的数量"&gt;4 尽可能减少类和方法的数量&lt;/h3&gt;
&lt;h3 id="重构"&gt;重构&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;上面的2-4点都可以通过重构的方法来达到；&lt;/li&gt;
&lt;li&gt;重构的目的：提升内聚、降低耦合、切分关注面（AOP）、模块化系统性关注面、缩小函数和类的尺寸、选取好的名称等；&lt;/li&gt;
&lt;li&gt;好的函数、好的类、好的系统是重构出来的，没有人能一开始就把事情做对；&lt;/li&gt;
&lt;li&gt;重构应该要及时，发现了坏味道要及时重构，当然这个需要测试来做保障。&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>《代码整洁之道》读书笔记(上)</title><link>https://fwhyy.com/2012/05/clear-code-reading-notes-one/</link><pubDate>Sun, 13 May 2012 00:00:00 +0800</pubDate><guid>https://fwhyy.com/2012/05/clear-code-reading-notes-one/</guid><description>&lt;p&gt;全书一共400多页，一共17章，第十三章讲并发，并且在附录A中有对并发的补充，第十四到十六章是一些Java代码的案例，第十七章相当于一个总结。本次写读书笔记主要涵盖前十二章的内容，由于篇幅分为上下两篇。&lt;/p&gt;
&lt;h2 id="第一章-整洁代码"&gt;第一章 整洁代码&lt;/h2&gt;
&lt;p&gt;代码的质量非常重要，糟糕的代码有可能会毁了一个公司。对于一个很注重代码质量的人来说呆在一个只关注交付而不关注代码质量的公司是很痛苦的。&lt;/p&gt;
&lt;p&gt;什么是整洁的代码，不同的人又不同的定义。我认为整洁的代码应该是符合所使用语言代码规范的；可复用的；便于维护的；简洁的。&lt;/p&gt;
&lt;p&gt;糟糕的代码想做太多的事情，意图混乱；整洁的代码的每个函数、类和模块都全神贯注一件事，不受周围的干扰。&lt;/p&gt;
&lt;p&gt;“破窗理论”：指的是窗户破损了的建筑让人觉得似乎无人照管，于是别人也不关心，任由窗户继续破损，最终自己也参与破坏活动，在墙上涂鸦，任垃圾堆积，一扇破损的窗户让该建筑走向了倾颓的道路。在程序中也是一样，一处出现了有坏味道的代码，有可能导致整个项目的代码都满目疮痍。所以到嗅到代码有坏味道的时候就要重构，我认为整洁的代码是重构出来的。&lt;/p&gt;
&lt;p&gt;美国“童子军军规”中有条说道“让一地比你来时更干净”，映射到我们编码中就是我们每日在提交代码的时候要确保代码时整洁的，否则就进行重构。好的代码是靠大家一点点积累出来的。问题发现的越早，修改的成本就越少。&lt;/p&gt;
&lt;h2 id="第二章-有意义的命名"&gt;第二章 有意义的命名&lt;/h2&gt;
&lt;p&gt;本章主要介绍在程序中命名的规范，命名随处可见，变量、函数、类、模块、命名空间等都需要有好的命名。一个名称如果能让阅读者一看就知道要表达的意思，就可以认为是一个好的名称。&lt;/p&gt;
&lt;p&gt;不要使用单个字母来做变量名，时间一长，自己都不清楚自己当初的命名是什么意思。小方法体，如循环中的计数器除外。&lt;/p&gt;
&lt;p&gt;不要使用有误导性的字母作为变量名，比如小写字母l和大写字母O，因为他们和数字的一和零很像，有的字体还比较好区分，但大多数字体很难分辨。&lt;/p&gt;
&lt;p&gt;“匈牙利命名法（HN）”：该命名法是一位叫 Charles Simonyi 的匈牙利程序员发明的，后来他在微软呆了几年，于是这种命名法就通过微软的各种产品和文档资料向世界传播开了。在当时那个时代编译器并不做类型检查，程序员需要使用HN来帮助自己记住类型。现在的一些高级静态编程语言具有更丰富的类型系统，编译器可以很好的做类型检查，所以使用NH纯属多余。使用NH的几个弊端：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;增加了名称的长度；&lt;/li&gt;
&lt;li&gt;使名称变得不可读，而在本章的2.5小节有强调要使用可以读得出来的名称；&lt;/li&gt;
&lt;li&gt;增加了修改名称的难度，修改了变量的类型，变量名就要随着修改，否则会造成误导。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本章中讲到对接口的命名不要使用“I”作为前缀，这点我持保留意见，可能因为我一直是从事的Net上的开发，DotNet的类库中的接口基本都是使用“I”作为前缀的，而且在《NET 设计规范》一书中也强调接口要使用“I”作为前缀。&lt;/p&gt;
&lt;p&gt;类名使用名词或是名词短语，而不应当使用动词。&lt;/p&gt;
&lt;p&gt;对于方法名做到每个概念一个词，应该保持一致，比如对于绑定数据的方法，不要有的地方用BindData，而另一些地方使用DataBind 。&lt;/p&gt;
&lt;p&gt;最后想说的是命名除了一些通用的法则外，对于一些规范性的问题还是要遵循所使用语言或平台规定或是约定俗成的惯例。比如方法名C#中推荐使用Passcal风格，而在Java中则是使用Camel风格。&lt;/p&gt;
&lt;h2 id="第三章-函数"&gt;第三章 函数&lt;/h2&gt;
&lt;p&gt;函数一直都被要求要短小，有不少书中都以行数来作为标准，比如一个行数在20行以内被称为小函数，或是要在5行以内才是小函数。以行数来要求似乎有些苛刻，有一些极端的情况，比如初始化一个Model，里面有几十个字段，这时这个初始化函数中就有几十行代码，而且是无法拆分的，所以我认为，函数只要是在做一件事情就可以了。通常来说如果函数只做一件事，自然就不会很长。我对函数长度的极限是横竖都不要超过一屏。&lt;/p&gt;
&lt;p&gt;函数应该只做一件事，判断函数时候只做一件事，看函数中是否很能够拆分，如果可以，就果断进行重构。&lt;/p&gt;
&lt;p&gt;当函数中有Swicth语句的时候，就不可避免的要做多件事情了，而且函数会随着Switch条件的增加会越来越长。因为函数中做了多件事情，违反了SRP原则，因为随着增加条件我们要去修改这个函数，违反了OCP原则。这个问题可以通过工厂模式来解决。&lt;/p&gt;
&lt;p&gt;函数的名称要使用描述性的名称，让人一看名称就知道该函数是做什么的。当函数只做一件事情的时候，取名就容易多了。还有比较重要的一点，风格要保持一致。&lt;/p&gt;
&lt;p&gt;函数的参数要尽可能的少，书中要求是最好不要超过3个参数，当超过的时候就要将参数提取为一个对象，但实际中很难做到，通常都是为了省事，但有N多参数的函数会给阅读者带来障碍，特别是当参数名还词不达意的时候。&lt;/p&gt;
&lt;p&gt;在一个函数中不要去调用职责之外的另外的函数，尤其是底层的函数，否则给高层调用带来风险。举个简单的例子：比如在用户登录的时候我们可能会有一个CheckPassword的方法来验证登录的用户名和密码，如果在CheckPassword函数中在验证成功后调用Session.Init()来对Session进行初始化，就会存在隐患。根据名称来看只是检查密码用，如果有人在非登录的情况下调用了该方法，会更改当前会话。&lt;/p&gt;
&lt;p&gt;使用异常代替错误返回码，如果使用错误返回码会要求立即处理错误，当在高层调用很多底层方法时，每个方法都要去根据错误返回码进行处理，会造成函数逻辑混乱，如果使用异常处理则只需要在catch中处理即可。&lt;/p&gt;
&lt;p&gt;远离重复，拒绝重复，方法有很多，抽象到基类或放到底层公共类库中。&lt;/p&gt;
&lt;p&gt;没有人能一次性就将函数写的很完美，好的函数是通过重构得到的。&lt;/p&gt;
&lt;h2 id="第四章-注释"&gt;第四章 注释&lt;/h2&gt;
&lt;p&gt;注释是一把双刃剑，好的注释能够给我们好的指导，不好的注释只会将我们误导。注释是弥补代码子表述不足的一种手段，就像设计模式是用来弥补语言不足一样。&lt;/p&gt;
&lt;p&gt;代码是我们获取信息的准确来源，注释随着项目人员的更替 反复的修改最终可能词不达意了，因为很多开发人员在整合代码，修改方法的时候并不是总是同步修改注释。&lt;/p&gt;
&lt;p&gt;有时候看到一个函数的代码写的很糟糕，逻辑很混乱，有开发人员可能想，给这个函数加上几行注释，这样有可能起到适得其反的作用，这时要做的是将函数整理干净。&lt;/p&gt;
&lt;p&gt;代码即注释，很多书和大师都这么讲，意思是我们要用代码本身来解释我们的意图，那就要求我们要控制好函数只做一件事，函数名和变量名要规范和可读。&lt;/p&gt;
&lt;p&gt;当然也不是所有的注释都没有用，像下面几种类型的注释是有必要的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;具有警示性的注释；&lt;/li&gt;
&lt;li&gt;描述一些负责业务场景；&lt;/li&gt;
&lt;li&gt;有些函数现在还是一个空壳，但在将来可能有用，有必要写。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当我们不得不写一些注释的时候，要确保言简意赅，能够很好的表达意思，不要造成误解，也不要写多余的废话。&lt;/p&gt;
&lt;p&gt;还有一种日志被称为日志式注释，一般出现在一个类的开始部分，记录每次修改时的时间、人员名称和修改内容。随着时间的推移这类注释会变得非常冗长。这类注释在一些项目中很普遍，而且有时会被严格要求写，但书中强调现在的源代码都会有源代码工具来进行管理，修改记录在源代码工具中有保存，这种日志式的注释应该全部删除。&lt;/p&gt;
&lt;p&gt;有的开发人员喜欢在注释中签上自己的名字，这种做法也没没有必要，因为我们有源码管理工具。&lt;/p&gt;
&lt;p&gt;项目代码中经常会出现被注释掉的代码，这对后面的维护人员会造成困扰，也会使代码变得混乱，这种代码同样可以删掉，因为我们有源码管理工具。&lt;/p&gt;
&lt;h2 id="第五章-格式"&gt;第五章 格式&lt;/h2&gt;
&lt;p&gt;代码并非可以工作就了事了，还要有良好的代码格式规范，开发人员在维护代码的时候经常会埋怨别人的代码写的很烂，如果不严格要求自己，以后就会成为别人眼中的“别人”了。&lt;/p&gt;
&lt;p&gt;代码格式规范，我认为首先要保证和当前使用的语言、平台保持一致，再就是整个团队中要保持一致。当公司项目的代码规范和使用语言、平台的规范有冲突时尽量说服团队和语言、平台保持一致，实在不行只能和项目组保持一致，像我现在所做的项目由于一些原因还在使用匈牙利命名法，虽然这让我很郁闷，但我还是和他们保持一致。如果是一个新项目则可以团队成员在一起来讨论制定代码规范。&lt;/p&gt;
&lt;p&gt;合理使用空白行，同一类的有关联的放在一起。&lt;/p&gt;
&lt;p&gt;变量申明尽可能靠近使用的地方。&lt;/p&gt;
&lt;p&gt;实体变量放在类的顶部，因为设计良好的类，实体变量会被大多数的方法使用。&lt;/p&gt;
&lt;p&gt;相关函数（一个函数调用了另一个函数）放在一起，也包括的重载的函数。&lt;/p&gt;
&lt;p&gt;一行代码的宽度：120字符之内，最好不要拖动横向滚动条。&lt;/p&gt;
&lt;p&gt;横向空格，比如一个表达式中的符号左边和右边与符号之间加空格，这个在VS中的代码格式化会自动帮我们做了。&lt;/p&gt;
&lt;p&gt;缩进，只有一行的也按缩进规则来。&lt;/p&gt;
&lt;h2 id="第六章-对象和数据结构"&gt;第六章 对象和数据结构&lt;/h2&gt;
&lt;p&gt;暂略&lt;/p&gt;</description></item></channel></rss>