可读代码编写炸鸡十一 - 小黄鸭从你的心里游到脑子里
可读代码编写的炸鸡很快要写到头了,从一开始的尝试到现在的倒计时,还是有一些成就感的。当然,这不是炸鸡篇幅缩短的托词。而是 越到后头,其实内容越少,越需要代码的实践。在上一篇炸鸡的结尾,我也提到了这一点,所以各位勿怪。
我们已经了解了,抽取不相干子问题,让代码段尽量一次只做一件事 对于代码可读性的优化,但是这些都是针对已经 出现不好代码 的情况。
而很多情况下,你要面对不少前人留下来的你不愿面对的糟糕代码,并且修改他,从此背上这口大锅。不过如果他们能 在写之前 就能想清楚该怎么写能尽量使代码通顺可读干净,你也少背锅,岂不美哉。
更何况,在编码前就要提醒自己尽量写出可读性高的代码,这也是对于自身的要求,与项目垃圾与否无关,不是吗。(当然,这句话是从我同事那里偷来的,他叫 pickline。)
再回顾一下可读代码编写的第三层:
后台回复 「第三层」获取源文件
所以接下来两篇炸鸡,都是站在 编码前就考虑如何减少不好代码 的角度而提供的建议。
小黄鸭
各位或许都听说过,小黄鸭调试法吧,所谓的小黄鸭调试法就是:
当程序员面对 bug 却不可自查,陷入困顿时。在电脑旁放一只小黄鸭,一边抚摸它一边讲述自己的代码思路与流程,希望借此找到代码的漏洞。
所以这只小黄鸭完全可以用在编码,也可以称作 小黄鸭编码法。小黄鸭编码法就是 面对一段需要实现的功能,先自己想一遍,说一遍功能的大致流程与细节,然后再对应地编码。
编码前的大脑
《编写可读代码艺术》第十二章中也提到,其实写程序,就是人利用计算机语言来向计算机解释一件事情,就好像我们跟朋友讲述一个故事。所以自己说一遍自己要写的东西,是有帮助的。
小黄鸭编码法对可读性的提高有所帮助,尤其是一些较为复杂的逻辑判断,这个在业务中也是常见的校验流程。
如果发现自己代码整体有点复杂冗长,就更应该先小黄鸭一下,理清楚思路,而不是盲目上手。提高可读性的方法是很多,但是从一开始就规范起来,可以少走弯路。
假设我们一个 依据配置提供下一题 这样的业务需求。在编码前,我们的大脑可能不太清醒,认为的步骤是这样的:
返回一个 next question。
question 有跳转类型,any 和 right 才有 next question 的数据,如果是 right, 需要答对才能继续。
同时回答题目没有结束。(not isEnd)
没结束需要看 nextQuestionId, -1 的是随机,不然就是固定。
好了 ,如果依据这样的想法,我们写出了之前炸鸡中出现的例子来了。
function QuestionSystem:giveNextQuestion(questionId, isAnswerCorrect)
local question = {
isEnd = false,
maxAnswerTime = 11,
nextQuestionId = 0,
}
local questionConfig = Config.getConfById(questionId)
if questionConfig.nextType == NEXT_TYPE.ANY
or (isAnswerCorrect
and questionConfig.nextType == NEXT_TYPE.RIGHT) then
if not self:isEnd() then
self._count = self._count + 1
question.isEnd = false
-- 随机一个
if questionConfig.nextQuestionId == -1 then
question.nextQuestionId = math.random(1, questionConfig.maxNum)
else
question.nextQuestionId = questionConfig.nextQuestionId
end
question.maxAnswerTime = Config.getConfById(question.nextQuestionId).maxAnswerTime
return question
else
question.isEnd = true
return question
end
end
end
写出的代码可读性较差,也反映了你的脑子里,还是稍显混乱的。
小黄鸭的游动
那么这时候,我们完全可以利用小黄鸭编码法,重新在讲述一遍业务需求和代码思路,将刚才稍显混乱的想法重新梳理。小黄鸭在你的心里出生,而它随着你的思绪游动到脑海。
我觉得刚才的思路需要重新梳理。
回答结束了,就直接结束了,不搞太多东西。
如果回答没结束需要看跳转类型,不符合就没数据返回。
确定有下一题,下一题需要确定随机还是固定生成。
接着把想法倾泻到代码中:
function QuestionSystem:giveNextQuestion(questionId, isAnswerCorrect)
local question = {
isEnd = false,
maxAnswerTime = 11,
nextQuestionId = 0,
}
if self:isEnd() then
question.isEnd = true
return question
end
local questionConfig = Config.getConfById(questionId)
local nextAnyway = questionConfig.nextType == NEXT_TYPE.ANY
local nextNeedRight = (isAnswerCorrect and questionConfig.nextType == NEXT_TYPE.RIGHT)
if not nextAnyway
and not nextNeedRight then
return question
end
self._count = self._count + 1
question.isEnd = false
if questionConfig.nextQuestionId == -1 then
question.nextQuestionId = math.random(1, questionConfig.maxNum)
else
question.nextQuestionId = questionConfig.nextQuestionId
end
question.maxAnswerTime = Config.getConfById(question.nextQuestionId).maxAnswerTime
return question
end
嵌套减少了,思路清晰了,可读性也提高了。
小结
全篇炸鸡其实都在讲一件事情:小黄鸭编码。其实小黄鸭编码就是给自己一个 同自己的想法对话的机会。
其实从事编码相关的工作也有一年了,个人感觉编码过程中最冒失的事情便是 —— 直接下手去写。倒不是反对实践,是反对盲目下手。因为这个时候那只小黄鸭还未振翅游来,你的思路是没有打开的,不仅可读性可能受到破坏,而且很可能达不到所需。
如果在编码之前,先同自己对话,找到一只小黄鸭,慢慢地梳理思路,势必会比莽撞敲键盘来得好。而且这也花不去多少时间,着什么急呢。
- 揭秘ThreadLocal
- Java异常体系中的秘密
- 10分钟搞懂蚁群算法
- 手把手0基础项目实战(三)——教你开发一套电商平台的安全框架
- 遇见requestAnimationFrame
- 认识createDocumentFragment
- 点击穿透原理及解决
- 如何使用Intellij搭建Spark开发环境
- 如何重置Cloudera Manager的admin密码
- 如何在CDH集群安装Anaconda&搭建Python私有源
- 如何使用Python Impyla客户端连接Hive和Impala
- 如何在Windows Server2008搭建DNS服务并配置泛域名解析
- 如何通过CM API优雅的获取元数据库密码
- CM启动报InnoDB engine not found分析
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- PHP中number_format()函数的用法讲解
- php7新特性的理解和比较总结
- PHP之认识(二)关于Traits的用法详解
- 详细分析Python可变对象和不可变对象
- spring-boot-route(十二)整合redis做为缓存
- ThinkPHP框架实现的微信支付接口开发完整示例
- spring-boot-route(十三)整合RabbitMQ消息队列
- spring-boot-route(十四)整合Kafka
- laravel 事件/监听器实例代码
- pytorch 多分类问题,计算百分比操作
- spring-boot-route(十五)整合RocketMQ
- spring-boot-route(十六)使用logback生产日志文件
- 你真的知道怎么实现一个延迟队列吗 ?
- 腾讯云容器服务日志采集最佳实践
- 深度剖析Lottie动画原理