Giao diện
📡 I/O Abstractions & Reflection
"Everything is a file" — Unix philosophy.
Trong Go, "Everything is an io.Reader". Master interface này để build generic, memory-efficient systems.
Trong Go, "Everything is an io.Reader". Master interface này để build generic, memory-efficient systems.
🌊 The io.Reader Universe
The Most Important Interface in Go
go
// io package - chỉ 2 methods core
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}🎓 Professor Tom's Deep Dive: Why io.Reader is King
Mọi thứ implement io.Reader:
| Source | Type | Example |
|---|---|---|
| Files | *os.File | os.Open("data.txt") |
| Network | net.Conn | TCP/UDP connections |
| HTTP | *http.Response.Body | API responses |
| Memory | *bytes.Buffer | In-memory data |
| Compression | *gzip.Reader | Wrapped reader |
| Strings | *strings.Reader | strings.NewReader("hello") |
Power: Viết code 1 lần, work với tất cả sources!
Composability Pattern
go
// Function xử lý BẤT KỲ io.Reader nào
func ProcessData(r io.Reader) error {
scanner := bufio.NewScanner(r)
for scanner.Scan() {
line := scanner.Text()
// Process line...
}
return scanner.Err()
}
// Gọi với file
file, _ := os.Open("data.txt")
defer file.Close()
ProcessData(file)
// Gọi với HTTP response
resp, _ := http.Get("https://api.example.com/data")
defer resp.Body.Close()
ProcessData(resp.Body)
// Gọi với string (testing!)
ProcessData(strings.NewReader("line1\nline2\nline3"))
// Gọi với gzip-compressed data
gzReader, _ := gzip.NewReader(file)
ProcessData(gzReader)
)🚀 Stream Processing: Memory Efficiency
The Problem: Loading Everything into RAM
🔥 Raizo's Pitfall: Memory Explosion
go
// ❌ BAD: Đọc toàn bộ file vào RAM
func ProcessFileBad(path string) error {
data, err := os.ReadFile(path) // 10GB file = 10GB RAM!
if err != nil {
return err
}
lines := strings.Split(string(data), "\n")
for _, line := range lines {
process(line) // Cũng copy thêm memory
}
return nil
}Với file 10GB:
- RAM usage: 10GB+ (file) + strings (copy)
- GC pressure: Massive
- Startup time: Must wait for full read
The Solution: Streaming
go
// ✅ GOOD: Stream processing - constant memory
func ProcessFileStream(path string) error {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
// Optional: increase buffer for long lines
scanner.Buffer(make([]byte, 64*1024), 1024*1024)
for scanner.Scan() {
line := scanner.Text()
process(line) // Process one line at a time
}
return scanner.Err()
}Memory comparison:
┌─────────────────────────────────────────────────────────────────────┐
│ MEMORY USAGE: 10GB FILE │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ReadFile (Bad): │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ ████████████████████████████████████████████ 10GB+ RAM │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ Stream (Good): │
│ ┌────┐ │
│ │ ██ │ ~64KB buffer (constant!) │
│ └────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘io.Copy: The Streaming Hero
go
// Copy data without loading into memory
func DownloadFile(url, filepath string) error {
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
file, err := os.Create(filepath)
if err != nil {
return err
}
defer file.Close()
// Streams data directly: Response → File
// Never loads entire response into RAM!
written, err := io.Copy(file, resp.Body)
if err != nil {
return err
}
log.Printf("Downloaded %d bytes", written)
return nil
}Reader Chaining (Composition)
go
// Stack readers for complex processing
func ProcessCompressedEncrypted(path string) error {
file, _ := os.Open(path)
defer file.Close()
// Chain: File → Decrypt → Decompress → Process
decrypted := crypto.NewReader(file, key)
decompressed, _ := gzip.NewReader(decrypted)
// Final reader is fully processed stream
scanner := bufio.NewScanner(decompressed)
for scanner.Scan() {
// Each line is decrypted + decompressed on-the-fly
}
return nil
}🔍 Reflection: Power with Responsibility
What is Reflection?
🎓 Under the Hood: reflect Package
Reflection cho phép inspect và manipulate types tại runtime:
go
import "reflect"
func InspectType(v interface{}) {
t := reflect.TypeOf(v)
v := reflect.ValueOf(v)
fmt.Printf("Type: %s\n", t.Name())
fmt.Printf("Kind: %s\n", t.Kind())
fmt.Printf("Value: %v\n", v)
}
type User struct {
ID int64 `json:"id"`
Name string `json:"name"`
}
InspectType(User{ID: 1, Name: "Raizo"})
// Type: User
// Kind: struct
// Value: {1 Raizo}Common Use Cases
go
// 1. Reading struct tags (JSON, DB, validation)
func GetJSONFieldNames(v interface{}) []string {
t := reflect.TypeOf(v)
var names []string
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
jsonTag := field.Tag.Get("json")
if jsonTag != "" && jsonTag != "-" {
names = append(names, jsonTag)
}
}
return names
}
// 2. Generic struct comparison
func StructsEqual(a, b interface{}) bool {
return reflect.DeepEqual(a, b)
}
// 3. Dynamic function calling
func CallMethod(obj interface{}, methodName string, args ...interface{}) {
v := reflect.ValueOf(obj)
method := v.MethodByName(methodName)
if method.IsValid() {
in := make([]reflect.Value, len(args))
for i, arg := range args {
in[i] = reflect.ValueOf(arg)
}
method.Call(in)
}
}Reflection Performance Warning
🔥 Raizo's Critical Warning: Reflection is SLOW
Reflection có overhead đáng kể:
go
// Benchmark results:
// BenchmarkDirectAccess-8 500000000 3.2 ns/op
// BenchmarkReflection-8 5000000 285.0 ns/op
// → Reflection ~90x slower!
// ❌ BAD: Don't use reflection in hot paths
func ProcessOrdersBad(orders []Order) {
for _, order := range orders {
v := reflect.ValueOf(order)
total := v.FieldByName("Total").Float() // Slow!
// ...
}
}
// ✅ GOOD: Direct access
func ProcessOrdersGood(orders []Order) {
for _, order := range orders {
total := order.Total // Fast!
// ...
}
}When to Use Reflection
| Use Case | Reflection OK? | Alternative |
|---|---|---|
| JSON/YAML marshaling | ✅ (library) | — |
| ORM field mapping | ✅ (library) | — |
| Validation frameworks | ✅ (library) | — |
| Hot path processing | ❌ | Direct access |
| Type checking | ❌ | Type switch |
| Generic containers | ❌ | Generics (Go 1.18+) |
🏗️ Building Generic Systems
Example: Universal Encoder
go
// Interface-based design (preferred)
type Encoder interface {
Encode(v interface{}) error
}
type JSONEncoder struct {
w io.Writer
}
func (e *JSONEncoder) Encode(v interface{}) error {
return json.NewEncoder(e.w).Encode(v)
}
type XMLEncoder struct {
w io.Writer
}
func (e *XMLEncoder) Encode(v interface{}) error {
return xml.NewEncoder(e.w).Encode(v)
}
// Usage - swap encoders easily
func Export(data interface{}, format string, w io.Writer) error {
var encoder Encoder
switch format {
case "json":
encoder = &JSONEncoder{w: w}
case "xml":
encoder = &XMLEncoder{w: w}
default:
return fmt.Errorf("unsupported format: %s", format)
}
return encoder.Encode(data)
}Example: Generic Data Pipeline
go
// Pipeline pattern with io.Reader
type Pipeline struct {
readers []func(io.Reader) io.Reader
}
func (p *Pipeline) AddTransform(t func(io.Reader) io.Reader) {
p.readers = append(p.readers, t)
}
func (p *Pipeline) Process(r io.Reader, w io.Writer) error {
current := r
// Chain all transformations
for _, transform := range p.readers {
current = transform(current)
}
_, err := io.Copy(w, current)
return err
}
// Usage
func main() {
pipeline := &Pipeline{}
// Add transformations
pipeline.AddTransform(func(r io.Reader) io.Reader {
return gzip.NewReader(r)
})
pipeline.AddTransform(func(r io.Reader) io.Reader {
return transform.NewReader(r, japanese.ShiftJIS.NewDecoder())
})
file, _ := os.Open("data.txt.gz")
defer file.Close()
pipeline.Process(file, os.Stdout)
}📊 Summary
| Concept | Key Point |
|---|---|
| io.Reader | Universal interface for all data sources |
| Streaming | Process data chunk-by-chunk, constant memory |
| io.Copy | Zero-copy data transfer between Reader and Writer |
| Reader Chaining | Compose readers for transformation pipelines |
| Reflection | Powerful but slow — use for frameworks, not hot paths |
| Generics | Prefer over reflection when possible (Go 1.18+) |
📌 HPN Standard: I/O Decision Tree
Need to read data?
├── Size known & small (< 10MB)?
│ └── ReadFile/ReadAll OK
└── Size unknown or large?
└── Use streaming (Scanner, io.Copy)
Need generic type handling?
├── Limited set of types?
│ └── Type switch
├── Go 1.18+?
│ └── Use Generics
└── Must work with arbitrary types?
└── Reflection (last resort)➡️ Tiếp theo
I/O và Reflection nắm vững rồi! Tiếp theo: Maps Internals - Hash table structure, buckets, và concurrent access patterns.