1

《代码规范》如何写出干净的代码(一)

 2 years ago
source link: https://blog.csdn.net/zy21131437/article/details/123673441
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

《代码规范》如何写出干净的代码(一)

在这里插入图片描述

大家好,最近比较忙,更新都变得不规律起来了,接下来尽量努力使得自己的分享能趋向稳定…求关注,求收藏,求点赞…非常感谢

不知有没有发现,很多优秀的开源项目往往具有代码结构清晰,注释完善,不管是其可阅读性,还是可扩展性都非常的厉害,让我非常羡慕,因此,如何写出干净、优秀的代码就是向大佬晋级的第一步;

到这里,可能会有小伙伴问,那么什么样的代码才是干净代码? 先不急,这个问题是我们最终才得出的结论,接下来记录一些我个人认为是干净代码所必需具备的要点;

第一个,当然是命名规则了,在看Vue这些优秀项目的源码时发现,其实注释相关的内容还是比较少的,但是通过方法名,函数名,变量名等等内容,可以大致看出其作用到底是什么,这里就是所谓的命名应该是具有其对应意义的而不是随便想到什么就写了什么,比如:

function UsEntity(){
  // 代码
}
const us = new UsEntity();

在这个示例中,定义了一个函数UsEntity,感觉这个应该是一个构造函数,因为根据约定首字母大写并且有一个Entity这个单词的意思应该是实例,并且下面也确实也这么用了,如果不写注释只根据名字,大概率我们是无法猜测这个这段代码的具体作用的,所以很明显这种就不大好,如果改一改

function User(){
  // 代码
}
const user = new User();

那么这个就很清晰了,就是用户,定义了一个关于用户的构造函数,并且下面实例化了一个user,接下来的操作也肯定和用户相关;
这么一看,好的名字确实很重要,它可以帮助我们在注释或者相关文档没有那么齐全的情况下快速了解函数或者变量的大致功能
那么问题来了,命名的规则究竟是什么样子的呢?我觉得这个还是得分情况:

变量和常量

对于变量和常量,其作用往往是用来做数据的存储,状态的控制等等,我觉得比较合适的规则是:名词 或者 短语+形容词

// 用户信息
const user = {};
const age = 12;
const name = "oliver";

// 验证结果,比如是否已登录
const isLogin = false;

这种我个人认为是比较合适的,相对通过变量名就可以看出其大致作用,另外,对于变量名我们采取的往往是小驼峰写法,但这个不是绝对的,这个需要根据每个团队自己定义的命名规范进行调整,而常量我们则往往采用全大写的规范,且单词与单词之间采用下划线进行分割,比如:

const USER = {};

到这里,可能会有小伙伴说,这个命名太简单了实际项目中往往会重复,那么在这种时候我们就可以对这个变量进行更多细节的描述,但是注意的度,不要过多,那样会显得太冗余,比如我们前台存储的数据,对于前台存储的用户信息我们一般会用user,但是如果有多种用户怎么办,那么我们可以加一些前缀

// 比如,已经认证过的用户
const authenticatedUser = {};
// name
const firstName = "";
// 是否激活
const isActiveUser = false;

小练习
题目:存储用户信息的命名

// 比较差的命名
const u = {};
const data = {};

这种,就是我个人认为比较差的命名,因为单看变量名完全看不出其用户,而data则是范围实在太广,非常多的数据可以用data来表示;

// 正常
const userInfo = {};
const userData = {};

可能有小伙伴说,这种不是很好吗,为什么只能说正常,个人感觉变量名有点冗余,user也是一个名词,它已经能代表当前这个变量用于跟用户相关的内容了,可以不需要加info和data了;

// 比较好的
const user = {};
const customer = {};

对于比较好的命名,应该要够简洁,并且能够说明其用途,user和customer这种完全能够说明其作用且非常简洁,所以个人认为比较合适了;

函数和方法

对于函数和方法,其作用往往是执行一些操作或者命令,以及计算一些结果,比如请求接口,验证表单,我觉得比较合适的规则是:动词 或者 短语+形容词

// 发送用户信息
function sendUser(){};

// 获取用户信息
function getUser(){};

对于函数与方法,我们一般也是遵循的小驼峰规范,除非这个函数是构造函数,对于构造函数,我们约定是以大驼峰来命名的;
同样,对于函数名和方法名也是可以进行进一步描述的,可以增加其准确性和不重名的可能性,比如

// 获取用户信息
function getUserByPhone(){};

这种就很合适,并且一看就能知道这个函数的作用,通过手机号码获得用户,当然还有一些验证的,计算布尔值的:

function phoneIsValid(){}

看名字应该也能看出这个函数的作用吧,判断phone是否通过校验的函数;

小练习
题目:存储用户信息到数据库

// 比较差的
function handle(){}

这种我认为是比较差的,还是上面变量说的原因,范围实在太广了,通过变量名handle,我们可以知道这是一个操作,但是具体什么操作就完全不知道了,很明显,这个不大合适;

// 正常
function save(){}

这种就相对要好一些,我们知道这是一个保存的函数,但是在写的过程中也许我们能知道这是保存什么,但是过了一段时间后,光看名字我们只能知道这是一个保存操作,具体保存什么就完全不得而知了;
相比handle,save的优点在于,具体能知道这是一个操作的类型了,只是说这个操作是对什么操作的,不得而知;

function saveUser(){}

明显的,这个意图就比较明确了,就是保存用户,因此这种事比较合适的,比较优秀的命名;

class类

对于class类,我感觉最大的用处是用来构建抽象事物,比如用户,产品,对于这种我感觉它的规则其实可以和变量差不多,规则大致也是:名词 或者 短语+形容词

// 用户
class User{}
class Customer{}

// 飞机游戏
class PlaneGame{}

对于类名,我们采用的往往是大驼峰的写法,因为定义到类往往就是构造函数;

小练习
题目:定义一个用户对象;

// 较差的
class UEntity{}

这种就是比较差的,完全看不出其意义是什么…只能感觉说是U的一个实体,猜不出;

// 普通的
class UserObj{}

这种就是很普通的,看名字我们能知道这是一个关于用户的obj集合,但是又有点冗余

class User{}
class Admin{}

这种就很好,看名字能直接知道其作用,Admin的话是User的具体权限划分,有时候我们需要更具体的用户信息,因此完全可以采用更具体化的名字来代表;

对于变量的描述不太建议超过3个单词,比如

// 不太建议
const userWithNameAndAge = ""

这种虽然看名字我们也能很快知道其作用和意义,但是实际上总觉得太冗余了,name和age一般都是user的固定属性,实际上不太需要name和age

// 新用户
const newUser = "";
// 已登录用户
const loggedInUser = "";

代码结构和格式

首先我们要明确一下,注释是对代码的解释和说明,目的是让别人阅读作者写的代码时可以更方便快捷的了解功能与业务,因此好的注释可以对协同,改BUG其事半功倍的效果,不好的注释则完全是拖后腿,本来就不懂了,看了注释就更加模凌两可…

不好的注释

直接看示例吧

// 冗余的注释
function getUserName(type) {
    if (type === "first") {
        // 如果type的值等于first返回first name
        return "first name";
    } else {
        // 否则返回last name
        return "last name";
    }
}

这种,有什么不好?很明显就是不看注释我们也一样能很快的明白代码的含义,而一旦写了注释,我们就需要花额外的精力去阅读,这个就不大合适了,注释的本意是协助更快的阅读,这边则有点本末倒置了

第二种注释也是比较讨厌的,就是变量名的命名和注释是冲突的,比如

// 变量名和注释冲突
function insertUser(user) {
	this.dbEngine.insert(user)	// 更新用户
}

就是类似与这种,insert我们一般代表的是插入,而这里的注释写的却是更新,更新一般使用的单词是update,因此其他开发人员一旦看到这个注释,就不得不去看具体的实现逻辑,判断这个方法到底是插入还是更新,因此注释一定要和变量名相合,不能产生误导

第三种注释,则是注释掉的代码,这个其实挺无奈的,随着项目越来越大,当时不确定的代码临时注释掉了,可能当时的本意是有另外的解决方案,但不一定对,注释掉的代码需要暂时保存,但是随着开发进度的深入,注释掉的代码往往会被永远保留,而对接手的人,则不敢随意删除,因此在这种情况下一般有两个推荐:

  • 代码review的时候务必审查注释掉的代码,发现后审查情况,判断是否需要删除;
  • 功能一旦确定,开发人员自己就要有意识的去删除注释掉的代码,否则这个坑会一直被保留下去;

有不好的,那肯定就有好的,好的注释一般有以下几种:

  1. 版权信息,包括时间,版本,法律信息,作者信息等等,这些信息可以快速帮助其他人有问题时联系作者;
  2. 对变量名的补充,比如变量名无法描述的信息,
// 邮箱 [text]@[text].[text],邮箱只有一个"@"和一个"."
const emailRegex = /\S+@\S+.\S+/
  1. 警告信息的描述,比如一些代码有适用性或者功能上的注意点,那么可以通过注释来表达
// 仅使用于浏览器环境
localStrage.setItem("user","name")
  1. 未完成的代码,比如当时写代码时因特殊原因未写完,那么可以写个注释提醒一下
function getUser(){
  // 待办事项,需要实施
}

代码格式简单的来说分为两种,一种是行与行之间的(比如行与行的空行),一种是同一行之间的(比如缩进),但是不管怎么样,我们的目的都是为了提高可读性;

行与行之间的代码,原则上不能有太多的跳跃,简单的说就是不要让人来回滚动,为了达到这个目的,我们一般会有以下几点考虑

  • 考虑将具有多个概念的文件进行拆分,比如一个文件中存在好多个class类,那么此时就可以考虑将每个class作为一个文件进行拆分;
  • 同一个文件中,不同的“区域”应该用间隔分开,比如一个大的功能界面里面有新增修改两块功能,那么新增相关的应该写在一起,修改相关的也应该写在一起;
  • 同一个文件中,相关的功能应该尽量靠近,比如新增和查询,一般我们针对表格的内容在新增成功后都会需要刷新表格内容,那么如果此时这两块区域离的非常远,那么就会对我们的阅读造成不便,比如下例:
// 起始
function start(){
  next()
}

// 下一步
function next(){
  last()
}

// 结尾
function last(){}

这种相对就比较合理,功能是按着顺序书写的,那么阅读的时候也就可以按着顺序阅读,极大的方便了我们的理解;

个人觉得同一行的规则中最主要的一点就是:代码行在不水平滚动的情况下能正常阅读,也就是尽量不要出现滚动条,一般而言我们会有以下几条规则约定:

  • 缩进,关于缩进需要根据团队的实际情况而定,一个tab2个空格或者4个空格都可以;
  • 将长语句拆分为多个短语句,这个拆分会有助于阅读理解;

本文中第一小节从变量,函数,class类等的命名规则阐述了如何编写才能方便阅读,方便理解,第二小节则从注释代码格式来描述开发时的一些注意点,总体小结大致如下:

  • 变量,函数,class类的命名规则可以使用:名词 或者 短语+形容词来描述;
  • 好的注释可以给阅读者在阅读代码时进行补充帮助快速理解过多的注释以及误导性的注释都是会大大增加理解成本;
  • 代码的的行与行之间需要判断上面一个区域于下面一个区域是否有一定的关联性,如果有那么则不需要分开,如果没有则需要进行空行分格;
  • 同一行之间要有缩进,以及代码长度不要过长出现滚动条

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK