Interfaces
Interface คืออะไร?
หัวข้อที่มีชื่อว่า “Interface คืออะไร?”Interface คือชื่อที่รวบรวม method signatures ไว้ด้วยกัน type ใดที่มี methods ทั้งหมดนั้นจะตอบสนอง interface — โดยไม่ต้องบอกอย่างชัดเจน
type Shape interface { Area() float64 Perimeter() float64}type ใดที่มี method Area() float64 และ Perimeter() float64 จะตอบสนอง Shape โดยอัตโนมัติ
Implicit satisfaction — ไม่มี implements
หัวข้อที่มีชื่อว่า “Implicit satisfaction — ไม่มี implements”นี่คือคุณสมบัติที่กำหนดตัวตน interfaces ของ Go เปรียบเทียบ:
| ภาษา | วิธีประกาศการตอบสนอง |
|---|---|
| Java / C# | class Circle implements Shape |
| Go | แค่มี methods ที่ถูกต้อง |
ผลลัพธ์คือโค้ดที่ decoupled type ใน package geometry สามารถตอบสนอง interface ใน package renderer โดยที่ไม่มี package ใดรู้จักอีก package ทำให้ Go interfaces compose ข้าม package boundaries ได้โดยไม่มี import dependency บน interface definition
Small interfaces
หัวข้อที่มีชื่อว่า “Small interfaces”standard library ของ Go สร้างบน interfaces เล็ก ๆ ที่มีจุดโฟกัส:
type Reader interface { Read(p []byte) (n int, err error) }type Writer interface { Write(p []byte) (n int, err error) }type Stringer interface { String() string }คำสอนของ Go: “ยิ่ง interface ใหญ่ abstraction ยิ่งอ่อนแอ” interface สอง methods ถูกตอบสนองโดย types จำนวนมากกว่า interface สิบ methods อย่างมาก ทำให้ reuse ได้มากกว่าอย่างมหาศาล
Empty interface — any
หัวข้อที่มีชื่อว่า “Empty interface — any”Interface ที่ไม่มี method ถูกตอบสนองโดยทุก type ตั้งแต่ Go 1.18 มี alias ว่า any:
var v any = 42v = "hello"v = []int{1, 2, 3}ใช้ any อย่างประหยัด — เพราะสละ type safety กรณีใช้งานปกติ: generic containers (ก่อนที่ Go จะมี generics), JSON unmarshalling รูปร่างที่ไม่รู้จัก, และฟังก์ชัน logging แบบ variadic
Accept interfaces, return structs
หัวข้อที่มีชื่อว่า “Accept interfaces, return structs”แนวทาง Go ที่ idiomatic:
- Function parameters ควรเป็น interfaces — รับ type กว้างที่สุดที่ครอบคลุมสิ่งที่คุณเรียกจริง
- Return values ควรเป็น concrete structs — ผู้เรียกได้รับ API เต็มของ type จริง; พวกเขา narrow เองได้
// ดี: รับ interface, คืน concrete typefunc NewBuffer(r io.Reader) *bytes.Buffer { ... }package main
import ( "fmt" "math")
type Shape interface { Area() float64 Perimeter() float64}
type Circle struct { Radius float64}
func (c Circle) Area() float64 { return math.Pi * c.Radius * c.Radius}
func (c Circle) Perimeter() float64 { return 2 * math.Pi * c.Radius}
type Rect struct { W, H float64}
func (r Rect) Area() float64 { return r.W * r.H }func (r Rect) Perimeter() float64 { return 2 * (r.W + r.H) }
func describe(s Shape) { fmt.Printf("area=%.2f perimeter=%.2f\n", s.Area(), s.Perimeter())}
func main() { shapes := []Shape{ Circle{Radius: 5}, Rect{W: 4, H: 3}, } for _, s := range shapes { describe(s) }}Loading Go runtime (first run only, ~8 MB)…
Runtime Behavior
หัวข้อที่มีชื่อว่า “Runtime Behavior”interface value คือ 2 words:
iface ├─ *itab → ┌──────────────────────┐ │ │ type: *Circle │ │ │ methods: [Area, │ │ │ Perimeter] │ │ └──────────────────────┘ └─ data → Circle{Radius: 5}
dynamic dispatch: shape.Area() ↓ itab.methods[0](*data) ← indirect call ↓ Circle.Area(data)ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| สิ่งที่ได้ | ประโยชน์ | ต้นทุน |
|---|---|---|
| implicit satisfaction | decoupled, no import dependency | ยากต่อการ discover ว่า type ใด implement interface ใด |
| small interface (1-2 methods) | composable, implement ง่าย | อาจต้องใช้หลาย interface ร่วมกัน |
| any / empty interface | รับ type ใดก็ได้ | สูญเสีย type safety ทั้งหมด |
| dynamic dispatch | flexibility | ~5-10ns overhead ต่อ call vs direct call |
ความเข้าใจผิดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ความเข้าใจผิดที่พบบ่อย”- interface ฟรี — interface เพิ่ม indirection 1 ชั้นและ dynamic dispatch overhead
- nil interface และ interface ที่เก็บ nil pointer เป็นเหมือนกัน — nil interface คือ (nil, nil), interface ที่เก็บ nil pointer คือ (*T, nil) ซึ่งไม่เท่ากัน
- ยิ่ง interface มี method มาก ยิ่งดี — Go idiom: “the bigger the interface, the weaker the abstraction”
- implicit satisfaction แปลว่า duck typing — Go ตรวจสอบ interface ตอน compile ไม่ใช่ runtime
💡 ตัวอย่างจากของจริง
io.Reader (1 method): http.Body, os.File, bytes.Buffer, strings.Reader ล้วน implement — composable ทั่ว ecosystem โดยไม่มี import dependency
Kubernetes ใช้ runtime.Object interface ให้ทุก Kubernetes resource ทำงานกับ generic controller ได้ — decoupling ผ่าน small interface