Menu
CoddyTech

方法链式调用

本课属于 Coddy GO 学习旅程的“面向对象编程”单元。第 81 课,共 107 课。

方法链式调用是一种方法返回接收者的技术,可以将多个方法调用链接在同一条语句中。你已经在 Builder 模式中见过这种写法,但它也是一个独立的概念,在许多场景中都很有用。

关键在于从每个方法返回指向该结构体的指针:

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
}

现在你可以流畅地将这些调用串联起来:

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

与分开的语句相比,这几乎就像自然语言一样。方法链在操作用于配置或修改对象,而不需要返回其他值时效果最好。它常用于查询构建器、配置 API 和测试断言库。

请记住,每个方法都必须使用指针接收者,并返回 *StructType,这样链式调用才能正常工作。

challenge icon

挑战

简单

让我们来构建一个流畅的 HTML 元素构建器,通过方法链式调用来创建带有属性和内容的 HTML 标签!这是方法链式调用的一种实际应用:你将创建一个 API,在构建 HTML 元素时,代码读起来几乎就像自然语言一样。

你将把代码分成两个文件:

  • element.go:定义带有可链式调用方法的 HTML 元素构建器。

    创建一个 Element 结构体,用于存储标签名、属性映射(字符串到字符串)和内部内容。编写以下可链式调用的方法,每个方法都返回 *Element:

    • NewElement(tag string):构造函数,根据给定的标签名创建一个新的 Element,并初始化一个空的属性映射
    • Attr(key, value string):为元素添加一个属性
    • ID(id string):便捷方法,用于设置 "id" 属性
    • Class(class string):便捷方法,用于设置 "class" 属性
    • Content(text string):设置元素的内部内容

    添加一个 Render() 方法,用于返回 HTML 字符串。属性应按键名的字母顺序排列。格式如下:

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

    如果没有属性,只需渲染 <tag>content</tag>。如果没有内容,则渲染 <tag attr="value"></tag>。

  • main.go:使用方法链式调用构建 HTML 元素。

    读取标签名,然后读取要执行的操作数量。每个操作属于以下类型之一:

    • id,后面跟着 ID 值
    • class,后面跟着类名
    • attr,后面跟着属性名和值(两行)
    • content,后面跟着内容文本

    将所有操作流畅地串联起来,然后输出渲染后的 HTML。

输入内容如下:

  • 第 1 行:标签名
  • 第 2 行:操作数量
  • 后续各行:操作类型和值

例如,给定以下输入:

div
3
id
container
class
main-content
content
Hello World

输出应为:

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

给定以下输入:

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

输出应为:

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

给定以下输入:

input
2
attr
type
text
attr
placeholder
Enter name

输出应为:

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

给定以下输入:

p
1
content
Simple paragraph

输出应为:

<p>Simple paragraph</p>

注意,方法链式调用能让你以清晰易读的方式逐步构建元素。每个方法都会修改元素并返回它,因此可以直接链式调用下一个方法。

亲手试试

package main

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

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

	// 读取标签名
	scanner.Scan()
	tagName := scanner.Text()

	// 读取操作次数
	scanner.Scan()
	numOps, _ := strconv.Atoi(scanner.Text())

	// 创建元素
	element := NewElement(tagName)

	// 处理每个操作
	for i := 0; i < numOps; i++ {
		scanner.Scan()
		operation := scanner.Text()

		// TODO: 根据操作类型,链式调用对应的方法
		// 操作可以是:"id"、"class"、"attr" 或 "content"
		// 对于 "id":读取下一行作为 ID 值
		// 对于 "class":读取下一行作为类的值
		// 对于 "attr":读取接下来的 TWO 行(先读取属性名,再读取值)
		// 对于 "content":读取下一行作为内容文本
		// 使用方法链式调用来构建元素

		_ = operation // 实现时删除这一行
	}

	// 输出渲染后的 HTML
	fmt.Println(element.Render())
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

面向对象编程中的所有课

自己练习: Go 在线编译器