全職寫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也是多範式語言,博採衆長。

相關文章