🙆
GoのBuilderパターンについて
現在携わっているプロジェクトで初めてBuilderパターンで実装したので備忘録も兼ねて記述。
Builderパターンとは?
オブジェクトの生成処理を行うためのデザインパターンの1つ。
他にもコンストラクタ関数(Factoryパターン)やFunctional Optionsパターンなどが存在する。
実装方法
package entity
import (
"errors"
)
type User struct {
id int
name string
email string
isActiive bool
}
// Getter
func (u *User) ID() int { return u.id }
func (u *User) Name() string { return u.name }
func (u *User) Email() string { return u.email }
func (u *User) IsActive() bool { return u.isActiive }
// ドメインルールの定義
var userDomainRules = DomainRules[User]{
{
Description: "ユーザー名が10文字以内であること",
RuleFunc: func(u User) error {
if len(u.name) > 10 {
return errors.New("ユーザー名は10文字以内である必要があります")
}
return nil
},
},
}
// Builderへ変換
func (u *User) ToBuilder() *UserBuilder {
return &UserBuilder{user: *u}
}
// Builder構造体
type UserBuilder struct {
user User
}
func NewUserBuilder() *UserBuilder {
return &UserBuilder{user: User{}}
}
func (b *UserBuilder) SetID(id int) *UserBuilder {
b.user.id = id
return b
}
func (b *UserBuilder) SetName(name string) *UserBuilder {
b.user.name = name
return b
}
func (b *UserBuilder) SetEmail(email string) *UserBuilder {
b.user.email = email
return b
}
func (b *UserBuilder) SetIsActive(isActive bool) *UserBuilder {
b.user.isActiive = isActive
return b
}
func (b *UserBuilder) Build() (*User, error) {
// ドメインルールを適用
if err := userDomainRules.Apply(b.user); err != nil {
return nil, err
}
return &b.user, nil
}
使用側では以下のように使用する。
package main
import (
"fmt"
"log"
"my_project/entity"
)
func main() {
user, err := entity.NewUserBuilder().
SetID(1).
SetName("Taro").
SetEmail("taro@example.com").
SetIsActive(true).
Build()
if err != nil {
log.Fatalf("ユーザー作成エラー: %v", err)
}
fmt.Printf("ユーザー: ID=%d, 名前=%s, Email=%s, Active=%v\n",
user.ID(), user.Name(), user.Email(), user.IsActive())
}
上記のように、パラメータを設定し、Build()でドメインルールを通した後にオブジェクトを生成できる。
また、以下のようにToBuilder()
メソッドを使用して、一部のプロパティだけ差し替えてentityを再生成できる。
updatedUser, err := existingUser.ToBuilder().
SetName("Takashi").
Build()
何が良いか
実際に使ってみて、以下の点が良いと感じた。
Entity生成時にドメインルールを通す
→ 不正な状態のEntityが生成されにくい。
一部のプロパティだけ差し替えて柔軟に再生成できる
→ 特に更新処理との相性が良い。
- データ更新APIを実装する場合
- repositoryからデータ取得しentityを生成
- リクエストパラメータの値をToBuilder()メソッドを通してセット
- repositoryにentityを渡し更新処理実施(一部更新メソッドなど実装しなくて良さそう)
フィールドをプライベートにできるため、不変性を保てる
→ 一度生成したEntityが勝手に変更されることがない。
Discussion