10 kĩ thuật hay được dùng trong golang
Go
50
golang
49
White

Hoàng Minh Trung viết ngày 15/06/2016

Bài viết dịch từ http://arslan.io/ten-useful-techniques-in-go

Sử dụng một GOPATH duy nhất

Sử dụng đồng thời nhiều GOPATH sẽ không giúp cho hệ thống mở rộng tốt. Bản thân GOPATH đã rất độc lập (thông qua import path). Việc sử dụng một lúc nhiều GOPATH sẽ đem lại hiệu ứng phụ, ví dụ như nhiều phiên bản khác nhau cho một package. Bạn có thể update version của một package tại chỗ này nhưng lại quên chỗ khác. Cá nhân tôi chưa từng thấy một project nào lại cần nhiều GOPATH khác nhau.
Một số dự án lớn như camlistore cũng vendoring bằng cách đóng băng tất cả các phụ thuộc vào một folder sử dụng godep. Điều đó có nghĩa là ngay cả các dự án đó cũng sử dụng một và chỉ một GOPATH.

Sử dụng hàm khi dùng select bên trong vòng for

func main() {

L:
    for {
        select {
        case t1 := <-time.After(time.Second):
            fmt.Println("hello", t1)
            if t1.Second()%4 == 0 {
                break L
            }
        }
    }

    fmt.Println("ending")
}

Đoạn code trên lấy timestap sau mỗi 4s, khi số giây chia hết cho 4 sẽ break. Đoạn code sử dụng label L để break bởi nếu chỉ dùng break đơn thuần thì sẽ không thoát ra khỏi cả vòng for.
Mặc dù đây là cách dùng đúng cho label, tuy nhiên cá nhân tôi không thích cách dùng này lắm. Nếu đoạn code được viết lại như sau thì sẽ không cần dùng label nữa

func foo() {
    for {
        select {
        case t1 := <-time.After(time.Second):
            fmt.Println("hello", t1)
            if t1.Second()%4 == 0 {
                return
            }
        }
    }
}

func main() {
    foo()
    fmt.Println("ending")
}

Như vậy nếu tách ra hàm một cách thích hợp thì sẽ không cần phải sử dụng GoTo nữa.

Khi khởi tạo struct, hãy sử dụng tên của thuộc tính

Giả sử chúng ta có struct dưới đây

type T struct {
    Foo string
    Bar int
}

Có 2 cách để khởi tạo T, và đều đem lại kết quả như nhau

t := T{"example", 123}
t := T{Foo: "example", Bar: 123}

Mặc dù đem lại kết quả giống nhau, tuy nhiên hãy hình dung ta thêm thuộc tính vào T

type T struct {
    Foo string
    Bar int
    Qux string
}

Khi đó thì cách không chỉ định tên của thuộc tính sẽ compile error (lỗi không đủ giá trị)

Hãy chia làm nhiều dòng khi khởi tạo struct

Thay vì viết là

T{Foo: "example", Bar:someLongVariable, Qux:anotherLongVariable, B: forgetToAddThisToo}

thì hãy viết

T{
    Foo: "example",
    Bar: someLongVariable,
    Qux: anotherLongVariable,
    B: forgetToAddThisToo,
}

Thêm hàm String() vào enum kiểu int

Giả sử chúng ta có kiểu enum như sau

type State int

const (
    Running State = iota
    Stopped
    Rebooting
    Terminated
)

Tuy nhiên khi sử dụng trong code thì sẽ khó phân biệt kiểu, do đó nếu chúng ta thêm method

func (s State) String() string {
    switch s {
    case Running:
        return "Running"
    case Stopped:
        return "Stopped"
    case Rebooting:
        return "Rebooting"
    case Terminated:
        return "Terminated"
    default:
        return "Unknown"
    }
}

thì khi print ra sẽ hiển thị s là "Running" thay vì 0

Khi sử dụng iota hãy nhớ thêm +1

Giả sử chúng ta có struct như sau:

type T struct {
    Name  string
    Port  int
    State State
}

và struct này được sử dụng

func main() {
    t := T{Name: "example", Port: 6666}
    fmt.Printf("%+v\n", t) // --> "{Name:example Port:6666 State:Running}"
}

Bạn có thể thấy chúng ta đã quên mất khởi tạo giá trị State, và nó ngẫu nhiên nhận giá trị Running, có vẻ là một giả định không hề tốt

Vậy điều cần làm là thay vì bắt đầu enum từ 0, chúng ta hãy bắt đầu từ 1

const (
    Running State = iota + 1
    Stopped
    Rebooting
    Terminated
)

Hoặc có thể làm một cách khác

onst (
    Unknown State = iota 
    Running
    Stopped
    Rebooting
    Terminated
)

Hãy trả lại function ngay khi có thể

Nếu có xử lý

func bar() (string, error) {
    v, err := foo()
    if err != nil {
        return "", err
    }

    return v, nil
}

thì hãy thay bằng

func bar() (string, error) {
    return foo()
}

Sử dụng slices, map dưới dạng custom type

Giả sử chúng ta có một hàm trả về server list

type Server struct {
    Name string
}

func ListServers() []Server {
    return []Server{
        {Name: "Server1"},
        {Name: "Server2"},
        {Name: "Foo1"},
        {Name: "Foo2"},
    }
}

Giả sử chúng ta muốn thêm việc filter tên Server vào thì sẽ phải sửa trực tiếp hàm ListServers, hoặc thêm hàm mới đại loại như ListServerFiltered, khá là phiền phức. Khi đó để xử lý đẹp đẽ, chúng ta chỉ cần gán type slice thành 1 type khác rồi bind xử lý mới vào nó

type Servers []Server

func (s Servers) Filter(name string) Servers {
 // trả về kết quả filter
}

Sử dụng hàm wrappter WithContext

Giả sử chúng ta có rất nhiều xử lý giống nhau

func foo() {
    mu.Lock()
    defer mu.Unlock()

    // foo
}

func bar() {
    mu.Lock()
    defer mu.Unlock()

    // bar
}

func qux() {
    mu.Lock()
    defer mu.Unlock()

    // qux
}

Chúng ta có thể viết lại đẹp đẽ hơn

func withLockContext(fn func()) {
    mu.Lock
    defer mu.Unlock()

    fn()
}

func foo() {
    withLockContext(func() {
        // foo 
    })
}

func bar() {
    withLockContext(func() {
        // bar 
    })
}

func qux() {
    withLockContext(func() {
        // qux
    })
}

Kĩ thuật trên na ná kĩ thuật decorator của python

Sử dụng hàm getter, setter khi access map

Vì Map trong golang không phải là threadsafe, nên sử dụng getter, setter sẽ tốt hơn cho việc debug

type Storage interface {
    Delete(key string)
    Get(key string) string
    Put(key, value string)
}
Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Hoàng Minh Trung

23 bài viết.
61 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
18 15
(Ảnh) Mục đích của bài viết là hướng dẫn cơ bản nhất cho những ai chưa biết về docker, môi trường thực hiện là mac OS. Chuẩn bị Cài đặt virtua...
Hoàng Minh Trung viết gần 3 năm trước
18 15
White
14 0
Bài viết dịch từ https://github.com/luciotato/golangnotes/blob/master/OOP.md Mục đích bài viết Học golang dễ dàng hơn với những kiến thức bạn đ...
Hoàng Minh Trung viết 2 năm trước
14 0
White
12 3
Theo định nghĩa mình tìm được ở (Link) thì lệnh trap trong shell script có nghĩa là The trap command allows you to execute a command when a signal...
Hoàng Minh Trung viết hơn 2 năm trước
12 3
Bài viết liên quan
White
9 2
Makefile thực hiện một số thao tác thường dùng trong Go Khi làm project Go mình thường tạo một file Makefile dạng này: Lưu ý nhớ thay thành tên m...
Huy Trần viết 2 năm trước
9 2
White
43 16
Go là gì? Dùng nó cho việc gì? Chắc hẳn đến thời điểm hiện tại, không ai là chưa nghe đến Go (hay còn gọi là Golang), một ngôn ngữ lập trình được ...
Huy Trần viết 3 năm trước
43 16
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
23 bài viết.
61 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!