我学习go的五个感悟(译)

原文

5 things about programming I learned with Go By MICHAŁ KONARSKI

Go在最近一段时间内开始变得十分流行。语言相关的论文和博客每天都在更新,新的golang相关的项目在github中也层出不穷。Go语言的会议也吸引了越来越多的开发者的关注。Go语言的时代已经来临,并且当选了TIOBE的2016年度语言,并一度进入流行度前十。

我一年前开始接触golang,然后决定试一试。经过一段时间的接触,我发现这绝对是一个值得学习的语言。即使你不打算长期使用,学习一段时间也会是你的编程技巧有很大的提升。接下来我会告诉大家我学习golang的过5点感悟,而且这五点感悟对其他编程语言也有用。

1. 具有动态类型特性的静态类型安全语言

我日常使用的编程语言是Ruby,我非常喜欢动态类型语言。这一特性使得语言非常容易学习、使用,并且开发效率非常高。随着项目越来越多、越来越复杂,代码变得不像其他静态类型安全语言那样安全可靠。及时我十分谨慎的测试代码,仍不能覆盖到所有的边缘状况,因此经常出现不希望出现的状况。那么,有没有哪种语言既有着动态语言的特性又有静态类型安全语言的可靠性。答案是肯定的,我们来讲一讲Go!

现在有一些争论是关于golang到底是不是面相对象的编程语言[1] [2] 。但是golang有个面相对象语言的特性--接口。格式上来看,和面相对象的语言Java比较相似,一个包含很多方法的结构体:

type Animal interface {
Speak() string
}

当然,golang也有类的等价实现--结构体。结构体也可以是数据和方法的封装:

type Dog struct {
name string
}

然后我们可以使用该结构体作为方法接收器--receiver,类似于类的成员方法:

func (d Dog) Speak() string {
return "Woof!"
}

这不就是面相对象的三大特性之一--封装么。

和其他面相对象语言不同的是,方法声明在结构体外。golang的作者希望给结构体的使用者更多的灵活性。即使 你不是结构体的作者,你也可以自由的为它加上新的“成员方法”。

那我们怎么做到类似多态呢?很简单:

func SaySomething(a Animal) {
fmt.Println(a.Speak())
} dog := Dog{name: "Charlie"}
SaySomething(dog)

Dog实现了接口Animal的所有方法,就可以作为Anmial来使用,不需要主动的声明。这种行为被称为a statically typed duck typing

“If it quacks like a duck, then it probably is a duck”.

正是因为接口的这种特性,可以让我们像使用动态类型语言一样使用golang,却同时得到类型安全的保障。

2. 比继承更好用的组合

在之前的blog中我描述过一个问题,如果过度的使用面向对象的特性,我们会让自己陷进去。举个例子,一个需求最初可以用一个类来建模,然后逐渐扩展,在某种程度上,继承似乎是不断增长的需求的完美答案。不幸的是,这样做导致我们有了一棵紧密相关的大树,在那里添加新的逻辑的同时想要保持简单性和避免代码重复是非常困难的。

我对这个故事的结论是,如果我们想要减少在代码复杂性中迷失的风险,我们需要避免继承而选择组合。改变观念非常困难,而使用一种不支持继承的语言能够帮得上忙,你猜的对,就是Go。

Go的结构体设计的时候没有继承的概念。Go语言设计者是想保持语言的简单和清爽。他们发现继承不是必须的,但是他们保留组合的特性。举个例子,汽车包含引擎和车身,使用两个interface来表示:

type Engine interface {
Refill()
} type Body interface {
Load()
}

现在,我们需要创建一个结构体Vechicle组合上述接口:

type Vehicle struct {
Engine
Body
}

发现什么奇怪的地方了么?我故意省略了接口类型的字段名。因此,我使用了叫做嵌入(embedding)的特性。这样,我们使用Vehicle的实体可以直接调用接口中的方法。我们可以方便的使用组合。代码如下:

vehicle := Vehicle{Engine: PetrolEngine{}, Body: TruckBody{}}
vehicle.refill()
vehicle.load()

3. 解决并发问题的利器-- channels and goroutines

channels 和 goroutines 是非常酷的工具帮助我们解决并发问题。

Goroutines 是Go的 green threads 由go自行管理和调度,而且占用非常少的系统资源。

Channel 是一个管道,可以用作协程间的通信。它可以让协程间方便的进行异步通信。

这里给出一个 Goroutine 和 Channel 共同工作的例子。假设我们有个方法执行一个耗时的计算任务,我们不希望它阻塞进程,我们可以这样做:

func HeavyComputation(ch chan int32) {
// long, serious math stuff ch <- result
}

正如你看到的,这个方法接受一个channel类型的参数,一旦计算出结果,就将结果放到channel中即可。那我们怎么调用这个方法呢:

ch := make(chan int32)
go HeavyComputation(ch)

这里的go关键字可以非常方便的进行异步处理。Go会新建一个协程执行HeavyComputation(ch),然后程序可以不阻塞的执行其他任务。获取结果也非常简单:

result := <-ch

ch里有计算结果的时候,可以直接读出,否则将阻塞直到计算协程放入结果。

channels 和 goroutines 是非常简单但是非常有效的并发处理机制。

4. Don’t communicate by sharing memory, share memory by communicating

(这里标题没有翻译,因为英文大家更熟悉。)

传统的编程语言一般在标准库里提供多个线程访问同一块共享内存的方法。为了同步和避免同时访问一般采用加锁的方法。但是由于Go有 goroutines 和 channels 可以使用其他的方法。与加锁的方式不同,Go可以方便的使用channel来实现,保证了同时只有一个协程能够改变其内容。Go 的官方文档给出了解释:

One way to think about this model is to consider a typical single-threaded program running on one CPU. It has no need for synchronization primitives. Now run another such instance; it too needs no synchronization. Now let those two communicate; if the communication is the synchronizer, there’s still no need for other synchronization.

这绝对不是一个新概念,但是对于很多人来说,对于任何并发问题,加锁仍然是首选的解决方案。当然,这并不意味着锁是无效的。它可以用来实现简单的东西,比如原子计数器。但是对于更高层次的抽象,最好考虑不同的东西。

注:我认为作者的意思是,更复杂的场景下,如果能更关心业务而不是加锁解锁的逻辑,系统会更加可靠。实际上channel的实现就是帮我们集成了加锁和解锁的过程,当一个协程操作channel的时候,都会伴随的加锁和解锁的过程。想详细的了解,可以参考Go语言中channel的实现。

5. Go的异常处理

一般语言都有异常捕获和异常处理的概念。Go不同,Go在设计的时候没有异常的概念。这仿佛把缺少一个特性当成了Go的特性。但是仔细想,它是有用的。当出现错误的时候,我们没有办法确定到底是那种错误--磁盘空间不足?IO网络问题?如果是捕获异常可能需要包含所有类型的异常。Go给出了不同的解决方案,将错误当做返回值。

f, err := os.Open("filename.ext").
if err != nil {
fmt.Println(err)
return err
} // do something with the file

坦白说,这不一定是最优雅的解决方案,但这是最有效的方法鼓励开发者处理错误。

总结

Go是一种有趣的语言,它提供了一种不同编写代码方法。它丢弃了一些我们从其他语言中了解的一些特性,比如继承或异常。相反,它鼓励用户使用自己的工具集来解决问题。因此,如果您想要编写可维护的、干净的、健壮的代码,您不妨以一种不同的、类似于Go的方式开始思考。这是一件好事,因为您在这里学到的技能可以在其他语言中成功使用。你的年龄可能会有所不同,但我认为一旦你开始接触Go,你很快就会发现它能够帮助你成为一个更好的程序员。

我学习go的五个感悟(译)的更多相关文章

  1. 我的MYSQL学习心得(五) 运算符

    我的MYSQL学习心得(五) 运算符 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据 ...

  2. 小菜学习设计模式(五)—控制反转(Ioc)

    写在前面 设计模式目录: 小菜学习设计模式(一)—模板方法(Template)模式 小菜学习设计模式(二)—单例(Singleton)模式 小菜学习设计模式(三)—工厂方法(Factory Metho ...

  3. python学习心得第五章

    python学习心得第五章 1.冒泡排序: 冒泡是一种基础的算法,通过这算法可以将一堆值进行有效的排列,可以是从大到小,可以从小到大,条件是任意给出的. 冒泡的原理: 将需要比较的数(n个)有序的两个 ...

  4. SpringMvc学习心得(五)控制器产生与构建

    SpringMvc学习心得(五)控制器产生与构建 标签: springspring mvc框架 2016-03-22 15:29 140人阅读 评论(0) 收藏 举报  分类: Spring(4)  ...

  5. opencv学习笔记(五)镜像对称

    opencv学习笔记(五)镜像对称 设图像的宽度为width,长度为height.(x,y)为变换后的坐标,(x0,y0)为原图像的坐标. 水平镜像变换: 代码实现: #include <ios ...

  6. RabbitMQ学习总结 第五篇:路由Routing

    目录 RabbitMQ学习总结 第一篇:理论篇 RabbitMQ学习总结 第二篇:快速入门HelloWorld RabbitMQ学习总结 第三篇:工作队列Work Queue RabbitMQ学习总结 ...

  7. WCF学习心得----(五)生成客户端

    WCF学习心得----(五)生成客户端 1.    通过Svcutil.exe工具直接生成客户端 1.1     将服务承载于IIS上 1.1.1 在IIS中新建网站,所示效果如下图: 1.1.2   ...

  8. Django学习笔记(五)—— 表单

    疯狂的暑假学习之  Django学习笔记(五)-- 表单 參考:<The Django Book> 第7章 1. HttpRequest对象的信息 request.path         ...

  9. VSTO学习笔记(五)批量编辑Excel 2010 x64

    原文:VSTO学习笔记(五)批量编辑Excel 2010 x64 近期因为工作的需要,经常要批量处理大量的Excel文件,如果纯手工一个个修改,非常的麻烦,于是写了这么一个帮助类,希望能对你有所帮助. ...

随机推荐

  1. 进击的新版NavMesh系统:看我飞檐走壁

    0x00 前言 unity5.6作为Unity5最后的一个版本,的确起到了一个承上启下的作用.除了上一篇文章<进击的AssetBundles和它的工具们>中提到的AssetBundles- ...

  2. oracle 小测

    01)oracle10i,oracle11g,oracle12c,其它i,g,c什么意思? i(Internet)互联网 g(grid)网格 c(cloud) 云02)sqlplus是什么意思? 是o ...

  3. .Net中关于相等的问题

    在.Net框架中,如果您查看所有类型的的基类:System.Object类,将找到如下4个与相等判断的方法: static Equals() virtual Equals() static Refer ...

  4. SetConsoleWindowInfo 函数--设置控制台窗口的大小和位置

    SetConsoleWindowInfo函数 来源:https://msdn.microsoft.com/en-us/library/windows/desktop/ms686125(v=vs.85) ...

  5. (转)Linux开机启动(bootstrap)

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 计算机开机是一个神秘的过程.我们只是按了开机键,就看到屏幕上的进度条或者一行行的输 ...

  6. Android高效内存之让你的图片省内存

    Android高效内存之让你的图片省内存 在做内存优化的时候,我们发现除了解决内存泄露问题,剩下的就只有想办法减少真实的内存占用.而在App中,大部分内存可能被我们图片占用了,所以减少图片的内存占用可 ...

  7. CSS规则

    CSS规则 --------------------------------------------- 1 前言 2 代码风格 2.1 文件 2.2 缩进 2.3 空格 2.4 行长度 2.5 选择器 ...

  8. 一次浴火重生的MySQL优化(EXPLAIN命令详解)

    一直对SQL优化的技能心存无限的向往,之前面试的时候有很多面试官都会来一句,你会优化吗?我说我不太会,这时可能很多人就会有点儿说法了,比如会说不要使用通配符*去检索表.给常常使用的列建立索引.还有创建 ...

  9. 电脑只能上QQ,不能上网浏览网站怎么解决

    这个问题通常是因为网络的DNS解析出错引起的.QQ一类的聊天软件有自动的网络解析,不需要DNS便可以使用,而一般的浏览器是需要DNS解析来访问网页的.所谓DNS,即域名服务器(Domain Name ...

  10. Intger To Roman

    这题意思是将一个输入的整型阿拉伯数字转化为罗马数字. 思路是将1-10对应的罗马数字放在字符串数组里,然后发现数据变化规律即可,eg:389 = 300 + 89 +9 分别对应的罗马数字. publ ...