序
1、勒布朗法则——稍后等于用不。
2、不应该羞于告知自己的想法。
我们可能把混乱的代码归咎于专注于项目进度的产品经理、苛求的用户,但是其实是我们太不专业了。
对于不合理的要求我们应该提出来,多数经理想要知道实情,即便他们看起来不喜欢实情。多数经理想要好代码,即便他们总是痴迷于进度。他们会奋力卫护进度和需求,那是他们应该干的。因此我们也应当以同样的热情卫护代码。
一、有意义的命名
3、代码块要尽量小
实际上,从有软件起人们就在反复强调这一点。越小越好。
4、命名要尽量名副其实
- 变量、函数或类的名称应该已经答复了所有的大问题。
- 不要害怕名称过长。
- 选个好名字要花时间,但省下来的时间比花掉的多。
- 一旦有更好的名称,就换掉旧的。
5、命名要避免误导
- 应当尽量避免使用与本意相悖的词。
- 别用accountList来指称一组账号,除非他真的是List类型。List一词对程序员有特殊意义。如果包纳账号容器并非真是个List,除非他真的是List类型。即使容器就是个List,最好也别再名称中写出容器名。甚至直接用accounts都会好一些。(PHP应该没这些要求吧)
- 相似的名称就别用了
6、废话就是冗余
命名中尽量避免废话。
7、命名要尽量使用读得出来的名称
人类长于记忆和使用单词。
8、定义常量!!!!
方便查找和修改。
9、避免思维映射
循环计数器自然有可能被命名为i或j或k(但千万别用字母I),这是因为传统上惯用但字母名称做循环计数器。
其他情况下最好就别用了。
10、类名应该是名词,如Customer,而不应该是动词。
11、方法名应当是动词或动词短语。
12、每个概念对应一个词
controller这种名字类似的
二、函数
13、短小
- 函数的第一规则是要短小
- 函数的第二条规则还要更短小
14、只做一件事
函数应该做一件事。做好这件事。只做这一件事。
要判断韩式是否不止做了一件事,有一个方法,就是看是否能再拆出一个函数,改函数不仅是单纯的重新诠释其实现。
15、每个函数一个抽象层级
抽象层级是个什么概念呢?
如:
getHtml()算是较高抽象层
String pagePathName = PathParser.render(pagePath) 算是中间抽象层
.append("\n") 算是相当低的抽象层
16、 switch 语句
- 确保每个switch都埋藏在较低的抽象层级,而且永远不重复
- 单一权责原则
简单说,就是一个类负责一个职责。 - 开放闭合原则
软件实体(类、模块、函数等等)应该可以扩展,但是不可修改。 - 尽可能抽象
示例
public Money calculatePay(Employee e)
throws InvalidEmployeeType {
switch (e.type) {
case COMMISSIONED:
return calculateCommissionedPay(e);
case HOURLY:
return calculateHourlyPay(e);
case SALARIEED:
return calculateSalariedPay(e);
default:
throw new InvalidEmployeeType(e.type);
}
}
上述代码可能到处都有类似结构的函数。可能是isPayday(Employee e, Date date)或deliverPay(Employee e, Date date);
解决方案就是:**将switch语句埋到抽象工厂地下,不让任何人看到。该工厂使用switch语句为Employee的派生物创建适当的实体,而不同的函数,如calculatePay、isPayday和deliverPay等,则藉由Employee接口多态的接受派遣。
17、 函数参数
- 参数尽量要少
最理想的参数数量是零(零参数函数),其次是一(单参数函数),再次是二(双参数函数),应尽量避免三(三参数函数)。有足够特殊的理由才能用三个以上参数(多参数函数)。——所以无论如何也不要这么做。
如果函数看起来需要两个、三个或三个以上参数,就说明其中一些参数应该封装为类了。
18、无副作用
什么是副作用?
副作用是函数只承诺做一件事情,但还是做其他被藏起来的实情。
如果我们忽略了,就做了可能我们并不期望的事情。
19、输出参数
static void appendFooter(StringBuilder sb) {
sb.append("Here you go!");
}
我们应该尽量避免使用appendFooter(s)这样的用法。应尽量使用report.appendFooter()方法。
因为我们会浪费检查函数声明的时间。
面向对象语言中对输出参数大部分的需求已经消失了,因为this也有输出函数的意味在内。
20、使用异常代替返回错误码
原因:在深层次的嵌套结构中,当返回错误码的时候,要求立即处理错误。
使用异常的话,错误处理代码就能从主路径代码中分离出来,得到简化。
21、抽离Try/Catch代码块
Try/Catch代码块丑陋不堪。他们搞乱了代码结构,把错误代码处理与正常流程混为一谈。最好把try 和 catch 代码块的主体部分抽离出来,另外形成函数。
22、错误处理就是一件事
函数应该只做一件事。错误处理就是一件事。
23、Error.java 依赖磁铁
返回错误码通常暗示某处有个类或是枚举,定义所有错误码。
如果使用它,Error枚举修改时,所有这些其他的类都需要重新编译和部署。这对Error类造成了负面压力 ===》程序员就使用旧的错误码,不愿意增加新的错误代码。
使用异常替代错误码,新异常就可以从异常类派生出来。无需重新编译或重新部署。
24、别重复自己!!!
不要写重复代码
重复代码会增加忽略错误的可能性
三、注释
注释说明我们代码写的并不好。
但是我们有时候又必须要写注释。
但是程序员又不会及时维护注释,所以随着时间流逝,注释离所描述的代码越来越远。
唯一真正好的注释是你想办法不去写的注释
用整理代码的决心替代创造废话的冲动。
25、警示注释
26、TODO注释
27、括号后面的注释
//while //try 类似的
这对于深层嵌套很有意义
但是我们为什么不写更小的、封装的函数呢?
28、注释掉的代码
直接把代码注释掉是讨厌的做法。别这么干!!!(我做了 55555~)
可能的原因是:1⃣️不敢删除2⃣️用于提示3⃣️……
但是这些都不可否认混乱了代码
现在的源代码控制系统已经保证代码丢不了了,删除即可
四、格式
先明确一点,代码格式很重要,代码格式不可忽略,必须严肃对待。(之前的我…………)
29、概念间垂直方向上的区隔
在封包声明、导入声明、每个函数等等之间。有空白行隔开。这条极其简单的规则非常有利于代码阅读。
30、垂直方向上的靠近
有紧密关系的代码应该靠近,方便阅读理解。
31、垂直距离
- 关系密切的概念应该互相靠近,有助于理解系统做什么。而不是把时间和精力找到和记住代码碎片在哪里。(除非有很好的理由放到不同的文件中)
- 变量声明 应该尽可能靠近其使用的位置。
- 实体变量 实体变量应该在类的顶部声明。
- 相关函数 若某个函数调用另外一个,就应该把他们放在一起,而且调用者应该尽可能发在被调者上面。
- 概念相关 概念相关的代码应该放到一起。相关性越强,彼此之间的距离就该越短。
32、横向格式
应该尽量保持代码行短小。
可以遵循一个原则:无需拖动滚动条到右边的原则。
五、对象和数据结构
对象暴露行为,隐藏数据。便于添加新对象类型而无需修改既有行为。同时也难以在既有对象中添加新行为。数据结构暴露数据,没有明显的行为,便于向既有数据结构添加新行为,同时也难以向既有函数添加新数据结构。
六、错误处理
错误处理很重要,但如果他搞乱了代码逻辑,就是错误的做法
33、使用异常而非返回码
如果使用了错误码,调用者必须在调用之后即刻检查错误。不幸的是,这个步骤很容易被遗忘。
34、先写Try-Catch-Finally语句
异常的妙处之一是,他们在程序中定义了一个范围
执行try-catch-finally语句中try部分的代码时,是在表明可随时取消执行,并在catch语句中继续
在编写可能抛出异常的代码时,最好先写出try-catch-finally语句。这能帮你定义代码的用户应该期待什么。无论try代码块中执行的代码出什么错都一样
35、使用不可控异常
- 可控异常的代价是违反了开放/闭合原则 。
- 如果你在方法中抛出可控异常而catch语句在三个层级之上,你就得在catch语句和抛出异常处之间的每个方法签名中声明该异常。
- 以此类推,最终得到的就是一个从软件最底端贯穿到最高端的修改链。
- 可控异常以这种方式破坏封装简直是一种耻辱
- 对于一般的应用开发,可控异常的依赖成本要高于收益。
36、给出异常发生的环境说明
有助于判断错误的来源和处所。