Go 中的构建器模式
本课属于 Coddy GO 学习旅程的“面向对象编程”单元。第 80 课,共 107 课。
构建器模式将复杂对象的构建过程与其表示分离。与函数式选项模式不同,构建器模式使用专用的构建器结构体,通过方法逐步配置对象。
下面介绍如何为 HTTP 请求实现一个构建器:
type Request struct {
method string
url string
headers map[string]string
body string
}
type RequestBuilder struct {
request *Request
}
func NewRequestBuilder() *RequestBuilder {
return &RequestBuilder{
request: &Request{
headers: make(map[string]string),
},
}
}
func (b *RequestBuilder) Method(m string) *RequestBuilder {
b.request.method = m
return b
}
func (b *RequestBuilder) URL(u string) *RequestBuilder {
b.request.url = u
return b
}
func (b *RequestBuilder) Header(key, value string) *RequestBuilder {
b.request.headers[key] = value
return b
}
func (b *RequestBuilder) Build() *Request {
return b.request
}每个方法都会返回构建器指针,从而支持方法链式调用。Build() 方法会完成构建并返回构造好的对象:
req := NewRequestBuilder().
Method("POST").
URL("https://api.example.com").
Header("Content-Type", "application/json").
Build()当对象需要经过多个配置步骤,或者你想强制采用特定的构造顺序时,建造者模式特别适用。它尤其适合创建不可变对象,或需要验证构造过程本身的情况。
挑战
简单让我们使用建造者模式来构建电子邮件!电子邮件有许多可选部分,例如收件人、抄送、主题、正文和附件,因此非常适合通过方法链逐步构建。
你会将代码分成两个文件:
email.go:定义电子邮件结构体及其建造者。创建一个
Email结构体,包含以下字段:from(字符串)to(字符串切片)cc(字符串切片)subject(字符串)body(字符串)
创建一个
EmailBuilder结构体,用于保存正在构建的 Email 的指针。实现NewEmailBuilder(),返回一个新的建造者,其中包含已初始化的 Email(to和cc为空切片)。添加以下建造者方法,每个方法都返回
*EmailBuilder,以便进行链式调用:From(address string):设置发件人To(address string):将收件人添加到to切片CC(address string):将收件人添加到cc切片Subject(subject string):设置主题Body(body string):设置电子邮件正文
添加一个
Build()方法,返回构建好的*Email。最后,在 Email 上添加一个
Summary()方法,以以下格式返回字符串:From: [from] To: [comma-separated to addresses] CC: [comma-separated cc addresses or "none"] Subject: [subject] Body: [body]main.go:使用建造者构建并显示电子邮件。读取发件人地址,然后读取收件人数量和每个收件人的地址。接着读取抄送收件人数量和每个抄送收件人的地址。最后读取主题和正文。
使用 EmailBuilder 进行方法链式调用来构建电子邮件,然后打印其摘要。
输入内容如下:
- 第 1 行:发件人地址
- 第 2 行:收件人数量
- 后续各行:每个收件人的地址
- 下一行:抄送收件人数量
- 后续各行:每个抄送收件人的地址
- 下一行:主题
- 最后一行:正文
例如,给定:
alice@company.com
2
bob@company.com
carol@company.com
1
manager@company.com
Weekly Report
Please find the weekly report attached.输出应为:
From: alice@company.com
To: bob@company.com, carol@company.com
CC: manager@company.com
Subject: Weekly Report
Body: Please find the weekly report attached.再例如,给定:
support@service.com
1
customer@email.com
0
Your Ticket Update
Your support ticket has been resolved.输出应为:
From: support@service.com
To: customer@email.com
CC: none
Subject: Your Ticket Update
Body: Your support ticket has been resolved.注意,建造者允许你通过重复调用 To() 一次添加一个收件人;即使有许多可选字段,这种模式也能让构建过程保持清晰易读。
亲手试试
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// 读取发件人地址
from, _ := reader.ReadString('\n')
from = strings.TrimSpace(from)
// 读取收件人数量
toCountStr, _ := reader.ReadString('\n')
toCount, _ := strconv.Atoi(strings.TrimSpace(toCountStr))
// 读取每个收件人的地址
toAddresses := make([]string, toCount)
for i := 0; i < toCount; i++ {
addr, _ := reader.ReadString('\n')
toAddresses[i] = strings.TrimSpace(addr)
}
// 读取抄送收件人数量
ccCountStr, _ := reader.ReadString('\n')
ccCount, _ := strconv.Atoi(strings.TrimSpace(ccCountStr))
// 读取每个抄送收件人的地址
ccAddresses := make([]string, ccCount)
for i := 0; i < ccCount; i++ {
addr, _ := reader.ReadString('\n')
ccAddresses[i] = strings.TrimSpace(addr)
}
// 读取主题
subject, _ := reader.ReadString('\n')
subject = strings.TrimSpace(subject)
// 读取正文
body, _ := reader.ReadString('\n')
body = strings.TrimSpace(body)
// TODO: 使用 EmailBuilder,通过链式调用方法构建邮件
// 先调用 NewEmailBuilder(),再依次链式调用 From()、To()、CC()、Subject()、Body()
// 最后调用 Build() 获取 Email
// TODO: 使用 Summary() 方法打印邮件摘要
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Go 在线编译器