0

我的Go之路

 4 years ago
source link: https://studygolang.com/articles/25397
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

全职写Go已经很多年了,我对于Go的认识,大概经历过三次升级,但每一次突破,都不是Go语言本身带来的,而是从其它语言领悟的,可见“功夫在诗外”。我想和你谈谈,这三次升级的关键的概念,它们是:接口,并发,反射。没有一个概念是轻易理解的,就当你当初写程序无法一下子理解变量一样,它们更甚。

第一次是当年移动开发热潮,我跟风买了Macbook Pro,装Xcode,写Objective-C。OC的作者Brad Cox原来写Smalltalk,因此在C上面加了大量的宏,让C支持面向对象。OC支持接口的方式是提供protocol。

我对iOS的框架UIKit中大量使用了delegate的设计方案印象深刻,尤其是UITableViewDelegate这个protocol,要完成一个TableView的操作,需要用户提供实现了这个接口的对象,这个对象可以是self,也可以其它对象,重点是要实现protocol定义的方法。我不知道多少人对self.delegate = self产生困惑。这种接口思想在Go标准库中,到外都有,io.Reader, io.Writer,json.Marshaler等等。可以说Go一定意义上是面向接口设计的语言。如果说面向对象设计的精髓是面向接口编程,那么Go便是。

另外Go的接口额外的两个特性是对象隐式实现接口,以及空接口作为一切类型的容器。隐式实现有点像动态语言的duck typing,如果对象没有实现接口定义的方法,那将无法通过编译。而空接口提供一定的泛型编程的能力,并且与反射息息相关。接口的概念很多语言都有,在Java中有大量的资料讲的是接口的设计,理解接口,设计能力会有极大提升。

第二次是对PHP并发的怨念促使我在Erlang中学习并发。Erlang是一门面向并发的编程语言,它强调一切都是进程,进程之间完全隔离,整个语言围绕着这个出发点而设计的;进程调度是完全公平的,不会因为某一个进程执行占用大量CPU而其它进程得不到CPU资源,它是并发的基础。

面向对象语言之父,同是也是Smalltalk之父,Alan Kay,解释面向对象时说道:“The big idea is messaging.", Erlang进程之间的通信就是靠发送消息,发送消息是异步的——发送方不必等待接收方收到消息才算发送完成,反而所谓面向对象语言的函数调用是同步的。Go的channel在有缓存并且没满的时候,发送方是异步的,当channel是无缓存或者缓存满了,发送方就是同步的,需要等待接收方取走消息之后,发送方才算完成。要理解messaging或者channel,把这个过程放大,看做发送方把消息发给消息队列,接收方监听消息队列。

Go的并发哲学是:“Do not communicate by sharing memory; instead, share memory by communicating.” Go提供了与Erlang相似的并发机制,同时也照顾传统并发编程的需求提供了sync包——传统加锁的并发方案。并发编程可以有效提升程序性能,掌握之后对程序的设计能力也有显著的提高。

第三次是在学习《SICP》,想到《黑客与画家》作者Paul Graham在《拒绝平庸》一文里说道:”有了Lisp语言的帮助,我们的开发周期很短。有时候,竞争对手刚刚发布新闻稿宣布将引入新功能,我们就能在一两天内做出自己的版本。”Lisp的宏是一种元编程,可以说是最强大的元编程形态,代码即数据。

就像Lisp解释器把代码当数据解析,Go的反射把类型当参数。Go官方《The Laws of Reflection》总结的反射三定律是基础,如何应用才是重点。反射处理对象是代码本身,不是直接的业务数据,所以相关处理代码十分晦涩;通常反射代码不应该存在于业务逻辑中,而存在于库与框架中。翻一下框架与库,发现满眼的reflect,这是Go丑陋的一面,但是不得不面对的一面。好处是reflect也是静态类型,不会像Lisp那样当人不在状态的时候根本无法阅读。使用Go的反射可以写出对使用者而言整洁的代码,不用像使用空接口那样做类型断言,但相对而言,要生成反射对象本身,增加了运行时开销。Go的设计初衷是快速编译,因此舍弃了对泛型的支持,Go的作者作出了取舍。

元编程是一个高阶话题,不易理解,但就像《Ruby元编程》作者说道:“哪里有元编程,都只是编程而已。”,把代码当成数据,你能做的超乎你的想像。如果你想要做得更多,Go也提供了go/types, go/ast等包用于解析Go源代码,你就能做代码生成了。

我对于以上的三个Go的特性,接口、并发、反射,就像理解任何知识一样,需要对比参照与旧知识发生关联,再加上大量的实践,才能有所体会。Go还有很多优秀的设计,比如defer,struct embed, first-class function等。Go的设计充满了权衡,对语言的特性的取舍更多关注的是软件工程领域,Go也是多范式语言,博采众长。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK