【代码结构】从最简单的命名开始

282 阅读9分钟

【代码结构】是什么?

通常意义上的代码结构是指在一个文件内按照指定的语法或者规则来编写代码。比如一个html文件,它的代码结构就是这样:

<!DOCTYPE html>
<html>
    <head></head>
    <body></body>
</html>

这是 html 既定的代码结构,不这样写,它就会出错。而这里所说的【代码结构】则是在前者之下更细化的写法。

一个最常见的例子,script 标签的位置,它可以放在 head 里,也可以放在 body 里。但实际上会根据 script 里代码的功能决定是放在 head 里,或者 body 尾部。因为,需要考虑一个 js 阻塞的问题。同理,【代码结构】也是为了解决特定的问题而存在的规则。

【代码结构】能解决什么问题?

在软件开发里有一个永远绕不过去的问题——代码维护。维护需要阅读代码,小到弄清变量的赋值,函数的作用,大到模块逻辑,调用方式等等。有了这些准备,才能对代码进行修改,以保证维护后的代码能够按照预期进行。

好的代码结构可以表达编写人员的逻辑和意图,极大的减少维护人员阅读代码的时间。同时兼具易修改,易扩展,易移植的特性。反之,糟糕的代码结构会让维护人员迷惑,难以理解程序的逻辑和意图,往往会导致修改之后无法得到预期的效果。更有甚者会埋下极深的问题,只待某天碰上那个倒霉的维护人员。

从最简单的命名开始

在一个项目中需要命名的地方太多了,从项目文件夹,代码文件,资源文件,到模块,函数,变量,乃至一个对象字段。这些命名都是有讲究的,不是随意的。以下仅针对代码中的命名,即函数,变量,对象字段讲些常见的问题,以及如何去做得更好。

我相信绝大部分程序员在学习中一定会听老师说过,“不要以 a,b,c 这种毫无意义的字母来作为变量名称。”其次,也一定去找寻过变量命名的方法,比如驼峰,下划线等等变量的拼写方式。这是最最基本的命名要求,它们诚然有用,但同时也不是那么有用。因为这些基础规则就像是只告诉孩子“你要做个善良的人。”而没告诉他如何去成为善良的人,以及为什么要成为善良的人。

如何确定好的命名?

一个好的命名会有两个特点,一是名副其实,二是规律可寻。这两个特点包含一个即可,它们针对的是不同的使用场景。 先说名副其实,这很好理解,如下代码:

ps:我英语不太好,示例中的单词未必在语法上是准确的,所以不要纠结这个,理解思想就好。
<template>
    <div>
        <img :src="image" />
    </div>
</template>
<script>
    export default {
        image: '',
    }
</script>

表示一张图片可以使用 image,那么别人看到这个变量时就已经知道它是图片。但是,它仅仅只是停留在了 image 的层面,还不够准确。到底这个 image 是图标呢,是封面呢,还是用户头像呢?所以,image 这个变量命名可以更精确一点。修改后如下:

<template>
    <div>
        <img :src="icon" />
        <img :src="cover" />
        <img :src="avatar" />
    </div>
</template>
<script>
    export default {
        icon: '',   // 图标
        cover: '',  // 封面
        avatar: '', // 头像
    }
</script>

这样以来,是不是更准确了呢?别人一看就已经知道它们对应的是产品中的哪些数据。但在复杂的项目中这样的命名还是远远不够的,比如 icon 这个命名,只知道是图标,但它究竟是什么图标呢?

这个时候就可以往后添加更详细的单词,比如 iconMore,iconCross,iconSearch。习惯性我会在大类名称后面添加小类名称,这样符合由大到小的思维习惯。再比如表达某个事物的开始时间和结束时间,我会使用 timeBegin 和 timeEnd ,而不是 beginTime 和 endTime 。

由 time 开头有两个好处,一是上面说的符合思维习惯。二是如果某个大类下有多个小类,代码编写上也会更美观。

{
    // 好的命名
    timeBegin: '',
    timeEnd: '',
    timeNow: '',
      
    // 差的命名
    beginTime: '',
    endTime: '',
    nowTime: '',
}

对比以上两种排序是不是前者比后者更清晰呢?前者等于把所有与时间相关的变量都放在了一起,而后者则显得有点杂乱。如果页面的变量再多一些,则前者的优势会更明显。

但是,这种以类别排序,由大到小的命名方式也有缺陷。当类别需要细分到非常深的层级时,就会陷入一个命名很长的错误里。

比如这种:clubActivityTimeBegin,clubActivityTimeBeginSignUp(在实际维护代码的经历中我遇到过更长的,足足有八个单词,简直是考验眼力)。

之所以会产生这种问题是因为没有合理的规划对象属性,即产品中的对象属性。比如:

{
    // 好的命名
    adminInfo: {
        name: '',
        password: '',
        account: ''
    },
    userInfo: {
        name: '',
        password: '',
        account: ''
    },
      
    // 差的命名
    adminInfoName: '',
    adminInfoPassword: '',
    adminInfoAccount: '',
    userInfoName: '',
    userInfoPassword: '',
    userInfoAccount: '',
}

如果有多个类别名称高度重复的命名,则应该考虑将其放到某个对象中,作为对象的字段。以牺牲一定的属性读取深度换取命名的长度,这样就不会出现长到离谱的命名混在一起。从我的编程经验来看,两到四个单词是比较合适的命名长度和读取深度。所以,合理权衡两者之间的长度和深度是在复杂场景中需要思考的。

说完了名副其实,再说规律可寻。

这个特点更多的出现在 css 文件中,因为样式的类别重复度非常大,而且很多无法用单词来标识。比如表示不同字体大小的样式,就不适合使用单词,而更适合使用数字,如下:

{
  .fs10: { font-size: 10px; }
  .fs12: { font-size: 12px; }
  .fs14: { font-size: 14px; }
  .fs16: { font-size: 16px; }
  ...
}

同理,控制图标大小,边距,内边距等等,我会分别使用 is(iconSize),mt(marginTop),pt(paddindTop)这样的简短字母拼接来标识。

这种的命名显然跟名副其实特点是对立的,因为纯粹从名称上根本看不出更多的信息,最多只能猜测而已,但为什么它们依旧是好的命名呢?因为它们容易记忆,拓展度高,学习成本低,适合高频率的使用。

比如常见的 flex 布局的几个属性,一般会有以下几种定义方式:

// 1.几乎完整的命名
.flex-row { display: flex; flex-direction: row; }
.flex-column { display: flex; flex-direction: column; }
.flex-content-start { justify-content: flex-start; }
.flex-align-start { align-items: flex-start; }

// 2.相对简短的命名
.flex-row { display: flex; flex-direction: row; }
.flex-column { display: flex; flex-direction: column; }
.flex-cs { justify-content: flex-start; }
.flex-as { align-items: flex-start; }

// 3.简短的命名
.row { display: flex; flex-direction: row; }
.column { display: flex; flex-direction: column; }
.cs { justify-content: flex-start; }
.as { align-items: flex-start; }

如果使用第一种方式命名,在实际的开发过程中简直是噩梦。它会有以下几个弊端:

  1. 命名太长,难以记忆。说实话,即便是现在让我默写 flex 的几个属性名称我都写不出来(狗头)。
  2. 使用类名的标签变得更长。
  3. 需要输入更多的字母,浪费时间。

第二种跟第一种实际上是一样的,只不过程度稍微轻一点。所以,第三种才是好的命名方式,完美的解决了以上三个弊端。但是,肯定会有人有疑问了,这样简短的命名难道不会和其它命名混淆吗?如果有个银色样式 cs(colorSilver) 不就和布局样式 cs 重复了吗?

首先在确定全局样式命名时会对两种样式的使用频率进行比较,一般来说颜色样式肯定没有布局样式使用来得频繁,所以,cs(colorSilver) 可能会变成 csilver 。

其次是如何知道 cs 是 flex 布局的类名呢?这里会涉及到另一个规范,即样式类名的书写顺序。根据上面的类名定义不能单独的使用 cs 和 as,肯定是以 row 或者 column 开头来使用,例如:

<div class="row cs as"></div>
<div class="column cc"></div>

那么实际就会变成只要有类名中以 row 或者 column 开头,就已经知道后面的类名是 flex 布局的属性修饰了。所以是绝对不可以写成下面这种顺序,这会导致理解上的困惑。

<div class="as cs row"></div>
<div class="cc column"></div>

通常在标签上使用类名时,我也会遵循由大到小的原则,即样式对元素影响程度大小的顺序排列,写出来像这样:

<div class="mt12 row cs as fs10 c00"></div>

从元素自身的布局样式,到元素内部的对齐方式,再到字体大小,字体颜色这些无关痛痒的样式属性。

ps:现在已经有很多成熟的 css 框架可供使用,不一定会自己从零开始写样式。不过,多一种视角也不是坏事。

函数的命名

函数的命名和变量的命名类似,遵循名副其实的特点。但要避免几种常见的问题,例如万能的 getList,getDetail。这种命名就和前面说的 image 一样,只表示了这个函数大概是做什么的,而没有确切的表示出获取了什么东西的列表,及详情。所以,也应当确切的标识出来,比如 getUserList ,getCityList。

为了更直观的区分函数的作用类型,预先规范一些函数的前缀也是必要的,比如 get 是获取某些数据。set 是设置或者初始化某些数据,on 是对元素操作的响应,event 则是某些内部事件的回调等等。这些单词不是固定的,只要团队内部统一即可。

当所有函数按照这样去命名之后,整个代码结构就会比较清晰,也更容易定位问题和修改。