為什麼建議你常閱讀原始碼?

謝偉發表於2019-05-08

我叫謝偉,是一名側重在後端的程式設計師。

自身智力一般,技術迭代又非常快,為不至於總處於入門水平,給自己定位是後端,進一步定位現階段是 web後臺開發。

為保持好奇心,經常會嚐鮮新技術,其實不僅僅是技術,比如攝影、演示設計、拍視訊、自媒體寫作啊等,都會經常性的涉獵。

如果此刻我是一個成功人士,看到上面的領域,有人會羨慕說:"斜槓",遺憾的是,在下沒有成功,所以,上面的領域都一定程式上會被人認為:"不務正業",不重要的,重要的是:我依然是一名後端程式設計師。

記憶

記憶有遺忘曲線,這是大家都懂的道理,所以為了防止忘記,最重要的方法是經常使用、反覆使用。這也就是為什麼,有些人說:在工作中學最容易進步,因為工作的流程、專案啊之類的不會頻繁變動,你會經常性的關注一個或者多個專案進行開發,假以時日,你會越來越熟悉,理所當然,你會越做越快。這個時候,也叫達到了所謂的:舒適圈。要再想進步,你得跳到"學習區"內去。再反覆這個動作。

問題是,除了工作之外,你很少有其他機會再進行技能鍛鍊了。

創造機會

  • 主動承接更為複雜的任務

這個很清晰,更為複雜的任務,你才可能嘗試使用新的技術棧,這樣你有機會進行其他技能的鍛鍊,這樣就能進入學習區。

如果公司專案就這麼點,沒有太複雜的,或者說新專案和你接觸的相差不多,只不過應用場景不同而已。這個時候,任務如果一定需要你的參與,這個時候,你最好嘗試新的架構,嘗試新的技術點,儘管大體相同,可以將你認為原系統不合理的地方改進,這樣也能創造機會進入學習區。

但就我認為,一般專案開發時間都非常緊,開發人員有可能沒有充足的時間進行考慮,會依然使用原有技術點,這樣進入學習區的機會就被浪費了,你只是使用一份經驗,做了兩個型別的專案而已。

舊知識補全

剛進入職場,核心位置就那麼幾個人佔著,論經驗、論資歷,你都不如別人,你接觸到的資源有限,沒有新專案讓你獨立開發,只有舊專案的Bug讓你修復,那該怎麼辦?

換坑嗎?怕不怕是另外一個坑?

  • 補知識體系

即使是你能完成的任務,你有沒有嘗試過自己獨立寫一個,你有沒有嘗試過自己彌補下不懂的知識點,你有沒有嘗試過總結下自己的開發流程是否是最優的,

你有沒有嘗試過總結下專案的技術要點,你有沒有嘗試過提煉可以複用的技術點...

如果你都沒有,恭喜你,你又找到了一個進入學習區的點,即:補充原有技術棧。

也許你對一門程式語言能用,但都是靠 Google、stackOverflow,你是不是要嘗試梳理下整個程式語言的知識體系,當然梳理的切入點依然是和工作相關為先,因為這

最迫切,最能反覆,使用頻率最高。

也許你對資料庫相關知識能用,對優化資料知識點不是很懂,你是不是要嘗試下找相關資料彌補下。

也許...

也許你還可以經常性的翻閱原始碼,比如內建庫的實現,之前我還不太會關注這些,寫起程式碼來也不是很有底,後來經常性的檢視原始碼,藉助IDE的跳轉功能

可以實現對原始碼的閱讀,再結合IDE的structure ,可以對檔案的函式、結構體、方法等組織起來。這樣從整體觀一目瞭然,看得多了,

你甚至可以總結出一些共性:

  • 比如包的錯誤處理一般定義在包的頂部幾行,而且格式都統一
  • 比如Interface 是方法的集合,內建的常用的Interface其實不多,很多內建包都相互實現
  • 比如包的結構體,可以例項化一個預設的,這樣可以直接呼叫函式,比如http.DefaultClient ...

閱讀庫的原始碼,我一般是怎麼做的呢?(不要太關注具體的實現,除非你完全能看懂)

  • 官方文件:瞭解常用使用方法
  • 思維導圖:輸出可匯出的結構體、函式、方法等,依然選擇最為常用的
  • IDE 的 structure 功能,檢視檔案的具體組織形式,看可匯出的結構體、函式、方法等
  • 持續總結

舉個示例:net/http 包幾乎奠定了Go領域所有web 框架、網路請求庫的基礎。

1. 瞭解 HTTP 相關知識 隨意找本相關的書,發現是個大塊知識啊。

結合一般的歷史經驗,你可能作出這麼張思維導圖。

為什麼建議你常閱讀原始碼?

整個過程像是:你從一本書總摘出的目錄,前提是看過書的內容而得出來的。

2. net/http 客戶端

網路請求分為兩個層面:1。客戶端發起網路請求 2。服務端提供網路請求訪問資源

func getHandle(rawString string) {
	response, err := http.Get(rawString)
	if err != nil {
		return
	}
	defer response.Body.Close()
	content, _ := ioutil.ReadAll(response.Body)
	fmt.Println(string(content))
}

複製程式碼

看上去發起網路請求很簡單,只需要使用 http.Get 即可。

為完善認知,去看看原始碼,直接使用IDE 的跳轉功能。

為什麼建議你常閱讀原始碼?

結合 IDE 的structure 功能,原來http.Get 是原始碼中有一個預設的DefaultClient 呼叫其方法 Get

func Get(url string) (resp *Response, err error) {
	return DefaultClient.Get(url)
}

複製程式碼

同理,http.Posthttp.PostForm 也是。

理所當然,你可以自定義個定製化的client,呼叫其 Do或者 Get、Post、PostForm 方法等

type Client struct {
	...
}
複製程式碼

這個時候,你的思維匯出可能是這樣:

為什麼建議你常閱讀原始碼?

好,關於net/http 客戶端層面,你現在應該清楚如何使用了。你既可以使用預設的,預設的如果達不到需求,你

可以自己例項化一個client, 你還可以例項化 http.Request ,最後呼叫 client.Do 方法,都可以完成目的。

3. net/http 服務端

服務端比較重要,日常web 開發,也絕大部分是在服務端工作。

如何啟動web服務呢?

type SelfHandler struct {
}

func (SelfHandler) ServeHTTP(writer http.ResponseWriter, req *http.Request) {
	writer.Write([]byte("Hello Python"))
}

func main(){
		// method One
    	http.HandleFunc("/hello_golang", func(writer http.ResponseWriter, request *http.Request) {
    		writer.Write([]byte("Hello Golang"))
    	})
    
    	// method Two
    	var self SelfHandler
    	http.Handle("/hello_python", self)
    	log.Fatal(http.ListenAndServe(":9090", selfServerMux))
}

複製程式碼

分為兩步:定義路由和觸發路由之後的動作、啟動服務。

看上去也很簡單啊,看看原始碼,服務端比客戶端更為複雜。

func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}

type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}
複製程式碼

原來又使用了內部一個預設的DefaultServeMux(請求路由器)。Handler 是個介面。上文中 SelfHandler 實現了 Handler 介面。

之前你可能會有點混淆,Handle、HandleFunc、Handler 之間的關係。對比著看,你應該會清晰些。

既然使用預設的DefaultServeMux, 那麼也可以定製化ServeMux

func main(){
		var selfServerMux *http.ServeMux
    	selfServerMux = &http.ServeMux{}
    	selfServerMux.HandleFunc("/hello_golang_2", func(writer http.ResponseWriter, request *http.Request) {
    		writer.Write([]byte("Hello Golang 2"))
    	})
    	log.Fatal(http.ListenAndServe(":9090", selfServerMux))

}

複製程式碼

同時可以定製化服務端的配置資訊。


func main(){
	var selfServer http.Server

	var selfMux *http.ServeMux
	
	selfMux = &http.ServeMux{}
	
	selfMux.HandleFunc("/d", func(writer http.ResponseWriter, request *http.Request) {
		writer.Write([]byte("Hello Mux"))
	})

	selfServer = http.Server{
		Handler: selfMux,
		Addr:    "localhost:9098",
	}
	log.Fatal(selfServer.ListenAndServe())
}
複製程式碼

至此,你的思維導圖大概這樣:

為什麼建議你常閱讀原始碼?

當然企業級的web 服務開發還需要考慮:

  • 路由設計
  • 請求引數檢驗
  • 響應資訊
  • 前後端分離
  • 錯誤處理資訊
  • ...

4. 使用一些 web 框架

  • gin
  • echo
  • iris
  • beego ...

總結

上文就是我日常的一些思考,希望對你有所啟發。

整個的過程,其實是梳理你的知識的過程,如果你接觸的知識很多,不經常理,在你腦海中,可能只是堆積,用到的時候不能及時的呼叫出來,所以會經常說:這個我見過啊。

及時的梳理,在腦中模組化,呼叫起來自然順暢的多,當然,迫於遺忘曲線,這些動作,你還需要反覆的重複。

相關文章