รู้จักกับ Golang
Golang หรือ Go เป็นภาษาโปรแกรมที่ถูกพัฒนาโดย Google ในปี 2007 และเปิดตัวในปี 2009 โดยมีเป้าหมายเพื่อสร้างภาษาที่ง่ายต่อการเรียนรู้และใช้งาน แต่ยังคงมีประสิทธิภาพสูงในการพัฒนาแอปพลิเคชันที่มีความซับซ้อน ส่วนใหญ่ Golang จะถูกใช้ในการพัฒนาเว็บเซิร์ฟเวอร์, ระบบเครือข่าย, และแอปพลิเคชันที่ต้องการประสิทธิภาพสูง เช่น Docker และ Kubernetes ซึ่งเป็นเครื่องมือที่ได้รับความนิยมในวงการ DevOps และ Cloud Computing ที่สำคัญ Golang มีความแรงในเรื่อง api ด้วยเพราะมี framework ตัวนึงที่เขียน api ได้แรงกว่า rust ด้วยซ้ำไปครับ นั่นก็คือ Fiber จัดการ cpu ได้ดีกว่า rust 3 เท่า latency ต่ำกว่า rust ประมาณนึง แต่เรื่องของ memory แพ้ยับ rust ทำได้ดีกว่า 10 เท่าเลยครับ ย้ำว่ามีแค่ fiber เท่านั้นนะครับที่ทำได้ดีกว่า rust ในเรื่องของ cpu และ latency ส่วน framework อื่นๆ ก็ยังแพ้ rust อยู่ดี ในเมื่อมันแรงกว่า และเขียนง่ายกว่า ทำไมถึงไม่เรียนกันล่ะครับ?
Install Golang
การติดตั้ง Golang สามารถทำได้ง่ายๆ โดยไปที่เว็บไซต์ทางการของ Golang ที่ https://golang.org/dl/ อีกอย่างที่ผมชอบคือ mise สามารถอ่านได้จากที่นี่เลยครับ [https://mise.jdx.dev/getting-started.html]
mise use -g go@latestหลังจากติดตั้งเสร็จแล้ว คุณสามารถตรวจสอบเวอร์ชันของ Golang ที่ติดตั้งได้โดยใช้คำสั่ง:
go versionซึ่งจะทำให้คุณเห็นเวอร์ชันของ Golang ที่คุณติดตั้งอยู่
สร้างโปรเจกต์แรกของคุณ
หลังจากที่คุณติดตั้ง Golang เรียบร้อยแล้ว เรามาสร้างโปรเจกต์แรกของคุณกันเถอะ!
- สร้างโฟลเดอร์สำหรับโปรเจกต์ของคุณ:
mkdir golang-introcd golang-intro-
เปิด IDE ของคุณและสร้างไฟล์ใหม่ชื่อ
main.goในโฟลเดอร์golang-intro -
ใส่โค้ดต่อไปนี้ลงในไฟล์
main.go:
TIPถ้าคุณใช้ Visual Studio Code คุณสามารถใช้ Extension ที่ชื่อว่า “Go” เพื่อช่วยในการเขียนโค้ด Golang ได้ง่ายขึ้นครับ คุณสามารถพิมพ์ pkgm แล้วกด tab เพื่อสร้าง Package main และ func main ได้เลย
package main
import "fmt"
func main() { fmt.Println("Hello, World!")}- บันทึกไฟล์และเปิดเทอร์มินัลในโฟลเดอร์
golang-introจากนั้นรันคำสั่ง:
go run main.gooutput ที่คุณจะเห็นคือ:
Hello, World!ยินดีด้วย! คุณเพิ่งสร้างโปรเจกต์ Golang แรกของคุณและรันมันได้สำเร็จแล้วครับ ในบทความต่อไปเราจะมาดูพื้นฐานของภาษา Golang และวิธีการใช้งานฟีเจอร์ต่างๆ ของมันกันครับ
Variables และ Data Types
ใน Golang การประกาศตัวแปรสามารถทำได้หลายวิธี แต่ที่นิยมใช้กันมากที่สุดคือการใช้คำสั่ง var และการใช้การประกาศแบบย่อด้วย :=
package mainimport "fmt"func main() { // การประกาศตัวแปรด้วย var var name string = "Alice" var age int = 30
// การประกาศตัวแปรแบบย่อ city := "Bangkok" isStudent := false
fmt.Println("Name:", name) fmt.Println("Age:", age) fmt.Println("City:", city) fmt.Println("Is Student:", isStudent)}ในกรณีที่เราจะ update ค่าของตัวแปร เราสามารถทำได้โดยการใช้ชื่อของตัวแปรและกำหนดค่าใหม่ให้กับมัน เช่น:
name := "Alice" fmt.Println("Name:", name) // จะสังเกตว่าตัวที่เคยประกาศไปแล้ว เราไม่ต้องใช้ := แต่ใช้แค่ = ได้เลยครับ name = "Bob" fmt.Println("Updated Name:", name)ใน Golang มีหลักๆอยู่ 4 ประเภทที่คุณสามารถใช้ได้ เช่น:
string: ใช้สำหรับเก็บข้อความ เช่น"Hello, World!"int: ใช้สำหรับเก็บจำนวนเต็ม เช่น42float64: ใช้สำหรับเก็บจำนวนทศนิยม เช่น3.14bool: ใช้สำหรับเก็บค่าความจริง เช่นtrueหรือfalse
กลุ่มของข้อมูล (Arrays, Slices และ Maps)
ใน Golang มีโครงสร้างข้อมูลหลักๆ ที่ใช้ในการจัดเก็บข้อมูลหลายๆ ค่า ได้แก่ Arrays, Slices และ Maps
Arrays
Array เป็นโครงสร้างข้อมูลที่มีขนาดคงที่และเก็บข้อมูลชนิดเดียวกัน เช่น:
package mainimport "fmt"func main() { var numbers [5]int numbers[0] = 1 numbers[1] = 2 numbers[2] = 3 numbers[3] = 4 numbers[4] = 5
fmt.Println("Numbers:", numbers)}Slices
Slice เป็นโครงสร้างข้อมูลที่มีขนาดยืดหยุ่นและสามารถเก็บข้อมูลชนิดเดียวกันได้ เช่น:
package mainimport "fmt"func main() { numbers := []int{1, 2, 3, 4, 5} fmt.Println("Numbers:", numbers)}Slices มีฟังก์ชันที่ช่วยในการจัดการกับข้อมูล เช่น append ที่ใช้เพิ่มค่าเข้าไปใน slice:
numbers := []int{1, 2, 3} fmt.Println("Numbers:", numbers) // เพิ่มค่าเข้าไปใน slice numbers = append(numbers, 4, 5) fmt.Println("Updated Numbers:", numbers)Maps
Map เป็นโครงสร้างข้อมูลที่ใช้เก็บคู่ของคีย์และค่า เช่น:
package mainimport "fmt"func main() { person := make(map[string]string) person["name"] = "Alice" person["city"] = "Bangkok" fmt.Println("Person:", person)}Control Structures (if, for, switch)
If-Else
package mainimport "fmt"func main() { age := 30 // สิ่งที่ golang ไม่เหมือนกับภาษาอื่นๆคือวงเล็บของ if ไม่ต้องมีวงเล็บครับ แต่จริงๆใส่ก็ได้นะเดี๋ยวมันก็ลบออกให้เอง if age >= 18 { fmt.Println("You are an adult.") } else { fmt.Println("You are a minor.") }}Switch
package mainimport "fmt"func main() { day := "Monday" switch day { case "Monday": fmt.Println("Start of the week.") case "Friday": fmt.Println("End of the week.") default: fmt.Println("Midweek.") }}For Loop
ใน Golang ไม่มีคำสั่ง while แต่คุณสามารถใช้ for เพื่อสร้างลูปได้ เช่น:
package mainimport "fmt"func main() { // ลูปแบบ for ปกติ for i := 0; i < 5; i++ { fmt.Println("Iteration:", i) }
// ลูปแบบ while count := 0 for count < 5 { fmt.Println("Count:", count) count++ }
// ลูปแบบ for-each สำหรับ slice เราจะได้ใช้แบบนี้บ่อยๆ ครับ numbers := []int{1, 2, 3, 4, 5} for index, value := range numbers { fmt.Printf("Index: %d, Value: %d\n", index, value) }}ฟังก์ชัน
ฟังก์ชันทั่วไป
ฟังก์ชันใน Golang ถูกประกาศด้วยคำสั่ง func เรามาดูตัวอย่างการสร้างฟังก์ชันง่ายๆ กันครับ:
package mainimport "fmt"
// ฟังก์ชันที่ไม่คืนค่าfunc greet(name string) { fmt.Printf("Hello, %s!\n", name)}
// ฟังก์ชันที่รับพารามิเตอร์และคืนค่าfunc add(a int, b int) int { return a + b}
func main() { greet("Alice") result := add(5, 3) fmt.Println("Result:", result)}คืนค่าหลายค่าก็ได้
ใน Golang ฟังก์ชันสามารถคืนค่าหลายค่าได้ เช่น:
package mainimport "fmt"func divide(a int, b int) (int, error) { if b == 0 { return 0, fmt.Errorf("Cannot divide by zero") } return a / b, nil}
func main() { result, err := divide(10, 2) if err != nil { fmt.Println("Error:", err) } else { fmt.Println("Result:", result) }}Structs และ Methods
ใน Golang เนี่ยเราไม่มี class แต่เรามี struct ที่ใช้ในการสร้างโครงสร้างข้อมูลที่ซับซ้อนขึ้นมาได้ครับ เช่น:
package mainimport "fmt"type Person struct { Name string Age int}// วิธีการจำคือมันจะเป็น func ตามด้วยชื่อ struct Person ในวงเล็บมันไม่ใช่ parameter นะ เพราะ parameter จะอยู่ข้างหลัง ในวงเล็บของ Greet ซึ่งฟังก์ชันนี้ไม่มีfunc (p Person) Greet() { fmt.Printf("Hello, my name is %s and I am %d years old.\n", p.Name, p.Age)}func main() { person := Person{Name: "Alice", Age: 30} person.Greet()}Interfaces
Interface มีอยู่สอง feature ที่สำคัญมากๆ คือ Abstraction และ Polymorphism
พูดง่ายๆคือบังคับและยืดหยุ่น เรามาเริ่มที่อันแรก Abstraction บังคับ
package mainimport "fmt"
type Shape interface { Area() float64}type Circle struct { Radius float64}
func (c Circle) Area() float64 { return 3.14 * c.Radius * c.Radius}
func main() { var s Shape
s = Circle{Radius: 5} fmt.Printf("Area of the circle: %.2f\n", s.Area())}ต่อมา Polymorphism ยืดหยุ่น ในตัวอย่างระบบจ่ายเงินที่มีหลายวิธีการจ่ายเงิน เช่น บัตรเครดิต, PayPal, และ Bitcoin เราสามารถสร้าง interface ที่ชื่อว่า PaymentMethod ที่มีฟังก์ชัน ProcessPayment() และแต่ละวิธีการจ่ายเงินจะ implement interface นี้ได้ เช่น:
package mainimport "fmt"type PaymentMethod interface { ProcessPayment(amount float64) error}type CreditCard struct { CardNumber string}func (cc CreditCard) ProcessPayment(amount float64) error { fmt.Printf("Processing credit card payment of $%.2f for card number %s\n", amount, cc.CardNumber) return nil}type PayPal struct { Email string}func (pp PayPal) ProcessPayment(amount float64) error { fmt.Printf("Processing PayPal payment of $%.2f for email %s\n", amount, pp.Email) return nil}type Bitcoin struct { WalletAddress string}func (bc Bitcoin) ProcessPayment(amount float64) error { fmt.Printf("Processing Bitcoin payment of $%.2f for wallet address %s\n", amount, bc.WalletAddress) return nil}
func main() { var payment PaymentMethod // ทีนี้ user จะจ่ายด้วยอะไรผมไม่รู้แหละ แต่ผมแค่รู้ว่ามันต้อง implement interface PaymentMethod เท่านั้นเองครับ payment = CreditCard{CardNumber: "1234-5678-9012-3456"} payment.ProcessPayment(100.00) payment = PayPal{Email: "alice@example.com"} payment.ProcessPayment(50.00) payment = Bitcoin{WalletAddress: "1A1zP1eP5QADt9KZz5rYvG8M2sT1gZ"} payment.ProcessPayment(0.005)}คุ้นไหมครับ ใช่ครับเหมือน Generic แต่ก็ไม่ใช่ครับ เพราะ Generic มันจะบังคับให้ type ที่เข้ามาเป็นแบบเดียวกัน แต่ Interface มันจะบังคับแค่ให้ type ที่เข้ามา implement interface เท่านั้นเองครับ
Multiple files และ Packages
ในความเป็นจริงแล้วโปรเจกต์ Golang ส่วนใหญ่จะมีหลายไฟล์และหลายแพ็กเกจเพื่อช่วยในการจัดระเบียบโค้ดให้ดีขึ้น เราจะมาทำในสิ่งที่ควรทำกันตั้งแต่ตอนแรกเลย
go mod init golang-introคำสั่งนี้จะสร้างไฟล์ go.mod
package calculator
เราจะสร้างโฟลเดอร์ใหม่ชื่อ calculator และสร้างไฟล์ calculator.go ขึ้นมาในโฟลเดอร์นั้น
package calculatorfunc Add(a int, b int) int { return a + b}func Subtract(a int, b int) int { return a - b}main.go
ในไฟล์ main.go เราจะ import package calculator ที่เราสร้างขึ้นมาและใช้ฟังก์ชันที่อยู่ในนั้น
package mainimport ( "fmt" "golang-intro/calculator")func main() { sum := calculator.Add(5, 3) difference := calculator.Subtract(5, 3) fmt.Println("Sum:", sum) fmt.Println("Difference:", difference)}CAUTIONเราจะไม่สามารถสร้าง package ที่ชื่อไม่เหมือนกันใน layer เดียวกันได้ เช่น
golang-intro/├── main.go (package main)└── calculator/├── calculator.go (package calculator)|└── another_file.go (package newpackage)
pointer
ใน Golang การใช้ pointer เป็นเรื่องที่สำคัญมากๆ เพราะมันช่วยให้เราสามารถแก้ไขค่าของตัวแปรจากฟังก์ชันได้โดยตรง โดยไม่ต้องคืนค่าใหม่กลับมา เรามาดูตัวอย่างการใช้ pointer กันครับ:
package mainimport "fmt"func increment(value *int) { *value++}func main() { num := 5 fmt.Println("Before increment:", num) increment(&num) fmt.Println("After increment:", num)}มาดู case ที่มีความเป็นจริงมากขึ้นหน่อยครับ
package mainimport "fmt"type User struct { Name string Age int}func updateUser(user *User) { user.Age++}func main() { user := User{Name: "Alice", Age: 30} fmt.Printf("Before update: %+v\n", user) updateUser(&user) fmt.Printf("After update: %+v\n", user)}TIPส่วนใหญ่แล้วใน Golang เราจะใช้ pointer กับ struct เป็นหลักครับ มันช่วยให้ประหยัด memory และทำให้โค้ดของเรามีประสิทธิภาพมากขึ้น ส่วน data type อื่นๆ เช่น int, string, bool ส่วนตัวผมไม่ค่อยใช้ pointer กับมันเท่าไหร่ครับ เพราะมันมีขนาดเล็กและการส่งผ่านค่าจะไม่ทำให้เกิดปัญหาเรื่อง performance มากนัก ส่วน slice และ map เนี่ย จริงๆ แล้วมันก็เป็น reference type อยู่แล้วครับ เพราะฉะนั้นไม่จำเป็นต้องใช้ pointer กับมันอีกทีนึงครับ