title: “ 《代码整洁之道》笔记\t\t”
tags:

  • 笔记
    url: 1460.html
    id: 1460
    categories:
  • C/C++
    date: 2018-12-30 22:11:29

本书从命名、函数声明、注释、格式、对象及数据格式定义、错误处理、单元测试等多方面说了代码整洁的重要性和意义。由于之前读过google编程规范Google C++编程风格,对比一下,其实是相辅相成的,这本书详细讲解了“乱”的危害,而编程规范通过明确的规则要求写出代码的样子,而这个样子这个规则就是为了避免乱,也算是更详细的介绍了规则背后的原因和违反的后果。 书主要针对java的。。。有些无法在c++中用的就没写(应该有很多能用的,我技术不到理解不到位)

命名

名字要有意义、相同的概念用一个词语不要过多同义词。 分清领域,尽量用程序员懂得词语,除非必要时刻再用业务领域名词。 让命名表示出应有的意思,而不是通过注释去不从描述。少用双关词语导致误解。

函数

函数要短小,最好20航以内。宽度也应有上线,150以内。 函数应该做一件事、做好这件事,只做一件事。同时要避免副作用(往往因为做的事情超出了范围) 函数内容应处于一个抽象层级。对于switch如果出现同类型应只出现一次。 名称应具有描述性。参数越少越好,如非必要情况不应有三个及三个以上的参数。标志性参数因尽量避免,如bool类型,可以用连个函数分别实现。 使用异常代替返回错误码,当catch有固定逻辑时或复杂时应将其作为单独函数,以避免原始函数过长不好理解,try应出现在函数第一行。

注释

注释不能美化糟糕的代码,最好的最理想的是没有注释的代码,应用变量、函数、类名称来充分地表示意思。同时注释更新不及时可能误导(代码更新注释未改动)。 法律信息、提供信息、阐释、警示、todo、公共api的接口说明等可以出现,但要保证其必要性。 多余注释:喃喃自语、多余注释、误导性注释、日志式、循规式注释、废话、位置标记、反括号后的注释(说明函数太长了才需要确定是谁的反括号)、归属、注释掉的代码(都可以用源代码管理系统恢复、记住谁添加的等信息不用写到代码里)、html风格注释……

格式

看编程规范

对象和数据结构

固定顺序的调用,可以修改接口,通过每一个调用的参数必须是上一个对象或者每一步调用返回下一步的对象等方式实现在调用过程必须符合顺序,而不是要求使用者自己掌握顺序。

错误处理

异常应描述环境信息,对重复式处理方法的多种异常应进一步打包后抛出,而不是多个相同的catch。 对异常的处理若有固定流程也应打包。 不要返回、传递null 一切以函数短小、易懂为准

边界

对于第三方代码,应有自己的一层边界,对接口进行打包,可以对不存在代码情况下进行开发,也可避免第三方代码接口变化导致的错误。

单元测试

TDD三定律:在编写不能通过的单元测试前,不写生产代码;只编写刚好无法通过的测试,不能编译也算不通过;只编写刚好足以通过当前失败测试的生产代码。 F.I.R.S.T.快速fast、独立independent、可重复/repeatable、自足验证self0validating、及时timely

类也应该短小,单一职责原则(权责、SRP)、内聚、

系统

将构造与使用分开,main中也应分离,main负责构造? 工厂实现构造过程 依赖注入dependency injection,DI。控制反转inversion of control,IoC

迭进

并发编程

并发防御原则:但以权责、限制数据作用域、使用数据副本、线程尽可能独立 执行模型:生产者-消费者、读者-作者、宴席哲学家。 线程:互斥、线程饥饿、死锁、活锁(这个刚知道。。。) 互斥:每一时刻只有一个线程能访问共享数据或共享资源 线程饥饿:一个或一组线程在很长时间内或永久被禁止。如,总是让执行的快的线程先运行,假如执行快的一直有,那执行时间长的会挨饿。 死锁:两个或多个线程互相等待执行结束。每个线程都拥有其他线程需要的资源,得不到其他线程拥有的资源就无法终止 活锁:执行次序一致的线程,每个都想要起步,但发现其他线程已经在路上,由于竞步原因,线程会持续尝试起步,但很长时间无法如愿甚至永远无法启动。 多线程时,很难编写正确的关闭代码,经常与死锁有关。要尽早考虑平稳关闭的问题。 测试多线程:将伪失败看做可能得线程问题,先使非线程代码正常运行、编写可拔插的线程代码、编写可调整的线程代码、运行于多处理器数量的线程、在不同平台运行、装置试错代码、硬编码、自动化。。。。。。。看来写个多线程的测试好复杂。

逐步改进

这是一个例子

JUnit内幕

java的框架,跳过了没看,希望没错过什么

重构SerialDate

也是一个例子,借着重构讲解前面提到的。

味道与启发

总结性的说了一些要求、注意事项。这块内容转自:https://blog.csdn.net/jiaozhenxinaichenme/article/details/53535000 17.1注释 a.不恰当的注释信息 让注释传达本该更好地在源代码控制系统,问题追踪系统或任何其他记录系统中保存的信息,是不恰当的。例如,修改历史记录只会用大量过时而无趣的文本搞乱源代码文件。注释只应该描述有关代码和设计的技术性信息。 b.废弃的注释 过时,无关或不正确的注释就是废弃的注释。 c.冗余注释 如果注释描述的是某种充分自我描述了的东西,那么注释就是多余的。注释应该谈及代码自身没有提到的东西。 d.注释要写就要写最好的注释,别画蛇添足 e.注释掉的代码,看到注释掉的代码就删。 17.2 环境 a.需要多步才能实现的构建 构建系统应该是单步的小操作。不应该从源代码控制系统中一小点一小点签出代码。不应该需要一系列神秘指令或环境依赖脚本来构建单个元素。不应该四出寻找额外的小jar,xml文件和其他系统所需的杂物。你应该能够用单个命令签出系统,并单个指令构建它。 b.需要多步才能做到测试 你应该能够用单个指令就可以运行全部单元测试。能够运行全部测试是如此基础和重要,应该快速,轻易和直接了当地做到。 17.3 函数 a.函数的参数应该少。 b.输出参数违反直觉。如果非要修改,那就修改函数所在对象好了。 c.标示参数,布尔值参数宣告函数做了不止一件事,应该消灭掉,即函数只做一件事。 d.永不被调用的方法应该丢弃。 17.4 一般性问题 a.如果代码需要有多种语言,那么应该尽力减少源文件中额外语言的数量和范围。 b.明显的行为未被实现,如果明显的行为未被实现,读者和用户就不能再依靠他们对函数名称的直觉。他们不再信任原作者,不得不阅读代码细节。 c.不正确的边界行为,追索每种边界条件,编写测试。 d.忽视安全 e.重复,尽可能找到并消除重复。 f.在错误的抽象层级上的代码,创建分离较高层级一般性概念与较低层级细节概念的抽象模型,这很重要。 g.基类不可多度依赖于派生类,基类应该对派生类一无所知。 h.信息过多,设计良好的模块有着非常小的接口,让你事半功倍。设计良好的接口并不提供许多需要依靠的函数,所以耦合度比较低。 优秀的软件开发人员学会限制类或模块中暴露的接口数量。类中的方法越少越好。函数知道的变量越少越好。类拥有的实体变量越少越好。隐藏你的数据。隐藏你的工具函数。隐藏你的常量和你的临时变量。不要创建拥有大量方法或大量实体变量的类。不要为子类创建大量受保护变量和函数。尽力保持接口紧凑。通过限制信息来控制耦合度。 i.死代码一定要删除。 j.垂直分隔,变量和函数应该在靠近被使用的地方定义。私有函数应该刚好在其首次被使用的位置下面定义。 k.前后不一致,从一而终即一旦选中,就小心持续遵循。如果在特定函数中用了response的变量来持有HttpServletResponse对象,则在其他用到HttpServletResponse对象的函数中也用同样的变量名。这样会让你的代码更易阅读。 l.混淆视听,没用的变量或者函数直接删掉,以免造成不必要的干扰。 m.人为耦合,不互相依赖的东西不该耦合。一般来说,人为耦合是指两个没有直接目的之间的模块的耦合。其根源是将变量,常量或函数不恰当地放在临时方便的位置。 n.特性依恋,类的方法只应对其所属类中的变量和函数感兴趣,不该垂青其他类中的变量和函数。当方法通过某个其他对象的访问器和修改器来操作该对象内部数据,则它就依恋于该对象所属类的范围。 o.选择算子参数,尽量不要把boolean作为函数参数 p.晦涩意图,代码要尽可能具有表达力。 q.位置错误的权责,我们可以采用最小惊异原则来帮助我们即代码应该放在读者自然而然期待它所在的地方。 r.不恰当的静态方法,对于没有机会打算让它有多态行为的函数可以作为动态函数,否则可选用静态函数。 s.使用解释性变量,命名要有意义。 t.函数名称应该表达其行为 u.理解算法 v.把逻辑依赖改为物理依赖: 1.逻辑依赖:原始数据和业务逻辑之间的依赖关系。例如: 定义的常量和业务逻辑存在着依赖关系或者说被捆绑在了一起,即当业务逻辑发生变化或者存在其他可能性的时候且定义的常量无法满足导致常量需要修改。 2.物理依赖:函数方法和业务逻辑之间的依赖关系。例如: 为了解决逻辑依赖的问题,我们可以通过构造一个方法将数据和业务隔离。 w.用多态替代if/else 或 switch/case x.遵循标准约定 y.用命名常量替代魔术数,魔术数不仅仅指的是数字,也泛指任何不能自我描述的符号。 z.准确,在可以用List的时候,非要把变量声明为ArrayList就过分拘束了。在代码中做决定时,确认自己足够准确。明确自己为何要这么做,如果遇到异常情况如何处理。别懒得理会决定的准确性。代码中的含糊和不准确性要么是意见不同的结果,要么源于懒惰。无论原因是什么,都要消除。 z+1.结构甚于约定,坚守结构甚于约定的设计决策。 z+2.封装条件,如果没有if或while语句的上下文,布尔逻辑就难以理解。应该把解释了条件意图的函数抽离出来。 例如: if(shouldBeDeleted(timer)) 要好于 if(timer.hasExpired() && !timer.isRecurrent()) z+3.避免否定性条件,尽可能将条件表示为肯定形式。 z+4.函数只该做一件事 z+5.掩蔽时序耦合,排列函数参数,好让它们被调用的次序显而易见。 z+6.别随意,构建代码需要理由,而且理由应于代码结构相契合。 z+7.封装边界条件,把处理边界条件的代码集中到一处,不要散落于代码中。 z+8.函数应该只在一个抽象层级上,函数中的语句应该在同意抽象级上,该层级应该是函数名所示操作的下一层。 z+9.在较高层级放置可配置数据,如果你有个已知并在较高抽象层级的默认常量或配置值,不要将它埋藏到较低层级的函数中。把它作为较高层级函数调用较低层级函数时的一个参数。位于较高层级的配置性常量易于修改。它们向下贯穿应用程序。应用程序的较低层级并不拥有这些常量的值。 z+10.避免传递浏览,编写害羞代码。 z+11.不要继承常量