失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 编写可读代码 提高工作效率

编写可读代码 提高工作效率

时间:2022-10-22 23:23:38

相关推荐

编写可读代码 提高工作效率

本次分享是怎么做到“可读性”的

首先,正在进行的,说明下本文的可读性。

1.背景 根据今年形势996icu,加班加点的情况比较多。与其抱怨,不如改变。

从内因去改变:主题,编写可读代码,大大工作效率。

2.观点:代码是写给人看的,推荐一本书《编写可读代码的艺术》。

3.方法:以《编写可读代码的艺术》内容,结合实际项目代码,给出13条建议。

4.个性化建议。

5.项目代码举例。

6.总结,可读性的好处,解决的问题。

友情声明

本次分享中提到的想法实践,纯属个人见解,仅供参考,多多海涵。

一、主题:编写可读代码,提高工作效率

职业生涯难题,996icu,加班加点。

与其抱怨,不如改变。

外因,不可控。

内因,可控。

想想能为一起coding的同事做点什么?

《编写可读代码,提高工作效率》。

1.现状:加班加点

需求又变了?加班

工作量有点大,进度催的紧,先加班为敬

系统bug多,顾此失彼,心情忧伤。

接手同事代码,看不懂。没文档、没流程图,产品经理和测试,不停追问业务细节。

可测试。测试经常打听实现细节。

联调真费劲,队友真...

2.根本原因之一:代码可读性差

代码可读性差,不好维护,容易修改出问题。

3. 解决办法

抓住1个根本性问题:写出的代码,可读性强,能让人快速理解、轻松维护、容易扩展。

为什么说能?且听下文分解...

二、观点:代码是写给人看的

读过一本书《编写可读代码的艺术》,推荐阅读。

1.观点

程序员之间的互相尊重体现在他所写的代码中,他们对工作的尊重也体现在那里。

代码最重要的读者不是编译器,解释器或电脑,而是人。

写出的代码能让人快速理解、轻松维护、容易扩展的程序员才是专业的程序员。

2 可读性怎么定义?

可读性基本定理:代码的写法,应当使别人理解它所需要的时间最小化。

"别人"应当指所有阅读你的代码的人,包括同事,也包括6个月后的你自己!

2.1 示例1:

增加变量让代码更可读

if(name != null && name != ""){}if(StringUtils.isNotEmpty(name)){}boolean isNotEmpty = StringUtils.isNotEmpty(name);if(isNotEmpty){}boolean isNotEmpty = StringUtils.isNotEmpty(name);if(isNotEmpty && name != "admin"){}

2.2 示例2:

需求变动:开始只有1个规则,后来变为2个规则

boolean ruleOne =calcRuleOne();boolean roleTwo = calcRuleTwo();if(ruleOne && ruleTwo){}else if(){}else if(){}else{}

2.3 示例3

如果不认真写代码,出了bug,让你怀疑人生。

@RestController@RequestMapping("manage/dictionary")public class DictionaryController extends BaseController{//获得数据字典@RequestMapping("/getDictionary")private String getDictionary(Dictionary dictionary){// code}//收款人列表@RequestMapping("/payeeList")public String payeeList(){// code}}

2.3 示例4,歧义的目录

src/main/resources/template/abc.xlsx(Maven打包到 classes/template/abc.xlsx)Generator.class.getClassLoader().getResourceAsStream("template/abc.xlsx");

src/main/template/abc.xlsxMaven打包到 classes/abc.xlsx)Generator.class.getClassLoader().getResourceAsStream("abc.xlsx");

三、方法:编写可读代码,13条建议

第一部分 表面层次的改进

1.把信息装到名字里

1.1选择专业的词

getPage(url); 是从缓存获取页面,还是实时从互联网上获取呢?

根据url,获得1页内容?获得1个变量。

更专业的词:fetchPageFromCache,downloadPage,getPage。

1.2避免象tmp这样泛泛的名字

String tmp =user.name();tmp += " "+user.email();

用userInfo这样的名字更具有描述性。

建议:tmp这个名字只应用于短期存在且临时性为其主要存在因素的变量。

1.3 用具体的名字代替抽象的名字

serverCanStart:检测服务是否可以监听某个给定的TCP/IP端口。

更好的名字:

canLinstenOnPort:这个名字直接地描述了这个方法要做什么事情。

1.4为名字附带更多信息

var start = new Date().getTime();//do sthvar end = new Date().getTime();var costTime = (start-end)/1000;(时间的单位是秒s,还是毫秒ms?)costTimeMs?

1.5名字应该有多长

int d;int days;int daysSinceLastUpdate;

在小的作用域可以使用短的名字,大的作用域使用长的名字。看看当前上下文是否有足够的信息。

1.6利用名字的格式来传递含义

//常量名和类名的取名方式不一样

private String userBtn;public static final int MAX_NUMBER= 100;public class Number{}

2.不会误解的名字

2.1容易产生误解的例子

//挑出?减掉?allPersons.filter(“age>100”);

2.2 推荐用first和last来表示包含的范围

推荐用begin和end来表示包含/排除范围

String str ="abcd";str.substring1(int first,int last);str.substring2(int bigin,int end);

2.3 给布尔值命名

public boolean addUser(){boolean flag= true;return flag;}

把flag换成addSucceed

2.4与使用者的期望相匹配

private String name;//很多程序都习惯了把以get开始的方法当作“轻量级访问器”这样的用法//它只是简单地返回一个内部成员变量。private String getName(){return name;}//badprivate String getName(){return "My name is:"+Name+" !";}

2.5 变量命名的一致性

命名一致

userNameusernamename

类型一致

varchar(32) comp_id, String compIdint(11) comp_id , Integer compId;

3.审美

3.1把声明按块组织起来

//get/query/find/select 查询类方法 (高频方法)//add 增加类方法//update 修改类方法//delete 删除类方法

3.2把代码分成“段落”

String name;updateName="";String email;sendEmail();String address;saveAddress();

3.3个人风格与一致性

class Name{}class Name{}

一致的风格比“正确”的风格更重要。

4.该写什么样的注释

4.1什么不需要注释

//用户模块public class UserService{}

建议:不要为那些能从代码本身快速推断的事实写注释。

boolean isEmpty = StringUtils.isEmpty(name);if(isEmpty){}

没用的注释/*** 导出列表excel** @param params* @param resp* @return*/@Overridepublic Result export(Map<String, Object> params, HttpServletResponse resp) {}/*** 导出列表excel*/@Overridepublic Result export(Map<String, Object> params, HttpServletResponse resp) {}

4.2记录你的思想

4.2.1 加入“导演评论”

//准确率可以达到99%,没有必要达到100%

getValue();

4.2.2 为代码中的瑕疵写注释

//冒泡排序不够快

bubbleSort();

4.2.3 给常量加注释

//人的最大年龄

public static final int MAX_AGE=150;

4.3 站在读者的角度

4.3.1公布可能的陷阱

//调用外部服务来发送邮件。(1分钟之后超时)

sendEmail();

4.3.2 总结性注释

//求和

int[] array = {1,2,3};for(int index=0;index<array.length;index++){sum += array[index];}

4.4 精确地描述函数的行为

//返回文件的行数//计算换行符(\n)的个数int countLines(String fileName);

项目代码举例

@Slf4j@RestController@RequestMapping(value = "/api/bill")public class BillController extends BaseController {/*** 获取账单列表和回款列表未处理的记录总数*/@RequestMapping(value = "/undealwithcount")public Result getUndealwithCount() {try {Integer billResultCount = 0;Integer refundResultCount = 0;Map<String, Integer> resutMap = new HashMap<>();BillVo vo = new BillVo();UserInfoDto userInfo = getUserInfo();vo = getAuthBillVo(vo, userInfo);vo.setBillInAccStatus("0,1");vo.setStoredBillStatus("1");vo.setStartBillMonth("01");//g过滤掉的未核账数据PageBean<AccountStatement> billDataPage = billService.getBillDataPageByRoleId(vo);long totalRecord = billDataPage.getTotalRecord();billResultCount = (int) totalRecord;log.info("bicontroller getUndealwithCount query refund from bi param userId={}", userInfo.getUserId());String unSplitCountUrl = unSplitCounturl + "?casUserId=" + userInfo.getUserId();log.info("bicontroller getUndealwithCount query refund from bi param unSplitCountUrl={}", unSplitCountUrl);String resp = httpClientUtil.get(unSplitCountUrl);log.info("bicontroller getUndealwithCount query refund from bi result resp={}", resp);if (null != resp && StringUtils.isNotBlank(resp)) {JSONObject jsonObject = JSONObject.parseObject(resp);String code = jsonObject.getString("code");Integer data = jsonObject.getInteger("data");String message = jsonObject.getString("message");if ("000000".equals(code)) {refundResultCount = data;} else {log.info("查询bi系统回款列表未拆分记录数异常message={}", message);}}resutMap.put("billList", billResultCount);resutMap.put("refundList", refundResultCount);return ResultUtils.success(resutMap);} catch (Exception e) {log.error("获取账单未核算记录总数失败,异常信息={}.", e);e.printStackTrace();return ResultUtils.error(ResultEnums.QUERY_FAIL_ERROR);}}}```

一个方法,8处可改进

变量作用域过大

vo被改变了吗?

BillController里出现了bicontroller

billList和billResultCount

异常时,用error

log记录error有问题

如果正确,log打印了异常,还需要"e.printStackTrace()"

log.error("获取账单未核算记录总数失败,异常信息={}.", e);

e.printStackTrace();

代码重复

null != resp && StringUtils.isNotBlank(resp)

提取子方法,1个方法解决1个问题

String resp = httpClientUtil.get(unSplitCountUrl);

第2部分 简化循环和逻辑

5.把控制流变得易读

关键思想:把条件、循环以及其它对控制流的改变做得越“自然”越好。

运用一种方式使读者不用停下来重读你的代码。

5.1条件语句中参数的顺序

if(age >20){}比if(20 < age){}

更易读。

if(name == null){}比if(null == name){}

在中文和英文等自然语言中(“如果你的年龄大于20”)更常见,更符合一般用法。

即比较的左侧,它的值倾向于不断变化,比较的右侧,它的值倾向于稳定。

5.2 if/else语句块的顺序

if(a== b){//case one}else{//case two}

也可以写成

if(a != b){//case one}else{//case two}

之前你可能没想太多,但在有些情况下有理由相信其中一种顺序比另一种好:

a.首先处理正逻辑而不是负逻辑的情况。例如,if(debug)而不是if(!debug)。

b.先处理简单的情况。这种方式可能还会使得if和else在屏幕之内都可见,这很好。

c.先处理有趣的或者是可疑的情况。

下面所示是负逻辑更简单并且更有趣的一种情况,那么会先处理它

if (not the same username){//case one}else{//case two}

5.3三目运算符

它对于可读性的影响是富有争议的。

拥护者认为这种方式可以只写一行而不用写成多行,

反对者则说这可能会造成阅读的混乱而且很难用调试器来调试。

关键思想:相对于追求最小化代码行数,一个更好的度量方法是最小化人们理解它所需的时间。

建议:默认情况下都用if/else。

三目运算符?:只有在最简单的情况下使用。

5.4避免do/while循环

do{}while(condition);

do/while循环的奇怪之处是一个代码块是否会执行,是由其后的一个条件决定的。

通常来讲,逻辑条件应该出现在它们“保护”的代码之前,这是if,while和for语句的工作方式。

因为你通常会从前向后来读代码,这使得do/while循环有点不自然了。

5.5从函数中提前返回

public boolean contains(String str,String substr){if(str==null || substr==null){return false;}if(substr.equals("")){return true;}...}

5.6最小化嵌套

if(userResult==SUCCESS){if(permissionResult != SUCCESS){reply.writeErrors("error reading permission");reply.done();return;}reply.writeErrors("");}else{reply.writeErrors(userResult);}reply.done();

可以通过提前返回,来减少嵌套。

6.拆分超长的表达式

6.1用做解释的变量

if(line.split(",")[0].name=="root"){}增加一个解释变量String username = line.split(",")[0].name;if(name=="root"){}

6.2总结变量

即使一个表达式不需要变量(因为你可以看出它的含义),把它装入一个新变量中仍然有用。

我们把它叫做总结变量,因为它的目的是用一个短很多的名字来代替一大块代码,

这个名字会更容易思管理和思考。

if(request.user.id == document.user.id){//user can edit this document}if(request.user.id != document.user.id){//document is read only}

这里的表达式“request.user.id==document.user.id”看上去可能并不长,

但它包含5个变量,所以需要多花点时间来想一想如何处理它。

这段代码中的主要概念是:“该用户拥有此文档吗?”

这个概念可以通过增加一个总结变量来表达得更清楚。

final boolean userOwnDocument = (request.user.id==document.user.id);if(userOwnDocument){...}if(!userOwnDocument){...}

7.变量与可读性

关于变量的3个问题

a.变量越多,就越难全部跟踪它们的动向。

b.变量的作用域越大,就需要跟踪它的动向更久。

c.变量改变得越频繁,就越难以跟踪它的当前值。

7.1减少变量

没有价值的临时变量

now = datetime.time();rootMessage.lastVisitTime=now;

减少控制流变量

boolean done=false;if(condition && !done){if(...){done=true;continue;}}

可以改为

if(condition){if(...){break;}}

7.2缩小变量的作用域

把定义向下移int a=0;int b=0;int c=0;//handle a//handle b//handle c改为int a=0;//handle aint b=0//handle b

全局变量改为局部变量。

7.3 只写一次的变量更好

"1"表示什么意思?

vo.setStoredBillStatus("1");public static final int MAX_AGE=140;

常量、枚举,可能更能表达变量的含义

public enum CompLevelEnum {SME(4, "小客户"),GENERAL_CUSTOMER(3, "一般客户"),AREA_KEY_CUSTOMER(2, "区域级重点客户"),COMP_KEY_CUSTOMER(1, "公司级重点客户");private Integer code;private String message;CompLevelEnum(Integer code, String message) {this.code = code;this.message = message;}public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}}

第三部分 重新组织代码

8. 三种组织代码的方法

a.抽取出那些与程序主要目的“不相关的子问题”。

b.重新组织代码,使它一次只做一件事情。

c.先用自然语言描述代码,然后用这个描述来帮助你找到更整洁的解决方案。

9.抽取不相关的子问题

本章的建议是“积极地发现并抽取不相关的自逻辑”,我们是指:

a.看看某个函数或代码块,问问你自己,这段代码高层次的目标是什么?

b.对于每一行代码,问一下:它是直接未来目标而工作吗?这段代码高层次的目标是什么呢?

c.如果足够的行数在解决不相关的子问题,抽象代码到独立的函数中。

介绍性的例子

int[] array = {2,4,1,3}; 求最大值和最小值。 void method(){ //排序函数,这就是1个子问题 //取第1个和最后1个 }

10.纯工具代码

文件操作,邮件发送等。

创建大量通用代码

通用代码,它完全地从项目的其它部分解耦出来。这样的代码容易开发,容易测试,并且容易理解。SQL数据库、JavaScript库、XML库等。

项目专有的功能

把名字转换成1个URL,这类项目特有的功能,也是可以提取出来的。

11.一次只做一件事情

同时在做几件事的代码很难理解。

一个代码块可能初始化对象,清除数据,解析输入,然后应用业务逻辑,所有这些都同时进行。

如果所有这些代码都纠缠在一起,对于每个"任务"都很难靠其自身来帮你理解它从哪里开始,到哪里结束。

12. 把想法变成代码

当你把一件复杂的事向别人解释时,那些小细节很容易就会让他们迷惑。

把一个想法用“自然语言”解释是个很有价值的能力,因为这样其它知识没有你这么渊博的人才可以理解它。 这需要把一个想法精炼成最重要的概念。

这样做,不仅帮助他人理解,而且也帮助你自己把这个想法想得更清楚。

一个示例:用户在浏览器访问1篇文章

用户输入网站地址:如“”;

浏览器解析网址到IP,如122.96.184.84;

浏览器建立和该IP的Socket;

浏览器与该主机通信,取得网页;

显示网页内容。

13.少写代码

知道什么时候不写代码,可能对于一个程序员来讲是他所要学习的最重要的技巧。

你所写的每一行代码都是需要测试和维护的。

通过重用库或者减少功能,你可以节省时间并且让你的代码保持精简节约。

最好读的代码就是没有代码。

13.1保持小代码库。

创建“工具”代码减少重复代码;减少无用代码或者没有用的功能; 在一个成熟的库中,每一行代码都代表大量的设计、调试、重构、文档、优化和调试。

Collections,Lang,BeanUtils,Compress

13.2 别费神实现那个功能–你不会需要它。

很多功能没有完成,或者没有用,也可能是让程序更复杂。

一个功能,不是只有开发,还有测试,最后还有维护和升级。

13.3 质疑和拆分你的需求。

不是所有的程序都需要运行的快,100%准确,并且能处理所有的输入。 如果你真的仔细检查你的需求,有时你可以把它削减成一个简单的问题。

四、6点个性化建议

1、不错的建议

1.1约定优于配置

user_name,userName,UserMapper,UserService,UserController

1.2只写必要的注释,业务复杂的地方写注释

1.3削减代码行数

2、有争议的建议,个人特殊习惯

2.1 Service只要实现类,不要接口。

2.2 数据库字段,采用java驼峰命名,减少映射。

2.3 慎用设计模式。

五、代码举例:Talk is cheap, Show me the Code

1. ,账务系统,Web项目

例子1,定时任务,可读性更强

@Component@Slf4jpublic class TaskScheduler {@Resourceprivate CrmApiRpc crmApiRpc;/*** 1小时1次*/@Scheduled(initialDelay = 30*60*1000, fixedRate = 1* 60 * 60 * 1000)public void task1() {log.info("-----task start-----");try {doTask();} catch (Exception e) {log.error("task error", e);}log.info("-----task end-----");}}/*** 1小时1次*/@Scheduled(cron = "0 */1 * * *")public void task2(){}

例子2,相关代码,统一放在一起

CrmApiRpcCrmInfoDelegate

例子3,核心业务,用1个类单独维护

PaymentSplitService

例子4,用MybatisPlus框架,轻松coding少写代码

AccConfigInfoController

2. ,Mybatis代码生成器,工具项目

可借鉴的点:流程清晰 生成器入口

3.1初始化配置

3.2根据配置生成代码

3.2.1获得数据库连接

3.2.2得到所有表名

3.2.3循环生成每个表对应的模版

1).根据数据库连接和数据库表名,构造模版的数据模型2).将Java模型转换成Map格式3).生成4个标准文件(读取模版,根据Map,渲染,保存)a. GeneratorTool.generateModel(generatorModel);b. GeneratorTool.generateBean(generatorModel);c. GeneratorTool.generateMapperJava(generatorModel);d. GeneratorTool.generateMapperXml(generatorModel);

3.3自动打开生成文件的目录

六、总结

1.可读代码的手段

命名精确,望文知义、单一职责、及时重构、流程清晰、可测试

2.可读代码的好处

降低复杂度-读懂代码花费的时间少、方便修改和维护、方便交接(代码)、bug少、方便测试、方便复用和重构、与产品经理测试等非写代码的人交流。

3.结论

编写可读代码,提高工作效率。

七、QA

八、参考资料

1.读书笔记-编写可读代码的艺术[上]

https://fansunion./article/details/12159019

2.读书笔记-编写可读代码的艺术[中]

https://fansunion./article/details/12159345

3.读书笔记-编写可读代码的艺术[下]

https://fansunion./article/details/12159431

4.《编写可读代码的艺术》

5.《重构-改善既有代码的设计》

个人观点:一次书写,人人阅读。 Write once,Read anyone。 Coding for fun, coding for my life。

如果觉得《编写可读代码 提高工作效率》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。