sort.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 74 trên 107.
Gói sort là một ví dụ tuyệt vời khác về thiết kế dựa trên giao diện. Để sắp xếp một tập hợp tùy chỉnh, kiểu của bạn phải triển khai sort.Interface:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}Ba phương thức này cung cấp cho thuật toán sắp xếp mọi thứ cần thiết: độ dài của tập hợp, cách so sánh các phần tử và cách hoán đổi chúng. Sau đây là cách để sắp xếp một lát cắt gồm các struct tùy chỉnh:
type Person struct {
Name string
Age int
}
type ByAge []Person
func (a ByAge) Len() int { return len(a) }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func main() {
people := []Person{
{"Alice", 30},
{"Bob", 25},
{"Carol", 35},
}
sort.Sort(ByAge(people))
fmt.Println(people)
// [{Bob 25} {Alice 30} {Carol 35}]
}Điểm mấu chốt là tạo một kiểu có tên (ByAge) dựa trên lát cắt của bạn. Nhờ đó, bạn có thể xác định các cách sắp xếp khác nhau cho cùng một dữ liệu. Bạn có thể tạo ByName với cách triển khai Less khác để sắp xếp theo thứ tự bảng chữ cái.
Khi kiểu dữ liệu của bạn đáp ứng sort.Interface, nó tự động hoạt động với sort.Sort(), sort.Reverse() và sort.IsSorted().
Thử thách
DễHãy xây dựng một hệ thống kiểm kê sản phẩm có thể sắp xếp để minh họa sức mạnh của sort.Interface trong Go! Bạn sẽ tạo kiểu Product và triển khai nhiều cách sắp xếp, giúp sắp xếp cùng một tập hợp theo nhiều cách khác nhau.
Bạn sẽ tổ chức code trong hai tệp:
product.go: Định nghĩa kiểu sản phẩm và các cách triển khai sắp xếp.Tạo một struct
Productcó ba trường:Name(chuỗi),Price(float64) vàQuantity(int).Tạo hai kiểu có tên dựa trên
[]Product:ByPrice- dùng để sắp xếp sản phẩm theo giá tăng dầnByQuantity- dùng để sắp xếp sản phẩm theo số lượng giảm dần (số lượng cao nhất trước)
Mỗi kiểu cần triển khai ba phương thức mà
sort.Interfaceyêu cầu:Len(),Less(i, j int)vàSwap(i, j int). Phương thứcLessxác định thứ tự sắp xếp của từng kiểu.main.go: Tạo và sắp xếp danh mục sản phẩm.Đọc chế độ sắp xếp (
pricehoặcquantity), sau đó đọc số lượng sản phẩm rồi đến thông tin chi tiết của từng sản phẩm. Mỗi sản phẩm được cung cấp trên ba dòng: tên, giá và số lượng.Tạo một slice sản phẩm, sắp xếp chúng bằng kiểu sắp xếp phù hợp dựa trên chế độ, sau đó in từng sản phẩm theo định dạng này:
[Name]: $[Price] (x[Quantity])Hiển thị giá với hai chữ số thập phân.
Đầu vào sẽ có dạng như sau:
- Dòng 1: Chế độ sắp xếp (
pricehoặcquantity) - Dòng 2: Số lượng sản phẩm
- Các dòng tiếp theo: Thông tin chi tiết sản phẩm (tên, giá, số lượng, ba dòng cho mỗi sản phẩm)
Ví dụ, với đầu vào:
price
3
Laptop
999.99
5
Mouse
29.99
50
Keyboard
79.99
25Đầu ra của bạn sẽ là:
Mouse: $29.99 (x50)
Keyboard: $79.99 (x25)
Laptop: $999.99 (x5)Và với đầu vào:
quantity
3
Laptop
999.99
5
Mouse
29.99
50
Keyboard
79.99
25Đầu ra của bạn sẽ là:
Mouse: $29.99 (x50)
Keyboard: $79.99 (x25)
Laptop: $999.99 (x5)Lưu ý rằng cùng một dữ liệu sản phẩm có thể được sắp xếp theo nhiều cách khác nhau chỉ bằng cách dùng một kiểu có tên khác. Khi các kiểu của bạn đáp ứng sort.Interface, chúng sẽ hoạt động liền mạch với sort.Sort() trong thư viện chuẩn.
Tự làm thử
package main
import (
"bufio"
"fmt"
"os"
"sort"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Đọc chế độ sắp xếp
var mode string
fmt.Fscanln(reader, &mode)
// Đọc số lượng sản phẩm
var count int
fmt.Fscanln(reader, &count)
// Đọc các sản phẩm
products := make([]Product, count)
for i := 0; i < count; i++ {
name, _ := reader.ReadString('\n')
name = strings.TrimSpace(name)
priceStr, _ := reader.ReadString('\n')
priceStr = strings.TrimSpace(priceStr)
price, _ := strconv.ParseFloat(priceStr, 64)
qtyStr, _ := reader.ReadString('\n')
qtyStr = strings.TrimSpace(qtyStr)
quantity, _ := strconv.Atoi(qtyStr)
products[i] = Product{Name: name, Price: price, Quantity: quantity}
}
// TODO: Sắp xếp sản phẩm theo mode
// Nếu mode là "price", dùng kiểu ByPrice
// Nếu mode là "quantity", dùng kiểu ByQuantity
// Dùng sort.Sort() với kiểu phù hợp
// TODO: In từng sản phẩm theo định dạng:
// [Name]: $[Price] (x[Quantity])
// Dùng fmt.Printf với %.2f để định dạng giá
}
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ệp11Thư viện chuẩn và OOP
io.Reader và io.Writersort.Interfaceinterface fmt.Stringerencoding/json với structinterface http.HandlerÔn tập: mô hình REST API3Con 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