Menu
CoddyTech

Triển khai ngầm định

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 26 trên 107.

Trong nhiều ngôn ngữ hướng đối tượng như Java hoặc C#, bạn phải khai báo rõ ràng rằng một kiểu triển khai một giao diện bằng các từ khóa như implements. Go có cách tiếp cận khác: việc triển khai giao diện là ngầm định.

Một kiểu dữ liệu đáp ứng một interface chỉ cần triển khai tất cả phương thức của interface đó. Bạn không cần khai báo mối quan hệ này ở đâu cả. Trình biên dịch tự động xác định điều đó.

type Writer interface {
    Write(data string) int
}

type FileWriter struct {
    Filename string
}

// FileWriter ngầm triển khai Writer
func (f FileWriter) Write(data string) int {
    fmt.Println("Writing to", f.Filename)
    return len(data)
}

Lưu ý rằng không có khai báo implements Writer trên FileWriter. Vì FileWriter có phương thức Write(data string) int khớp với chữ ký của giao diện, nên nó tự động thỏa mãn giao diện Writer.

Thiết kế này mang lại một hệ quả mạnh mẽ: bạn có thể định nghĩa giao diện sau khi các kiểu đã tồn tại. Nếu một thư viện bên thứ ba có kiểu với các phương thức bạn cần, bạn có thể tạo một giao diện mà kiểu đó đã đáp ứng mà không cần sửa đổi code gốc.

func Save(w Writer, content string) {
    w.Write(content)
}

func main() {
    fw := FileWriter{Filename: "data.txt"}
    Save(fw, "Hello!")  // Chạy được vì FileWriter đáp ứng Writer
}

Việc triển khai ngầm giúp code Go tách rời và linh hoạt. Các kiểu dữ liệu không cần biết trước về các interface, nhờ đó bạn dễ kết hợp những hệ thống từ các thành phần độc lập.

challenge icon

Thử thách

Dễ

Hãy xây dựng một hệ thống gửi tin nhắn để minh họa cách các kiểu dữ liệu ngầm định thỏa mãn giao diện trong Go. Bạn sẽ tạo các kiểu người gửi tin nhắn khác nhau cùng thỏa mãn một giao diện, mà không cần khai báo rõ mối quan hệ đó.

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

  • messengers.go: Định nghĩa giao diện Messenger với một phương thức duy nhất là Send(message string) string. Sau đó, tạo hai cấu trúc sẽ ngầm định thỏa mãn giao diện này:
    • EmailMessenger có trường Address (chuỗi)
    • SMSMessenger có trường PhoneNumber (chuỗi)
    Mỗi cấu trúc cần có phương thức Send trả về một chuỗi mô tả việc gửi tin nhắn. Không cấu trúc nào cần khai báo rõ rằng nó triển khai Messenger: Go sẽ tự động xác định điều đó dựa trên chữ ký phương thức.
  • main.go: Tạo một hàm có tên DeliverMessage nhận bất kỳ Messenger nào và một chuỗi tin nhắn, sau đó trả về kết quả gọi Send. Đọc thông tin liên hệ từ đầu vào, tạo cả hai kiểu người gửi tin nhắn và dùng hàm của bạn để gửi tin nhắn qua từng kiểu.

Chương trình sẽ nhận các đầu vào sau:

  • Dòng 1: Địa chỉ email
  • Dòng 2: Số điện thoại
  • Dòng 3: Tin nhắn cần gửi

Các phương thức Send của bạn cần trả về chuỗi theo những định dạng sau:

  • EmailMessenger: Email to [Address]: [message]
  • SMSMessenger: SMS to [PhoneNumber]: [message]

Ví dụ, với các giá trị alice@example.com, 555-1234 và Hello!, đầu ra cần là:

Email to alice@example.com: Hello!
SMS to 555-1234: Hello!

Điểm mấu chốt là hàm DeliverMessage của bạn nhận bất kỳ Messenger nào, còn cả EmailMessenger và SMSMessenger đều thỏa mãn giao diện đó chỉ bằng cách có một phương thức Send khớp. Không có từ khóa implements nào ở bất cứ đâu: cơ chế triển khai ngầm định của Go xử lý tất cả.

Tự làm thử

package main

import (
	"bufio"
	"fmt"
	"os"
)

// TODO: Tạo hàm (function) DeliverMessage nhận bất kỳ Messenger nào
// và một chuỗi thông điệp, rồi trả về kết quả khi gọi Send

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// Đọc địa chỉ email
	email, _ := reader.ReadString('\n')
	email = email[:len(email)-1]
	
	// Đọc số điện thoại
	phone, _ := reader.ReadString('\n')
	phone = phone[:len(phone)-1]
	
	// Đọc thông điệp
	message, _ := reader.ReadString('\n')
	if len(message) > 0 && message[len(message)-1] == '\n' {
		message = message[:len(message)-1]
	}
	
	// TODO: Tạo EmailMessenger với địa chỉ email
	
	// TODO: Tạo SMSMessenger với số điện thoại
	
	// TODO: Dùng DeliverMessage để gửi thông điệp qua từng messenger
	// và in ra kết quả
}
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