Go における Builder パターン
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 80/107。
Builder Patternは、複雑なオブジェクトの構築をその表現から分離します。Functional Optionsパターンとは異なり、Builderは、オブジェクトを段階的に設定するメソッドを備えた専用のbuilder構造体を使用します。
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
}各メソッドは builder ポインターを返すため、メソッドの chaining が可能になります。Build() メソッドは構築されたオブジェクトを完成させて返します。
req := NewRequestBuilder().
Method("POST").
URL("https://api.example.com").
Header("Content-Type", "application/json").
Build()Builder パターンは、オブジェクトに複数の設定手順が必要な場合や、特定の構築順序を強制したい場合に優れています。特に、不変オブジェクトを作成する場合や、構築プロセス自体に検証が必要な場合に役立ちます。
チャレンジ
簡単Builder Patternを使ってメールメッセージ composer を作成しましょう!メールメッセージには、recipients、CC、subject、body、attachmentsなど、多くのオプションのコンポーネントがあるため、method chainingによる段階的な構築に最適です。
コードを2つのファイルに整理します。
email.go:Emailの構造体とそのbuilderをDefineします。次のfieldsを持つ
EmailstructをCreateします。from(string)to(stringのslice)cc(stringのslice)subject(string)body(string)
構築中のEmailへのpointerをholdsする
EmailBuilderstructをCreateします。initializedされたEmail(toとccにはemptyなslice)を持つ新しいbuilderを返すNewEmailBuilder()をImplementします。それぞれchainingのために
*EmailBuilderを返す、次のbuilder methodsをAddします。From(address string)- senderを設定しますTo(address string)-tosliceにrecipientをAddしますCC(address string)-ccsliceにrecipientをAddしますSubject(subject string)- subject lineを設定しますBody(body string)- email bodyを設定します
constructedされた
*Emailを返すBuild()methodをAddします。Finally、Emailに、次のformatでstringを返す
Summary()methodをAddします。From: [from] To: [comma-separated to addresses] CC: [comma-separated cc addresses or "none"] Subject: [subject] Body: [body]main.go:builderを使用してemail messagesをBuildし、表示します。sender addressをReadし、続けてrecipientsのnumberと各recipient addressをReadします。次に、CC recipientsのnumberと各CC addressをReadします。Finally、subjectとbodyをReadします。
EmailBuilderをmethod chainingとともに使用してemailをconstructし、そのsummaryをprintします。
次のinputsが提供されます。
- Line 1:Sender address
- Line 2:Recipientsのnumber
- Following lines:各recipient address
- Next line:CC recipientsのnumber
- Following lines:各CC address
- Next line:Subject
- Final line:Body
たとえば、次のように入力された場合:
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.builderを使うと、繰り返しTo()を呼び出すことで複数のrecipientsを1つずつAddできること、また、多くのオプションのfieldsがあっても、このpatternによって構築プロセスをcleanでreadableに保てることに注目してください。
自分で試してみよう
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)
}
// CC受信者の数を読み取る
ccCountStr, _ := reader.ReadString('\n')
ccCount, _ := strconv.Atoi(strings.TrimSpace(ccCountStr))
// 各CCアドレスを読み取る
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()メソッドを使用してメールの概要を出力する
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー自分で練習してみよう: Goオンラインコンパイラ