『No25: 编写可读代码的艺术(2)』

GOPHER_VIKING.png

大家好,我叫谢伟,是一名程序员。

上节从编程语言特性的角度讲述了编写可读代码的几个要点。

编写可读的代码的艺术

本节接着从编程语言的语言特性:流程控制和循环等角度,再次谈谈编写可读代码的要点。

还记得吗,编写可读代码的核心的要点是什么?

写易于理解的代码

1. 流程控制

1.1 条件参数的顺序

编程语言关于流程控制的语句有哪些?

  • if ... else
  • while
  • switch

涉及流程控制的话,一般涉及条件判断,你有认真思考条件判断语句中的参数的顺序吗?

比如:

if (number < 10) {} // A

if (10 > number) {} // B

if (receivedNumber < expectedNumber) // C

if (expectNumber > receivedNumber) // D

上述实例,你会条件语句的参数顺序你会选择哪个?

A, C

那么应该准从什么样的尊则?

左边倾向于变量,右边倾向于常量;

其实这不是什么新的东西,在我们学习数学中的未知数的时候就是这么做的。

x < 2

1.2 if...else 语句块的顺序

可以参照下面的下面准则:

  • 先判断正向逻辑的,再判断负向逻辑
  • 先处理简单
  • 先处理有趣的或者可疑的
if createParam.Data.ShopType != RegionEntrances {
    newShop.ShopUUID = tools.GenerateUUID(16, createParam.Data.Name, company.Name)
} else {
    var tmpShop models.Shop
    if notFound := database.POSTGRES.Where("company_id = ? AND shop_type = ?", company.ID, createParam.Data.ShopType).First(&tmpShop).RecordNotFound(); notFound {
        newShop.ShopUUID = tools.GenerateUUID(16, createParam.Data.Name, company.Name)
    } else {
        newShop.ShopUUID = tmpShop.ShopUUID
    }

}


上文根据输入的 ShopType 字段是否是 RegionEntrances;

  1. 如果是,搜索数据库,看数据是否是有此字段,存在则获取shopUUID
  2. 否则 产生 shopUUID
  3. 如果根本不是 RegionEntrances 字段,则产生 shopUUID

上文没有一定的规范,搞的整个流程不容易理解。

根据:先处理正向逻辑,处理简单的,处理可疑或者有趣的准则,改善如下(仅仅只是调换顺序)

if createParam.Data.ShopType == RegionEntrances {
    var tmpShop models.Shop
    if notFound := database.POSTGRES.Where("company_id = ? AND shop_type = ?", company.ID, createParam.Data.ShopType).First(&tmpShop).RecordNotFound(); !notFound {
        newShop.ShopUUID = tmpShop.ShopUUID
    } else {
        newShop.ShopUUID = tools.GenerateUUID(16, createParam.Data.Name, company.Name)
    }
} else {
    newShop.ShopUUID = tools.GenerateUUID(16, createParam.Data.Name, company.Name)

}

1.3 避免使用三目运算符

三目运算符一定程度上能够精简代码,减少代码的行数,但是却存在另外一个缺点,即:不容易理解(虽然大学教材总会考这类题目,判断执行的顺序和结果)

只在简单场景下使用三目运算符。

1.4 函数什么时候返回

经常我们编写函数的时候,喜欢声明一个变量用来存储结果,到所有的逻辑结束后返回这个变量作为函数的返回值。

几个建议:

  • 可以提前进行函数返回值,多几个 return, 没关系
  • 最好函数都要有返回值,Golang 里建议至少返回一个 错误信息

1.5 减少多层级的嵌套

层级的增多,增加了认知的负担。而且容易出现不容易发现的 bug。

如何减少嵌套:

  • 提前函数返回
  • 在循坏内使用 continue

2. 表达式

建议使用短表达式

如何做到短表达式:

  • 已有的项目:拆分
  • 新的代码:有意识的使用短表达式

如何拆分:

使用中间变量

中间变量的用途可以划分为:

  • 解释型变量
  • 总结性变量

比如:


if createParam.Data.ShopType == RegionEntrances {}

感觉表达式长了,怎么做:

var shopTypeEqual = createParam.Data.ShopType == RegionEntrances

if shopTypeEqual {}

3. 变量

编程语言支持显式申明,也支持自动识别变量类型,你觉得哪种好?

var number int
number = 10

numberMax := 100

显式的命名更好,强类型编程语言遇到的问题可能还不多,弱类型的编程语言,可能存在隐藏的 bug.

变量的申明区分:全局和局部

问:全局变量多点好?还是少点好?
问:局部变量是统一在函数下侧统一命名,还是靠近需要使用变量的语句处?

全局变量少用,随着项目越来越复杂,可能在某个角落,全局变量就进行了更改。这样引起的 异常处理很难进行追踪和分析。

局部变量在靠近使用该变量的地方声明并使用,这样,逻辑、思维不容易断。

比如:

var fetchMaxNumber = func( ) int {

    var maxNumber int
    var minNumber int
    ...
    ...
    ...
    do...
    
    return maxNumber
}

var fetchMaxNumber = func()int{
    ...
    ...
    var (
        maxNumber int
        minNUmber int
    )
    
    do...
    return maxNumber
}

第二种变量的组织方式优于第一种,而且更利于思维。像第一种,读到真正的处理逻辑,还需要回过头去看下变量的声明,给思维造成了额外的认知负担,尤其你还喜欢写大段代码的函数。

一个准则:全局变量的个数需要尽可能少,如果有可能,使用常量替代。局部变量最好在需要使用变量的地方进行申明。

好,那么我们的目的便是尽可能的减少变量。

如何减少?

  • 减少没有价值的变量,甚至是没有价值的代码
  • 减少控制流变量(经常会使用一个诸如 Flag 的变量等来进行控制流的判断,其实完成可以省略,仅靠调整语句遍可实现)
  • 缩小变量的作用域:全局变量多处使用,赋值之类的可能变更变量,在函数内的变量作用域有限,不影响外部变量

4. 重新组织代码,持续迭代

软件架构有一种很流行的设计方法,叫:领域驱动设计,对持续迭代的微服务有很大的帮助。该领域驱动方法将项目划分为4个层级。

  • 领域层:即领域内操作的集合
  • 基础设施层:即辅助服务操作的集合
  • 用户界面层:即用户层
  • 应用层

其中谈到领域,和我们之前变量的命名建议使用专业的词、领域内的词不谋而合。

同时,基础设施层是将一些辅助性的任务集合。比如文件处理、比如网络请求处理、比如字符串处理等

组织代码节也提倡这么做。

实现核心的业务需求时,尽量将这些工具类的功能规整在独立的基础设施里,专注于实现核心的业务。

代码的组织,一个是项目的组织,一个良好的项目组织方式,一定程度上能体现代码的逻辑性。

另外一个比较重要的是函数的组织。

有下面几条准则:

  • 不相干的任务,提取出来
  • 一次只专注干一件事
  • 梳理逻辑时,如果你能使用自然语言表述出来,对你写出逻辑清晰的代码很有帮助
  • 单函数行数不宜过长 30 ~ 50 为佳。再一个评判方法是,查看函数的内容无需滚动鼠标进行翻页。
  • 少些代码:每写一行都需要维护;不需要的功能,砍掉,不需要的代码,删掉

全文完,我是谢伟,再会。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容

  • 第2章 基本语法 2.1 概述 基本句法和变量 语句 JavaScript程序的执行单位为行(line),也就是一...
    悟名先生阅读 4,114评论 0 13
  • 线程同步方案 OSSpinLock 自旋锁 os_unfair_lock pthread_mutex dispat...
    那位小姐阅读 1,639评论 0 19
  • 朝天开的 我桌上摆着一幅向日葵 带着阳光般的激情 散发着淡淡的温暖 我日渐消沉 在纷繁复杂的大社会里努力生存 在熙...
    水花w阅读 222评论 0 2
  • 遭遇了一件糟心的事,见识了一种恶劣的人,早就体验过人性的恶,当再次经历时,依然让人觉得恶心,是的,不是气愤或是惊讶...
    老弗阅读 358评论 0 0
  • 终于等到这一天了,梦想的318。 早上从老宋家青旅出发(这个青旅竟然男女混住,哈哈),先骑到天府广场,看看毛爷爷的...
    DJohn阅读 400评论 0 0