【Go 語言教程】Go語言函式說明
在程式設計中,模組設計是最小的設計單元,模組設計可以對應面相物件設計中的類設計,也可以對應到函式(方法)設計。所以程式設計中很多時候函式作為模組設計的最小單元。函式設計也有很多方法和規定,以及設計原則。那麼go語言的函式設計都有些什麼原則和要求呢,讓我們一起來看一看。
1 入口函式
作為編譯型的程式語言,go語言和其他流行的語言(C/C++, JAVA等)都有程式入口,就是所謂的入口函式main。go語言程式作為一個可執行程式需要至少有一個且僅為一個main方法。main方法是go語言程式編譯後執行的第一個方法(當然特殊方法init除外)。main方法的寫法也往往仿照C/C++或者Java的引數模式,如代表命令列的argv引數陣列和引數個數的argc,在go語言中使用元件os.args帶代替和邏輯編寫。命令列引數的陣列下標以0開始。flag也是操作命令列中常用的模組。所以常見的入口方法命令列引數獲取方式如下:
import ( "flag" "fmt" "os" ) func main() { // 列印os.args的內容 fmt.Println("args=", os.Args) // 獲取引數值 bVal := flag.Bool("bVal", false, "if flag is true") id := flag.Int("id", 0, "id") port := flag.String("port", ":8080", "the port") var name string // 換種方式獲取引數值 flag.StringVar(&name, "name", "dean", "name") // 轉換 flag.Parse() // 打印出來讀取的命令列引數值 fmt.Println("bVal:", *bVal) fmt.Println("id:", *id) fmt.Println("port:", *port) fmt.Println("name:", name) }
執行結果 ./flag -bVal=true -id 1001 -name kate args= [./flag -bVal=true -id 1001 -name kate] bVal: true id: 1001 port: :8080 name: kate
2 函式定義
go語言的函式定義方式和C/C++,Java等是不太一樣的。定義格式如下:
func func_name(params list) return_types { // 函式體 }
其中
func代表方法的宣告
func_name代表函式的方法名字
params list代表引數列表,組成格式為:var type 變數名和型別
return_types代表返回的型別列表:如bool或者(bool, int)等
函式體部分放置方法的實現邏輯程式碼
3 例子程式碼
以下將上面的main方法獲取命令列引數進行方法的封裝,原始碼如下:
package main import ( "fmt" "flag" ) func ShowFlags() { // 獲取引數值 bVal := flag.Bool("bVal", false, "if flag is true") id := flag.Int("id", 0, "id") port := flag.String("port", ":8080", "the port") var name string // 換種方式獲取引數值 flag.StringVar(&name, "name", "dean", "name") // 轉換 flag.Parse() // 打印出來讀取的命令列引數值 fmt.Println("bVal:", *bVal) fmt.Println("id:", *id) fmt.Println("port:", *port) fmt.Println("name:", name) } func ShowArgs(args []string) bool { if len(args) <= 0 { return false } // 列印os.args的內容 fmt.Println("args=", args) return true }
//執行結果如下 ./flag args= [./flag] Show Flagstrue bVal: false id: 0 port: :8080 name: dean
4 函式的呼叫
當定義了一個函式,那麼就定義了一個功能的模組集合,函式實現了這個模組的功能,並帶有輸入引數和返回引數。那麼函式的呼叫也比較簡單,程式碼如下:
package main import ( "fmt" "os" ) func main() { // 列印os.args的內容 ret := ShowArgs(os.Args) fmt.Println("Show Flags ", ret) // 獲取引數值 ShowFlags() }
其中有帶引數的函式呼叫ShowArgs(os.Args),也有不帶引數的函式呼叫ShowFlags()。函式的呼叫有先後順序,也代表了程式的執行順序,函式呼叫遵循棧的先進後出。
5 多返回值函式
函式的定義除了正常的有返回和單一的返回值(C/C++,Java等語言就是如此,只能有一個返回值)。但是go原因是可以定義帶多個返回值的函式的,這個也給程式設計帶來了便利,因為很多時間一個返回值往往並不能滿足我們隊業務模組定義的需要,而且多返回值不管是在進行類似業務邏輯:如果true就做什麼事情(需要什麼資料),需要返回2個數據物件,又或者進行分別處理。程式碼如下;
package main import "fmt" func SwapNumber(left, right int) (int, int) { return right, left } func main() { a := 2 b := 3 // 交換前 fmt.Println(a, b) a, b = SwapNumber(a, b) // 交換後 fmt.Println(a, b) }
如果你對於C/C++語言對於交換2個數的實現熟悉,可以好好觀察,go語言在進行這個操作的時候的便捷性。沒有任何的第三個變數(或者中間變數),對吧。
// 執行結果 ./swap 2 3 3 2
6 函式引數說明
go語言的函式引數分為傳值和引用傳遞2種,章節5中的交換2個數就是值傳遞,值傳遞就是就是咱們常說的copy(複製)一份傳遞(賦值)給下一個變數。引用傳遞則表達的是咱們認識的傳地址(也就是指向變數的地址)。預設的go語言的引數傳遞是值傳遞,不會影響到原始的變數的值。也就是說在函式呼叫過程中,對於實參是沒有任何影響的。
7 函式的用法
寫過其他語言的程式碼都比較清楚,函式的用法也是出了正常的呼叫,計算,執行等之外。可以這麼認為定義的函式可以作為抽象的值,在語句中賦值給變數或者新的函式(傳參),還有就是C/C++中沒有的匿名函式方法的定義。匿名函式定義和普通函式定義類似,只是匿名函式直接出現在程式碼邏輯中,不需要單獨將函式方法塊獨立放在方法定義之外,而是直接寫方法體,如下:
// 定義 func(a, b int, z float) bool { return a*b < int(z) } // 用途,直接複製給變數 f := func(x,y int) int { return x*y } // 花括號後面直接加括號表示方法呼叫 func(a, b int) { fmt.Println(a, b) }(2, 3)
其實匿名方法,就是不帶方法名字的函式定義。
8 函式的作用域
我們知道go語言的包概念(package)是對應著不同的作用域,而在不同的包內,如果要呼叫非包內的(或者其它模組)的實現方法,那麼是可以直接呼叫麼?答案當然是不行的。這裡順便分享個設計概念,就是包設計,當設計一個包的時候需要定義那些介面是需要對外部開放的,哪些是包內實現的,不需要對外開放的。設計的時候就可以想好,而包內對外開放的介面就是必須要以大寫字母開頭的定義,外部才是可見的。對於小寫字母開頭的方法實現,外部是不可見的。所以函式的作用域就是和函式定義的名字的首字母大小寫有管理。
當然函式內部的定義的作用域可見度只包含於函式內部。出了函式花括號,一定是不可見的。
9 經驗分享
一個函式的設計好壞,和函式的名字,傳參,以及返回引數都有密切的管理。
好的函式設計有一個可以簡單的單詞,但是卻可以表達這個函式是做什麼功能的;同時一個好的函式設計對於傳參和返回值也是比較講究的,原則是要精簡和方便,比如以下程式碼:
// 2個數相加 Add(a, b int) int // 顯示使用者資訊 ShowUserInfo(Persion) // 不好2個數相加的設計 aaa(a, b int) int // 不好的顯示使用者資訊的設計 printui(Persion)
一個好的函式設計(或者說一段好的程式碼)有這樣一些原則,可以分享給你,你可以先記在腦海,以後的程式碼實踐中慢慢思考和琢磨。
- 可讀性良好
- 可以方便除錯
- 可以方便進行測試
- 易於維護
- 可以很容易擴充套件
- 有效的(效率高)
關於本專欄——Go 語言教程,專欄致力於為希望可以系統地,一條一條地,一步一步的學習Go語言的朋友提供一個可以持續閱讀的地方,當然也希望可以通過我的實踐經驗以及相應的基礎,可以給你一些學習之外的想法和總結,以及提高。如果真的有幫助,哪怕一點點,可以把這個一點點的提高分享給我,歡迎跟我勾搭聯絡。