Builderパターンとは
Builderパターンは、複雑なオブジェクトを段階的に構築できる、生成に関するデザインパターン。
例えばHouse(家)オブジェクトを作成する場合を考える。家といっても屋根の形や庭、ベランダ等の様々なオプションが考えられる。このようなオブジェクトが必要な場合、主な解決策は2つある。一つがHouseクラスを基底クラスにして、それをもとに様々なサブクラスを用意すること。もう一つが屋根の形やベランダの有無等のパラメータをすべてコンストラクタで渡すようにすることである。
全てのパターンのサブクラスを用意するのは非常に面倒であるし、コンストラクタで渡す場合も、あるオブジェクト生成する場合に、すべてのパラメータが必要になるわけではない。
これをオブジェクトの構築コードをBuilderに切り出すことで解決したのがBuilderパターンになる。Builderパターンではコンストラクタ内で行われる処理を、buildStepA,B,Cのように各ステップごとに実装し、生成の際にそれらを呼び出している。
Builderパターンを利用することで下記のようなメリットを得ることができる。
- 異なる型・表現のオブジェクトを同じ構築コードを利用して生成することができる。
- 複雑なオブジェクト構築手順をビジネスロジック(Client側処理)から隠蔽できる。
(単一責任の原則)
クラス図

- iBuilder
インスタンス生成に必要な様々な構築ステップのインターフェースを宣言する。 - concreteBuilder
iBuilderで宣言された各ステップの処理を実装する。また生成したインスタンスを取得するためのメソッドが用意され - Product
生成されるインスタンスを定義する。 - Director
iBuilderで宣言されたAPIを利用してインスタンスを生成する。オブジェクト生成の一連のステップ呼び出しを抽出している。よく使われる生成手順を置いておいて再利用しやすくしている。
concreteBuilderに依存した処理は行わない。→Builderインターフェースを実装してさえいれば、別の型のオブジェクトでも生成可能。
厳密には必要ないが、Clientからインスタンス構築の詳細を完全に隠蔽することができる。 - Client
Builderオブジェクトの一つをDirectorオブジェクトに紐づける。Directorに存在しない生成方法の場合はBuilderの構築ステップを直接実行する。
実装
iBuilder
package builder
// インスタンスを作成するためのインターフェース(API)を宣言する
// インスタンスの各ステップのメソッドを用意する。
type iBuilder interface {
SetHandle()
SetTireSize()
SetFrame()
Build() interface{} // 様々な型に対応するため戻り値はinterface{}で定義
}
func GetBuilder(builderType string) iBuilder {
if builderType == "bike" {
return NewBikeBuilder()
}
if builderType == "manual" {
return NewManualBuilder()
}
return nil
}
Director
package builder
// BuilderのAPIを用いてインスタンスを生成する
// Builderの具象クラスには依存しない
type Director struct {
builder iBuilder
}
func NewDirector(b iBuilder) *Director {
return &Director{builder: b}
}
func (d *Director) SetBuilder(b iBuilder) {
d.builder = b
}
func (d *Director) Build() interface{} {
d.builder.SetHandle()
d.builder.SetTireSize()
d.builder.SetFrame()
// 最終的なオブジェクトはビルダーで生成したものを返す
return d.builder.Build()
}
product
package builder
type bike struct {
handle string
tireSize int
frame string
}
package builder
type manual struct {
handlePage string
tirePage string
framePage string
}
concreteBuilder
package builder
type BikeBuilder struct {
bike
}
func NewBikeBuilder() *BikeBuilder {
return &BikeBuilder{}
}
func (b *BikeBuilder) SetHandle() {
b.handle = "bike handle"
}
func (b *BikeBuilder) SetTireSize() {
b.tireSize = 30
}
func (b *BikeBuilder) SetFrame() {
b.frame = "bike frame"
}
func (b *BikeBuilder) Build() interface{} {
return bike{
handle: b.handle,
tireSize: b.tireSize,
frame: b.frame,
}
}
package builder
type ManualBuilder struct {
manual
}
func NewManualBuilder() *ManualBuilder {
return &ManualBuilder{}
}
func (b *ManualBuilder) SetHandle() {
b.handlePage = "handel manual"
}
func (b *ManualBuilder) SetTireSize() {
b.tirePage = "tire manual"
}
func (b *ManualBuilder) SetFrame() {
b.framePage = "frame manual"
}
func (b *ManualBuilder) Build() interface{} {
return manual{
handlePage: b.handlePage,
tirePage: b.tirePage,
framePage: b.framePage,
}
}
動作確認
package main
import (
"fmt"
. "builder/builder"
)
func main() {
bb := GetBuilder("bike")
d := NewDirector(bb)
bike := d.Build()
fmt.Printf("Bike: %+v\n", bike)
mb := GetBuilder("manual")
d.SetBuilder(mb)
manual := d.Build()
fmt.Printf("Manual: %+v\n", manual)
}
>> go run .
Bike: {handle:bike handle tireSize:30 frame:bike frame}
Manual: {handlePage:handel manual tirePage:tire manual framePage:frame manual}
コメント