我的Go之路

全职写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也是多范式语言,博采众长。