天天看點

Go語言特性小結-2017.03.29

學習背景

作為動态類型語言的PHP在工程實作上提供了很多便利,但也導緻編碼上的随意程度越來越高,恰巧内部推出了一個比賽,主題是在限定條件的伺服器環境下,如何實作一個最快的echo server。技術方案當然是不設限制,由于在去年下半年涉及了一部分流式分發系統的設計内容,加上組内大牛的一直推廣,對golang産生了興趣。是以最終選擇了go1.6方向的調研,學習過程中的一些point會彙總在這兒。

參考資料

  • 《go語言程式設計》
  • godoc

正文

語言特性

  • golang中不存在類、對象的概念,是以是一個基于并發模型的語言,并不是完整的面向對象程式設計。替代方法是以type為中心,定義類型方法,也可以達到部分對象的功能,好處是代碼組織上有迹可尋,同一類的用同一個type+一組類型方法即可。同時,類型方法支援值傳遞和引用傳遞,以面向對象的角度看,會在程式設計過程中,大部分使用引用傳遞。
  • 以package來組織代碼,名為main的package是整個項目的入口,其餘的package以庫的形式起作用,被import引進相關的package。目前了解的是,go為了做到環境無關,把相關的庫都靜态編譯成一個可執行檔案,是以編譯出來的go程式明顯體積都大于代碼,這是編譯類語言中比較少見的。
  • main方法作為程式的入口,在main之前執行的為init方法(每個源檔案可以且隻可以包含一個init方法),是一個現代的用法,更貼近于我們在業務開發中為流程準備的hook。是以該方法在業務上也通常用來:
    1. 程式執行之前對環境、資料進行檢驗或者修複,保證程式狀态正确;
    2. 在main程式之前調用背景執行的gorutine。

      目前看,對init的使用要慎重,因為順序不可控,起碼在ide(gogland)裡是如此。 這是一個謬誤,init的調用是嚴格有順序的,按照import的順序來進行調用。驗證參考《go語言的初始化順序,包,變量,init》結論就是,在一個go的main package檔案中, 初始化順序規則: 引入的包 -> 目前包中的變量常量 -> 目前包的init -> main函數。

  • go語言對格式、引入代碼是否被使用的校驗是很嚴格的,如果引入了package、定義了變量,但在程式執行流程中沒有使用,則編譯無法通過。
  • go為強類型(值得推敲的一句)語言《強、弱、靜态、動态類型》,是以在值類型變換時要注意精度的問題,資料類型轉換可能會有精度損失。fmt.Printf列印各種類型需要熟記,例如bool類型的%t,golang結構獨有的%v、%+v等等。輸入輸出可以參考一下這篇blog。可以對字元類型做Type操作,但這個操作和别名不完全相同,因為無法調用原有類型的函數:Type TZ int 将int類型一個命名為TZ。
  • 區分make和new,首先這兩個關鍵字都是golang語言預留的用于記憶體配置設定的原語,
  • 4種引用類型:map slice channel interface 其中前三者都常用make初始化。
  • go get = git clone + go install 因為gw問題使用:http_proxy=http://localhost:8123 go get來安裝所需的包
  • go官方所有的源碼包都托管在github上,是以,理論上去github上都可以下載下傳
  • 同一代碼塊内的defer,後聲明先執行,是一個棧的結構,後聲明在回收過程中先聲明
  • import (_ "xxx/fff") 下劃線代表隻需要執行包中的init,并不需要所有内容,是以使用這種方式import的包,内部的方法依然無法直接調用。import(. "xxxx/fff")這種引入方式,則不需要使用報名稱引用方法,問題是使用這種引用方式的話,多個包内有同名函數在編譯過程中會出沖突,是以包字首在這裡是個類似于命名空間的功能。第三種特别的寫法為import(test "xxx/fff")表示給這個包一個别名,不需要使用package的名字,直接使用别名(這種使用場景需要進一步摸索-> todo
  • 為了更快的開發http服務,學習beego。作為一個restful架構,提供了很多友善的工具子產品,是以不限于http web server。參考位址beego官方網站
  • golang各個包的init加載順序是可控的,是以可以大量使用(eg:beego)執行流程如下圖:
    Go語言特性小結-2017.03.29
  • 匿名引用是golang面向對象的核心,一直質疑一個問題如果實作一個接口所有的func就預設是這個接口的實作(implement)那麼在代碼組織或者閱讀上天生有缺陷,這種隐式實作的方式有一定問題,目前(17.09.06)選擇隻要是struct實作了某種接口的情況下就在這個struct中增加匿名的該接口,但問題就在于,這樣的情況,即使struct不實作interface定義的func,依然可以算作一種interface的實作,是以不能說是很完美的解決了這個問題。具體參考Golang中的面向對象繼承
  • new和make的差別深入學習golang。new出來的資料結構是一個指向該結構的指針,make隻建立slice channel map,并且傳回的是值。new會把相關結構的值置為零值(bool 為false int 為0 string為"")
  • golang中沒有構造函數,通常建立一個對象都是由全局函數Newxxx(... interface{})(*T) T為要建立的對象。
  • switch case 中專門應對interface{}變量,增加了一個value.(type)寫法,可以根據空接口具體的資料類型來做不同的操作。
  • 計算機語言上的反射指的是支援程式運作時的狀态檢測
  • select下面case命中的執行是沒有明确順序的,select 配合多條channel來使用,更多是用來傳遞信号,如果要傳遞大量資料的話合适麼?這個需要調研
  • make隻能建立切片、channel與map,之是以這麼劃分,是因為此三者在使用前都需要提前配置設定空間(類似于malloc),同時傳回是一個具體資料結構的執行個體(做了初始化)。new

踩小坑

  • 有時使用go build | go install時候編譯不過,清除一下pkg庫。TODO:梳理一下golang編譯過程檔案之間的依賴關系。
  • 注意包之間的引用,不能直接用包名來作為形參引用,在編譯過程會被拒絕。
  • 網絡和并發是golang的兩大核心優勢,是以我們在學習過程中也要持續關注語言的應用場景。golang被稱為網際網路的c語言,在需要寫server的時候,使用起來很友善。而實際上一個server需要關注的也就是這兩部分,在并發中golang提供的channel和goroutine是并發相關的兩大feature。
  • 使用yaml配置檔案解析包的過程中,yaml的key隻能用小寫,如果混合大小寫,則不能識别
  • 花了半個下午,踩了個坑... 在做producer consumer的時候,沒有先把消費的gorutine先啟來,先運作select,阻塞住了。導緻程式不向下運作,還沒定位到問題,腦子沒有跟上。從現象上看,一邊開會,一邊寫代碼的效率,大概為原來的1/3。
  • gogland上有按步調試,但是暫時還沒有很好的使用起來。
  • 太久沒有使用編譯型語言,對于struct内map在資料定義過程中隻是個聲明,在使用時需要先make初始化
  • 為啥在寫代碼之前沒有好好梳理這種知識?關于golang實作繼承群組合的方法。主要是2點:1. 如何用struct + method(值方法 | 引用方法)實作類;2. 如何用struct實作繼承:匿名接口|匿名struct|匿名struct指針
  • 昨天解了自己心中的一個問題,關于對interface程式設計,為何不能用interface的實作struct直接替代在函數中對應interface的形參,會報一個錯誤( MyType does not implement Stringer (String method has pointer receiver)),在這種情況下使用引用傳遞是沒問題的,參考stackoverflow:
    //資料結構定義
      type Stringer interface {
          String() string
      }
    
      type MyType struct {
          value string
      }
    
      func (m *MyType) String() string { return m.value }
    
      //錯誤用法
      m := MyType{value: "something"}
      var s Stringer
      s = m // cannot use m (type MyType) as type Stringer in assignment:
            //   MyType does not implement Stringer (String method has pointer receiver)
    
      //正确用法1
      s = &m
      fmt.Println(s)
    
      //正确用法2是在定義時把struct的方法定義成值類型,但是這種用法很多時候無法滿足需要,也就是需要在函數中修改MyType内部變量的時候,值引用不符合場景
      func (m MyType) String() string{ return m.value }
               

這些都是語言的特性,其中還包括在 structs and embedding 中的特點。struct A實作了interface x,當struct B 包含Struct A 時,将Struct B直接指派給interface x類型會報錯,需要将struct *B指派給interface x類型;或者讓struct B包含struct *A,則可以直接對interface x類型指派,當然在這種情況下,struct *B也可以直接對interface x類型指派。

  • 一個可能會出現的錯誤:panic: runtime error: index out of range in Go。因為引用了無法索引的數組/切片下标。
  • 為什麼我們不用struct指針方法的模式做構造函數?因為無法将這樣初始化出來的内容直接賦給其實作的接口,接口及其實作之間的指派隻能用指針。
  • 可以對外傳回函數局部變量的指針,然後可用。我們了解簡單類型聲明即可用,因為變量空間開辟在棧上,struct等複雜類型聲明之後需要顯示的在堆上開辟空間,make/new 這種了解目前還沒有理論來佐證。
  • redis-client一個坑 連續new兩個Client,後者的db會切到與前一個相同的db,之後來解除,TA這麼搞 就沒法做多庫的一緻性hash了
  • 資料的位數很重要,昨天利用snowflake算法寫一個發号器,以ip string 2 int 來做其中的主機标志段,果然沒長腦子的溢出了,浪費了很久的時間

想知道

  • 在gogland裡面定義一個叫producer.yml的檔案,結果沒有文法提示 pro.yml就行?!為了個啥?(命名問題)
  • 如何在golang中面向接口程式設計?寫的時候都像個樣子,真正用的時候就慫了。需要将抽象的接口具象到具體的struct,這個動作什麼時候實施?匿名成員-> 結構體方法(值傳遞、指針傳遞)
  • PHP和JS允許使用變量作為函數名,或者使用CALL、CALL_STATIC這種類似的方法來調用一個命名為aaa的方法。而go作為靜态類型語言,需要預先定義好方法,目前用switch...case...的方法去調用對應的func,隻能使用靜态工廠模式,過多的使用寫死。

轉載于:https://www.cnblogs.com/asfan/p/7698519.html