前言
随着容器编排(ContainerOrchestration)、微服务(MicroServices)、云技术(CloudTechnology)等在IT行业不断盛行,年诞生于Google的Golang(Go语言,简称Go)越来越受到软件工程师的欢迎和追捧,成为如今炙手可热的后端编程语言。
在用Golang开发的软件项目列表中,有Docker(容器技术)、Kubernetes(容器编排)这样的颠覆整个IT行业的明星级产品,也有像Prometheus(监控系统)、Etcd(分布式存储)、InfluxDB(时序数据库)这样的强大实用的知名项目。当然,Go语言的应用领域也绝不局限于容器和分布式系统。如今很多大型互联网企业在大量使用Golang构建后端Web应用,例如今日头条、京东、七牛云等;长期被Python统治的框架爬虫领域也因为简单而易用的爬虫框架Colly的崛起而不断受到Golang的挑战。Golang已经成为了如今大多数软件工程师最想学习的编程语言。下图是HackerRank在年调查程序员技能的相关结果。
那么,Go语言真的是后端开发人员的救命良药呢?它是否能够有效提高程序员们的技术实力和开发效率,从而帮助他们在职场上更进一步呢?Go语言真的值得我们花大量时间深入学习么?本文将详细介绍Golang的语言特点以及它的优缺点和适用场景,带着上述几个疑问,为读者分析Go语言的各个方面,以帮助初入IT行业的程序员以及对Go感兴趣的开发者进一步了解这个热门语言。
Golang简介
Golang诞生于互联网巨头Google,而这并不是一个巧合。我们都知道,Google有一个20%做业余项目(SideProject)的企业文化,允许工程师们能够在轻松的环境下创造一些具有颠覆性创新的产品。而Golang也正是在这20%时间中不断孵化出来。Go语言的创始者也是IT界内大名鼎鼎的行业领袖,包括Unix核心团队成员RobPike、C语言作者KenThompson、V8引擎核心贡献者RobertGriesemer。Go语言被大众所熟知还是源于容器技术Docker在年被开源后的爆发式发展。之后,Go语言因为其简单的语法以及迅猛的编译速度受到大量开发者的追捧,也诞生了很多优秀的项目,例如Kubernetes。
Go语言相对于其他传统热门编程语言来说,有很多优点,特别是其高效编译速度和天然并发特性,让其成为快速开发分布式应用的首选语言。Go语言是静态类型语言,也就是说Go语言跟Java、C#一样需要编译,而且有完备的类型系统,可以有效减少因类型不一致导致的代码质量问题。因此,Go语言非常适合构建对稳定性和灵活性均有要求的大型IT系统,这也是很多大型互联网公司用Golang重构老代码的重要原因:传统的静态OOP语言(例如Java、C#)稳定性高但缺乏灵活性;而动态语言(例如PHP、Python、Ruby、Node.js)灵活性强但缺乏稳定性。因此,“熊掌和鱼兼得”的Golang,受到开发者们的追捧是自然而然的事情,毕竟,“天下苦Java/PHP/Python/Ruby们久矣“。
不过,Go语言并不是没有缺点。用辩证法的思维方式可以推测,Golang的一些突出特性将成为它的双刃剑。例如,Golang语法简单的优势特点将限制它处理复杂问题的能力。尤其是Go语言缺乏泛型(Generics)的问题,导致它构建通用框架的复杂度大增。虽然这个突出问题在2.0版本很可能会有效解决,但这也反映出来明星编程语言也会有缺点。当然,Go的缺点还不止于此,Go语言使用者还会吐槽其啰嗦的错误处理方式(ErrorHandling)、缺少严格约束的鸭子类型(DuckTyping)、日期格式问题等。下面,我们将从Golang语言特点开始,由浅入深多维度深入分析Golang的优缺点以及项目适用场景。
语言特点
简洁的语法特征
Go语言的语法非常简单,至少在变量声明、结构体声明、函数定义等方面显得非常简洁。
变量的声明不像Java或C那样啰嗦,在Golang中可以用:=这个语法来声明新变量。例如下面这个例子,当你直接使用:=来定义变量时,Go会自动将赋值对象的类型声明为赋值来源的类型,这节省了大量的代码。
funcmain(){valInt:=1//自动推断int类型valStr:=hello//自动推断为string类型valBool:=false//自动推断为bool类型}
Golang还有很多帮你节省代码的地方。你可以发现Go中不会强制要求用new这个关键词来生成某个类(Class)的新实例(Instance)。而且,对于公共和私有属性(变量和方法)的约定不再使用传统的public和private关键词,而是直接用属性变量首字母的大小写来区分。下面一些例子可以帮助读者理解这些特点。
//定义一个struct类typeSomeClassstruct{PublicVariablestring//公共变量privateVariablestring//私有变量//公共方法func(c*SomeClass)PublicMethod()(resultstring){returnThiscanbecalledbyexternalmodules//私有方法func(c*SomeClass)privateMethod()(resultstring){returnThiscanonlybecalledinSomeClass//生成实例someInstance:=SomeClass{PublicVariable:hello,privateVariable:world,}
如果你用Java来实现上述这个例子,可能会看到冗长的.java类文件,例如这样。
//SomeClass.javapublicSomeClass{publicStringPublicVariable;//公共变量privateStringprivateVariable;//私有变量//构造函数publicSomeClass(Stringval1,Stringval2){this.PublicVariable=val1;this.privateVariable=val2;//公共方法publicStringPublicMethod(){returnThiscanbecalledbyexternalmodules;//私有方法publicStringprivateMethod(){returnThiscanonlybecalledinSomeClass;...//Application.javapublicApplication{publicstaticvoidmain(String[]args){//生成实例SomeClasssomeInstance=newSomeClass(hello,world);
可以看到,在Java代码中除了容易看花眼的多层花括号以外,还充斥着大量的public、private、static、this等修饰用的关键词,显得异常啰嗦;而Golang代码中则靠简单的约定,例如首字母大小写,避免了很多重复性的修饰词。当然,Java和Go在类型系统上还是有一些区别的,这也导致Go在处理复杂问题显得有些力不从心,这是后话,后面再讨论。总之,结论就是Go的语法在静态类型编程语言中非常简洁。
内置并发编程
Go语言之所以成为分布式应用的首选,除了它性能强大以外,其最主要的原因就是它天然的并发编程。这个并发编程特性主要来自于Golang中的协程(Goroutine)和通道(Channel)。下面是使用协程的一个例子。
funcasyncTask(){fmt.Printf(Thisisanasynchronizedtask)funcsyncTask(){fmt.Printf(Thisisasynchronizedtask)goasyncTask()//异步执行,不阻塞syncTask()//同步执行,阻塞goasyncTask()//等待前面syncTask完成之后,再异步执行,不阻塞
可以看到,关键词go加函数调用可以让其作为一个异步函数执行,不会阻塞后面的代码。而如果不加go关键词,则会被当成是同步代码执行。如果读者熟悉JavaScript中的async/await、Promise语法,甚至是Java、Python中的多线程异步编程,你会发现它们跟Go异步编程的简单程度不是一个量级的!
异步函数,也就是协程之间的通信可以用Go语言特有的通道来实现。下面是关于通道的一个例子。
funclongTask(signalchanint){//不带参数的for//相当于while循环for{//接收signal通道传值v:=-signal//如果接收值为1,停止循环ifv==1{break}time.Sleep(1*Second)//声明通道sig:=make(chanint)//异步调用longTaskgolongTask(sig)//等待1秒钟time.Sleep(1*time.Second)//向通道sig传值sig-1//然后longTask会接收sig传值,终止循环
面向接口编程
Go语言不是严格的面向对象编程(OOP),它采用的是面向接口编程(IOP),是相对于OOP更先进的编程模式。作为OOP体系的一部分,IOP更加强调规则和约束,以及接口类型方法的约定,从而让开发人员尽可能的