Struct Embedding
TypeScript: class inheritance และ mixin
หัวข้อที่มีชื่อว่า “TypeScript: class inheritance และ mixin”TypeScript ให้คุณ extend class ด้วย extends (single inheritance) และเลียนแบบ multiple inheritance ได้คร่าว ๆ ผ่าน mixin โดย mental model คือ hierarchy: child IS-A parent
Go ไม่มี class และไม่มี inheritance แต่ใช้ embedding แทน คือ type หนึ่งรวม type อื่นไว้เป็น anonymous field ได้ แล้ว method กับ field ของ type ที่ embed เข้ามาจะถูก promote ขึ้นมาที่ outer type โดย mental model คือ composition: outer HAS-A inner
การ embed พื้นฐาน
หัวข้อที่มีชื่อว่า “การ embed พื้นฐาน”ประกาศ embedded type ด้วยการเขียนชื่อ type ลงไปตรง ๆ โดยไม่ต้องตั้งชื่อ field ภายใน struct body
// TypeScript — class inheritanceclass Animal { name: string; constructor(name: string) { this.name = name; } speak(): string { return `${this.name} makes a sound`; }}
class Dog extends Animal { breed: string; constructor(name: string, breed: string) { super(name); this.breed = breed; } speak(): string { return `${this.name} barks`; }}
const d = new Dog("Rex", "Labrador");console.log(d.speak()); // Rex barksconsole.log(d.name); // Rex — inherited// Go — struct embedding (composition)type Animal struct{ Name string }
func (a Animal) Speak() string { return a.Name + " makes a sound"}
type Dog struct { Animal // embedded — ไม่มีชื่อ field Breed string}
func (d Dog) Speak() string { return d.Name + " barks" // d.Name promote มาจาก Animal}
func main() { d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Labrador"} fmt.Println(d.Speak()) // Rex barks fmt.Println(d.Animal.Speak()) // Rex makes a sound fmt.Println(d.Name) // Rex — promoted field}Method promotion
หัวข้อที่มีชื่อว่า “Method promotion”เมื่อ method มีอยู่เฉพาะบน embedded type (ไม่ถูก override) Go จะ promote method นั้นขึ้นมาที่ method set ของ outer type ให้อัตโนมัติ ผลคือ outer type satisfy interface ที่ inner type implement ได้ไปด้วยเลย
// TypeScript — mixin pattern สำหรับ reusetype Serializable = { serialize(): string;};function withSerialize<T extends object>(Base: new(...a: any[]) => T) { return class extends Base implements Serializable { serialize() { return JSON.stringify(this); } };}type Logger struct{ Prefix string }
func (l Logger) Log(msg string) { fmt.Println(l.Prefix+":", msg)}
type UserService struct { Logger // promoted: UserService.Log() ทำงานได้ repo UserRepository}
// UserService มี .Log() โดยไม่ต้อง implement ใหม่func (s UserService) CreateUser(name string) { s.Log("creating user: " + name) // ... business logic}Embedding และ Interface
หัวข้อที่มีชื่อว่า “Embedding และ Interface”เพราะ promoted method นับเป็น method set ของ outer struct embedding จึงมักใช้เพื่อ satisfy interface โดยไม่ต้องเขียน delegation boilerplate
// TypeScript — implement interface แบบ manualinterface Writer { write(data: string): void;}class BufferedWriter implements Writer { private inner: Writer; constructor(inner: Writer) { this.inner = inner; } write(data: string) { this.inner.write(data); // delegation แบบ manual }}type Writer interface { Write(p []byte) (n int, err error)}
type CountingWriter struct { io.Writer // embed interface นั้นเอง! count int}
func (cw *CountingWriter) Write(p []byte) (int, error) { n, err := cw.Writer.Write(p) // delegate ไปยัง embedded cw.count += n return n, err}// CountingWriter satisfy Writer อัตโนมัติpackage main
import "fmt"
type Animal struct { Name string}
func (a Animal) Speak() string { return a.Name + " makes a sound"}
func (a Animal) Describe() string { return "Animal: " + a.Name}
type Dog struct { Animal // embedding promote Name, Speak, Describe Breed string}
// Override Speak — Describe ยังคง promote มาจาก Animalfunc (d Dog) Speak() string { return d.Name + " barks"}
type Cat struct { Animal}
// Cat ไม่ override Speak — ใช้ version ที่ promote มา
func main() { d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Labrador"} c := Cat{Animal: Animal{Name: "Whiskers"}}
fmt.Println(d.Speak()) // Dog override fmt.Println(d.Animal.Speak()) // เรียก base อย่างชัดเจน fmt.Println(d.Describe()) // promote มาจาก Animal fmt.Println(d.Name) // promoted field fmt.Println(d.Breed) // field ของ Dog เอง
fmt.Println(c.Speak()) // promote มาจาก Animal}Loading Go runtime (first run only, ~8 MB)…