Go是一門有亮點的語言,老許是牛人,但這本書著實一般。

老趙發表於2013-04-07

這幾天看了《Go語言程式設計》這本書,在微博上吐了不少槽,得到了不少反饋,於是現在趁著新鮮記錄一些簡單的感受吧。

簡單地說,這本書並不算好,至少相較於Go這門語言以及老許的技術水平來說,這本書著實寫得一般。Go的確是一門有亮點的語言,尤其是相對C語言來說,例如其中的介面特性、“基於物件”的抽象方式、自然還有goroutine等一些被人廣為了解的特色等等。不過問題關鍵在於,書中有些地方描述地太過火了,白白多出各種槽點。

以書中出現的順序為準,隨便摘錄幾個我還有印象的地方:

1、前言中關於defer關鍵字的描述:

在Java中,你可能這樣寫程式碼來保證資源正確釋放:

Connection conn = ...;
try {
    Statement stmt = ...;
    try {
        ResultSet rset = ...;
        try {
            ... // 正常程式碼
        }
        finally {
            rset.close();
        }
    }
    finally {
        stmt.close();
    }
}
finally {
    conn.close();
}

完成同樣的功能,相應的Go程式碼只需要寫成這樣:

conn := ...
defer conn.Close()

stmt := ...
defer stmt.Close()

rset := ...
defer rset.Close()

事實上,在如今的Java語言中,我們基本都是用try-with-resources特性來寫這樣的程式碼的:

try (Connection conn = ...;
     Statement stmt = ...;
     ResultSet rset = ...;) {

}

同時,對於defer關鍵字可以跟匿名函式的特點,其實對於支援匿名函式和閉包的語言來說,幾乎都不成問題,也不失其優雅性。後面的章節還提到了recoverpanic,給人的感覺就是個普通的異常丟擲和捕獲機制,但是在那個地方卻沒有對這顯而易見會出現的對比進行解釋了。

2、第1章中關於多返回值的描述:

Go語言革命性地在靜態開發語言陣營中率先提供了多返回值功能。

這種說法讓ML系(SML、OCaml、F#)或Scala等語言情何以堪?更何況這個特性跟動態靜態語言基本毫無關係,事實上都有人總結過這個問題,在搜尋引擎上排名數一數二。

3、第2章中關於變數初始化的描述:

對於宣告變數時需要進行初始化的場景,var關鍵字可以保留,但不再是必要的元素,如下所示:

var v1 int = 10 // 正確的使用方式1
var v2 = 10 // 正確的使用方式2,編譯器可以自動推匯出v2的型別
v3 := 10 // 正確的使用方式3,編譯器可以自動推匯出v3的型別

以上三種用法的效果是完全一樣的(除了第三種宣告方式不能用於宣告全域性變數)。與第一種用法相比,第三種用法需要輸入的字元數大大減少,是懶程式設計師和聰明程式設計師的最佳選擇。這裡Go語言也引入了另一個C和C++中沒有的符號(冒號和等號的組合:=),用於明確表達同時進行變數宣告和初始化的工作。

說實話,這段描述,尤其是“懶程式設計師和聰明程式設計師的最佳選擇”這種說法讓我十分不滿,在我看來,這不是硬把臭的說成香的麼?多這麼第三種方式有什麼意義?難道就是因為省了三個字元?當然,事實上:=是有一定意義的,例如重新為之前宣告過的變數賦值,或是直接在表示式內賦值並使用等等。儘管我覺得這個設計多餘且有些自找麻煩,但這可以作為taste來討論,遠比書中“懶程式設計師和聰明程式設計師”要有意義地多。

此外,書中到處使用:=來建立區域性變數,是否是一種誤用?當然這是題外話。

4、第3章中關於“物件導向程式設計”的描述:

在Go語言中,物件導向的神祕面紗被剝得一乾二淨。對比下面的兩段程式碼:

func (a Integer) Less(b Integer) bool { // 物件導向
    return a < b
}

func Integer_Less(a Integer, b Integer) bool { // 程式導向
    return a < b
}

a.Less(2) // 物件導向的用法
Integer_Less(a, 2) // 程式導向的用法

可以看出,物件導向只是換了一種語法形式來表達。C++語言的物件導向之所以讓有些人迷惑的一大原因就在於其隱藏的this指標。一旦把隱藏的this指標顯露出來,大家看到的就是一個程式導向程式設計。感興趣的讀者可以去查閱《深度探索C++物件模型》這本書,看看C++語言是如何對應到C語言的。而Java和C#其實都是遵循著C++語言的慣例而設計的,它們的成員方法中都帶有一個隱藏的this指標。如果讀者瞭解Python語法,就會知道Python的成員方法中會有一個self引數,它和this指標的作用是完全一樣的。

我們對於一些事物的不理解或者畏懼,原因都在於這些事情所有意無意帶有的絢麗外衣和神祕面紗。只要揭開這一層直達本質,就會發現一切其實都很簡單。

上面這段話從“可以看出”開始就顯得無厘頭了一些,將程式設計正規化和內部實現混在了一起。為什麼Java的使用者要關注this是怎麼傳遞的?這對於物件導向程式設計這種程式設計正規化沒有任何意義。這章最後還提到:

本章我們詳細講解了Go語言物件導向程式設計的相關特性。眾多讀者可能會有一個疑問,那就是與C++、Java和C#這些經典的面嚮物件語言相比,為什麼只用了比第2章還短的篇幅就介紹完本章?是Go語言在物件導向程式設計上的支援力度不夠,還是本書沒有介紹完整呢?

在回答這個問題之前,讀者可以先考慮一個問題:基於本章介紹的Go語言的物件導向程式設計特性,有C++、Java和C#可以實現而Go語言無法表達和實現的場景嗎?事實上我們很難想出這種場景,也就是Go語言看似簡陋的物件導向程式設計特性已經可以滿足需求,這反而對映了其他語言在這方面可能做了過多的工作。用簡單的語法、更少的程式碼就可以做完的事情,為什麼我還要自 尋煩惱去學習繁複的語法,然後為眾多實現細節而煩惱呢?

Go語言給我們開闢了一個新的視野,原來問題可以這麼簡單地解決。

其實我可以舉出很多Java和C#中可以實現而Go語言無法表達的內容,但是我也可以自己按照“已經可以滿足需求”來進行回應,但是這種應對方式是語言討論中最沒有意義的方式之一。Go語言的確提供了一種相對於Java和C#更為簡單的資料抽象方式,也可能的確夠用(這點我不會簡單粗暴地反對),但因此說它實現了物件導向,能用來應對物件導向能實現的各種場景,這我表示看不下去。事實上,我不認為Go語言做到了物件導向(當然我也沒說物件導向是必須的),它只不過是一種基於物件組合的方式來提供資料抽象能力(當然還有介面等等)。

印象中大部分的槽點都出現在本書的前半部分,描述Go語言基礎能力的那些,而後半主要是圍繞著類庫和周邊環境,所以沒有太多可談的地方。總體而言,這本書給我的印象,就好像在看一個Go語言粉絲,再竭力推銷這門語言,但對於其中講述內容的嚴肅性、正確性便有所放低了,這不是老許這一級別的人物應該給人的印象。這本書裡很多地方對於Java和C#的批評並不是那麼有道理(我又想起了在“併發程式設計”裡的一段描述,但懶得舉了)。

其實Go語言還是有所特色的,例如goroutine方面,儘管我認為它只是把其他一些語言/平臺中用類庫實現的功能做進了語言(有機會我會詳述這一點),不過在語言級別強調一種程式設計方法/正規化也是這種語言的文化,Erlang便是前例之一。可惜的是,書中只用了非常簡短的篇幅描述了這個功能,但是對於它應該如何使用,如何以此來設計一個應用,如何進行架構規劃,幾乎可以說是隻字未提。事實上,這方面通過一個稍微複雜點的案例也能較好地說明問題了,這在我之前看過的一本ManningProgmatic的Erlang書(具體哪本忘了)中就有體會。

假如要我給這本書打分(滿分10分),我會打6分。只是再考慮到這本書在宣傳時給人的印象,以及老許給人的期待,我只能給它打5分。假如有人問我學習Go語言是否該看這本書,我會建議他們去看Go語言的官方文件。其實老許和七牛在Go語言方面的積累絕對可以出一本好書,這本實在令人有些失望。

最後再說些有點關聯又有些無關的話,好東西不需要太多修飾,多多描述就行了。不光是面向技術,還有其他方面。例如,不給牛人帶高帽子他們就不是牛人了嗎

相關文章