Bài học theo mô-đun (3/5)
Thành phần (nhúng)
Đi không có quyền thừa kế. Câu trả lời thành ngữ để tái sử dụng là thành phần và đặc biệt là nhúng: bao gồm một loại bên trong một cái khác mà không đặt tên trường cho nó. Các lĩnh vực và phương pháp loại được nhúng được thăng cấp, có thể truy cập trực tiếp như thể họ thuộc về kiểu bên ngoài.
Nhúng cấu trúc
type Animal struct {
Name string
}
func (a Animal) Greet() string {
return "ciao da " + a.Name
}
type Dog struct {
Animal // embedding — NO field name
Breed string
}
c := Dog{
Animal: Animal{Name: "Rex"},
Breed: "Pastore",
}
fmt.Println(c.Name) // "Rex" — promoted from Animal
fmt.Println(c.Greet()) // "ciao da Rex" — promoted method
fmt.Println(c.Breed) // own fieldTrường được nhúng vẫn có thể truy cập được bằng tên của loại:
c.Animal.Name hoạt động và giống hệt c.Name. Nó cần thiết khi
có va chạm.
Ghi đè (một phần): phương thức "che bóng"
Nếu kiểu bên ngoài định nghĩa một phương thức có cùng tên với kiểu được thăng cấp một, phương pháp của nó "thắng". Từ bên trong bạn luôn có thể tiếp cận được phần nhúng một thông qua tên của loại:
func (c Dog) Greet() string {
return "BAU! " + c.Animal.Greet()
}Nó không phải là sự kế thừa đa hình: nó chỉ là sự phân giải tên từ vựng. Để đa hình, bạn sử dụng giao diện (Mô-đun 6).
Nhiều nhúng
Bạn có thể nhúng nhiều loại:
type Logger struct{ /*...*/ }
func (l Logger) Log(msg string) { /*...*/ }
type Timer struct{ /*...*/ }
func (t Timer) Now() time.Time { /*...*/ }
type Service struct {
Logger
Timer
Name string
}
s := Service{Name: "api"}
s.Log("start") // promoted from Logger
s.Now() // promoted from TimerNếu hai loại được nhúng có một phương thức có CÙNG tên, truy cập trực tiếp
trở nên mơ hồ và trình biên dịch yêu cầu dạng rõ ràng
(s.Logger.Foo() hoặc s.Timer.Foo()).
Nhúng giao diện
Các giao diện cũng có thể được "nhúng" (xem Mô-đun 6):
type ReadWriter interface {
io.Reader
io.Writer
}Nó tương đương với sự kết hợp các phương thức của Reader và Writer.
Khi nào nên sử dụng bố cục rõ ràng (không nhúng)
Nếu bạn muốn một trường được đặt tên và không tự động thăng hạng, hãy sử dụng đơn giản thành phần:
type Dog struct {
Anim Animal // NORMAL field, no promotion
Breed string
}
c.Anim.Name // you need the field nameNhúng là một công cụ ủy quyền tiện dụng, không phải là một lựa chọn thiết kế hoàn toàn tốt hơn bố cục rõ ràng: hãy sử dụng nó khi bạn thực sự muốn để quảng bá API của loại bên trong.
Hãy thử nó
Xác định Dog nhúng Động vật (nhúng, không có tên trường) và có trường chuỗi Giống.
Hiển thị gợi ý
Nhúng: viết LOẠI mà không đặt tên trường.
Giải pháp khả dụng sau 3 lần thử
Thêm phương thức chuỗi Greet() vào Animal và gọi nó trực tiếp trên biến Dog (quảng bá phương thức).
Hiển thị gợi ý
Phương thức được xác định trên Animal được thăng cấp thành Dog nhờ nhúng.
Giải pháp khả dụng sau 3 lần thử
Go có hỗ trợ kế thừa cổ điển không?
type B struct { /* extends A? */ }Tóm tắt lại
- Không có tính kế thừa: bạn soạn bằng cách nhúng.
- Nhúng = ghi LOẠI mà không có tên trường trong cấu trúc.
- Các trường và phương thức thuộc loại được nhúng được thăng cấp.
- Luôn có sẵn quyền truy cập rõ ràng:
outer.Inner.Field. - Ghi đè "Từ vựng": phương thức của loại bên ngoài che mờ phương thức được thăng cấp.
- Nhúng ≠ is-a: để đa hình, hãy sử dụng giao diện.