Menu
CoddyTech

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.

challenge icon

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: Struct Book từ các bài học trước, gồm các trường ID, Title, Author, ISBN và Available. Thêm hàm khởi tạo NewBook.
  • user.go: Struct User, gồm các trường ID, Name, Email và BorrowedBooks. Thêm hàm khởi tạo NewUser.
  • 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 SearchStrategy với một phương thức duy nhất:

    Match(book *Book, query string) bool

    Triể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ường
    • AuthorSearch: 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ường
    • ISBNSearch: khớp nếu ISBN của sách chính xác bằng truy vấn
  • library.go: Struct Library với các map lưu sách và người dùng. Thêm phương thức Search nhận bất kỳ SearchStrategy nà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ức Match củ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, author hoặc isbn), 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 in No 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
B003

Vớ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
B001

Vớ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,B002

Hã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

Tự luyện tập: Trình biên dịch Go online