Menu
CoddyTech

Chuỗi phương thức

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

Nối chuỗi phương thức (method chaining) là kỹ thuật trong đó các phương thức trả về đối tượng nhận, cho phép liên kết nhiều lời gọi phương thức trong cùng một câu lệnh. Bạn đã thấy kỹ thuật này trong mẫu Builder, nhưng đây cũng là một khái niệm độc lập, hữu ích trong nhiều tình huống.

Điều cốt yếu là trả về con trỏ đến struct từ mỗi phương thức:

type Query struct {
    table   string
    columns []string
    where   string
    limit   int
}

func (q *Query) Select(cols ...string) *Query {
    q.columns = cols
    return q
}

func (q *Query) From(table string) *Query {
    q.table = table
    return q
}

func (q *Query) Where(condition string) *Query {
    q.where = condition
    return q
}

func (q *Query) Limit(n int) *Query {
    q.limit = n
    return q
}

Giờ bạn có thể nối các lời gọi này một cách trôi chảy:

query := &Query{}
query.Select("name", "email").From("users").Where("active = true").Limit(10)

Cách viết này gần giống ngôn ngữ tự nhiên hơn so với các câu lệnh riêng lẻ. Việc nối chuỗi phương thức phù hợp nhất khi các thao tác cấu hình hoặc chỉnh sửa một đối tượng mà không cần trả về giá trị nào khác. Cách này thường được dùng trong các trình tạo truy vấn, API cấu hình và thư viện kiểm thử điều kiện.

Hãy nhớ rằng mỗi phương thức phải dùng receiver là con trỏ và trả về *StructType để chuỗi gọi hoạt động đúng cách.

challenge icon

Thử thách

Dễ

Hãy xây dựng một trình tạo phần tử HTML linh hoạt, sử dụng cách gọi chuỗi phương thức (method chaining) để tạo thẻ HTML cùng thuộc tính và nội dung! Đây là một ứng dụng thực tế của cách gọi chuỗi phương thức: bạn sẽ tạo một API có cách viết gần giống ngôn ngữ tự nhiên khi xây dựng các phần tử HTML.

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

  • element.go: Định nghĩa trình tạo phần tử HTML với các phương thức có thể gọi nối tiếp.

    Tạo một cấu trúc Element lưu tên thẻ, một ánh xạ thuộc tính (từ chuỗi sang chuỗi) và nội dung bên trong. Xây dựng các phương thức có thể gọi nối tiếp sau đây, mỗi phương thức trả về *Element:

    • NewElement(tag string) - hàm khởi tạo tạo một Element mới với tên thẻ được cung cấp và một ánh xạ thuộc tính đã khởi tạo (để trống)
    • Attr(key, value string) - thêm một thuộc tính vào phần tử
    • ID(id string) - phương thức tiện ích đặt thuộc tính "id"
    • Class(class string) - phương thức tiện ích đặt thuộc tính "class"
    • Content(text string) - đặt nội dung bên trong của phần tử

    Thêm phương thức Render() để trả về chuỗi HTML. Các thuộc tính cần xuất hiện theo thứ tự bảng chữ cái của khóa. Định dạng như sau:

    <tag attr1="value1" attr2="value2">content</tag>

    Nếu không có thuộc tính nào, chỉ cần tạo <tag>content</tag>. Nếu không có nội dung, hãy tạo <tag attr="value"></tag>.

  • main.go: Tạo các phần tử HTML bằng cách gọi chuỗi phương thức.

    Đọc tên thẻ, sau đó đọc số lượng thao tác cần thực hiện. Mỗi thao tác thuộc một trong các loại sau:

    • id theo sau là giá trị ID
    • class theo sau là giá trị class
    • attr theo sau là tên và giá trị thuộc tính (hai dòng)
    • content theo sau là nội dung

    Gọi nối tiếp tất cả thao tác một cách liền mạch, sau đó in HTML đã tạo.

Đầu vào sẽ có dạng như sau:

  • Dòng 1: Tên thẻ
  • Dòng 2: Số lượng thao tác
  • Các dòng tiếp theo: Loại thao tác và (các) giá trị

Ví dụ, với đầu vào:

div
3
id
container
class
main-content
content
Hello World

Đầu ra sẽ là:

<div class="main-content" id="container">Hello World</div>

Với đầu vào:

a
3
attr
href
https://example.com
attr
target
_blank
content
Click here

Đầu ra sẽ là:

<a href="https://example.com" target="_blank">Click here</a>

Với đầu vào:

input
2
attr
type
text
attr
placeholder
Enter name

Đầu ra sẽ là:

<input placeholder="Enter name" type="text"></input>

Với đầu vào:

p
1
content
Simple paragraph

Đầu ra sẽ là:

<p>Simple paragraph</p>

Hãy chú ý cách gọi chuỗi phương thức giúp bạn xây dựng phần tử từng bước một cách dễ đọc. Mỗi phương thức thay đổi phần tử rồi trả về phần tử đó, nhờ vậy bạn có thể gọi nối tiếp phương thức tiếp theo.

Tự làm thử

package main

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

func main() {
	scanner := bufio.NewScanner(os.Stdin)

	// Đọc tên thẻ
	scanner.Scan()
	tagName := scanner.Text()

	// Đọc số lượng thao tác
	scanner.Scan()
	numOps, _ := strconv.Atoi(scanner.Text())

	// Tạo phần tử
	element := NewElement(tagName)

	// Xử lý từng thao tác
	for i := 0; i < numOps; i++ {
		scanner.Scan()
		operation := scanner.Text()

		// TODO: Dựa vào loại thao tác, nối chuỗi phương thức phù hợp
		// Các thao tác có thể là: "id", "class", "attr" hoặc "content"
		// - Với "id": đọc dòng tiếp theo làm giá trị ID
		// - Với "class": đọc dòng tiếp theo làm giá trị lớp
		// - Với "attr": đọc HAI dòng tiếp theo (tên thuộc tính, rồi đến giá trị)
		// - Với "content": đọc dòng tiếp theo làm văn bản nội dung
		// Dùng cách nối chuỗi phương thức để tạo phần tử một cách liền mạch

		_ = operation // Xóa dòng này khi triển khai
	}

	// In ra HTML sau khi kết xuất
	fmt.Println(element.Render())
}
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