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 — no field name Breed string}
func (d Dog) Speak() string { return d.Name + " barks" // d.Name promoted from 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 for 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() works repo UserRepository}
// UserService now has .Log() without re-implementing itfunc (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 manuallyinterface 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); // manual delegation }}type Writer interface { Write(p []byte) (n int, err error)}
type CountingWriter struct { io.Writer // embed the interface itself! count int}
func (cw *CountingWriter) Write(p []byte) (int, error) { n, err := cw.Writer.Write(p) // delegate to embedded cw.count += n return n, err}// CountingWriter satisfies Writer automaticallypackage 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 promotes Name, Speak, Describe Breed string}
// Override Speak — Describe is still promoted from Animalfunc (d Dog) Speak() string { return d.Name + " barks"}
type Cat struct { Animal}
// Cat does not override Speak — uses promoted version
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()) // explicit base call fmt.Println(d.Describe()) // promoted from Animal fmt.Println(d.Name) // promoted field fmt.Println(d.Breed) // Dog's own field
fmt.Println(c.Speak()) // promoted from Animal}Compiling on go.dev (Go 1.27)… offline? an in-browser runtime (~8 MB, Go 1.26) is used instead