Menu
CoddyTech

Đa hình qua 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 41 trên 107.

Tính đa hình cho phép xử lý các kiểu khác nhau một cách thống nhất thông qua một giao diện chung. Trong Go, điều này hoàn toàn được thực hiện thông qua các giao diện, không cần kế thừa hay hệ thống phân cấp lớp.

Khi một hàm nhận kiểu giao diện làm tham số, bạn có thể truyền vào bất kỳ kiểu cụ thể nào triển khai giao diện đó. Hàm không cần biết kiểu cụ thể là gì. Hàm chỉ quan tâm đến hành vi mà giao diện định nghĩa:

type Speaker interface {
    Speak() string
}

type Dog struct{ Name string }
func (d Dog) Speak() string { return "Woof!" }

type Cat struct{ Name string }
func (c Cat) Speak() string { return "Meow!" }

func MakeSound(s Speaker) {
    fmt.Println(s.Speak())
}

Giờ đây, MakeSound hoạt động với mọi kiểu có phương thức Speak():

func main() {
    dog := Dog{Name: "Rex"}
    cat := Cat{Name: "Whiskers"}
    
    MakeSound(dog)  // Gâu!
    MakeSound(cat)  // Meo!
}

Cùng một lời gọi hàm sẽ tạo ra hành vi khác nhau tùy thuộc vào kiểu thực tế được truyền vào. Đây là tính đa hình đang hoạt động. Hàm MakeSound chỉ cần viết một lần nhưng có thể hoạt động với vô số kiểu, miễn là chúng đáp ứng giao diện Speaker.

Cách tiếp cận này giúp code của bạn linh hoạt và dễ mở rộng. Để thêm một kiểu mới có thể phát ra âm thanh, bạn chỉ cần triển khai giao diện, không cần sửa các hàm hiện có, và mọi thứ sẽ tự động hoạt động.

challenge icon

Thử thách

Dễ

Hãy xây dựng một hệ thống mô tả phương tiện để minh họa cách tính đa hình hoạt động. Bạn sẽ tạo nhiều loại phương tiện có chung một hành vi thông qua một interface, sau đó viết một hàm duy nhất có thể làm việc với mọi phương tiện.

Bạn sẽ sắp xếp code trong hai tệp:

  • vehicles.go: Định nghĩa interface Describer, yêu cầu có phương thức Describe() string. Sau đó tạo ba loại phương tiện, mỗi loại triển khai interface này theo cách riêng:
    • Car có các trường Brand và Model. Phương thức Describe() trả về Car: [Brand] [Model]
    • Motorcycle có các trường Brand và EngineCC (int). Phương thức Describe() trả về Motorcycle: [Brand] [EngineCC]cc
    • Bicycle có trường Type, chẳng hạn như "Mountain" hoặc "Road". Phương thức Describe() trả về Bicycle: [Type]
  • main.go: Tạo một hàm tên là PrintDescription, nhận bất kỳ Describer nào và in ra kết quả khi gọi Describe(). Đọc thông tin phương tiện từ đầu vào, tạo một phương tiện thuộc mỗi loại, rồi truyền từng phương tiện vào PrintDescription để minh họa rằng cùng một hàm có thể làm việc với cả ba loại khác nhau.

Dữ liệu đầu vào sẽ gồm:

  • Dòng 1: Hãng xe
  • Dòng 2: Mẫu xe
  • Dòng 3: Hãng xe máy
  • Dòng 4: Dung tích động cơ xe máy tính bằng CC (số nguyên)
  • Dòng 5: Loại xe đạp

Ví dụ, với các giá trị Toyota, Camry, Honda, 600 và Mountain, đầu ra sẽ là:

Car: Toyota Camry
Motorcycle: Honda 600cc
Bicycle: Mountain

Lưu ý rằng PrintDescription không cần biết mình nhận một Car, Motorcycle hay Bicycle. Hàm chỉ cần gọi Describe(), rồi mỗi loại sẽ trả về đầu ra riêng. Đó là tính đa hình: một hàm, nhiều hành vi.

Tự làm thử

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

// TODO: Tạo hàm (function) PrintDescription nhận bất kỳ Describer nào
// và in ra kết quả khi gọi Describe()

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Đọc thông tin ô tô
	carBrand, _ := reader.ReadString('\n')
	carBrand = strings.TrimSpace(carBrand)
	carModel, _ := reader.ReadString('\n')
	carModel = strings.TrimSpace(carModel)

	// Đọc thông tin xe máy
	motoBrand, _ := reader.ReadString('\n')
	motoBrand = strings.TrimSpace(motoBrand)
	motoEngineStr, _ := reader.ReadString('\n')
	motoEngineStr = strings.TrimSpace(motoEngineStr)
	motoEngine, _ := strconv.Atoi(motoEngineStr)

	// Đọc thông tin xe đạp
	bicycleType, _ := reader.ReadString('\n')
	bicycleType = strings.TrimSpace(bicycleType)

	// TODO: Tạo Car, Motorcycle và Bicycle bằng các giá trị đầu vào

	// TODO: Gọi PrintDescription cho từng phương tiện để minh họa tính đa hình
	fmt.Println("TODO: Print vehicle descriptions")
}
quiz iconTự kiểm tra

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

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