こんにちは。KOUKIです。
以前、Compositeパターンについて記事を書きましたが、別パターンでも実装したので紹介します。

<目次>
Compositeパターン
Compositeパターンは再起的な構造の取り扱いを容易にするデザインパターンです。
サンプルを確認しながら理解を深めていきましょう。
まずは基本から
Compositeパターンを実装する時、「まとめたいもの」を想像することが大切です。Compositeは「複合物」を意味するからです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package composite import "fmt" type Athlete struct{} func (a *Athlete) Train() { fmt.Println("Training") } func Swim() { fmt.Println("Swimming") } type CompositeSwimmerA struct { MyAhlete Athlete MySwim *func() } |
上記のコードでは、Athlete(アスリート)構造体とSwim関数を実装し、CompositeSwimmer構造体でひとまとめにしました。
テストコードを実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
package composite import ( "fmt" "testing" ) func TestAthlete_Train(t *testing.T) { Athlete := Athlete{} Athlete.Train() } func TestSwimmer_Swim(t *testing.T) { localSwim := Swim swimmer := CompositeSwimmmrA{ MySwim: &localSwim, } swimmer.MyAhlete.Train() (*swimmer.MySwim)() } |
TestAthlete_Train関数では、Athleteをインスタンス化し、Trainメソッドを呼び出しています。
一方、TestSwimmer_Swim関数では、CompositeSwimmmrAをインスタンス化し、そのプロパティであるMyAhleteからTrainメソッドを呼び出しています。
このように、再起的な処理を可能にしてくれます。
テストコードを実行しましょう。
1 2 3 4 5 6 7 8 9 10 |
$ go test -v === RUN TestAthlete_Train Training --- PASS: TestAthlete_Train (0.00s) === RUN TestSwimmer_Swim Training Swimming --- PASS: TestSwimmer_Swim (0.00s) PASS ok github.com/hoge/go-algorithms/design-pattern/composite 0.530s |
Swimmerの別パターン
先ほど実装したSwimmerの別パターンです。今度はインターフェースを使います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
type Trainer interface { Train() } type Swimmer interface { Swim() } type SwimmerImplementor struct{} func (s *SwimmerImplementor) Swim() { fmt.Println("Swimming!") } type CompositeSwimmerB struct { Trainer Swimmer } |
テストコードを実装します。
1 2 3 4 5 6 7 8 |
func TestSwimmer_Swim2(t *testing.T) { swimmer := CompositeSwimmerB{ &Athlete{}, &SwimmerImplementor{}, } swimmer.Train() swimmer.Swim() } |
CompositeSwimmerB構造体にAthlete及びSwimmerImplementor構造体をそれぞれ渡しています。インターフェースを実装できているのでパラメーターとして受け取ることができます(AthleteはTrainを実装、SwimmerImplementorはSwimを実装)。
インターフェースを構造体のパラメータとして渡すことで、「柔軟性のある(疎結合)」プログラミングを実装することができるわけです。
このテクニックは私も苦手なので、マスターしたい^^;
テストを実行します。
1 2 3 4 5 6 7 |
$ go test -v === RUN TestSwimmer_Swim2 Training Swimming! --- PASS: TestSwimmer_Swim2 (0.00s) PASS ok github.com/hoge/go-algorithms/design-pattern/composite 0.681s |
Animalも!
Athleteだけではなく、AnimalもSwimできます!
1 2 3 4 5 6 7 8 9 10 |
type Animal struct{} func (a *Animal) Eat() { println("Eating") } type Shark struct { Animal Swim func() } |
さぁ、テストコードを実装しましょう!
1 2 3 4 5 6 7 |
func TestAnimal_Swim(t *testing.T) { fish := Shark{ Swim: Swim, } fish.Eat() fish.Swim() } |
AthleteとAnimalは全然違う生き物なのに、Swim関数を使い回せています。柔軟性があるプログラムですね^^
テストコードを実行しましょう。
1 2 3 4 5 6 7 |
$ go test -v === RUN TestAnimal_Swim Eating Swimming --- PASS: TestAnimal_Swim (0.00s) PASS ok github.com/hoge/go-algorithms/design-pattern/composite 0.569s |
Treeも!
Tree構造体もCompositeパターンと相性が良いです。
1 2 3 4 5 |
type Tree struct { LeafValue int Right *Tree Left *Tree } |
テストコードを実装/実行しましょう。
1 2 3 4 5 6 7 8 9 10 11 |
func TestTree(t *testing.T) { root := Tree{ LeafValue: 0, Right: &Tree{ LeafValue: 5, Right: &Tree{6, nil, nil}, }, Left: &Tree{4, nil, nil}, } fmt.Println(root.Right.Right.LeafValue) } |
1 2 3 4 5 6 |
$ go test -v === RUN TestTree 6 --- PASS: TestTree (0.00s) PASS ok github.com/hoge/go-algorithms/design-pattern/composite 0.524s |
OKですね。
おわりに
golangのInterfaceはかなり使えます。特にプログラムを疎結合にしたい場合には有効です。
Interfaceのおすすめの使い方を以下の記事で紹介しているので、よかったら遊びに来てください!
それでは、また!
Go記事まとめ
ソースコード
composite.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 |
package composite import "fmt" type Athlete struct{} func (a *Athlete) Train() { fmt.Println("Training") } func Swim() { fmt.Println("Swimming") } type CompositeSwimmmrA struct { MyAhlete Athlete MySwim *func() } // ------------------------------- type Trainer interface { Train() } type Swimmer interface { Swim() } type SwimmerImplementor struct{} func (s *SwimmerImplementor) Swim() { fmt.Println("Swimming!") } type CompositeSwimmerB struct { Trainer Swimmer } // ------------------------------- type Animal struct{} func (a *Animal) Eat() { println("Eating") } type Shark struct { Animal Swim func() } // ------------------------------- type Tree struct { LeafValue int Right *Tree Left *Tree } |
composite_test.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 |
package composite import ( "fmt" "testing" ) func TestAthlete_Train(t *testing.T) { Athlete := Athlete{} Athlete.Train() } func TestSwimmer_Swim(t *testing.T) { localSwim := Swim swimmer := CompositeSwimmmrA{ MySwim: &localSwim, } swimmer.MyAhlete.Train() (*swimmer.MySwim)() } func TestSwimmer_Swim2(t *testing.T) { swimmer := CompositeSwimmerB{ &Athlete{}, &SwimmerImplementor{}, } swimmer.Train() swimmer.Swim() } func TestAnimal_Swim(t *testing.T) { fish := Shark{ Swim: Swim, } fish.Eat() fish.Swim() } func TestTree(t *testing.T) { root := Tree{ LeafValue: 0, Right: &Tree{ LeafValue: 5, Right: &Tree{6, nil, nil}, }, Left: &Tree{4, nil, nil}, } fmt.Println(root.Right.Right.LeafValue) } |
コメントを残す
コメントを投稿するにはログインしてください。