深入 Go 建造者模式:从零打造 API 请求构建器

什么是建造者模式?

建造者模式是一种创建型设计模式,通过分步构建复杂对象,允许灵活配置属性。它通过链式调用或分步设置提供流畅的接口,特别适合需要多个可选参数的场景。

核心要素

  • 产品:最终构建的对象。
  • 建造者:定义构建步骤。
  • 具体建造者:实现构建逻辑。
  • 导演(可选):控制构建顺序。
  • 客户端:使用建造者创建对象。

适用场景

  • 复杂对象配置(如 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.Mutexsync.Once 确保线程安全。
  • 返回请求副本,避免并发修改。
  • 优势:适合高并发场景。

常见问题

  1. 建造者模式和工厂模式区别? 建造者模式注重分步构建复杂对象;工厂模式创建不同类型对象。

  2. Go 中需要建造者模式吗? 在复杂对象构建场景中,建造者模式提高可读性和灵活性。

  3. 如何优化并发? 为每个 goroutine 创建独立建造者,或使用对象池。

总结

建造者模式在 Go 中通过结构体和方法链实现,适合 API 请求等场景。本文展示了简单建造者、带导演的建造者和并发安全的实现。希望这篇文章帮助您掌握建造者模式!

欢迎留言:分享您在 Go 项目中使用建造者模式的经验!

评论 0