4前言:为什么我们需要一门新语言口代码风格规范口错误处理规范口包管理口契约规范(接口)口单元测试规范口功能开发的流程规范Go语言很可能是第一个将代码风格强制统一的语言,例如Go语言要求pub1ic的变量必须以大写字母开头,private变量则以小写字母开头,这种做法不仅免除了public、private关键字,更重要的是统一了命名风格。另外,Go语言对()应该怎么写进行了强制,比如以下风格是正确的:if expression (1但下面这个写法就是错误的:if expressionf3而C和Java语言中则对花括号的位置没有任何要求。哪种更有利,这个见仁见智。但很显然的是,所有的Go代码的花括号位置肯定是非常统一的。最有意思的其实还是Go语言首创的错误处理规范:f,err := os.Open(filename)if err != nil (log.Println("openfile failed:",err)returnJdefer f.close().。//操作已经打开的f文件这里有两个关键点。其一是defer关键字。defer语句的含义是不管程序是否出现异常,均在函数退出时自动执行相关代码。在上面的例子中,正是因为有了defer,才使得无论后续是否会出现异常,都可以确保文件被正确关闭。其二是Go语言的函数允许返回多个值。大多数函数的最后一个返回值会为error类型,以在错误情况下返回详细信息。error类型只是一个系统内置的interface,如下:type error interface fError() string0有了error类型,程序出现错误的逻辑看起来就相当统一。在Java中,你可能这样写代码来保证资源正确释放:Connection conn =...i图灵社区会员soooldier(soooldier@live.com)专享尊重版权
4 前言:为什么我们需要一门新语言 代码风格规范 错误处理规范 包管理 契约规范(接口) 单元测试规范 功能开发的流程规范 Go语言很可能是第一个将代码风格强制统一的语言,例如Go语言要求public的变量必须以 大写字母开头,private变量则以小写字母开头,这种做法不仅免除了public、private关键 字,更重要的是统一了命名风格。 另外,Go语言对{ }应该怎么写进行了强制,比如以下风格是正确的: if expression { . } 但下面这个写法就是错误的: if expression { . } 而C和Java语言中则对花括号的位置没有任何要求。哪种更有利,这个见仁见智。但很显然 的是,所有的Go代码的花括号位置肯定是非常统一的。 最有意思的其实还是 Go 语言首创的错误处理规范: f, err := os.Open(filename) if err != nil { log.Println("Open file failed:", err) return } defer f.Close() . // 操作已经打开的f文件 这里有两个关键点。其一是defer关键字。defer语句的含义是不管程序是否出现异常,均 在函数退出时自动执行相关代码。在上面的例子中,正是因为有了defer,才使得无论后续是否 会出现异常,都可以确保文件被正确关闭。其二是Go语言的函数允许返回多个值。大多数函数 的最后一个返回值会为error类型,以在错误情况下返回详细信息。error类型只是一个系统内 置的interface,如下: type error interface { Error() string } 有了error类型,程序出现错误的逻辑看起来就相当统一。 在Java中,你可能这样写代码来保证资源正确释放: Connection conn = .; 图灵社区会员 soooldier(soooldier@live.com) 专享 尊重版权
前言:为什么我们需要一门新语言5trytStatement stmt =...try(Resultset rset - ...itry(1I正常代码1finally(rset.close():01finally(stmt.close():11finally(conn.close();J完成同样的功能,相应的Go代码只需要写成这样:conn :=defer conn.Close()stmt :=defer stmt.close()rset :-defer rset.close()正常代码对比两段代码,Go语言处理错误的优势显而易见。当然,其实Go语言带给我们的惊喜还有很多,后续有机会我们可以就某个更具体的话题详细展开来谈一谈。编程哲学计算机软件经历了数十年的发展,形成了多种学术流派,有面向过程编程、面向对象编程、函数式编程、面向消息编程等,这些思想究竟敦优熟劣,众说纷绘。C语言是纯过程式的,这和它产生的历史背景有关。Java语言则是激进的面向对象主义推崇者,典型表现是它不能容忍体系里存在孤立的函数。而Go语言没有去否认任何一方,而是用批判吸收的眼光,将所有编程思想做了一次梳理、融合众家之长,但时刻警惕特性复杂化,极力维持语言特性的简洁,力求小而精。从编程范式的角度来说,Go语言是变革派,而不是改良派。对于C++、Java和C#等语言为代表的面向对象(OO)思想体系,Go语言总体来说持保守态度,有限吸收。首先,Go语言反对函数和操作符重载(overload),而C++、Java和C#都充许出现同名函数或操作符,只要它们的参数列表不同。虽然重载解决了一小部分面向对象编程(OOP)的问题,但图灵社区会员soooldier(soooldier@live.com)专享尊重版权
前言:为什么我们需要一门新语言 5 1 2 3 4 5 9 6 7 8 try { Statement stmt = .; try { ResultSet rset = .; try { . // 正常代码 } finally { rset.close(); } } finally { stmt.close(); } } finally { conn.close(); } 完成同样的功能,相应的Go代码只需要写成这样: conn := . defer conn.Close() stmt := . defer stmt.Close() rset := . defer rset.Close() . // 正常代码 对比两段代码,Go语言处理错误的优势显而易见。当然,其实Go语言带给我们的惊喜还有 很多,后续有机会我们可以就某个更具体的话题详细展开来谈一谈。 编程哲学 计算机软件经历了数十年的发展,形成了多种学术流派,有面向过程编程、面向对象编程、 函数式编程、面向消息编程等,这些思想究竟孰优孰劣,众说纷纭。 C语言是纯过程式的,这和它产生的历史背景有关。Java语言则是激进的面向对象主义推崇 者,典型表现是它不能容忍体系里存在孤立的函数。而Go语言没有去否认任何一方,而是用批 判吸收的眼光,将所有编程思想做了一次梳理,融合众家之长,但时刻警惕特性复杂化,极力维 持语言特性的简洁,力求小而精。 从编程范式的角度来说,Go语言是变革派,而不是改良派。 对于C++、Java和C#等语言为代表的面向对象(OO)思想体系,Go语言总体来说持保守态 度,有限吸收。 首先,Go语言反对函数和操作符重载(overload),而C++、Java和C#都允许出现同名函数或 操作符,只要它们的参数列表不同。虽然重载解决了一小部分面向对象编程(OOP)的问题,但 图灵社区会员 soooldier(soooldier@live.com) 专享 尊重版权
6前言:为什么我们需要一门新语言同样给这些语言带来了极大的负担。而Go语言有着完全不同的设计哲学,既然函数重载带来了负担,并且这个特性并不对解决任何问题有显著的价值,那么Go就不提供它。其次,Go语言支持类、类成员方法、类的组合,但反对继承,反对虚函数(virtualfunction)和虚函数重载。确切地说,Go也提供了继承,只不过是采用了组合的文法来提供:type Foo struct(Basefunc (foo *Foo)Bar()(}再次,Go语言也放弃了构造函数(constructor)和析构函数(destructor)。由于Go语言中没有虚函数,也就没有vptr,支持构造函数和析构函数就没有太大的价值。本着“如果一个特性并不对解决任何问题有显著的价值,那么Go就不提供它”的原则,构造函数和析构函数就这样被Go语言的作者们干掉了。在放弃了大量的OOP特性后,Go语言送上了一份非常棒的礼物:接口(interface)。你可能会说,除了C这么原始的语言外,还有什么语言没有接口呢?是的,多数语言都提供接口,但它们的接口都不同于Go语言的接口。Go语言中的接口与其他语言最大的一点区别是它的非侵入性。在C++、Java和C#中,为了实现一个接口,你需要从该接口继承,具体代码如下:classFooimplementsIFoo(//Java文法classFoo:publicIFoof//C++文法1IFoo* foo=new Foo;在Go语言中,实现类的时候无需从接口派生,具体代码如下:typeFoostruct(//Go丈法0var foo IFoo =new(Foo)只要Foo实现了接口IFoo要求的所有方法,就实现了该接口,可以进行赋值。Go语言的非侵入式接口,看似只是做了很小的文法调整,实则影响深远。其一,Go语言的标准库再也不需要绘制类库的继承树图。你只需要知道这个类实现了哪此方法,每个方法是啥含义就足够了。其二,不用再纠结接口需要拆得多细才合理,比如我们实现了Fi1e类,它有下面这些方法:图灵社区会员soooldier(soooldier@live.com)专享尊重版权
6 前言:为什么我们需要一门新语言 同样给这些语言带来了极大的负担。而Go语言有着完全不同的设计哲学,既然函数重载带来了 负担,并且这个特性并不对解决任何问题有显著的价值,那么Go就不提供它。 其次,Go语言支持类、类成员方法、类的组合,但反对继承,反对虚函数(virtual function) 和虚函数重载。确切地说,Go也提供了继承,只不过是采用了组合的文法来提供: type Foo struct { Base . } func (foo *Foo) Bar() { . } 再次,Go语言也放弃了构造函数(constructor)和析构函数(destructor)。由于Go语言中没 有虚函数,也就没有vptr,支持构造函数和析构函数就没有太大的价值。本着“如果一个特性 并不对解决任何问题有显著的价值,那么Go就不提供它”的原则,构造函数和析构函数就这样 被Go语言的作者们干掉了。 在放弃了大量的OOP特性后,Go语言送上了一份非常棒的礼物:接口(interface)。你可能 会说,除了C这么原始的语言外,还有什么语言没有接口呢?是的,多数语言都提供接口,但它 们的接口都不同于Go语言的接口。 Go语言中的接口与其他语言最大的一点区别是它的非侵入性。在C++、Java和C#中,为了实 现一个接口,你需要从该接口继承,具体代码如下: class Foo implements IFoo { // Java文法 . } class Foo : public IFoo { // C++文法 . } IFoo* foo = new Foo; 在Go语言中,实现类的时候无需从接口派生,具体代码如下: type Foo struct { // Go 文法 . } var foo IFoo = new(Foo) 只要Foo实现了接口IFoo要求的所有方法,就实现了该接口,可以进行赋值。 Go语言的非侵入式接口,看似只是做了很小的文法调整,实则影响深远。 其一,Go语言的标准库再也不需要绘制类库的继承树图。你只需要知道这个类实现了哪些 方法,每个方法是啥含义就足够了。 其二,不用再纠结接口需要拆得多细才合理,比如我们实现了File类,它有下面这些方法: 图灵社区会员 soooldier(soooldier@live.com) 专享 尊重版权
前言:为什么我们需要一门新语言7Read(buf [jbyte)(n int,err error)Write(buf [jbyte) (n int, err error)Seek(off int64,whence int) (pos int64,err error)close()error那么,到底是应该定义一个IFile接口,还是应该定义一系列的IReader、IWriter、ISeeker和Ic1oser接口,然后让File从它们派生好呢?事实上,脱离了实际的用户场景,讨论这两个设计哪个更好并无意义。问题在于,实现Fi1e类的时候,我怎么知道外部会如何用它呢?其三,不用为了实现一个接口而专门导入一个包,而自的仅仅是引用其中的某个接口的定义。在Go语言中,只要两个接口拥有相同的方法列表,那么它们就是等同的,可以相互赋值,如对于以下两个接口,第一个接口:package onetype Readwriter interface(Read(buf [] byte)(n int,err error)Write(buf[) byte)(n int,err error)第二个接口:package twotype Istreaminterface(Write(buf [j byte) (n int,err error)Read(buf [] byte)(n int, err error)1这里我们定义了两个接口,一个叫one.Readwriter,一个叫two.Istream,两者都定义了Read()和write()方法,只是定义的次序相反。one.Readwriter先定义了Read()再定义Write(),而two.Istream反之。在Go语言中,这两个接口实际上并无区别,因为:口任何实现了one.Readwriter接口的类,均实现了two.Istream;口任何one.Readwriter接口对象可赋值给two.Istream,反之亦然;口在任何地方使用one.Readwriter接口,与使用two.Istream并无差异。所以在Go语言中,为了引用另一个包中的接口而导人这个包的做法是不被推荐的。因为多引用一个外部的包,就意味着更多的耦合。除了OOP外,近年出现了一些小众的编程哲学,Go语言对这些思想亦有所吸收。例如,Go语言接受了函数式编程的一些想法,支持匿名函数与闭包。再如,Go语言接受了以Erlang语言为代表的面向消息编程思想,支持goroutine和通道,并推荐使用消息而不是共享内存来进行并发编程。总体来说,Go语言是一个非常现代化的语言,精小但非常强大。图灵社区会员soooldier(soooldier@live.com)专享尊重版权
前言:为什么我们需要一门新语言 7 1 2 3 4 5 9 6 7 8 Read(buf []byte) (n int, err error) Write(buf []byte) (n int, err error) Seek(off int64, whence int) (pos int64, err error) Close() error 那么,到底是应该定义一个IFile接口,还是应该定义一系列的IReader、IWriter、 ISeeker和ICloser接口,然后让File从它们派生好呢?事实上,脱离了实际的用户场景,讨 论这两个设计哪个更好并无意义。问题在于,实现File类的时候,我怎么知道外部会如何用它 呢? 其三,不用为了实现一个接口而专门导入一个包,而目的仅仅是引用其中的某个接口的定义。 在Go语言中,只要两个接口拥有相同的方法列表,那么它们就是等同的,可以相互赋值,如对 于以下两个接口,第一个接口: package one type ReadWriter interface { Read(buf [] byte) (n int, err error) Write(buf [] byte) (n int, err error) } 第二个接口: package two type IStream interface { Write(buf [] byte) (n int, err error) Read(buf [] byte) (n int, err error) } 这里我们定义了两个接口,一个叫one.ReadWriter,一个叫two.IStream,两者都定义 了Read()和Write()方法,只是定义的次序相反。one.ReadWriter先定义了Read()再定义 Write(),而two.IStream反之。 在Go语言中,这两个接口实际上并无区别,因为: 任何实现了one.ReadWriter接口的类,均实现了two.IStream; 任何one.ReadWriter接口对象可赋值给two.IStream,反之亦然; 在任何地方使用one.ReadWriter接口,与使用two.IStream并无差异。 所以在Go语言中,为了引用另一个包中的接口而导入这个包的做法是不被推荐的。因为多 引用一个外部的包,就意味着更多的耦合。 除了OOP外,近年出现了一些小众的编程哲学,Go语言对这些思想亦有所吸收。例如,Go 语言接受了函数式编程的一些想法,支持匿名函数与闭包。再如,Go语言接受了以Erlang语言为 代表的面向消息编程思想,支持goroutine和通道,并推荐使用消息而不是共享内存来进行并发编 程。总体来说,Go语言是一个非常现代化的语言,精小但非常强大。 图灵社区会员 soooldier(soooldier@live.com) 专享 尊重版权
8前言:为什么我们需要一门新语言小结在十余年的技术生涯中,我接触过、使用过、喜爱过不同的编程语言,但总体而言,Go语言的出现是最让我兴奋的事情。我个人对未来10年编程语言排行榜的趋势判断如下:口Java语言的份额继续下滑,并最终被C和Go语言超越;口C语言将长居编程榜第二的位置,并有望在Go取代Java前重获语言榜第一的宝座:口Go语言最终会取代Java,居于编程榜之首。由七牛云存储团队编著的这本书将尽可能展现出Go语言的迷人魅力。希望本书能够让更多人理解这门语言,热爱这门语言,让这门优秀的语言能够落到实处,把程序员从以往繁杂的语言细节中解放出来,集中精力开发更加优秀的系统软件。许式伟2012年3月7日图灵社区会员soooldier(soooldier@live.com)专享尊重版权
8 前言:为什么我们需要一门新语言 小结 在十余年的技术生涯中,我接触过、使用过、喜爱过不同的编程语言,但总体而言,Go语 言的出现是最让我兴奋的事情。我个人对未来10年编程语言排行榜的趋势判断如下: Java语言的份额继续下滑,并最终被C和Go语言超越; C语言将长居编程榜第二的位置,并有望在Go取代Java前重获语言榜第一的宝座; Go语言最终会取代Java,居于编程榜之首。 由七牛云存储团队编著的这本书将尽可能展现出Go语言的迷人魅力。希望本书能够让更多 人理解这门语言,热爱这门语言,让这门优秀的语言能够落到实处,把程序员从以往繁杂的语言 细节中解放出来,集中精力开发更加优秀的系统软件。 许式伟 2012年3月7日 图灵社区会员 soooldier(soooldier@live.com) 专享 尊重版权