Go: พื้นฐานสำหรับนักพัฒนา Java/Python ในปี 2026

เรียนรู้ Go อย่างรวดเร็วโดยใช้ประสบการณ์จาก Java หรือ Python Goroutine, channel, interface และ pattern สำคัญสำหรับการเปลี่ยนผ่านอย่างราบรื่น

คู่มือ Go สำหรับนักพัฒนา Java และ Python

Go (หรือ Golang) ได้สถาปนาตนเองเป็นภาษาที่เลือกใช้สำหรับ microservice, เครื่องมือ CLI และระบบแบบกระจาย สร้างโดย Google ในปี 2009 ผสมผสานความเรียบง่ายของ Python เข้ากับประสิทธิภาพระดับ C สำหรับนักพัฒนาที่มาจาก Java หรือ Python การเปลี่ยนมาใช้ Go นั้นราบรื่นอย่างน่าประหลาดใจเมื่อแนวคิดหลักเข้าที่แล้ว

ทำไมต้อง Go ในปี 2026?

Go เป็นรากฐานของ Docker, Kubernetes, Terraform และโครงสร้างพื้นฐานคลาวด์จำนวนมาก การคอมไพล์ที่รวดเร็ว รองรับ concurrency แบบ native และการ deploy เป็นไฟล์ binary เดียว ทำให้ Go เหมาะสำหรับการพัฒนา backend สมัยใหม่

การติดตั้งและตั้งค่า Go

การติดตั้ง Go ทำได้ง่ายและสม่ำเสมอบนทุกแพลตฟอร์ม เครื่องมือ go จัดการการคอมไพล์ dependency และการทดสอบ

bash
# install.sh
# Installation on macOS with Homebrew
brew install go

# Installation on Linux (Ubuntu/Debian)
sudo apt update && sudo apt install golang-go

# Verify installation
go version
# go version go1.22.0 linux/amd64

โครงสร้างโปรเจกต์ Go เป็นไปตามข้อตกลงที่เข้มงวดแต่เรียบง่าย ไฟล์ go.mod กำหนดโมดูลและ dependency ต่าง ๆ

bash
# project-setup.sh
# Create a new project
mkdir my-project && cd my-project
go mod init github.com/user/my-project

# Generated structure:
# my-project/
# ├── go.mod    # Module manifest
# └── main.go   # Entry point

# Essential commands
go build          # Compile the project
go run main.go    # Compile and execute
go test ./...     # Run all tests
go fmt ./...      # Format code automatically

โปรแกรม Go แรก

ต่อไปนี้เป็นโปรแกรมง่าย ๆ ที่แสดง syntax พื้นฐานของ Go การเปรียบเทียบกับ Java และ Python ช่วยให้เห็นความแตกต่างได้ชัดเจน

main.gogo
package main

import "fmt"

// Program entry point
func main() {
    // Declaration with type inference
    message := "Hello, Go!"
    fmt.Println(message)

    // Explicit declaration
    var count int = 42
    fmt.Printf("Count: %d\n", count)
}

สิ่งที่สังเกตได้ทันที: ไม่มีเครื่องหมายอัฒภาค ไม่มีวงเล็บรอบเงื่อนไข และการอนุมานประเภทด้วย := Go ให้ความสำคัญกับความกระชับโดยไม่เสียสละความอ่านง่าย

ตัวแปรและประเภทข้อมูลพื้นฐาน

Go เป็นภาษา statically typed แต่มีการอนุมานประเภทที่ยอดเยี่ยม ประเภทพื้นฐานครอบคลุมกรณีใช้งานส่วนใหญ่

types.gogo
package main

import "fmt"

func main() {
    // Short declaration (inside functions only)
    name := "Alice"        // string
    age := 30              // int
    height := 1.75         // float64
    active := true         // bool

    // Explicit declaration
    var score int = 100
    var rate float64 = 3.14

    // Multiple declaration
    var (
        firstName string = "Bob"
        lastName  string = "Smith"
        points    int    = 0
    )

    // Zero values (default values)
    var count int      // 0
    var text string    // "" (empty string)
    var flag bool      // false
    var ptr *int       // nil

    fmt.Println(name, age, height, active)
}
ค่า Zero ใน Go

ต่างจาก Java หรือ Python ตรงที่ Go จะกำหนดค่าเริ่มต้นให้ตัวแปรเป็น "zero value" โดยอัตโนมัติ: 0 สำหรับตัวเลข, "" สำหรับ string, false สำหรับ bool, nil สำหรับ pointer และ slice

ฟังก์ชันและการคืนค่าหลายค่า

Go อนุญาตให้คืนค่าหลายค่าได้ ซึ่งเป็นคุณสมบัติที่ใช้อย่างกว้างขวางในการจัดการข้อผิดพลาด

functions.gogo
package main

import (
    "errors"
    "fmt"
)

// Simple function with typed parameters
func add(a, b int) int {
    return a + b
}

// Multiple returns (idiomatic pattern for errors)
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

// Named returns
func getUser(id int) (name string, age int, err error) {
    if id <= 0 {
        err = errors.New("invalid ID")
        return
    }
    name = "Alice"
    age = 30
    return
}

// Variadic function
func sum(numbers ...int) int {
    total := 0
    for _, n := range numbers {
        total += n
    }
    return total
}

func main() {
    // Simple call
    result := add(5, 3)
    fmt.Println("5 + 3 =", result)

    // Explicit error handling
    quotient, err := divide(10, 3)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Printf("10 / 3 = %.2f\n", quotient)

    // Ignore a returned value with _
    name, _, _ := getUser(1)
    fmt.Println("User:", name)

    // Variadic call
    total := sum(1, 2, 3, 4, 5)
    fmt.Println("Sum:", total)
}

Struct และ method

Struct เป็นส่วนประกอบพื้นฐานสำหรับสร้างประเภทข้อมูลที่กำหนดเองใน Go Method ผูกกับประเภทผ่าน receiver

structs.gogo
package main

import "fmt"

// Struct definition
type User struct {
    ID       int
    Username string
    Email    string
    Active   bool
}

// Constructor (convention: NewTypeName)
func NewUser(id int, username, email string) *User {
    return &User{
        ID:       id,
        Username: username,
        Email:    email,
        Active:   true,
    }
}

// Method with value receiver (copy)
func (u User) FullInfo() string {
    status := "inactive"
    if u.Active {
        status = "active"
    }
    return fmt.Sprintf("%s <%s> (%s)", u.Username, u.Email, status)
}

// Method with pointer receiver (modification possible)
func (u *User) Deactivate() {
    u.Active = false
}

// Method with pointer receiver for modification
func (u *User) UpdateEmail(newEmail string) {
    u.Email = newEmail
}

func main() {
    // Create with constructor
    user := NewUser(1, "alice", "alice@example.com")
    fmt.Println(user.FullInfo())

    // Modify via method
    user.Deactivate()
    fmt.Println(user.FullInfo())

    // Direct creation
    user2 := User{
        ID:       2,
        Username: "bob",
        Email:    "bob@example.com",
    }
    fmt.Println(user2.FullInfo())
}
Value vs Pointer Receiver

ใช้ pointer receiver (*User) เมื่อ method เปลี่ยนแปลงสถานะหรือเมื่อ struct มีขนาดใหญ่ ใช้ value receiver (User) สำหรับ method อ่านอย่างเดียวบน struct ที่เบา

Interface: polymorphism แบบ implicit

Interface ใน Go ถูก implement แบบ implicit ประเภทหนึ่งจะเป็นไปตาม interface ถ้า implement method ทั้งหมดของมัน โดยไม่ต้องประกาศอย่างชัดเจน

interfaces.gogo
package main

import (
    "fmt"
    "math"
)

// Interface definition
type Shape interface {
    Area() float64
    Perimeter() float64
}

// Rectangle implements Shape implicitly
type Rectangle struct {
    Width, Height float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}

// Circle also implements Shape
type Circle struct {
    Radius float64
}

func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

func (c Circle) Perimeter() float64 {
    return 2 * math.Pi * c.Radius
}

// Function accepting the interface
func PrintShapeInfo(s Shape) {
    fmt.Printf("Area: %.2f, Perimeter: %.2f\n", s.Area(), s.Perimeter())
}

func main() {
    rect := Rectangle{Width: 10, Height: 5}
    circle := Circle{Radius: 7}

    // Polymorphism via interface
    PrintShapeInfo(rect)
    PrintShapeInfo(circle)

    // Slice of interfaces
    shapes := []Shape{rect, circle}
    for _, shape := range shapes {
        PrintShapeInfo(shape)
    }
}

แนวทางนี้แตกต่างอย่างสิ้นเชิงจาก Java ที่ implements เป็นสิ่งจำเป็น ใน Go การสอดคล้องเป็นแบบโครงสร้าง ไม่ใช่แบบชื่อ

พร้อมที่จะพิชิตการสัมภาษณ์ Go แล้วหรือยังครับ?

ฝึกฝนด้วยตัวจำลองแบบโต้ตอบ, flashcards และแบบทดสอบเทคนิคครับ

Slice และ map: คอลเลกชันแบบไดนามิก

Slice เป็นมุมมองแบบไดนามิกบน array และ map เป็น hash table ในตัว

collections.gogo
package main

import "fmt"

func main() {
    // Slice: dynamic array
    numbers := []int{1, 2, 3, 4, 5}

    // Append elements
    numbers = append(numbers, 6, 7)

    // Slicing (similar to Python)
    subset := numbers[1:4]  // [2, 3, 4]
    fmt.Println("Subset:", subset)

    // Create slice with make
    scores := make([]int, 0, 10)  // len=0, cap=10
    scores = append(scores, 100, 95, 88)

    // Iteration with range
    for index, value := range numbers {
        fmt.Printf("numbers[%d] = %d\n", index, value)
    }

    // Map: hash table
    users := map[string]int{
        "alice": 30,
        "bob":   25,
    }

    // Add/Update
    users["charlie"] = 35

    // Check existence
    age, exists := users["alice"]
    if exists {
        fmt.Println("Alice's age:", age)
    }

    // Delete
    delete(users, "bob")

    // Map iteration
    for name, age := range users {
        fmt.Printf("%s is %d years old\n", name, age)
    }
}

การจัดการข้อผิดพลาดแบบ idiomatic

Go ไม่มี exception ข้อผิดพลาดเป็นค่าที่คืนกลับอย่างชัดเจน บังคับให้จัดการอย่างเข้มงวด

errors.gogo
package main

import (
    "errors"
    "fmt"
    "os"
)

// Sentinel error (for comparison)
var ErrNotFound = errors.New("resource not found")
var ErrInvalidInput = errors.New("invalid input")

// Custom error with context
type ValidationError struct {
    Field   string
    Message string
}

func (e *ValidationError) Error() string {
    return fmt.Sprintf("validation failed on %s: %s", e.Field, e.Message)
}

// Function returning different error types
func GetUser(id int) (string, error) {
    if id <= 0 {
        return "", &ValidationError{
            Field:   "id",
            Message: "must be positive",
        }
    }
    if id > 1000 {
        return "", ErrNotFound
    }
    return "Alice", nil
}

// Error wrapping (Go 1.13+)
func ReadConfig(path string) ([]byte, error) {
    data, err := os.ReadFile(path)
    if err != nil {
        return nil, fmt.Errorf("reading config %s: %w", path, err)
    }
    return data, nil
}

func main() {
    // Basic pattern
    user, err := GetUser(-1)
    if err != nil {
        fmt.Println("Error:", err)

        // Type assertion for custom error
        var valErr *ValidationError
        if errors.As(err, &valErr) {
            fmt.Printf("Field: %s\n", valErr.Field)
        }

        // Comparison with sentinel error
        if errors.Is(err, ErrNotFound) {
            fmt.Println("User not found")
        }
    } else {
        fmt.Println("User:", user)
    }
}
errors.Is และ errors.As

ตั้งแต่ Go 1.13 ใช้ errors.Is() เพื่อเปรียบเทียบกับ sentinel error และ errors.As() เพื่อดึงประเภท error เฉพาะจากสาย error ที่ถูก wrap

Goroutine: concurrency น้ำหนักเบา

Goroutine เป็น thread น้ำหนักเบาที่จัดการโดย runtime ของ Go การเริ่ม goroutine ใช้หน่วยความจำเพียงไม่กี่ KB

goroutines.gogo
package main

import (
    "fmt"
    "sync"
    "time"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()  // Decrement counter when done

    fmt.Printf("Worker %d starting\n", id)
    time.Sleep(time.Second)
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup

    // Launch 5 goroutines
    for i := 1; i <= 5; i++ {
        wg.Add(1)
        go worker(i, &wg)  // 'go' prefix launches the goroutine
    }

    // Wait for all goroutines to complete
    wg.Wait()
    fmt.Println("All workers completed")
}

sync.WaitGroup ช่วยให้รอจนกว่า goroutine หลายตัวจะทำงานเสร็จ นี่คือ pattern พื้นฐานสำหรับการทำงานแบบขนานใน Go

Channel: การสื่อสารระหว่าง goroutine

Channel เป็นท่อที่มีประเภทสำหรับการสื่อสารระหว่าง goroutine ช่วยให้มีการซิงโครไนซ์ที่ปลอดภัยและการแลกเปลี่ยนข้อมูล

channels.gogo
package main

import (
    "fmt"
    "time"
)

func producer(ch chan<- int) {
    for i := 1; i <= 5; i++ {
        fmt.Println("Producing:", i)
        ch <- i  // Send on channel
        time.Sleep(100 * time.Millisecond)
    }
    close(ch)  // Close channel when done
}

func consumer(ch <-chan int, done chan<- bool) {
    for value := range ch {  // Iterate until closed
        fmt.Println("Consuming:", value)
    }
    done <- true
}

func main() {
    ch := make(chan int)     // Unbuffered channel
    done := make(chan bool)

    go producer(ch)
    go consumer(ch, done)

    <-done  // Wait for consumer to finish
    fmt.Println("All done")
}

Buffered channel และ select

channels_advanced.gogo
package main

import (
    "fmt"
    "time"
)

func main() {
    // Buffered channel (capacity 3)
    buffered := make(chan int, 3)
    buffered <- 1
    buffered <- 2
    buffered <- 3
    // buffered <- 4  // Would block since buffer is full

    fmt.Println(<-buffered)  // 1

    // Select: channel multiplexing
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        time.Sleep(100 * time.Millisecond)
        ch1 <- "from ch1"
    }()

    go func() {
        time.Sleep(200 * time.Millisecond)
        ch2 <- "from ch2"
    }()

    // Wait for first available message
    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-ch1:
            fmt.Println("Received:", msg1)
        case msg2 := <-ch2:
            fmt.Println("Received:", msg2)
        case <-time.After(500 * time.Millisecond):
            fmt.Println("Timeout!")
        }
    }
}
ปรัชญาของ Go: CSP

Go ปฏิบัติตามโมเดล CSP (Communicating Sequential Processes): "อย่าสื่อสารโดยการแชร์หน่วยความจำ แต่จงแชร์หน่วยความจำโดยการสื่อสาร" Channel ป้องกัน race condition

การทดสอบใน Go

Go มี framework ทดสอบที่เรียบง่ายแต่มีประสิทธิภาพ ไฟล์ทดสอบลงท้ายด้วย _test.go

calculator.gogo
package calculator

func Add(a, b int) int {
    return a + b
}

func Divide(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}
calculator_test.gogo
package calculator

import (
    "testing"
)

// Basic test
func TestAdd(t *testing.T) {
    result := Add(2, 3)
    expected := 5

    if result != expected {
        t.Errorf("Add(2, 3) = %d; want %d", result, expected)
    }
}

// Table-driven tests (recommended pattern)
func TestAddTableDriven(t *testing.T) {
    tests := []struct {
        name     string
        a, b     int
        expected int
    }{
        {"positive numbers", 2, 3, 5},
        {"negative numbers", -2, -3, -5},
        {"zero", 0, 0, 0},
        {"mixed", -5, 10, 5},
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            result := Add(tt.a, tt.b)
            if result != tt.expected {
                t.Errorf("Add(%d, %d) = %d; want %d",
                    tt.a, tt.b, result, tt.expected)
            }
        })
    }
}

// Error test
func TestDivideByZero(t *testing.T) {
    _, err := Divide(10, 0)
    if err == nil {
        t.Error("Expected error for division by zero")
    }
}

// Benchmark
func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ {
        Add(100, 200)
    }
}

Test รันด้วย go test ./... และ benchmark ด้วย go test -bench=.

HTTP: เว็บเซิร์ฟเวอร์แบบ minimalist

Go เก่งในการสร้าง HTTP server ประสิทธิภาพสูงด้วย standard library

server.gogo
package main

import (
    "encoding/json"
    "log"
    "net/http"
)

type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

func main() {
    // Simple route
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hello, Go!"))
    })

    // JSON route
    http.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) {
        users := []User{
            {ID: 1, Name: "Alice"},
            {ID: 2, Name: "Bob"},
        }

        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(users)
    })

    // Route with method
    http.HandleFunc("/api/user", func(w http.ResponseWriter, r *http.Request) {
        switch r.Method {
        case "GET":
            w.Write([]byte("Get user"))
        case "POST":
            w.Write([]byte("Create user"))
        default:
            http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
        }
    })

    log.Println("Server starting on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

เซิร์ฟเวอร์นี้จัดการการเชื่อมต่อพร้อมกันหลายพันรายการได้ด้วย goroutine แต่ละ request จะถูกประมวลผลโดยอัตโนมัติใน goroutine ของตัวเอง

สรุป

Go มอบแนวทางเชิงปฏิบัติสำหรับการพัฒนา backend: syntax เรียบง่าย คอมไพล์เร็ว concurrency แบบ native และเครื่องมือที่ยอดเยี่ยม สำหรับนักพัฒนา Java หรือ Python การเปลี่ยนผ่านต้องยอมรับข้อตกลงที่แตกต่างบางประการ (การจัดการข้อผิดพลาดแบบชัดเจน, generics ที่จำกัดก่อน Go 1.18) แต่ประโยชน์ด้านประสิทธิภาพและการบำรุงรักษาเห็นได้ทันที

รายการตรวจสอบเพื่อเริ่มต้น

  • ✅ ติดตั้ง Go จากเว็บไซต์ทางการหรือ package manager
  • ✅ เชี่ยวชาญคำสั่ง go build, go run, go test, go fmt
  • ✅ เข้าใจความแตกต่างระหว่าง slice และ array
  • ✅ นำ pattern if err != nil มาใช้ในการจัดการข้อผิดพลาด
  • ✅ ใช้ goroutine และ channel สำหรับ concurrency
  • ✅ เขียน table-driven test ด้วย package testing

เริ่มฝึกซ้อมเลย!

ทดสอบความรู้ของคุณด้วยตัวจำลองสัมภาษณ์และแบบทดสอบเทคนิคครับ

ระบบนิเวศของ Go มีความสมบูรณ์ มี framework ยอดนิยมอย่าง Gin, Echo และ Fiber สำหรับพัฒนาเว็บ และเครื่องมืออย่าง Cobra สำหรับ CLI ด้วยรากฐานที่มั่นคงเหล่านี้ การสำรวจหัวข้อขั้นสูงอย่าง generics (Go 1.18+), package context และ pattern ของ concurrency จะเป็นเรื่องที่เข้าถึงได้ง่าย

แท็ก

#go
#golang
#concurrency
#goroutines
#backend

แชร์

บทความที่เกี่ยวข้อง