味道与启发
1.前言(无关的碎碎念)
周末应该用来看书和学习,而不能用来加班。如果周末也用来工作,那什么时候才能学习呢?正如章老师所言,不学习会导致平时的效率低下,效率低下又导致了没有学习的时间,然后进入了恶性循环。如何才能打破恶性循环?一是抓紧周末的时间赶紧学习,只有周末加油积累,才能厚积薄发,在平时工作中进行输出。二是提高平时的工作效率。总之这两条真是说的简单做着难,幸好第一条还是能做的。
工作中遇到了很多的痛点和困难,很多时候不理解为什么原有的系统要设计成那样,有些地方明明可以做的更好,分离抽象的层次,把一些共性的实体抽象出一个基类,会不会有个更好的扩展性?那些书上经常强调的设计原则,有时候我很奇怪是否在工作中还被提倡甚至是否被作为一种可以量化的考评指标呢?
痛点还有中台。系统对接了好多中台,但是中台提供的功能并不能让业务端满意。一些本该封装起来的操作,并没有由中台进行封装,业务端感知了太多不必须的信息,是否又破坏了封装性呢?经常会怀疑是否为了中台而中台,对接了一些本来不适合业务的中台。
这周还收获了一些很有意义的书籍。包括一些英文原版书。比如 章老师推荐的《如何阅读一本书》,以及《敏捷软件开发——原则模式与实践》,开卷有益,打开书之后我会感慨,嗯,这就是我想要看到的,这就是我在追寻的问题的答案。
好了,先逼逼到这里吧~ 下面是正式的内容,主要来自《代码整洁之道》的第十七章 味道与启发 的读书笔记
2 注释
极限编程提倡尽量少的注释和文档。应该由尽量完善的单测补足注释和文档的作用。这一点我认为是很有道理的。首先为什么需要尽量少的注释和文档?因为注释和文档,都是代码的“影子”,代码的含义不仅要在代码中维护,也要在注释和文档中进行维护。当代码变更的时候,还要去更新注释和文档,这是额外的工作量。如果某个成员忘记更新注释和文档,那对于其他成员,尤其是项目的新成员来说,就是一个大坑。但是如果使用单测来补充注释和文档的作用,情况就会大不相同了。因为单测是必须随着代码更新的,每一次代码的更新和修改,都需要运行单测,使所有单测全部运行通过。所以单测总是最新的一份“文档”。另外,单测使用的是代码的方式,测试和调用开发代码,对于程序员来说,可能比文档中的信息更加具体和直接。
总体上说,注释应该解释的是代码的目的,而不应该关注代码的实现细节。所有跟细节相关的信息,应该由代码本身进行说明,也就是我们说的,代码应该是自解释的。如果觉得代码解释不清楚细节,那么就说明代码应该被重构了(很可能是代码中的命名不够合适)。
另外,注释也可以关注复杂的逻辑和算法,简单地说明一个复杂的算法是如何实现的。
这一部分可以参考《代码整洁之道》的第四章
1. 注释不应该有不恰当的信息
对于代码的变化,请让源码控制系统(如git)等去追踪,而不是将代码注释起来。
注释只应该描述有关代码和设计的技术性信息
2. 废弃的注释
不要让注释成为孤岛,遇到废弃的注释应该立即更新或者删除。
3.冗余的注释
注释应该描述代码没有提到的东西
4.糟糕的注释
写注释要字斟句酌,别闲扯,别画蛇添足,保持注释的简洁。
5.注释掉的代码
删除掉注释的代码
2.环境
1.需要多步才能实现的构建不是一个好的环境
2.需要多步才能做到的测试不是好的测试
对于maven项目,只需要运行mvn test 命令,就可以执行全部的单元测试。
3.函数
1.过多的参数不是好事
函数的参数应该尽量少
2.输出参数
输出参数常常违反直觉
3.标识参数
布尔值参数大声宣布了函数做了不止一件事,应该被消灭掉。
4.死函数
永远不会被调用的方法应该被丢弃。别害怕删除
4.一般性问题
1.一个源文件中存在多种语言不是好事
这个实在是令人难受
2.明显的行为未被实现
违背了“最小惊异原则”,相当于给自己和同事挖坑,而且看到之后会让人费解。
如果明显的行为未被实现,读者和用户就不能再依靠他们对函数名称的直觉,他们不再信任原作者,不得不阅读代码细节。
3.不正确的边界行为
别依赖直觉,追索每种边界条件并编写测试。
4.忽视安全
这种情况在我厂要么被扫出漏洞,要么要修复安全工单。
5.重复
这是《代码整洁之道》中最重要的原则之一。这个原则也被称之为DRY原则(Dont Repeat Yourself)
每看到重复代码,都代表遗漏了抽象。将重复代码放入抽象中,其他程序员就可以用到你创建的抽象设施。编码会越来越快,错误也会越来越少,因为你提升了抽象的层级。
不断重复出现、检测同一组条件的switch/case或者if/else链,可以用多态来代替。
注:模板模式,工厂模式 + 多态 都可以去除代码中不必要的重复。如果看到代码中又重复的检测同一组条件的switch/case 或者 if/else 说明代码中有重复了,已经有一些代码位于错误的抽象层级上。
注2:设计模式的根本目的,应该是去解决那些违背了设计原则的问题代码。设计模式是药,问题代码是病,代码的腐坏味道就是病的症状。对于一个可能进行重构的程序员,必须像医生一样,对症状敏感,快速地从症状找出病因,并对症下药,用设计模式甚至多种设计模式结合起来去纠正问题。但是另一方面,我们必须知道哪些设计模式可以解决哪些问题,否则就是乱开药。对于没病的代码,不要给他们开药。
更隐蔽的重复的形态是采用类似算法但是具体代码不同的模块,这也是一种重复,可以使用模板方法模式或者策略模式进行修正。
6.在错误的抽象层级上的代码
孤立抽象是软件开发者最难做到的事情之一,而且一旦做错了也没有快捷的修复手段。
创建分离较高层级的一般性概念与较低层级的细节概念的抽象模型是很重要的。有时候我们创建抽象类来容纳较高层级的概念,用派生类容纳较低层级。这种情况下,与细节实现有关的函数、变量和常量等,不可以出现在抽象类中,抽象类应该对此一无所知。
7.基类不应该依赖于派生类
派生类依赖于基类,基类不能依赖于派生类。如果看到基类中提到了派生类的相关信息,那就有问题。基类应该对派生类一无所知。
通常会将基类和派生类放到不同的jar包中,这样,修改一组接口的实现或者是基类的实现的时候,不需要重新部署基组件。意味着修改产生的影响大大降低了,维护系统也变的更加简单。
8.信息过多不是好事
应该学会限制类或者模块中暴露接口的数量,类中的方法越少越好,函数知道的变量越少越好,类拥有的实体变量越少越好。
通过限制信息来限制耦合度。
9.死代码应该被体面埋葬
10.垂直分隔
变量和函数应该在靠近被使用的地方定义
11.前后不一致
同一个系统中的编程风格需要保持一致
12.混淆视听
13.人为耦合
人为耦合是指两个没有直接目的之间的模块的耦合。其根源是将变量、常量或者函数不恰当地放在临时方便的位置。
程序员需要花点时间研究应该在什么地方声明函数、常量和变量。
14.特性依恋
类的方法只应该对其所属类中的变量和函数感兴趣,不该垂青其他类中的变量和函数。当方法通过某个其他对象的访问起和修改器来操作该对象的内部数据的时候,则它就是依恋于该对象所属类的范围。
15.选择算子函数
使用多个函数,通常优于向单个函数传递某些代码来选择函数行为
16.晦涩的意图
17.位置错误的权责
软件开发者做出的最重要的决定之一就是在哪里放置代码。应该遵循最小惊异原则,代码应该放在读者自然而然期待它出现的地方
18.不恰当的静态方法
通常应该倾向于选用非静态方法。如果有疑问,就是用非静态函数。如果的确需要静态函数,确保没有机会打算让它有多态的行为
19.使用解释性变量
解释性变量多多益善,只要把计算过程打散成一系列良好命名的中间变量,不透明的模块就会突然变得透明。
20.函数的名称应该表达其行为
21.理解算法
22.把逻辑依赖改成物理依赖
23.使用多态来替代if/else 或者switch/case
24.遵循标准约定
25.用命名常量替代魔术数字
26.准确
27.结构甚于约定
28.封装条件
29.避免否定性条件
30.函数应该只做一件事
31.不要掩蔽时序耦合
32.别随意
33.封装边界条件
34.函数应该只在一个抽象层级上
35.在较高层级放置可配置数据
36.避免传递浏览
5.Java
1.通过使用通配符避免过长的导入清单
2.不要继承常量
不要通过继承来访问常量,而是要将常量导入
3.有时候枚举比常量更好
6.名称
1.采用描述性名称
2.名称应该与抽象层级相符
3.尽可能使用标准命名法。
可以使用程序员的黑话