Go 语言高质量编程

news2024/11/9 2:40:04

编写高质量的 Go 代码~

前言:

本次课程简要介绍了高质量编程的定义和原则,分享了代码格式、注释、命名规范、控制流程、错误和异常处理五方面的常见编码规范,帮助我们在今后的开发过程中写出更加优秀的代码 …


什么是高质量编程?

编写的代码能够达到正确可靠、简洁清晰的目标可称之为高质量代码,一份高质量的代码应该具备以下特点:

  • 各种边界条件考虑完备。
  • 异常情况处理,稳定性保证。
  • 易读易维护。
编程原则:

Go 语言开发者 Dave Cheney 给出了三条编程原则,在编程中我们应该尽可能遵循这些原则。

  1. 简单性:消除“多余的复杂性”,以简单清晰的逻辑写代码。
  2. 可读性:编写可维护代码的第一步是确保代码可读。
  3. 生产力:团队整体工作效率非常重要。

编码规范

如何编写高质量的 Go 代码?

注释:

包中声明的每个公共的符号(变量、常量、函数…)都要添加注释;任何既不明显也不简短的公共功能必须予以注释;无论长度或复杂度如何,对库中的任何函数都必须进行注释。

注释应该解释代码的作用、代码是如何做的以及代码的实现原因,还应该解释代码什么情况会出错。

🎈比如 Go 的标准库中对于函数也有注释来说明功能:

// ReadAll reads from r until an error or EOF and returns the data it read.
// A successful call returns err == nil, not err == EOF. Because ReadAll is
// defined to read from src until EOF, it does not treat an EOF from Read
// as an error to be reported.
func ReadAll(r Reader) ([]byte, error) {
    b := make([]byte, 0, 512)
    for {
	if len(b) == cap(b) {
            // Add more capacity (let append pick how much).
            b = append(b, 0)[:len(b)]
	}
	n, err := r.Read(b[len(b):cap(b)])
	b = b[:len(b)+n]
	if err != nil {
            if err == EOF {
		err = nil
            }
            return b, err
	}
    }
}

PS:有一个例外,不需要注释实现接口的方法。

image.png

代码格式:

推荐使用 gofmt 自动格式化代码。

gofmt 是 Go 语言官方提供的工具,能自动格式化 Go 语言代码为官方统一风格,常见 IDE 都支持方便的配置。此外 goimports 也是 Go 语言官方提供的工具,可以实现自动增删依赖的包引用、将依赖包按字母序排序并分类。

🎈在 GoLand 中开启 gofmt 支持:

GoLand 提供了 File Watchers 功能,将 go fmt 添加进去,修改触发的条件即可。

image.png

配置完成后每次保存代码时 go fmt 就会自动格式化代码。


命名规范

1. 变量:
  • 简洁胜于冗长。
  • 缩略词全大写(比如 ServeHTTP),但当其位于变量开头且不需要导出时,使用全小写(比如 xmlHTTPRequest)。
  • 变量距离其被使用的地方越远,则需要携带越多的上下文信息。
2. 函数:
  • 函数名不携带包名的上下文信息,因为包名和函数名总是成对出现的。
  • 函数名尽量简短。
  • 当名为 foo 的包某个函数返回类型 Foo 时,可以省略类型信息而不导致歧义。
  • 当名为 foo 的包某个函数返回类型 T 时(T 并不是 Foo),可以在函数名中加入类型信息。
3. package:
  • 只由小写字母组成,不包含大写字母和下划线等字符。
  • 简短并包含一定的上下文信息,例如 schema、task 等。
  • 不要与标准库同名。

流程控制

流程控制语句应该优先处理错误情况、特殊情况,尽早返回或继续循环来减少嵌套。

✔原则:尽量保持正常代码路径为最小缩进。

比如下面的代码就是一个错误的示范:

// Bad
func OneFunc() error {
   err := doSomething()
   if err == nil {
      err := doAnotherThing()
      if err == nil {
         return nil    // normal case
      }
      return err
   }
   return err
}
  • 这段代码正常的流程路径被嵌套在两个 if 条件内,成功退出的条件是 return nil,必须仔细匹配大括号才能发现;
  • 函数最后一行返回一个错误,需要追溯到匹配的左括号,才能了解何时会触发错误。
  • 并且如果后续正常流程需要增加一步操作,调用新的函数,则又要增加一层嵌套。

调整后的代码如下:

// Good
func OneFunc() error {
   if err := doSomething(); err != nil {
      return err
   }
   if err := doAnotherThing(); err != nil {
      return err
   }
   return nil    // normal case
}

编写流程控制代码时要尽可能遵循线性原理,处理逻辑尽量走直线,避免复杂的嵌套分支。


错误和异常处理

1. 简单错误:
  • 简单的错误指仅出现一次的错误,且在其他地方不需要捕获该错误。
  • 优先使用 errors.New() 来创建匿名变量来直接表示简单错误。
  • 如果有格式化的需求,使用 fmt.Errorf()

💻 Github 仓库中的示例代码:

func defaultCheckRedirect(req *Request, via []*Request) error {
    if len(via) >= 10 {
        return errors.New("stopped after 10 redirects")
    }
    return nil
}
2. 错误的 Wrap 和 Unwrap:
  • 错误的 Wrap 实际上是提供了一个 error 嵌套另一个 error 的能力,从而生成一个 error 的跟踪链。
  • fmt.Errorf() 中使用 %w 关键字来将一个错误关联至错误链中。

💻 Github 仓库中的示例代码:

list, _, err := c.GetBytes(cache.Subkey(a.actionID, "srcfiles"))
    if err != nil {
        return fmt.Errorf("reading srcfiles list: %w", err)
}
3. 错误判定:
  • 在错误链上获取特定种类的错误,使用 errors.AS()

💻 Github 仓库中的示例代码:

if _, err := os.Open("non-existing"); err != nil {
    var pathError *fs.PathError
    if errors.As(err, &pathError) {
        fmt.Println("Failed at path:", pathError.Path)
    } else {
        fmt.Println(err)
    }
}
4. panic & recover:

Go语言不支持传统的 try…catch…finally 这种异常,但是 Go 中可以抛出一个 panic 的异常,然后在 defer 中通过 recover 捕获这个异常,然后正常处理。

✔ panic 的注意事项:

  • 当程序启动阶段发生不可逆转的错误时,可以在 init 或 main 函数中使用 painc()
  • 不建议在业务代码中使用 panic(),若问题可以被解决或屏蔽,建议使用 error 替代。

✔ recover 的注意事项:

  • recover() 只能在被 defer 的函数中使用。
  • 嵌套无法生效。
  • 只在当前 goroutine 生效。

🎈补充 - Go 中 defer 的概念:

Go 语言的 defer 语句会将其后面跟随的语句进行延迟处理,在 defer 归属的函数即将返回时,将延迟处理的语句按 defer 的逆序进行执行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/420726.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

凌恩生物文献分享|微刊:三代全长16s扩增子——环境多样性研究的明星

在微生物研究领域,PacBio三代全长的时代已经来临,如果你还没用过那就太可惜了! 要问三代有什么好,那我可得说道说道。 相比于传统二代Illumina平台测序,PacBio Sequel lle 平台获得的序列更长,信息量更多…

Java Servlet Tomcat(HttpServlet)处理底层机制详解总括

以tomact服务器为例: 热知识:Servlet是java定义的处理动态资源(非静态资源)的java接口规范,HttpServlet是tomcat实现了servlet接口的类 一.当第一次发送请求时候: 1.查询web.xml中的url-parrtern中配置的…

LNMP及论坛搭建

安装 Nginx 服务 systemctl stop firewalld systemctl disable firewalld setenforce 01.安装依赖包 #nginx的配置及运行需要pcre、zlib等软件包的支持,因此需要安装这些软件的开发包,以便提供相应的库和头文件。 yum -y install pcre-devel zlib-deve…

排序(4)——归并排序

目录 前言 1.归并排序的递归实现 1.1 归并排序概念 1.2 归并排序递归实现 2.归并排序的非递归实现 前言 今天给大家带来比较排序的最后一种,归并排序,这个排序,需要我们对递归,循环控制要有着较强的理解,我相信大…

【iOS的NSNULL nil Nil 】

前言 偶然看到了NSNULL 简单了解他的兄弟nil Nil记录一下。 NSNULL Nil nil 在iOS中,nil、Nil和NSNull都表示“空值”的概念,但它们在使用时有所不同。 nil和Nil都表示空指针,可以用于指针类型的变量、对象类型的变量、和Objective-C对象…

浙江海發進出口股份有限公司官网上线|LTD五金技术行业案例分享

​浙江海發進出口股份有限公司 (以下简称海發)是一家多元化的国际贸易企业。拥有自己的工厂,稳定的资金储备和最好的服务,在商业领域赢得了很高的声誉。地处长江三角洲交通经济中心嘉兴市。 浙江海發進出口股份有限公司 (以下简称海發)是一家多元化的国…

Python轻量级Web框架Flask(5)——Flask模型基础和数据迁移

0、前言:学习这部分的前提是对python的面向对象有一定的了解,同时对MySQL有扎实的学习 1、Flask模型基础知识: Flask模型 ORM (注意:在flask中用ORM可以实现SQL语句功能,但是并不意味着SQL语句不重要&am…

NISACTF2023 WP

NISACTF2023 WP 前言 2年多没玩CTF了,pwn显得手生了不少,我的PWN环境已经在硬盘的某个角落里吃灰了。今天参加了一场校赛,捣鼓了一下午,Reverse和PWN都AK了。其实比赛是新手向,没啥难度,不过有道PWN设计的…

ChatGPT实战100例 - (02) 自动出PPT它不香么?

文章目录ChatGPT实战100例 - (02) 自动出PPT它不香么?一、需求与思路1. 需求:出个PPT,5分钟后要用2. 思路:生成markdown然后转化二、生成markdown语法的思维导图1. 问题2. 回答三、把markdown文本转换成PPTChatGPT实战100例 - (02…

STM32 gpio外部中断详解

什么是中断? 打断CPU执行正常的程序,转而处理紧急程序,然后返回原暂停的程序继续运行,就叫中断 中断的作用和意义 中断的意义:高效处理紧急程序,不会一直占用CPU资源 STM32 GPIO外部中断简图 NVIC 什么…

JSTL标签库

英文全称:Java Standard Tag Lib(Java标准的标签库) 使用目的:JSTL标签库通常结合EL表达式一起使用。目的是让JSP中的java代码消失。 使用位置:JSTL标签是写在JSP当中的,但实际上最终还是要执行对应的jav…

Ubuntu 开机启动 通过crontab定时器去检查脚本 实现

有个项目的程序需要实现开机启动 通过添加一个qmcy.service服务的方法 发现 确实执行脚本了 但是脚本的程序缺并没有起来 但是如果手动执行这个脚本 程序是能起来的 不知道为啥 没办法 网上搜了下 可以通过 crontab定时器去检查 程序是否启动 没启动的话去 执行对应的脚…

【毕业设计】基于程序化生成和音频检测的生态仿真与3D内容生成系统----程序化生成多图层地形贴图的算法设计

(2条消息) 【开发日志】2023.04 ZENO----Image Processing----CompositeCV、Composite2、Composite3_EndlessDaydream的博客-CSDN博客 (2条消息) 【开发日志】2023.04 ZENO----Image Processing----ImageEdit、EditRGB、EditHSV_EndlessDaydream的博客-CSDN博客 (2条消息) 【…

telegraf在iiot领域的基本应用(Modbus,OPC)

熟悉telegraf是因为influxdb缘故,当时telegraf主要是作为granfa监控的agent一环,很多文章有相关的介绍,但作为java开发对telegraf(go语言开发)也仅仅只是适用级别,这边文章也只讲到一些简单的应用。希望能帮…

<STM32>STM32CubeMX-CAN通信(扫描读取数据方式)(5)

<STM32>STM32CubeMX-CAN通信(扫描读取数据方式)(5) 本节主要讲解CAN通信的功能,主要采用扫面检测接收数据的方式; CAN的详细解说可参考《STM32F4XXX中文参考手册》,资料有…

CDH 之 Kerberos 安全认证和 Sentry 权限控制管理(一)

一、Kerberos 和 Sentry 概述 1.1 什么是 Kerberos Kerberos是一种计算机网络授权协议,用来在非安全网络中,对个人通信以安全的手段进行身份认证。这个词又指麻省理工学院为这个协议开发的一套计算机软件。软件设计上采用客户端/服务器结构,…

java微服务商城高并发秒杀项目--011.授权规则和系统规则

授权规则在shop-order-server中新建RequestOriginParserDefinition.java,定义请求来源如何获取Component public class RequestOriginParserDefinition implements RequestOriginParser {Overridepublic String parseOrigin(HttpServletRequest request) {/*** 定义从请求的什么…

文本分类论文阅读

1.ChineseBERT: Chinese Pretraining Enhanced by Glyph and Pinyin Information(ACL2021) 字形嵌入根据汉字的不同字体获得,能够从视觉特征中捕捉汉字语义,拼音嵌入表征汉字的发音,解决了汉语中非常普遍的异义异义现…

四、vue基础-指令(一)、vscode代码片段

一、vscode代码片段 我们在前面联系Vue的过程中,有些代码片段是需要经常写的,我们再VSCode中我们可以生成一个代码片段,方便我们快速生成。VSCode中的代码片段有固定的格式,所以我们一般会借助于一个在线工具来完成。 具体步骤如…

01_什么是Uboot

目录 U-Boot简介 获取Uboot U-Boot初次编译 U-Boot烧写与启动 U-Boot简介 Linux系统要启动就必须需要一个bootloader程序(裸机程序),也就说芯片上电以后先运行一段bootloader程序。这段bootloader程序会先初始化DDR等外设,然后将Linux镜像从flash(NAND,NOR FLASH,SD,EMMC等…