我学习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. 是否使用安全模式启动word

          打开word,出现了一个提示,显示着“word遇到问题需要关闭.我们对此引起的不便表示抱歉.”下面有选项“恢复我的工作并重启word”,选中它.点下面的“不发送”.      在出现的提示 ...

  2. java循环、数组练习

    System.out.println("请输入学生个数"); int a=sc.nextInt();//定义一个变量说明学生的数量 int max=0; int[] scores= ...

  3. docker~大叔对术语的解释

    回到目录 接触和使用docker已经有1年多了,起初对概念本身理解的不是很好,或者说,没有找到一本比较好的书,在自己的研究下,对docker一些基本的概念有了自己的一些认识和看法,现在分享给正在学习d ...

  4. 【转载】CANoe 入门 Step by step系列(三)简单例子的剖析

    来源:http://www.cnblogs.com/dongdonghuihui/archive/2012/09/26/2704623.html 最好的学习方式是什么?模仿.有人会问,那不是山寨么?但 ...

  5. 平方根的C语言实现(一)

    曾经做一个硬件成本极度控制的项目,因为硬件成本极低,并且还需要实现较高的精度测量,过程中也自己用C语言实现了正弦.余弦.反正切.平方根等函数. 以下,无论是在我的实际项目中还是本地的计算机系统,int ...

  6. Spring Data JPA在Spring Boot中的应用

    1.JPA JPA(Java Persistence API)是Sun官方提出的Java持久化规范.它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据.他的出现主要是为了简 ...

  7. JavaBean和jsp的开发模型

    1.实体类 package com.zdsofe.javaBean.work; public class Student { public String name; public String sex ...

  8. Java 9 揭秘(20. JDK 9中API层次的改变)

    Tips 做一个终身学习的人. 在最后一章内容中,主要介绍以下内容: 下划线作为新关键字 改进使用try-with-resources块的语法 如何在匿名类中使用<>操作符 如何在接口中使 ...

  9. 表达式求值(栈方法/C++语言描述)(二)

    上篇中完成了对表达式求值的整体过程,接下来看看如何处理不同类型的token. 对运算数的处理比较简单,它直接调用函数strtod(),将字符串中的运算数转换为浮点类型并将它压入运算数栈中: void ...

  10. 双向循环链表(C语言描述)(四)

    下面以一个电子英汉词典程序(以下简称电子词典)为例,应用双向循环链表.分离数据结构,可以使逻辑代码独立于数据结构操作代码,程序结构更清晰,代码更简洁:电子词典的增.删.查.改操作分别对应于链表的插入. ...