MyException - 我的异常网
当前位置:我的异常网» Go » 小弟我学习go的五个感悟(译)

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

www.MyException.Cn  网友分享于:2013-09-19  浏览:0次
我学习go的五个感悟(译)

我学习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,你很快就会发现它能够帮助你成为一个更好的程序员。

4楼愤怒的TryCatch
+1,至少,我用了golang之后,大部分的东西都要自己思考,自己去构造了。
Re: artong0416
@愤怒的TryCatch,新的语言就需要新的思维
3楼YSOcean
你好,请问你右边公告栏的“加关注“是自己自定义加上去的还是皮肤自带的?
Re: artong0416
@YSOcean,应该是自带的 我没有设置过
2楼大葱哥
最近也在用go开发项目,我能说用的不爽吗....
Re: artong0416
@大葱哥,仁者见仁智者见智的语言,如果是从弱类型语言 转到任何强类型都会不爽,如果是坚实的面向对象思维难以改变,也很难转变,所以你之前是用的什么语言
1楼GavinJun
不错
Re: artong0416
@GavinJun,谢谢

文章评论

为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
代码女神横空出世
代码女神横空出世
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
鲜为人知的编程真相
鲜为人知的编程真相
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
程序员都该阅读的书
程序员都该阅读的书
漫画:程序员的工作
漫画:程序员的工作
总结2014中国互联网十大段子
总结2014中国互联网十大段子
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
编程语言是女人
编程语言是女人
程序员的鄙视链
程序员的鄙视链
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
程序员必看的十大电影
程序员必看的十大电影
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
 程序员的样子
程序员的样子
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
我是如何打败拖延症的
我是如何打败拖延症的
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
旅行,写作,编程
旅行,写作,编程
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
我的丈夫是个程序员
我的丈夫是个程序员
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
中美印日四国程序员比较
中美印日四国程序员比较
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
一个程序员的时间管理
一个程序员的时间管理
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
每天工作4小时的程序员
每天工作4小时的程序员
程序员应该关注的一些事儿
程序员应该关注的一些事儿
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
老程序员的下场
老程序员的下场
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
那些争议最大的编程观点
那些争议最大的编程观点
10个调试和排错的小建议
10个调试和排错的小建议
程序员和编码员之间的区别
程序员和编码员之间的区别
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有