こんにちは、KOUKIです。
この記事では、デザインパターンの一つであるBuilderパターンについて、紹介しています。
今回は、インスタンスのプロパティの面白い設定方法を紹介します。
<目次>
デザインパターン
シチュエーション
例えば、こんな構造体があるとします。
1 2 3 4 5 6 7 |
type Person struct { // Group1; Lives Address, PostCode, City string // Group2: Works Company, Occupation, Position string } |
Person構造体のプロパティとして、2つのグループを設定しました。一つは、Livesプロパティ、もう一つはWoksプロパティです。
通常なら、以下のようにインスタンス化すると思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
func main() { p := Person{ // Group1: Lives Address: "Japan", PostCode: "222222", City: "Tokyo", // Group2: Works Company: "Great Ship", Occupation: "SoftWare Developer", Position: "Normal", } fmt.Println(p) } |
1 2 |
$ go run main.go {Japan 222222 Tokyo Great Ship SoftWare Developer Normal} |
Builderパターンを使うことで、このインスタンス生成を(実用的かどうかは置いておいて)面白い感じで行えます。
Builderパターン Facets適用
では、早速実装していきましょう。
Builder構造体を定義
Builderパターンは、Building(構築)したい対象を構造体として定義することからは始めます。
今回は、Person、Lives(Group1)、Works(Group2)であるため、以下のように構造体を定義しました。
1 2 3 4 5 6 7 8 9 10 11 |
type PersonBuilder struct { person *Person } type PersonLivesBuilder struct { PersonBuilder } type PersonWorksBuilder struct { PersonBuilder } |
PersonBuilderが全体の親で、PersonLivesBuilderおよびPersonWorksBuilderがアグリゲート(集合)しています。これが、基本の型です。
コンストラクタの定義
Builderパターンの肝は、コンストラクタの定義です。コンストラクタは、インスタンス生成時に実装される関数で、以下のように実装します。
1 2 3 4 |
// コンストラクタ func NewPersonBuilder() *PersonBuilder { return &PersonBuilder{&Person{}} } |
Builderパターンは、インスタンスの生成を単純にすることが用途の一つになります(そう思っている)。そのため、コンストラクタに渡す引数はなるべく少ない方がいいと個人的に考えています。
アグリゲートメソッドの定義
次に、アグリゲートメソッドを定義します。この言葉(アグリゲートメソッド)は、私の造語ですw
1 2 3 4 5 6 7 |
func (b *PersonBuilder) Lives() *PersonLivesBuilder { return &PersonLivesBuilder{*b} } func (b *PersonBuilder) Works() *PersonWorksBuilder { return &PersonWorksBuilder{*b} } |
PersonLivesBuilder/PersonWorksBuilder構造体は、PersonBuilder構造体をアグリゲートしており、これらの構造体インスタンス化するメソッドを実装しました。
そうすることで、以下のようなことが行えます。
1 2 3 4 |
pb := NewPersonBuilder() // Livesでインスタンスが返却されるので、それ専用のメソッドが使える pb.Lives().XXXX(メソッド) |
具体的には、「使ってみる」項目を参照してください。
プロパティ設定用メソッドの実装
PersonLivesBuilder/PersonWorksBuilder構造体のプロパティを設定するメソッドを実装しましょう。
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 |
func (b *PersonLivesBuilder) At(address string) *PersonLivesBuilder { b.person.Address = address return b } func (b *PersonLivesBuilder) In(city string) *PersonLivesBuilder { b.person.City = city return b } func (b *PersonLivesBuilder) With(postcode string) *PersonLivesBuilder { b.person.PostCode = postcode return b } func (b *PersonWorksBuilder) At(companyName string) *PersonWorksBuilder { b.person.Company = companyName return b } func (b *PersonWorksBuilder) AsA(occupation string) *PersonWorksBuilder { b.person.Occupation = occupation return b } func (b *PersonWorksBuilder) In(position string) *PersonWorksBuilder { b.person.Position = position return b } |
これらのメソッドも全てインスタンスを返却しているので、メソッドを繋げて呼び出すことが可能です。
Builderメソッドの定義
最後に忘れてはいけないのが、Builderメソッドの定義です。
このメソッドは、最終的に生成した構造体を返却します。
1 2 3 |
func (b *PersonBuilder) Build() *Person { return b.person } |
使ってみる
main関数に以下のように設定しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
func main() { // パラメータが少ないので、インスタンス化が楽 pb := NewPersonBuilder() pb. // Group1 Lives(). At("Japan"). In("Tokyo"). With("222222"). // Group2 Works(). At("Great Ship"). AsA("SoftWare Developer"). In("Normal") // 生成したインスタンスを取得 person := pb.Build() fmt.Println(person) } |
1 2 |
$ go run main.go &{Japan 222222 Tokyo Great Ship SoftWare Developer Normal} |
OKですね。
こんな実装ができるなんて、面白いですよね。
どのタイミングで使うべきなのか見出せていませんが、知っていればいずれ役に立つことでしょう^^
次回
次回は、BuilderパターンのParameterバージョンを学びます。
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 |
package main import "fmt" type Person struct { // Group1; Lives Address, PostCode, City string // Group2: Works Company, Occupation, Position string } type PersonBuilder struct { person *Person } type PersonLivesBuilder struct { PersonBuilder } type PersonWorksBuilder struct { PersonBuilder } // コンストラクタ func NewPersonBuilder() *PersonBuilder { return &PersonBuilder{&Person{}} } func (b *PersonBuilder) Lives() *PersonLivesBuilder { return &PersonLivesBuilder{*b} } func (b *PersonBuilder) Works() *PersonWorksBuilder { return &PersonWorksBuilder{*b} } func (b *PersonLivesBuilder) At(address string) *PersonLivesBuilder { b.person.Address = address return b } func (b *PersonLivesBuilder) In(city string) *PersonLivesBuilder { b.person.City = city return b } func (b *PersonLivesBuilder) With(postcode string) *PersonLivesBuilder { b.person.PostCode = postcode return b } func (b *PersonWorksBuilder) At(companyName string) *PersonWorksBuilder { b.person.Company = companyName return b } func (b *PersonWorksBuilder) AsA(occupation string) *PersonWorksBuilder { b.person.Occupation = occupation return b } func (b *PersonWorksBuilder) In(position string) *PersonWorksBuilder { b.person.Position = position return b } func (b *PersonBuilder) Build() *Person { return b.person } func main() { // パラメータが少ないので、インスタンス化が楽 pb := NewPersonBuilder() pb. // Group1 Lives(). At("Japan"). In("Tokyo"). With("222222"). // Group2 Works(). At("Great Ship"). AsA("SoftWare Developer"). In("Normal") // 生成したインスタンスを取得 person := pb.Build() fmt.Println(person) } |
コメントを残す
コメントを投稿するにはログインしてください。