Che khuất phương thức được nhúng
Thuộc phần Lập trình hướng đối tượng trong hành trình GO của Coddy. Bài học 39 trên 107.
Đôi khi, bạn cần struct bên ngoài tự triển khai một phương thức mà kiểu được nhúng đã có. Đây gọi là che khuất (shadowing), giúp bạn ghi đè các phương thức được thăng cấp bằng hành vi tùy chỉnh.
Khi bạn định nghĩa một phương thức trên struct bên ngoài có cùng tên với phương thức được nhúng, phương thức bên ngoài sẽ được ưu tiên:
type Animal struct {
Name string
}
func (a Animal) Speak() string {
return "Some sound"
}
type Dog struct {
Animal
Breed string
}
func (d Dog) Speak() string {
return "Woof!"
}
Bây giờ, khi bạn gọi Speak() trên một Dog, phương thức bên ngoài sẽ được dùng:
func main() {
d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Labrador"}
fmt.Println(d.Speak()) // Woof!: phương thức của Dog
fmt.Println(d.Animal.Speak()) // Some sound: phương thức của Animal
}
Phương thức được nhúng không biến mất. Bạn vẫn có thể truy cập phương thức đó một cách tường minh thông qua tên kiểu được nhúng. Cách làm này hữu ích khi bạn muốn tùy chỉnh hành vi nhưng vẫn giữ nguyên phần triển khai ban đầu, chẳng hạn như gọi phương thức đó bên trong phương thức ghi đè của bạn:
func (d Dog) Speak() string {
return d.Animal.Speak() + " (but actually Woof!)"
}
Shadowing giúp bạn kiểm soát hành vi nào được thể hiện, đồng thời vẫn truy cập được hành vi gốc thông qua cách gọi rõ ràng.
Thử thách
DễHãy xây dựng một hệ thống thông báo, trong đó các kênh thông báo khác nhau có thể tùy chỉnh cách định dạng và gửi tin nhắn. Bạn sẽ dùng cách che khuất phương thức để các kênh cụ thể ghi đè hành vi mặc định, đồng thời vẫn có thể truy cập phần triển khai ban đầu khi cần.
Bạn sẽ sắp xếp code trong ba tệp:
base.go: Tạo một structBaseNotificationvới các trườngTitlevàPriority(đều là chuỗi). Thêm phương thứcFormat() stringtrả về thông báo theo định dạng chuẩn:[Priority] Title.channels.go: Tạo hai kiểu kênh thông báo nhúngBaseNotificationvà che khuất phương thứcFormatbằng phần triển khai riêng:EmailNotificationcó trườngRecipient. Phương thứcFormat()sẽ trả vềEmail to [Recipient]: [Priority] TitleSlackNotificationcó trườngChannel. Phương thứcFormat()sẽ gọi phương thứcBaseNotification.Format()được nhúng và thêm#[Channel]:vào đầu kết quả
main.go: Đọc thông tin thông báo từ đầu vào, tạo cả hai kiểu thông báo và minh họa cách che khuất bằng cách in ra:- Kết quả đã định dạng của
EmailNotification(dùng phương thức bị che khuất) - Định dạng gốc của
EmailNotification(gọi rõ ràng phương thức được nhúng) - Kết quả đã định dạng của
SlackNotification(bên trong có dùng phương thức gốc)
- Kết quả đã định dạng của
Đầu vào sẽ có dạng sau:
- Dòng 1: Tiêu đề thông báo
- Dòng 2: Mức độ ưu tiên
- Dòng 3: Người nhận email
- Dòng 4: Tên kênh Slack
Ví dụ, với Server Alert, HIGH, admin@company.com và ops-alerts, đầu ra sẽ là:
Email to admin@company.com: HIGH Server Alert
[HIGH] Server Alert
#ops-alerts: [HIGH] Server AlertLưu ý cách EmailNotification thay thế hoàn toàn định dạng gốc, trong khi SlackNotification bổ sung vào đó bằng cách gọi phương thức ban đầu và thêm thông tin về kênh. Cả hai cách đều cho thấy những phương pháp khác nhau để sử dụng hiệu quả việc che khuất phương thức.
Tự làm thử
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Đọc tiêu đề thông báo
scanner.Scan()
title := scanner.Text()
// Đọc mức độ ưu tiên
scanner.Scan()
priority := scanner.Text()
// Đọc người nhận email
scanner.Scan()
recipient := scanner.Text()
// Đọc tên kênh Slack
scanner.Scan()
channel := scanner.Text()
// TODO: Tạo EmailNotification với thông báo cơ sở, tiêu đề, mức độ ưu tiên và người nhận
// TODO: Tạo SlackNotification với thông báo cơ sở, tiêu đề, mức độ ưu tiên và kênh
// TODO: In ra đầu ra đã định dạng của EmailNotification (dùng phương thức (method) ghi đè của nó)
// TODO: In ra định dạng cơ sở của EmailNotification (gọi trực tiếp phương thức được nhúng)
// TODO: In ra đầu ra đã định dạng của SlackNotification
fmt.Println() // Phần giữ chỗ, xóa khi triển khai
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Nền tảng OOP trong Go
Tệp bên ngoàiWorkspace và module trong GoPackage và importTên được export và không được exportGiới thiệu về OOP trong GoStruct như lớpĐịnh nghĩa phương thức cho structReceiver kiểu con trỏ và kiểu giá trịKhởi tạo structHàm khởi tạoÔn tập: máy tính đơn giản4Interface
Giới thiệu về interfaceTriển khai ngầm địnhInterface như một hợp đồngInterface rỗng (any)Khẳng định kiểuswitch kiểuKết hợp interfaceInterface Stringer và ErrorÔn tập: Máy tính hình học7Tính đóng gói
Trường được xuất và không được xuấtĐóng gói ở cấp packagePhương thức getter và setterẨn thông tin trong GoÔn tập: hồ sơ học sinh10Generics (Go 1.18 trở lên)
Giới thiệu về GenericsTham số kiểuRàng buộc kiểuStruct tổng quátGiải pháp thay thế cho phương thức tổng quátÔn tập: tập hợp tổng quát13Các mẫu thiết kế phần 1
Giới thiệu về mẫu thiết kếMẫu SingletonMẫu FactoryMẫu Abstract FactoryMẫu ObserverMẫu Strategy2Tìm hiểu chuyên sâu về kiểu dữ liệu và struct
Kiểu cơ bản và kiểu tổng hợpĐịnh nghĩa kiểu dữ liệu tùy chỉnhThẻ structStruct ẩn danhStruct lồng nhauGiá trị zero và giá trị mặc địnhÔn tập: sổ liên lạc5Ưu tiên kết hợp hơn kế thừa
Vì sao Go không có kế thừaNhập môn nhúng structTự động nâng phương thứcNhúng nhiều structNhúng và kết tậpChe khuất phương thức được nhúngÔn tập: hệ phân cấp nhân viên8Xử lý lỗi và lập trình hướng đối tượng
Giao diện errorCác kiểu lỗi tùy chỉnhBọc lỗi với fmt.ErrorfLỗi sentinelerrors.Is() và errors.As()Panic, defer và recoverÔn tập: trình phân tích tệp3Con trỏ và bộ nhớ
Cơ bản về con trỏ trong GoCon trỏ đến structTruyền theo giá trị và tham chiếuHàm new()Thu gom rác trong GoÔn tập: xây dựng danh sách liên kết6Đa hình trong Go
Đa hình qua interfaceĐịnh kiểu vịt trong GoQuy tắc thỏa mãn interfaceCác tập hợp đa hìnhTiêm phụ thuộcÔn tập: bộ xử lý thanh toán9Lập trình đồng thời và lập trình hướng đối tượng
Cơ bản về goroutineChannel và giao tiếpChannel có bộ đệm và không có bộ đệmCâu lệnh selectsync.Mutex và sync.RWMutexsync.WaitGroupThiết kế struct an toàn luồngÔn tập: worker poolTự luyện tập: Trình biên dịch Go online