ခဏလေး ဒီလိုပဲ စဉ်းစားကြည့်
ဒီ lesson က ပထမ Exercises lesson ထက် ခက်ခဲပြီး Advanced chapter မှာသင်ခဲ့တဲ့ interface, custom error type, goroutine, channel concept တွေကို ပေါင်းစပ်ပြီး စိန်ခေါ်ဖို့ ရည်ရွယ်ထားပါတယ်။ Task တစ်ခုချင်းစီက real-world Go program တွေမှာ တွေ့ရလေ့ရှိတဲ့ pattern တွေဖြစ်ပြီး polymorphism, safe error handling, concurrent processing ကို လက်တွေ့ကျင့်ကြံစေပါတယ်။ ဒီ practice set ကို ပြီးအောင်လုပ်နိုင်ရင် Projects chapter ရဲ့ Task Manager project ကို ကိုယ်တိုင် extend လုပ်ဖို့ အထောက်အကူ ဖြစ်စေပါလိမ့်မယ်။
လေ့ကျင့်ခန်းများ
Task 1: Shape ဆိုတဲ့ interface တစ်ခု Area() float64 method နဲ့ define လုပ်ပါ။ Circle{Radius float64} နဲ့ Rectangle{Width, Height float64} struct နှစ်ခု ဖန်တီးပြီး နှစ်ခုစလုံးအတွက် Area() method implement လုပ်ကာ Shape slice တစ်ခုထဲမှာ ရော run ကြည့်ပါ။ Task 2: InvalidAgeError ဆိုတဲ့ custom error type တစ်ခု (Error() string method ပါအောင်) ဖန်တီးပြီး validateAge(age int) error function ရေးကာ age က 0 ထက်နည်းရင် (သို့) 150 ထက်များရင် ဒီ custom error ကို return ပြန်ပါ။ Task 3: worker pool pattern သုံးပြီး channel တစ်ခုကနေ number 10 ခုလက်ခံကာ goroutine 3 ခု parallel run ပြီး number တစ်ခုချင်းစီကို square (နှစ်ထပ်ကိန်း) ပြောင်းကာ result channel ထဲ ပြန်ပို့တဲ့ program ရေးပါ။
Code နမူနာ
package main
import (
"fmt"
"sync"
)
// Task 1
type Shape interface {
Area() float64
}
type Circle struct{ Radius float64 }
type Rectangle struct{ Width, Height float64 }
func (c Circle) Area() float64 { return 3.14159 * c.Radius * c.Radius }
func (r Rectangle) Area() float64 { return r.Width * r.Height }
// Task 2
type InvalidAgeError struct{ Age int }
func (e *InvalidAgeError) Error() string {
return fmt.Sprintf("invalid age: %d", e.Age)
}
func validateAge(age int) error {
if age < 0 || age > 150 {
return &InvalidAgeError{Age: age}
}
return nil
}
// Task 3
func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
defer wg.Done()
for n := range jobs {
results <- n * n
}
}
func main() {
shapes := []Shape{Circle{Radius: 2}, Rectangle{Width: 3, Height: 4}}
for _, s := range shapes {
fmt.Println("Area:", s.Area())
}
if err := validateAge(200); err != nil {
fmt.Println("Error:", err)
}
jobs := make(chan int, 10)
results := make(chan int, 10)
var wg sync.WaitGroup
for w := 1; w <= 3; w++ {
wg.Add(1)
go worker(w, jobs, results, &wg)
}
for n := 1; n <= 10; n++ {
jobs <- n
}
close(jobs)
wg.Wait()
close(results)
for r := range results {
fmt.Println("squared:", r)
}
}Circle နဲ့ Rectangle ရဲ့ Area() တွေ print ထုတ်ပြီး၊ age 200 အတွက် custom error message ပေါ်ပြီး၊ worker pool ကနေ number 1-10 ရဲ့ square value 10 ခု (order မတူချေ) print ထုတ်ပေးပါလိမ့်မယ်။၅ မိနစ် စမ်းကြည့်
5 minutes အတွင်း worker count ကို 3 ကနေ 5 ပြောင်းကြည့်ပြီး output order ဘယ်လိုပြောင်းလဲ observe လုပ်ကာ goroutine scheduling က deterministic မဟုတ်ဘူးဆိုတာ ကိုယ်တိုင်တွေ့ကြည့်ပါ။
သတိလေးတစ်ချက်
Goroutine ပေါင်းများများ run တဲ့အခါ shared variable ကို တိုက်ရိုက် modify ရင် data race ဖြစ်နိုင်လို့ channel ကနေတစ်ဆင့် communicate လုပ်တဲ့ Go idiom ("don't communicate by sharing memory; share memory by communicating") ကို လိုက်နာသင့်ပါတယ်။