什么是建造者模式?
建造者模式是一种创建型设计模式,通过分步构建复杂对象,允许灵活配置属性。它通过链式调用或分步设置提供流畅的接口,特别适合需要多个可选参数的场景。
核心要素:
- 产品:最终构建的对象。
- 建造者:定义构建步骤。
- 具体建造者:实现构建逻辑。
- 导演(可选):控制构建顺序。
- 客户端:使用建造者创建对象。
适用场景:
- 复杂对象配置(如 HTTP 请求)。
- 多种构造方式(如报表生成)。
- 提高代码可读性。
为什么在 Go 中使用建造者模式?
Go 语言通过结构体和方法链实现建造者模式,适用于:
- API 请求:动态配置 URL、头信息、参数等。
- 配置文件:构建复杂配置对象。
- 消息构造:构造消息队列的消息体。
本文通过一个 API 请求构建器 的例子,展示建造者模式在 Go 中的实现。
简单建造者实现
简单建造者通过结构体和方法链构建请求,适合大多数场景。
代码实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
package apirequest
import (
"errors"
"net/http"
"net/url"
"strings"
"time"
)
// APIRequest 产品,表示最终的 HTTP 请求
type APIRequest struct {
URL string
Method string
Headers map[string]string
QueryParams url.Values
Body string
Timeout time.Duration
}
// APIRequestBuilder 建造者,负责构建 APIRequest
type APIRequestBuilder struct {
request APIRequest
}
// NewAPIRequestBuilder 创建建造者实例
func NewAPIRequestBuilder() *APIRequestBuilder {
return &APIRequestBuilder{
request: APIRequest{
Method: "GET",
Headers: make(map[string]string),
QueryParams: make(url.Values),
Timeout: 10 * time.Second,
},
}
}
// SetURL 设置请求 URL
func (b *APIRequestBuilder) SetURL(url string) *APIRequestBuilder {
b.request.URL = url
return b
}
// SetMethod 设置请求方法
func (b *APIRequestBuilder) SetMethod(method string) *APIRequestBuilder {
b.request.Method = strings.ToUpper(method)
return b
}
// AddHeader 添加请求头
func (b *APIRequestBuilder) AddHeader(key, value string) *APIRequestBuilder {
b.request.Headers[key] = value
return b
}
// AddQueryParam 添加查询参数
func (b *APIRequestBuilder) AddQueryParam(key, value string) *APIRequestBuilder {
b.request.QueryParams.Add(key, value)
return b
}
// SetBody 设置请求体
func (b *APIRequestBuilder) SetBody(body string) *APIRequestBuilder {
b.request.Body = body
return b
}
// SetTimeout 设置超时时间
func (b *APIRequestBuilder) SetTimeout(timeout time.Duration) *APIRequestBuilder {
b.request.Timeout = timeout
return b
}
// Build 构建最终的 APIRequest
func (b *APIRequestBuilder) Build() (*APIRequest, error) {
if b.request.URL == "" {
return nil, errors.New("URL is required")
}
if b.request.Method != "GET" && b.request.Method != "POST" {
return nil, errors.New("unsupported HTTP method")
}
return &b.request, nil
}
// Execute 执行 HTTP 请求(模拟)
func (r *APIRequest) Execute() (*http.Response, error) {
client := &http.Client{Timeout: r.Timeout}
req, err := http.NewRequest(r.Method, r.URL, strings.NewReader(r.Body))
if err != nil {
return nil, err
}
for k, v := range r.Headers {
req.Header.Set(k, v)
}
if len(r.QueryParams) > 0 {
req.URL.RawQuery = r.QueryParams.Encode()
}
return client.Do(req)
}
|
使用示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
package main
import (
"fmt"
"yourmodule/apirequest"
)
func main() {
builder := apirequest.NewAPIRequestBuilder()
request, err := builder.
SetURL("https://api.example.com/users").
SetMethod("POST").
AddHeader("Authorization", "Bearer token123").
AddHeader("Content-Type", "application/json").
AddQueryParam("user_id", "123").
SetBody(`{"name": "Alice"}`).
SetTimeout(5 * time.Second).
Build()
if err != nil {
fmt.Println("Build failed:", err)
return
}
resp, err := request.Execute()
if err != nil {
fmt.Println("Request failed:", err)
return
}
fmt.Println("Response Status:", resp.Status)
}
|
讲解
- 链式调用清晰,参数配置灵活。
Build
验证必填字段,确保对象有效。
- 局限性:未考虑并发场景。
带导演的建造者实现
导演控制构建流程,提供预定义模板。
代码实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
package apirequest
import "time"
// Director 导演,控制构建流程
type Director struct {
builder *APIRequestBuilder
}
// NewDirector 创建导演实例
func NewDirector(builder *APIRequestBuilder) *Director {
return &Director{builder: builder}
}
// ConstructDefaultGET 构建默认 GET 请求
func (d *Director) ConstructDefaultGET(url string, token string) (*APIRequest, error) {
return d.builder.
SetURL(url).
SetMethod("GET").
AddHeader("Authorization", "Bearer "+token).
AddQueryParam("format", "json").
SetTimeout(3 * time.Second).
Build()
}
// ConstructDefaultPOST 构建默认 POST 请求
func (d *Director) ConstructDefaultPOST(url, token, body string) (*APIRequest, error) {
return d.builder.
SetURL(url).
SetMethod("POST").
AddHeader("Authorization", "Bearer "+token).
AddHeader("Content-Type", "application/json").
SetBody(body).
SetTimeout(5 * time.Second).
Build()
}
|
使用示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
package main
import (
"fmt"
"yourmodule/apirequest"
)
func main() {
builder := apirequest.NewAPIRequestBuilder()
director := apirequest.NewDirector(builder)
getRequest, err := director.ConstructDefaultGET("https://api.example.com/users", "token123")
if err != nil {
fmt.Println("Build GET failed:", err)
return
}
resp, err := getRequest.Execute()
if err != nil {
fmt.Println("GET failed:", err)
return
}
fmt.Println("GET Response Status:", resp.Status)
postRequest, err := director.ConstructDefaultPOST(
"https://api.example.com/users",
"token123",
`{"name": "Bob"}`,
)
if err ≠ nil {
fmt.Println("Build POST failed:", err)
return
}
resp, err = postRequest.Execute()
if err ≠ nil {
fmt.Println("POST failed:", err)
return
}
fmt.Println("POST Response Status:", resp.Status)
}
|
讲解
- 导演封装标准流程,简化客户端代码。
- 优势:适合标准化构建场景。
并发安全的建造者实现
并发场景下需确保建造过程线程安全。
代码实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
package apirequest
import (
"errors"
"net/http"
"net/url"
"strings"
"sync"
"time"
)
// APIRequest 产品
type APIRequest struct {
URL string
Method string
Headers map[string]string
QueryParams url.Values
Body string
Timeout time.Duration
}
// APIRequestBuilder 并发安全的建造者
type APIRequestBuilder struct {
request APIRequest
mu sync.Mutex
once sync.Once
}
// NewAPIRequestBuilder 创建并发安全的建造者
func NewAPIRequestBuilder() *APIRequestBuilder {
builder := &APIRequestBuilder{}
builder.once.Do(func() {
builder.request = APIRequest{
Method: "GET",
Headers: make(map[string]string),
QueryParams: make(url.Values),
Timeout: 10 * time.Second,
}
})
return builder
}
// SetURL 设置 URL
func (b *APIRequestBuilder) SetURL(url string) *APIRequestBuilder {
b.mu.Lock()
defer b.mu.Unlock()
b.request.URL = url
return b
}
// SetMethod 设置方法
func (b *APIRequestBuilder) SetMethod(method string) *APIRequestBuilder {
b.mu.Lock()
defer b.mu.Unlock()
b.request.Method = strings.ToUpper(method)
return b
}
// AddHeader 添加请求头
func (b *APIRequestBuilder) AddHeader(key, value string) *APIRequestBuilder {
b.mu.Lock()
defer b.mu.Unlock()
b.request.Headers[key] = value
return b
}
// AddQueryParam 添加查询参数
func (b *APIRequestBuilder) AddQueryParam(key, value string) *APIRequestBuilder {
b.mu.Lock()
defer b.mu.Unlock()
b.request.QueryParams.Add(key, value)
return b
}
// SetBody 设置请求体
func (b *APIRequestBuilder) SetBody(body string) *APIRequestBuilder {
b.mu.Lock()
defer b.mu.Unlock()
b.request.Body = body
return b
}
// SetTimeout 设置超时
func (b *APIRequestBuilder) SetTimeout(timeout time.Duration) *APIRequestBuilder {
b.mu.Lock()
defer b.mu.Unlock()
b.request.Timeout = timeout
return b
}
// Build 构建 APIRequest
func (b *APIRequestBuilder) Build() (*APIRequest, error) {
b.mu.Lock()
defer b.mu.Unlock()
if b.request.URL == "" {
return nil, errors.New("URL is required")
}
if b.request.Method != "GET" && b.request.Method != "POST" {
return nil, errors.New("unsupported HTTP method")
}
requestCopy := b.request
return &requestCopy, nil
}
// Execute 执行请求
func (r *APIRequest) Execute() (*http.Response, error) {
client := &http.Client{Timeout: r.Timeout}
req, err := http.NewRequest(r.Method, r.URL, strings.NewReader(r.Body))
if err != nil {
return nil, err
}
for k, v := range r.Headers {
req.Header.Set(k, v)
}
if len(r.QueryParams) > 0 {
req.URL.RawQuery = r.QueryParams.Encode()
}
return client.Do(req)
}
|
使用示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
package main
import (
"fmt"
"sync"
"yourmodule/apirequest"
)
func main() {
var wg sync.WaitGroup
builder := apirequest.NewAPIRequestBuilder()
for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
request, err := builder.
SetURL(fmt.Sprintf("https://api.example.com/users/%d", id)).
SetMethod("GET").
AddHeader("Authorization", fmt.Sprintf("Bearer token%d", id)).
AddQueryParam("user_id", fmt.Sprintf("%d", id)).
SetTimeout(3 * time.Second).
Build()
if err != nil {
fmt.Printf("Goroutine %d: Build failed: %v\n", id, err)
return
}
resp, err := request.Execute()
if err != nil {
fmt.Printf("Goroutine %d: Request failed: %v\n", id, err)
return
}
fmt.Printf("Goroutine %d: Response Status: %s\n", id, resp.Status)
}(i)
}
wg.Wait()
}
|
讲解
- 使用
sync.Mutex
和 sync.Once
确保线程安全。
- 返回请求副本,避免并发修改。
- 优势:适合高并发场景。
常见问题
-
建造者模式和工厂模式区别?
建造者模式注重分步构建复杂对象;工厂模式创建不同类型对象。
-
Go 中需要建造者模式吗?
在复杂对象构建场景中,建造者模式提高可读性和灵活性。
-
如何优化并发?
为每个 goroutine 创建独立建造者,或使用对象池。
总结
建造者模式在 Go 中通过结构体和方法链实现,适合 API 请求等场景。本文展示了简单建造者、带导演的建造者和并发安全的实现。希望这篇文章帮助您掌握建造者模式!
欢迎留言:分享您在 Go 项目中使用建造者模式的经验!
评论 0