เมื่อเราพูดถึงประโยคนี้
Composition Over Inheritance
ในด้านของคนที่เขียน Go เอาจริงๆ ถ้าจะให้ตอบแบบห้วนๆเลยก็คือ เนื่องจาก Go ไม่ใช่ภาษา OOP มันเลยไม่มีการทำ Inheritance แต่สามารถใช้ Composition มาทดแทนได้
ทีนี้ก็ต้องมาดูกันว่า แล้วเราต้องการอะไรจากการทำ Inheritance ก่อน จะได้เอา Composition มาเทียบว่าแทนกันได้จริงหรือไม่ ซึ่งหลักๆเท่าที่รู้ก็จะเป็นเรื่อง
- ลด duplicated code
- reusability ไม่ต้องเขียนโค้ดซ้ำ โดยเฉพาะตัว base จะไม่ถูกแก้ไข
- extensibility เอาไปต่อเติมเพิ่มขยายได้โดยใช้ base เดิมมาตั้งต้น
เอาแค่นี้ก่อน เพราะถ้ามากกว่านี้เราจะไป 4 pillars ก็ละ 😅
เรามาดูท่ากันก่อนว่า composition ใน Go เขาทำกันอย่างไร
type Context struct {
kv map[string]string
}
func (c *Context) Get(k string) string {
return c.kv[k]
}
สมมุติว่าเรามี type ชื่อ Context และ type นี้มี 1 เมธอดชื่อ Get โดยมันจะสามารถดึงเอาค่า key/value ออกมาจาก Context ได้ด้วยการให้ key ลงไป
ทีนี้เราอยากสร้าง BusinessContext มาใช้งาน โดยมันจะมีความสามารถเดิมของ Context อยู่ด้วยแบบนี้
type BusinessContext struct {
Context
}
func (c *BusinessContext) Name() string {
return c.Context.Get("name")
}
ข้อสังเกตการใช้ c.Context.Get คือการเรียก .Get ผ่าน embedded field
จากตัวอย่าง เราเรียกว่า embedded field คือการ embed type Context เข้าไปใน BusinessContext ซึ่งการทำแบบนี้ จะทำให้ instance ของ BusinessContext จะยังสามารถใช้ .Get ได้เหมือนเดิม เช่น
ctx := &Context{kv:map[string]string{"name":"Pallat"}}
bctx := &BusinessContext{ctx}
println(bctx.Get("name"))
println(bctx.Name())
จะเห็นว่าเรายังสามารถเรียกใช้ .Get ผ่าน bctx ได้เหมือนใช้ ctx.Get และสิ่งที่เพิ่มเติมเข้าไปคือสามารถใช้ .Name ได้ด้วย
แต่ก็มีข้อควรระวังในการทำนิดหนึ่งคือว่า กรณีที่เราจะทำการ Override ของเดิมเช่น Get
func (c *BusinessContext) Get(k string) string {
return c.Get(k) + "."
}
แบบนี้จะมีปัญหา เพราะในฟังก์ชั่น Get ที่เราเขียนขึ้นมาใหม่ มันมีเรียก c.Get โดยไม่ได้ระบุชื่อ field แบบนี้ c.Context.Get
อันที่จริงมันเขียนแบบนี้ได้ แต่มันจะทำงานจริงไม่ได้ เพราะ c.Get มันเหมือนเรียกตัวเองแบบ recursive
ถ้าต้องการเรียก .Get จากตัว base ที่เรา embedded เข้ามา ต้องระบุ c.Context.Get
ลงไปตรงๆ โดยเฉพาะเมื่อเรามีการ override method ซึ่งถ้าจะให้ดี ก็ควรระบุชื่อ embedded เสมอนั่นเองครับ เพื่อความปลอดภัย
Top comments (0)