chapter1 Go简介 翻译实践

1 Go简介

计算机一直在演进,但编程语言却原地踏步.手机处理器已经比我们最初的电脑核心数还要多,高性能服务器已经有64,128个甚至更多的核心,但我们还在使用基于单个核心的编程技术.
编程的艺术也已经进化.许多程序已经不再是单一开发者:它们被分布在不同时区与不同时间的团队编写.大型项目被分成小块分配给程序员,然后以软件库或包的形式递送回组成完整的应用.
今天的程序员与计算越来越相信开源软件的力量.Go是一种轻松共享代码程序语言.Go工具集使得使用其它人的打包更轻松,也更容易共享自己的包.
在这一章你会看到Go与其它编程语言的不同.Go重新思考了传统的面向对象的开发,并提供了一个有效的代码重用思想.Go使得有效的利用现存服务器的所有核心更容易,在大型项目中的不利因素一去不复返.

1.1 使用Go解决当今编程挑战

Go团队长期致力于解决面向软件开发的问题.开发者经常为项目的快速开发与性能之间做痛苦的语言选择.C和C++运行快速,但例如ruby和python经常可以快速开发.Go架起了两者的桥梁,并为快速开发提供了高性能特性.
作为一门编程语言,Go被定义为不限定它包含的,也包括它不包含的.Go提供了简明的语法及少量的关键字易于记忆.Go提供了超快的编译器,以至于你会感觉不到它运行过.作为GO开发者,明显花费更少的时间等待项目创建.由于GO内置并行特性,软件不需要被调度使用资源而强制使用特殊的进程库.GO使用简单有效的类系统封装面向对象开发,你可以专注于代码重用.GO提供了GC垃圾回收,不需要关注内存管理.

1.1.1 敏捷开发

使用C或C++编译大型应用通常花费大量的时间.
GO通过使用智能编译器与简便的信赖解决算法提供快速轻量的编译.当你创建一个GO程序,编译器只需要查看直接包含的库,而不是像jave,c,c++一样追踪全部包所包含的所有库的依赖.所以许多GO应用秒内编译.全部的GO资源树在当今的硬件条件下20秒内编译.
使用动态语言编写应用使得产品快速开发因为它们在写代码与执行之间没有中间步骤.交换的代价是动态语言不能提供type安全,但静态语言可以.运行同时需要全面的测试套件去调用发现不正确的type bugs.
想像一下使用动态语言如javascript编写大型应用,通过一个函数等待接收一个ID字段.这个字段是一个整数,字符串还是UUID?只能通过查看源代码找出来.你可以尝试执行这个函数传入一个数字,字符串然后查看将会发生什么.在GO中,你不需要花费时间惊异,因为编译器会为你捕捉不同.

1.1.2 并行

作为程序员的一件困难的事是编写高效地利用硬件上运行的可用资源的应用.现在计算机会有多个核心,但多数程序语言没有高效的工具轻松调度那些附加的资源.它们通常需要大量的线程同步代码,容易带来错误.
GO的concurryency是它强大的特性之一.goroutines类型线程,但使用非常少的内存,只需要非常少的代码实现.Channels可以与内部go例行程序同步发送typed信息.这简化了一个程序模型,你可以在goroutines之间发送数据,而不是让goroutines序使用相同的数据.
下面来详细查看这些特性.

1.1.2.1 goroutines


goroutines是与其它goroutines并行运行的功能,是程序的接入口.类似在其它编程语言中使用的线程,但go语言中一个线程上运行多个goroutines.举例说明,如果你想写一个web服务程序同时处理不同的web请求,在C或java中你必须写大量的额外代码处理线程.但在GO内置goroutines使用net/http库处理并行.每个请求自动绑定并只在自己的goroutine中运行.goroutines相比threads只需更少内存,go runtime会自动调度goroutines的执行,配置分配给一些的逻辑处理器.每个逻辑处理器绑定到单个OS thread.如此使得你的应用显著的减少开发工作量更有效率.
如果你想并行的执行一些代码以便抽出时间完成其它事情,对此goroutine是完美的.
如上所述,goruotines的开销很小,所以成千上万的大量运行是常见的.

1.1.2.2 Channels


Channels是一种在goroutines之间安全的数据通信的数据构造.channels可以帮助减少编程语言共享内存访问中的常规错误.
并行最大的挑战是确保数据不会被并行运行的程序,线程或goroutines非预期的修改.当多个线程在没有锁或同步机制的状态下修改相同的数据,悲剧总是发生.在其它编程语言中,当你拥有全局变量和共享内存,你需要使用复杂的锁练习避免对同一变量的非同步修改.
Channels的解决方法是提供了一个pattern确保并行修改数据安全.Channels强制pattern同一时间只能有一个goroutine修改数据.想像一下应用有许多不同的进程需要顺序地调用或修改数据.使用goruntines和channels,你可以安全的建模.
如上图,有3个goroutines,2个channels.goroutine 1 通过channel one发送数据到已经等待中的goroutines 2.两个goroutines的数据交换是同步的,一旦传送发生,两个goroutines都会知道交换地址.当goroutine 2 使用这些数据处理完它的任务,它会发送这些数据到已经等待中的goroutine3.交换同样是同步的,双方都可以保证交换成功.这种安全的交换数据方式不需要其它的锁或同步架构.
重要的一点,channels不提供数据访问保护.如果多份数据通过channel交换,每个goroutine只能修改自己那份保证了数据安全.如果数据指向变动,每个goroutine要读写数据的话仍然需要同步.

1.1.3 GO的类型系统

Go提供了一个灵活的无层次的类型系统,它可以最小重构开销重用代码.它仍然面向对象开发,但没有传统的困扰.如果你曾经在java或C++程序中花费大量时间规划抽象类和接口,你会意识到GO简单类型系统的优点.GO composition使开发者重用功能只要简单的封闭类型.其它编程语言使用composition会深感疲惫于继承,非常复杂及难用.go的类型由小型类组成,区别于传统的基于继承的模式.
另外,GO有一个独特的接口实现,允许你建模行为,而不是建模类型.你不需要宣告你的行为接口,编译器的工作会确定类型值是否满足你正使用的接口.GO的基本库的接口通常非常小,只提供了少量的功能.在实践中这将花费一些时间适应,特别是你一直使用面向对象的语言,如java.

类型相当简单


Go拥有内置类如int和string,同时还有用户定义类型.一个典型的用户自定类是存储数据的类字段.如果你了解C的数据结构,GO的用户定义类看起来熟悉,操作也相似.但类需要宣告方法操作那些数据.崦不是创建一长串的继承结构-客户端扩展-用户扩展-实体.GO开发者创建小型类,客户与管理员,然后嵌入一个更大一点类.

小行为接口模型

接口表达了类行为.如果一个类值实现在接口,意味着这个类值拥有了一系列的行为.你不需要宣告你实现了一个接口,只要编写这个实现.在其它编程语言中称为鸭子类型–如果它叫起来像鸭子,它就可以是个鸭子.GO是这种说法的很好实现.在GO中,如果你的类实现一个接口方法,一个类值会被存储在接口上.不需要特别的声明.
在严格的面向对象语言如JAVA,接口总是被围绕.你总是需要在写代码前思考一个大型的继承链.

1
2
3
4
interface User {
public void login();
public void logout();
}

用java实现这个接口需要完整满足所有许诺的类,明确的宣告实现的接口.并为对比,GO接口类描述只需要一个操作.
举例说明,GO使用中一个最通用的接口io.Reader.这个接口提供一个简单的方法宣告类可被其它标准库理解的方式读取数据.

1
2
3
type Reader interface {
Read(p []byte) (n int, err error)
}

写一个类实现io.Reader接口,只需要实现一个read方法,接收一个byte数据片,返回一个整数或错误.
这与其它面向对象的编程语言使用的接口系统有着根本的区别.GO接口更小更直接.在实践中,这对重用代码与组合大有好处.你可以实现一个io.Reader在近乎任何有数据可用的类上,然后发送到任何知道如何从io.Reader读取的go函数.
GO中所有的网络库都使用io.Reader接口构建,因为它允许不同的网络操作需求与应用程序分离.使得接口有趣,优雅,灵活.同一个io.Reader能够对文件,缓存,套接字及其它数据源简单的操作.使用单一接口允许操作数据高效地,不分数据源.

1.1.4 内存管理

不当的内存管理会使用应用崩溃和内存泄漏.GO拥有一个现代垃圾回收机制.在其它系统语言中,如C,C++,你需要使用前分配一片内存,使用完后释放它.如果正确的执行失败,程序会崩溃或内存泄漏.一片内存不再需要时总是很难追踪.线程或重并行架构使得难上加难.如果你脑中有垃圾回收的想法去写代码,GO的垃圾回收只增加了一点的程序执行时间消耗,但显著减少了开发工作量.go去掉了编程的单调,把bean留给了会计师.

1.2 你好,GO

1
2
3
4
5
package main
import "fmt"
func main(){
fmt.Println("Hello Tianfei!)
}
坚持原创技术分享,您的支持将鼓励我继续创作!