Tìm kiếm với interface
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 101 trên 107.
Thử thách
DễHãy thêm khả năng tìm kiếm linh hoạt vào hệ thống quản lý thư viện! Bạn sẽ triển khai mẫu Strategy bằng các interface, cho phép người dùng tìm sách theo nhiều tiêu chí khác nhau mà không cần sửa mã cốt lõi của thư viện.
Bạn sẽ sắp xếp code trong năm tệp:
book.go: StructBooktừ các bài học trước, gồm các trườngID,Title,Author,ISBNvàAvailable. Thêm hàm khởi tạoNewBook.user.go: StructUser, gồm các trườngID,Name,EmailvàBorrowedBooks. Thêm hàm khởi tạoNewUser.search.go: Định nghĩa interface chiến lược tìm kiếm và các phần triển khai.Tạo interface
SearchStrategyvới một phương thức duy nhất:Match(book *Book, query string) boolTriển khai ba chiến lược tìm kiếm:
TitleSearch: khớp nếu tiêu đề sách có chứa truy vấn, không phân biệt chữ hoa chữ thườngAuthorSearch: khớp nếu tên tác giả của sách có chứa truy vấn, không phân biệt chữ hoa chữ thườngISBNSearch: khớp nếu ISBN của sách chính xác bằng truy vấn
library.go: StructLibraryvới các map lưu sách và người dùng. Thêm phương thứcSearchnhận bất kỳSearchStrategynào cùng một chuỗi truy vấn, rồi trả về một slice chứa các con trỏ đến sách khớp. Phương thức này cần duyệt qua tất cả sách và dùng phương thứcMatchcủa chiến lược để tìm kết quả.main.go: Tạo thư viện có sách, rồi tìm kiếm bằng các chiến lược khác nhau.Đọc số lượng sách, rồi đọc ID, tiêu đề, tác giả và ISBN của từng sách. Tiếp theo, đọc số lượng thao tác tìm kiếm. Mỗi thao tác gồm loại tìm kiếm (
title,authorhoặcisbn), sau đó là chuỗi truy vấn.Với mỗi lần tìm kiếm, in
Search by [type]: [query], rồi in ID của các sách khớp trên dòng tiếp theo, sắp xếp theo thứ tự tăng dần và phân tách bằng dấu phẩy. Vì map duyệt theo thứ tự ngẫu nhiên, hãy sắp xếp ID trước khi in. Nếu không có sách nào khớp, hãy inNo results.
Đầu vào sẽ có dạng sau:
- Số lượng sách, sau đó là ID, tiêu đề, tác giả và ISBN của từng sách, mỗi giá trị trên một dòng riêng
- Số lượng lần tìm kiếm, sau đó là loại tìm kiếm và truy vấn của từng lần, mỗi giá trị trên một dòng riêng
Ví dụ, với đầu vào:
3
B001
The Go Programming Language
Alan Donovan
978-0134190440
B002
Go in Action
William Kennedy
978-1617291784
B003
Learning Python
Mark Lutz
978-1449355739
3
title
go
author
kennedy
isbn
978-1449355739Đầu ra sẽ là:
Search by title: go
B001,B002
Search by author: kennedy
B002
Search by isbn: 978-1449355739
B003Với đầu vào:
2
B001
Clean Code
Robert Martin
978-0132350884
B002
The Pragmatic Programmer
David Thomas
978-0135957059
2
title
java
author
martinĐầu ra sẽ là:
Search by title: java
No results
Search by author: martin
B001Với đầu vào:
2
B001
Design Patterns
Gang of Four
978-0201633610
B002
Head First Design Patterns
Eric Freeman
978-0596007126
1
title
designĐầu ra sẽ là:
Search by title: design
B001,B002Hãy lưu ý rằng phương thức Search hoạt động với mọi chiến lược. Nó không biết mình đang tìm theo tiêu đề, tác giả hay ISBN. Cách tiếp cận đa hình này cho phép bạn thêm loại tìm kiếm mới, chẳng hạn tìm theo tình trạng sẵn có hoặc năm xuất bản, chỉ bằng cách tạo struct mới triển khai SearchStrategy!
Tự làm thử
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Đọc số lượng sách
numBooksStr, _ := reader.ReadString('\n')
numBooks, _ := strconv.Atoi(strings.TrimSpace(numBooksStr))
// Tạo thư viện
library := NewLibrary()
// Đọc thông tin chi tiết của từng cuốn sách
for i := 0; i < numBooks; i++ {
id, _ := reader.ReadString('\n')
id = strings.TrimSpace(id)
title, _ := reader.ReadString('\n')
title = strings.TrimSpace(title)
author, _ := reader.ReadString('\n')
author = strings.TrimSpace(author)
isbn, _ := reader.ReadString('\n')
isbn = strings.TrimSpace(isbn)
book := NewBook(id, title, author, isbn)
library.AddBook(book)
}
// Đọc số lượt tìm kiếm
numSearchesStr, _ := reader.ReadString('\n')
numSearches, _ := strconv.Atoi(strings.TrimSpace(numSearchesStr))
// Xử lý từng lượt tìm kiếm
for i := 0; i < numSearches; i++ {
searchType, _ := reader.ReadString('\n')
searchType = strings.TrimSpace(searchType)
query, _ := reader.ReadString('\n')
query = strings.TrimSpace(query)
// TODO: Chọn chiến lược tìm kiếm phù hợp dựa trên searchType
// searchType có thể là "title", "author" hoặc "isbn"
var strategy SearchStrategy
// TODO: Viết logic chọn chiến lược tại đây
// TODO: Tìm kiếm bằng library.Search(strategy, query)
// TODO: In kết quả theo định dạng yêu cầu
// Định dạng: "Search by [type]: [query]", tiếp theo là các ID khớp hoặc "No results"
fmt.Printf("Search by %s: %s\n", searchType, query)
// TODO: In các ID sách khớp, phân tách bằng dấu phẩy, hoặc "No results"
_ = strategy // Xóa dòng này khi bạn triển khai lời giải
}
}
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 pool12Các khái niệm OOP nâng cao
Mẫu tùy chọn hàmMẫu Builder trong GoChuỗi phương thứcBí danh kiểu dữ liệu và định nghĩa kiểuCơ bản về reflectionTổng quan về tạo code15Dự án: Quản lý thư viện
Tổng quan và cấu trúc dự ánStruct về sách và người dùngTự luyện tập: Trình biên dịch Go online