【Go 程式設計實踐】從零到一:建立、測試並行布自己的 Go 庫

2023-11-05 18:00:35

為什麼需要開發自己的 Go 庫

在程式語言中,包(Package)和庫(Library)是程式碼組織和複用的重要工具。在 Go 中,包是程式碼的基本組織單位,每個 Go 程式都由包構成。包的作用是幫助組織程式碼,提供封裝和程式碼複用的機制。

Go 包可以包含函數、型別、變數和常數等,這些元素可以被其他包參照和使用。例如,Go 的標準庫提供了大量的包,如 net/http 包提供了 HTTP 使用者端和伺服器實現,fmt 包提供了格式化、I/O 函數等。

而庫是一種特殊的包,不包含 main 函數,不能被直接執行,但可以被其他程式參照。庫通常包含一些常用的功能或演演算法,如字串處理、數學計算、網路通訊等。

開發自己的 Go 庫的優點:

  1. 複用性:當在多個專案中需要使用相同的功能時,可以將這些功能封裝在一個庫中,然後在需要的地方參照他。這樣可以避免重複編寫相同的程式碼,提高程式設計效率。
  2. 可維護性:當需要修改某個功能時,只需修改對應的庫,而不需要在多個地方進行修改,這樣可以使程式碼更易於理解和維護。
  3. 可測試性:為每個庫編寫單元測試,確保他們的功能正確。修改程式碼時,可以執行這些測試來檢查是否引入了新的錯誤。

接下來,將以 Asiatz(github.com/mazeyqian/asiatz)為例,詳細演示如何建立一個規範的 Go 庫。

Asiatz 主要功能是進行時區轉換,特別是對亞洲時區的處理,他能夠將各種時區轉換為 UTC 時間。

utcTime, err := asiatz.ShanghaiToUTC("08:00")
if err != nil {
    // handle error
}
fmt.Println(utcTime) // Output: 00:00

第 1 步:建立目錄

在本地建立一個新的目錄,名為 asiatz。這個目錄將包含所有的原始碼、測試和檔案檔案。

mkdir asiatz
cd asiatz

第 2 步:初始化專案

2.1 初始化 Go 模組

asiatz 目錄下,執行 go mod init <domain>/<username>/<module-name> 來初始化 Go 模組。

go mod init github.com/mazeyqian/asiatz

專案結構:

asiatz
└── go.mod

2.2 建立檔案

建立一個新的 Go 檔案,名為 asiatz.go。在此檔案中,定義一個名為 asiatz 的包,並編寫相對應的功能函數。

專案結構:

asiatz
├── asiatz.go
└── go.mod

程式碼範例:

package asiatz

import (
    "fmt"
    "strconv"
)

// ToUTC converts a time string (HH:mm) from a specified timezone to UTC time string (HH:mm).
func ToUTC(timezoneOffset float64, time string) (string, error) {
    hour, err := strconv.Atoi(time[:2])
    if err != nil {
        return "", err
    }
    minute, err := strconv.Atoi(time[3:])
    if err != nil {
        return "", err
    }
    totalMinutes := hour*60 + minute
    utcTotalMinutes := ((totalMinutes-int(timezoneOffset*60))%1440 + 1440) % 1440
    utcHour := utcTotalMinutes / 60
    utcMinute := utcTotalMinutes % 60
    utcTime := fmt.Sprintf("%02d:%02d", utcHour, utcMinute)
    return utcTime, nil
}

// ShanghaiToUTC converts a Shanghai time string (HH:mm) to UTC time string (HH:mm).
// For example, "08:00" in Shanghai is equivalent to "00:00" in UTC.
func ShanghaiToUTC(shanghaiTime string) (string, error) {
    return ToUTC(8, shanghaiTime)
}

第 3 步:編寫測試

Go 提供了內建的測試框架,可以方便地編寫和執行測試用例,以確保程式碼的正確性和穩定性。

asiatz 目錄下建立一個新的 Go 檔案,名為 asiatz_test.go。在這個檔案中編寫測試用例來測試 asiatz.go 中的函數。

專案結構:

asiatz
├── asiatz.go
├── asiatz_test.go
└── go.mod

程式碼範例:

package asiatz

import "testing"

type testConversion struct {
    time     string
    expected string
}

var tests = map[string][]testConversion{
    "Shanghai": {
        {"01:00", "17:00"},
        {"23:59", "15:59"},
    },
    // Others
}

func runConversionTests(t *testing.T, tests []testConversion, conversionFunc func(string) (string, error)) {
    for _, test := range tests {
        actual, err := conversionFunc(test.time)
        if err != nil {
            t.Errorf("Unexpected error for %s: %v", test.time, err)
            continue
        }
        if actual != test.expected {
            t.Errorf("Expected %s for %s but got %s", test.expected, test.time, actual)
        }
    }
}

func TestAllConversions(t *testing.T) {
    for timezone, tests := range tests {
        t.Run(timezone, func(t *testing.T) {
            switch timezone {
            case "Shanghai":
                runConversionTests(t, tests, ShanghaiToUTC)
            // Others
            default:
                t.Errorf("Unexpected timezone %s", timezone)
            }
        })
    }
}

檢視完整的用例可見:github.com/mazeyqian/asiatz/blob/main/asiatz_test.go

在當前目錄下執行 go test 檢視結果:

PASS
ok      github.com/mazeyqian/asiatz     0.449s

第 4 步:編寫檔案

為了方便其他人理解和使用 Asiatz 庫,需要編寫相應的使用檔案。檔案應包括庫的目的、功能函數的用法、使用範例和注意事項等。

asiatz 目錄下,建立一個新的 README.md 檔案,並在其中編寫檔案。

專案結構:

asiatz
├── asiatz.go
├── asiatz_test.go
├── go.mod
└── README.md

檔案範例:

第 5 步:釋出

5.1 上傳

將 Asiatz 庫上傳到 GitHub 或其他程式碼託管平臺,使其他人可以方便地獲取和使用。

go get github.com/mazeyqian/asiatz

5.2 版本控制

在 Git 倉庫上,還可以使用標籤來管理庫的不同版本。

git tag v1.0.0
git push origin v1.0.0

例如 Asiatz 目前有四個版本:v1.0.0v1.1.0v1.1.1v1.1.2,分別可以用以下命令獲取:

go get github.com/mazeyqian/[email protected]
go get github.com/mazeyqian/[email protected]
go get github.com/mazeyqian/[email protected]
go get github.com/mazeyqian/[email protected]

第 6 步:在真實專案中使用

以 Go 專案 github.com/mazeyqian/go-gin-gee 為例,首先在專案目錄(go-gin-gee)下執行命令 go get github.com/mazeyqian/asiatz 獲取 Asiatz 庫,然後引入使用即可:

// https://github.com/mazeyqian/go-gin-gee/blob/main/internal/api/controllers/schedules-controller.go
package controllers

import (
    "log"
    "github.com/mazeyqian/asiatz"
)

func Check() {
    // ...
    utcTime, err := asiatz.ShanghaiToUTC("10:00")
    if err != nil {
        // handle error
    }
    log.Println("UTC Time:", utcTime) // Output: 02:00
    // ...
}

總結

本文以 Asiatz 庫為例,詳細演示瞭如何從零開始建立、測試並行布自己的 Go 庫。無論是新手,還是有經驗的開發者;動手實踐,開發並行布自己的庫,不僅可以提高程式碼的複用性和維護性,提高自己的技能,還可以為社群做出貢獻。

版權宣告

本部落格所有的原創文章,作者皆保留版權。轉載必須包含本宣告,保持本文完整,並以超連結形式註明作者後除和本文原始地址:https://blog.mazey.net/4150.html

(完)