Method Receiver

ถ้ามองในมุม OOP Method แสดงถึง Behavior ของ Object กว่า Object นั้นกระทำ action อะไรได้บ้าง

สำหรับ Method ของ Go ก็เช่นกัน การกำหนด receiver ก็เพื่อบอกว่า type ไหนจะมีหน้าที่ทำงาน method นี้

ทีนี้ การทำหนด type ของ receiver เราสามารถกำหนดได้ทั้ง Type ธรรมดา และ pointer ของ Type นั้นๆ เรามาดูว่ามันแตกต่างกันยังไง

สมมุติเรามี type Account ดังนี้

type Account struct {
    Name string
    Balance float64
}

ในการสร้าง method มีหลักง่ายๆคือ ถ้าเราต้องการเปลี่ยนแปลงค่าของ Type ที่เป็น receiver เราจะเลือกใช้ pointer เป็น receiver ถ้าเราไม่ต้องการ เราจะเลือกใช้เป็น type receiver ธรรมดา ซึ่งเมื่อ method นั้นถูกเรียก จะเกิดการ copy ค่าไปให้ receiver แทนการอ้างอิงด้วย pointer นั่นเอง หรือบางกรณีถ้าโครงสร้างของ value เก็บขอมูลขนาดใหญ่ ก็เลือกใช้ pointer เพื่อลดเวลาการ copy ก็ได้เช่นกัน

ตัวอย่างเช่น ผมสร้าง method String ให้กับ type Account ดังนี้

func (acc Account) String() string {
	return fmt.Sprintf("Account %s have balance %0.2f", acc.Name, acc.Balance)
}

ตอนใช้งาน แม้ว่า method String จะกำหนดว่า receiver เป็น type Account แต่เราสามารถเรียกใช้ method นี้ผ่านทางตัวแปรแบบ ธรรมดา และ pointer ของ Account ได้ ตัวอย่างเช่น

func main() {
	acc := Account{Name: "Weerasak", Balance : 1000.00 }
	fmt.Println(acc.String())

	pacc := &acc
	fmt.Println(pacc.String())
}

ตัวอย่างต่อไปสร้าง method Deposit ดังนี้

func (acc *Account) Deposit(amount float64) {
	acc.Balance += amount
}

จะเห็นว่าทำการอัพเดทค่า Balance ได้ ถ้าเราไม่ใช้ pointer ค่าของ Account ที่เรียกจะไม่ถูกเปลี่ยนเพราะเป็นตัว copy ที่เปลี่ยนเลยไม่มีผล

ตัวอย่างใช้งานเช่น

func main() {
	acc := Account{Name: "Weerasak", Balance : 1000.00 }
	acc.Deposit(500.00)
	fmt.Println(acc)
}

จะเห็นว่า แม้ว่าจะสร้าง receiver เป็น pointer ก็เอาตัวแปรของ Account ธรรมดาเรียกได้เช่นกัน

สรุป ไม่ receiver จะเป็นแบบไหน ก็เอาตัวแปรแบบ Type ปกติ และ Pointer ของ Type นั้นเรียกใช้ได้เช่นกัน ความต่างคือภายใน method จะเปลี่ยนค่าได้ ไม่ได้ขึ้นอยู่กับเป็น pointer หรือไม่ นั่นเอง

สุดท้ายลองเล่นผ่าน playground ได้เลย