前言
最近读了《编程匠艺》这本书,它是由美国作者 Pete Goodliffe 编写的,它不仅是一本学习指南,更是一本激发编程激情的读物,展示了一种追求卓越的编程态度。
在我看来,它带来不仅仅是技术上的提升,更好地掌握编程技巧、提高自己的开发效率和质量,更重要的是对编程的思考和理解。
下面是读书笔记+个人理解,书中一共分24个章节,当前是 01章- 06章。
一、防御性编程
1.1 优秀的代码
有三种代码
- 能用的代码:面对常规输入没问题,但是遇到意外的输入可能会崩溃
- 正确的代码:这种代码绝不会崩溃,但是输入集可能会很庞大,并且难以测试,有时也会很难理解
- 优秀的代码:这种代码首先会是正确的,然后也是健壮、高效的
想到在《java解惑》这本书中第一章就是判断整数是否奇数的代码,修改后就从能用变为优秀
// 碰到负整数会有问题
public static boolean isOdd(int i) {
return i%2==1;
}
// 修改后
public static boolean isOdd(int i) {
return i%2!=0;
}
1.2 防御性编程技巧
自己一个人编写优秀代码似乎很简单,但是跟和他人合作或者接手旧代码,这种情况下依然写出优秀代码就很难,这时就需要用到防御性编程。
- 不要做设想,就像上面判断是否奇数,不能设想程序只会输入正整数,一个人写代码短时可能会记住,但时间一长或者在团队合作中其他人调用你的代码,不会考虑这些
- 使用好的编码风格和设计
- 不能仓促编写代码
- 不要相信任何人
- 编码目标是清晰,而不是简洁,不能只考虑一行代码解决,更要便于理解,无论自己还是他人
- 各司其职,在 java开发中,bean的属性要设为为私有,还有我认为类的职责要和名字搭配,不要加入无关的方法
- 在编译时打开所有警告开关,这针对 C/C++
- 使用静态分析工具
- 使用安全的数据结构
- 检查所有返回值
- 谨慎处理内存(或其它宝贵资源) 针对 C/C++
- 声明位置处初始化变量,针对 C/C++,Java声明处有默认值
- 尽可能推迟一些声明变量
- 使用标准语言工具,针对 C/C++
- 使用好的诊断日志工具,Java有log4j
- 谨慎使用强制转换
- 提供默认行为,switch default和if else 仔细考虑各种情况
- 考虑数字溢出
- 将可以设置为常量的变量都设置为常量
1.3 约束
C、C++通过断言assert来约束代码,Java也可以通过assert语句来约束代码
二、代码样式
2.1 面向人类
我们编写的代码面向的读者有三类:
- 自己:自己字写的难看,自己都很难阅读,更何况代码
- 机器:机器不管样式,只需要通过编译,就不会因为样式报错
- 其他人:这类人是最重要的,个人的能力总是有限的,人需要合作完成任务
所以代码样式是面向人类的,机器不管这些。优秀的程序员样式肯定和新手不一样,新手编写的代码样式总是混乱的,看看自己当年写的代码。
2.2 优秀样式
优秀样式有如下特征:
- 一致:缩进空格数要一致,大小括号的位置也需要一致等等
- 传统:使用业内流行的一种规范
- 简洁:简单,他人易学习,可接受
优秀的样式不是一成不变的,它一定是团队中大多数人所接受的
三、代码命名
3.1 命名 Why What How
-
为什么好的命名很重要
有名称的就是存在的,好的名称便于理解和记忆。在编程过程中,命名糟糕的实体不仅不方便管理,而且会产生误导,出差错。一个对象的名称应该清晰地描述这个对象
-
命名哪些
变量,函数,类,命名空间,包,宏,源文件等。这几样是编程过程中最常见的
-
怎样命名
见下文
3.2 优秀的命名
-
技术性正确,编译器不会报错
-
重点在于清晰而非简洁,无歧义
-
符合语言习惯,前后一致,统一
-
利用上下文,如类Tree有个方法countApplesInTree, 可以修改为Tree:countApples
3.3 变量命名
-
名词:通常表示某个物理对象,有些不能与现实对象对应的变量也能用名词,如用过的时间 elapsed_time
-
动词:如果变量不能用名词表示,那么它一般就是一个名词化的动词,如计数 count
-
数字变量的名称描述了对其值的解释,如设备长度 widget_length
-
逻辑变量的名称是个条件语句形式名称,如 is_red
-
变量和类型要区分开来,所以变量的首字母一般是小写,而类型是大写
3.4 函数命名
- 函数是一个动作,命名至少包含一个动词
- 从使用者的角度来为函数命名
- 可以使用下划线或者驼峰法来命名
3.5 类型命名
-
类型可以描述一些具有状态的数据对象,这种情况下,名称可能是名词
-
类型也可能是实现接口回调函数的类,这种,名称可能是动词
四、代码自文档化
4.1 为什么不愿编写文档
- 大多数程序员对文字工作都会感到头大,对编写大量注释都会感到头疼,并且编写文档十分耗时,阅读也是如此,所以更愿意编写程序
- 所有独立的文档都会随着代码的更改而更新,在大型项目中,这是种十分可怕的工作
- 大量的文档是很难管理的,必须和代码一样进行版本控制
- 外部文档和程序很难链接,很容易错过重要信息
4.2 自文档化的代码
- 自文档化的代码是优美的,可读性很强的代码,它本身就易于理解,不需要外部文档。
- 理想情况下的自文档化代码是不需要注释的,那会影响阅读,但实际很难做到,所以需要良好的注释
4.3 技巧
- 好的样式
- 在if/else中结构保持一致(例如,if后面总是处理正确情况,else后面处理错误情况),并且保持统一,不能后面就情况相反
- 避免过多嵌套,谨慎使用递归
- 谨慎优化代码
- 分解为原子函数
- 一个函数,只执行一种操作,选择一个毫无歧义说明这种操作的名称,一个好的名称就不需要编写额外的文档
- 减少任何出人意料的副作用
- 保持简短,短小的函数易于理解
- 选择合适类型
- 永远不需要修改的值,可以定义为常量
- 如果变量描述的值不包含负值,那么应该选用无符号类型(如果语言支持)
- 使用枚举来描述一组相关的值
- 选择合适的类型。c/c++将值的大小放在size_t变量中, 指针的运算结果放入ptrdiff_t变量
- 命名常量
-
if (count == 76)
中76的含义到底是什么,将76修改为常量 banaba_per_cake更好理解 - 上述像 76 这种被称为Magic Number,有些代码检测工具会检测到
-
- 强调重要的代码
- 在类中一定要按顺序进行声明。公共信息要放在首位,私有的实现信息放在最后
- 隐藏所有不重要的信息
- 不要隐藏重要的代码
- 限制嵌套条件语句数量
- 分组相关信息
- 所有相关的信息应该放在同一个地方
- java 中有package提供分组
- c/c++有命名空间
- 提供文件头
- 在文件的顶部提供一个注释块,用于描述文件作用,便于维护者理解
- 大多数公司或者说很多开源软件都出于法律方面考虑,都会在每个源文件加上版权或者开源声明
- 恰当处理错误
- 恰当的位置处理错误。如磁盘IO异常,就应该在访问磁盘的代码中处理该异常。该异常还会引起文件无法访问异常,那么就不能在这里处理IO错误
- 不要在最终的用户界面代码部分处理磁盘IO错误
- 编写有意义注释
- 只有你在无法以任何其它方式来提高代码清晰度的下,再添加注释
- 提高自身水平
- 写作技巧可通过 读越多的书进行提高。用批判的眼光来审视其他人的文章,可以学会分辨好坏
- 编写代码也一样,读各种优秀的代码(各个开源库),也会提高个人水平
五、代码注释
5.1 作用
- 注释可将优秀的代码和糟糕的代码区分开来,将粗糙复杂艰涩难懂的逻辑与清晰友好的算法区分开,但也不能过分夸大注释的作用。
- 注释可以锦上添花,但不能雪中送炭,不能够让酸代码变为甜的
5.2 是什么
- 从语法角度讲是编译器忽略不计的源代码块,从语义讲是对所处代码的解释
- 注释的目标读者是人,提高注释质量就必须满足人在阅读代码时所真正需要的
- 作为负责的程序员,有义务写好注释
5.3 该写什么
-
解释为什么,而不是怎么样
-
不要在注释中重复代码
-
当编写密密麻麻的注释来解释代码时,应该优化代码,而不是写注释
-
记录意想不到的内容,例如一个操作系统问题
-
写的文字必须是真实,有价值,清晰明了,容易理解的
5.4 不该写什么
-
过去的事,不要写过去版本的代码,现在都有版本控制系统,能够查找到过去的代码和注释
-
不想要的代码,同样有代码版本控制系统,
-
ASCII艺术,任何突出代码部分的样式都是不可取的,例子如下
aBadExample(n, foo(wibble)); // ^^^ // My favorite function
-
代码块结尾,用注释来标记控制块的结尾是不可取的,现代编辑器都有代码块折叠功能,很容易区分
// end if (a < 1)
5.4 使用注释
- 在注释中加 TODO 可以标记未完成的工作
- 使用注释注意要删除已经过时的注释,当修改代码时,维护代码周围的所有注释
- 当查看旧代码库时,最好不要删除找到的任何空洞无物的注释
六、代码错误处理
6.1 错误原因
产生错误的原因有很多种,不过大体可分为下面3种:文章来源:https://www.toymoban.com/news/detail-585563.html
- 用户错误,愚蠢的用户粗暴地对待了你心爱的程序,他或许提供了错误的输入,或进行了荒谬的操作
- 程序员错误,用户按钮都正确,但是程序还是出问题了,是由于编写的代码存在bug引起的,
- 意外情况,用户输入ok,程序员也没犯错,但是碰到意外情况,如网络断开,硬盘磁盘满了
6.2 错误管理报告
错误报告就是向客户端传播错误信息文章来源地址https://www.toymoban.com/news/detail-585563.html
- 不报告,直接终止程序,这不是个好方法,但是简单。
- 返回值判断,通过对函数返回值返回的状态码判断,是否发生错误
- 异常,java使用异常来管理错误
- 信号,是硬件中断的软件等价物,操作系统为每个信号提供合理的错误处理程序,c可以使用信号处理错误。
6.3 错误处理
- 通过日志来记录错误
- 程序需要在没有什么可做的情况下向用户报告错误,用户不能时时刻刻被错误轰炸,遇到可以恢复的情况,不要报告。当然有些只有用户能处理的错误,需要立即报告给他
- 有时错误并不能在代码端解决,或者你并不知道怎么解决,那么你需要向上传递错误
6.4 错误检查
- 检查代码是否进行了资源清理,特别是一些流的关闭
- 检查代码函数参数,是否是预期
- 检查函数调用上下文状态
参考
- 编程匠艺
到了这里,关于《编程匠艺》读书笔记(一)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!