用c语言写代码_如何避免用动态语言的思维写Go代码

由于招聘市场上Go工程师的供给量不足,所以在招人的时候我们招了不少愿意转型用Go语言进行开发的PHP工程师,不过虽说换了个语言,在他们代码的时候还是能发现很多PHP的影子。if语句后面非要带括号这种问题就不说了,这属于不懂事,gofmt就会强行把你掰过来。最大的问题还是因为以前用惯了PHP的数组,到写Go代码时还是不习惯先定义类型后使用这种习惯。还有就是以前写PHP的时候可能没养成使用异常的习惯,在返回值里约定特殊值来代表错误。所以后面我在团队内部做过一次培训,专门分享了怎么建立正确的Go编码习惯,以下是节选了当时演讲稿的一部分。其实不是专门针对PHP程序员,可能写动态语言的程序员在开始用Go写代码时都容易犯的一些错误。

a5c79841baf2e1087bb9b723dc6ad3cb.png

Go编程的注意事项及建议

接下来我们会说几个PHP程序员在刚开始用Go写程序时几个需要改变的编码习惯和要注意的地方。

尽量使用结构体切片代替字典

我们有的新同学特别爱使用Go里面的Map,有的时候还是切片里边套Map,比如我看一开始有的同学把一些配置信息放在map[string]string类型的Map里,多个的话再把Map放进切片里,比如这样。

var configMap = []map[string]string{ {  "stockNum": "100",  "name":     "芒果TV周卡",  "type":     "virtual",    },}

后面程序使用的时候再去用键去取值,这么做程序当然能实现,但你会发现Go里面因为是强类型,你在用上面字典里面的数值时还得对他们做类型转换。很多同学马上会说,那我把Map的类型换成map[string]interface{},我只能说你试试,看你用的时候Go让不让你做类型断言。

这其实是涉及一个思维的转变,那么在像Go这样的强类型语言里针对这种情况该怎么办呢?这就需要让我们养成先定义结构体类型后使用的习惯了,比如像上面的情况我就可以先定义一个类型。

type Product struct { StockNum  int64 Name      string Type      string}var configs = []*Product { {  StockNum: 100,  Name: "芒果TV周卡",  Type: "virtual", },  ......}

这么做就能避免像上面那样使用StockNum前还得把它转成整型的问题了,而且编辑器还能做类型提示,不需要你刻意记得Map里的键,还能避免你一时疏忽把键拼错导致BUG的尴尬。

除了上面说的还有人喜欢在返回值里返回Map,这种写法除了会导致上面说的那样问题,让别人使用起来也特别不方便。比如我要用你的方法我还得进去看看你的代码里这个Map到底有哪些键。

所以我们写Go代码时,其实Map的使用率要比在PHP里使用数组低很多,很多时候都是用结构体以及结构体切片的,对于那种key为数据ID,值为数据Map的这种映射,也是改成Key为数据ID,值为数据自己定义的类型才对。比如下面这个Map类型的变量,它的Key是产品的ID,值的类型是我们上面定义的Product结构体

var productMap = map[int64]*Product { 123:  {  StockNum: 100,  Name: "芒果TV周卡",  Type: "virtual", },}

针对这部分说的这个问题我觉得记住:**"根据数据先定类型再使用"**这个原则就行了。

说完这个在代码里出现率最高的问题后,下面我们再说几个写Go代码时的要注意的细节。

零值陷阱

未进行初始化的变量默认值为其类型的零值,需要注意的是slice,map,chan和*T类型对应的零值是nil。

这些类型的变量在未初始化前是无法在程序里直接使用的,有些情况下会导致运行时错误。

常见的两种运行时错误是:

  • panic: assignment to entry in nil map
  • panic: invalid memory address or nil pointer dereference

第一个错误是因为对一个未初始化的map进行赋值导致的,所以使用map类型的变量前要记得用make函数对变量进行初始化,与map类似的切片在使用append函数 向nil slice追加新元素就可以,原因是append函数会生成新的切片,在底层为切片分配了底层数组。

第二个错误是对nil指针进行了解引用导致的,指针的零值nil与*T{}并不相等。所以指针类型的变量在使用前要注意使用new函数进行初始化。

还有就是前端同学们非常不喜欢接口返回值的字段有数据的时候是个列表,没数据的时候是Null,这也是切片未初始化导致的,如果数据库里没查到数据,那么在代码逻辑里就执行不到给切片append数据的循环里,所以就会出现这个问题。这是一个保持接口字段类型一致性的一个很重要的细节。

使用error返回函数错误

在使用PHP时,函数的错误是通过抛出异常,甚至是通过返回0,false之类的值来表示函数遇到的错误(这种,即使写PHP也不推荐这种做法)

比如好的写法,可这样写:

public function updateUserFavorites(User $user, $favoriteData){    try {        // database execution    ......    } catch (QueryException $queryException) {        throw new UserManageException(func_get_args(), 'Error Message', '501' , $queryException);    }    return true;}

但很多的人会这么写:

public function updateUserFavorites(User $user, $favoriteData){    // database execution  if ($conn.AffectedRows <= 0) {        return false    }    return true;}

在Go语言里虽然没有异常机制,但是可以让函数返回error明确遇到的错误。所以除非确定函数不需要返回error,多数情况下我们的函数都是需要返回error的,所以在定义函数时要明确,返回的数据和error的区别,两种返回值的职责范围不一样。要通过函数返回的error是否为空,而不是返回数据是0或者false之类的值判断函数是否执行成功。

谨慎使用map[string]interface{}做参数

写过PHP的同学都知道,PHP里的数组近乎万能,可以用来当列表、字典,而且当字典用时还能保证字典key的遍历顺序,这点是很多语言的字典类型办不到的事情。

很多刚从PHP转到用Go开发的同学还是带着在PHP里使用数组参数的习惯,那么在Go语言里,最像PHP数组的可能就是map[string]interface{}了。

这种还是典型的动态语言编程的思维,在使用Go的时候,针对比较复杂的代表一类事物的参数,我们也是应该先定义结构体,然后使用结构体指针或者结构体指针切片作为参数。尽量不使用map[string]interface{}这种类型的参数,IDE也没法帮助提示这些参数的内部结构,这让其他人使用这个代码时就会很苦恼,还得先看看函数实现里具体用到了字典的哪些键。比如下面这两个函数的对比:

type UserInput struct{  Name     string  Age      int32  Password string}func AuthenticateUser(input *UserInput) error {    findUser(input.Name, input.Password)    ...}func DummyAuthenticateUser(input map[string]interface{}) error {    findUser(input["name"], input["password"])    ...}

一般在业务级别的程序开发里,我们要传递存储在数据表里的额外信息的时候才会使用到map[string]interface{}类型的参数。写表前把这部分数据编码成JSON格式再写入,当然这个主要看使用场景,凡事没有绝对,这里只是强调一些在编码习惯上的问题。

总结

最近两年在学习中我写了不少Go语言的文章,其中Web编程入门和Go并发编程这两个系列我自认为还是对新手很有帮助的。

来源:网管叨bi叨

作者:KevinYan11

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

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

相关文章

java ee cdi_Java EE CDI依赖关系消歧示例

java ee cdi在本教程中&#xff0c;我们将向您展示如何避免CDI bean中的依赖关系消除歧义。 在CDI中&#xff0c;我们可以为应用程序中不同客户端的接口的多个实现实现依赖项注入。 依赖性消除歧义的问题是客户端如何在不同的实现中调用特定的实现&#xff0c;而不会发生任何错…

tfw文件如何导入cad_教你三维家3d设计软件如何导入cad文件

一、导入CAD户型图的&#xff0c;注意点&#xff1a;CAD图要求&#xff1a;除墙体外&#xff0c;其它线段不可出现&#xff0c;(如&#xff1a;门、柱子、窗、阳台、标注、家具、植物、摆件等)且墙体线条要闭合&#xff0c;保存DXF格式。第一步:点击户型图--本地上传CAD图。第二…

用EnumMaps映射枚举键

这是一种在JDK中存在很长时间的类型&#xff0c;当我们要定义以枚举类型作为键的映射时会派上用场&#xff1a; EnumMap是一种特殊的Map 。 我们将为给定的枚举创建一个映射&#xff1a; public enum CoffeeType {ESPRESSO, POUR_OVER, FRENCH_PRESS }EnumMap在创建时需要注意…

同级选择器_10-CSS3选择器详解

CSS3在CSS2基础上&#xff0c;增强或新增了许多特性&#xff0c; 弥补了CSS2的众多不足之处&#xff0c;使得Web开发变得更为高效和便捷。CSS3的现状浏览器支持程度不够好&#xff0c;有些需要添加私有前缀移动端支持优于PC端不断改进中应用相对广泛应对的策略&#xff1a;渐进…

机器人庄园作文_十年后的家乡作文精选8篇

十年后的家乡作文精选8篇十年后的家乡作文&#xff1a;十年后的家乡十年前我的家乡美丽富饶&#xff0c;家前的小溪清澈见底&#xff0c;小溪妹妹还&#xff1a;“哗啦啦”的唱起了欢乐的歌谣。树木葱葱茏茏&#xff0c;花朵都露出了美丽可爱的笑脸&#xff0c;蝴蝶、蜜蜂、小鸟…

java heroku_Heroku和Java –从新手到初学者,第2部分

java heroku问题 所以过了几天&#xff0c;我可以回到我的Recaps小项目。 我从检查日志开始&#xff0c;发现了以下内容&#xff1a; 2012-03-04T01:52:5100:00 heroku[web.1]: Idling 2012-03-04T01:52:5300:00 heroku[web.1]: Stopping process with SIGTERM 2012-03-04T01:…

存储限制_明年6月份开始,谷歌相册将终止免费无限存储服务

站长之家(ChinaZ.com)11月12日 消息:在提供免费服务5年后&#xff0c;谷歌对外宣布将终止提供无限容量免费照片存储服务&#xff0c;转而只提供的15GB免费存储空间&#xff0c;超过部分就需要向谷歌付费。这一变化将于2021年6月1日生效&#xff0c;这意味着如果用户上传的照片超…

下载 沙耶之歌Android_沙耶之歌安卓版apk-沙耶之歌下载手机版v1.2-飘荡下载

一款超经典的日式ADV游戏&#xff0c;游戏中有着精致的动漫风格画风&#xff0c;并且讲述了一个非常重口味但又异常纯洁的恋爱故事&#xff0c;玩家将会扮演男主进行游戏&#xff0c;超级丰富精彩的剧情等你来体验&#xff0c;并且还有着不同的剧情选项可以选择&#xff0c;能否…

Maven,Eclipse和Java 9

任何在eclipse中使用M2E&#xff08;maven-to-eclipse&#xff09;插件的人都知道您在哪里运行构建的问题&#xff0c;然后在项目上更新maven只是让它重置JRE并抛出一堆项目错误&#xff01; 我在使用Open Liberty与Java 9一起运行Java EE 8的帖子中注意到了这个问题 解决方案…

python变量持久化_Python 数据持久化:JSON

Python 数据持久化&#xff1a;JSON编程派微信号&#xff1a;codingpy淡蓝色字体可以直接点击查看上周更新的《Think Python 2e》第14章讲述了几种数据持久化的方式&#xff0c;包括dbm、pickle等&#xff0c;但是考虑到篇幅和读者等因素&#xff0c;并没有将各种方式都列全。本…

jwt配置 restful_SpringBoot实现JWT保护前后端分离RESTful API

本文将用不到100行Java代码, 教你如何在Spring Boot里面用JWT保护RESTful api.登录前登录之后即可得到正确结果登陆后1. 什么是JWT了解JWT的同学可以跳过这一部分废话少说, 我们先看看什么是JWT. JSON Web Token其实就是一个包含认证数据的JSON, 大概长这样子分三个部分,第一部…

fusion构建器代码语法_构建器模式:适用于代码,适用于测试

fusion构建器代码语法我发现构建器设计模式偶尔在代码中有用&#xff0c;但在测试中经常有用。 本文简要概述了该模式&#xff0c;然后介绍了在测试中使用该模式的一个有效示例。 请参阅github中的代码。 生成器模式的背景 根据GoF的书 &#xff0c;构建器设计模式用于“将复杂…

6000毫安以上智能手机_三星超长续航神机,6000毫安+128GB,上市半年不到跌至1499...

现在的手机是越来越智能了&#xff0c;无论是苹果还是安卓&#xff0c;基本都能为用户的生活添加几分乐趣&#xff0c;因为&#xff0c;当我们感到无聊时&#xff0c;基本都可以通过智能手机来打发时间。据我所知&#xff0c;不少人在用智能手机时都有个困扰&#xff0c;就是续…

使用RabbitMQ进行消息传递

RabbitMQ是一个强大的消息代理&#xff0c;可用于实现不同的消息传递模式。 即使有出色的教程 &#xff08;使用不同的语言和框架&#xff09;&#xff0c;也很难理解这些概念。 在这篇文章中&#xff0c;我想展示一些可以用RabbitMQ实现的不同范例&#xff0c;以及为什么要为某…

android 为什么fragment在调用hide方法后没有生效_Android 多 Fragment 切换优化

code小生,一个专注 Android 领域的技术平台公众号回复 Android 加入我的安卓技术群作者&#xff1a;DDDong丶链接&#xff1a;https://www.jianshu.com/p/c8e8a0249911声明&#xff1a;本文已获DDDong丶授权发表&#xff0c;转发等请联系原作者授权问题分析一直在简书里看别人的…

mysql如何查看远程用户_MySQL系列(十)--用户权限及远程访问

本文基于MySQL8.0&#xff0c;记录一下完整的远程访问的过程&#xff0c;以及这个过程中可能遇到的问题&#xff0c;MySQL运行在阿里云服务器&#xff0c;操作系统&#xff1a;CentOS 7.6 64位顺便说下&#xff0c;买服务器还是要双十二这种拉新活动再买&#xff0c;用一个新的…

spring mvc拆分_Spring集成–强大的拆分器聚合器

spring mvc拆分健壮是什么意思&#xff1f; 在本文的上下文中&#xff0c;健壮性是指在不立即返回给调用者的情况下管理流中的异常条件的能力。 在某些处理方案中&#xff0c; n个 m个回答足以做出结论。 通常具有这些趋势的示例处理场景是&#xff1a; 财务&#xff0c;保…

mysql typeindex_explain mysql的type字段,索引的类型

4.type这列很重要,显示了连接使用了哪种类别,有无使用索引.从最好到最差的连接类型为const、eq_reg、ref、range、indexhe和ALL(1).system这是const联接类型的一个特例。表仅有一行满足条件.如下(t3表上的id是 primary key)mysql> explain select * from (select * from t3 …

JasperReports:棘手的部分

如果您使用Java进行编程的时间足够长&#xff0c;则有可能需要为业务用户生成报告。 就我而言&#xff0c;我已经看到几个项目使用JasperReports库来生成PDF和其他文件格式的报告。 最近&#xff0c;我荣幸地观察了Mike和他的团队使用上述报告库及其面临的挑战。 简而言之Jasp…

win mysql 2003错误_windows MySql 报1067错误 2003错误

1067错误原因是我把安装mysql的目录的名字改了。但是位于目录里面的my.ini配置文件没有修改&#xff0c;玛德我真傻逼。把my.ini的#Path to installation directory. All paths are usually resolved relative to this.basedir"D:/My_MySQL/"这下面一行的路径名改成目…