こんにちは、KOUKIです。
この記事では、デザインパターンの一つであるVisitorパターンについて紹介します。
<目次>
デザインパターンまとめ
シチュエーション
Visitor は、「訪問者」を意味する英単語で、Visitor パターンは、既存のコードを変更せずに既存のクラス階層に新しい処理を追加できるパターンです。
Go言語にはクラスという概念がないので、Struct(構造体)が対象になると思います。
Visitorパターンの適用
サンプルとして、顔文字v(^ – ^ )vを生成するプログラムをVisitorパターンで実装してみましょう。
Visitorインターフェース
まず、Visitorが持つ共通のインターフェースを実装しましょう。
1 2 3 4 |
// 共通のインターフェース type Expression interface { Show(sb *strings.Builder) } |
Visitorの実装
次にVisitorを実装します。
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 |
// Visitor -「目」 type EyeExpression struct { eye string } // Expressionインターフェースを実装 func (s *EyeExpression) Show(sb *strings.Builder) { sb.WriteString(fmt.Sprintf("%s", s.eye)) } // Visitor -「口」 type MouthExpression struct { mouth string } // Expressionインターフェースを実装 func (m *MouthExpression) Show(sb *strings.Builder) { sb.WriteString(fmt.Sprintf("%s", m.mouth)) } // Visitor -「その他」 type OtherExpression struct { other string } // Expressionインターフェースを実装 func (a *OtherExpression) Show(sb *strings.Builder) { sb.WriteString(fmt.Sprintf("%s", a.other)) } |
これらは、すべてShowメソッドを持ちます。Showメソッドの中身は同じなので、本来であれば構造体を分ける必要はないのかもしれませんが、「既存のコードを変更せずに既存のクラス階層に新しい処理を追加できる」を示すために、構造体を分けています。
ポイントは、Expressionインターフェースを満たすために、Showメソッドを実装していることです。
訪問先構造体
訪問先の構造体を実装しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 |
// Expressionインターフェースを実装していれば、この構造体を // 変更せずに新しい処理を追加できる type CombineExpression struct { left, center, right Expression } // 処理を実行 func (c *CombineExpression) Show(sb *strings.Builder) { c.left.Show(sb) c.center.Show(sb) c.right.Show(sb) } |
この構造体のパラメータの型は、Expressionインターフェースです。つまり、Showメソッドを実装した構造体ならなんでも渡せます。
これにより、新しいVisitorを実装することで、構造体の振る舞いを変更せずに追加の処理を実装できるという訳です。
インターフェースは、本当に便利です。
使ってみよう
ここまで実装したコードを使ってみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
func main() { // v(^_^)v e := CombineExpression{ &OtherExpression{"v("}, &CombineExpression{ &EyeExpression{"^"}, &MouthExpression{"_"}, &EyeExpression{"^"}, }, &OtherExpression{")v"}, } sb := strings.Builder{} e.Show(&sb) fmt.Println(sb.String()) } |
ご覧の通り、CombineExpressionを軸に、階層的にインスタンスを追加していけます。
実行します。
1 2 |
$ go run main.go v(^_^)v |
OKですね。
Showメソッドの統合
インターフェースにShowメソッドを持たせないで、統合する方法もあります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// 共通のインターフェース type Expression interface { // 削除 } func Show(e Expression, sb *strings.Builder) { if ee, ok := e.(*EyeExpression); ok { sb.WriteString(fmt.Sprintf("%s", ee.eye)) } else if me, ok := e.(*MouthExpression); ok { sb.WriteString(fmt.Sprintf("%s", me.mouth)) } else if oe, ok := e.(*OtherExpression); ok { sb.WriteString(fmt.Sprintf("%s", oe.other)) } else if ce, ok := e.(*CombineExpression); ok { Show(ce.left, sb) Show(ce.center, sb) Show(ce.right, sb) } } |
構造体毎に用意していたShowメソッドを一つの関数に統合しました。こちらの方が修正そうですね。
プログラムを実行してみましょう。
1 2 |
$ go run main.go v(^_^)v |
おわりに
このパターンは、インターフェースの活用が肝ですね。
インターフェースの活用方法については、次の記事にまとめたので、よかったら参考にしてください。
次回
次回も引き続き、Visitorパターンを学習しましょう!
Go言語まとめ
ソースコード
Ver1
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 |
package main import ( "fmt" "strings" ) // 共通のインターフェース type Expression interface { Show(sb *strings.Builder) } // Visitor -「目」 type EyeExpression struct { eye string } // Expressionインターフェースを実装 func (s *EyeExpression) Show(sb *strings.Builder) { sb.WriteString(fmt.Sprintf("%s", s.eye)) } // Visitor -「口」 type MouthExpression struct { mouth string } // Expressionインターフェースを実装 func (m *MouthExpression) Show(sb *strings.Builder) { sb.WriteString(fmt.Sprintf("%s", m.mouth)) } // Visitor -「その他」 type OtherExpression struct { other string } // Expressionインターフェースを実装 func (a *OtherExpression) Show(sb *strings.Builder) { sb.WriteString(fmt.Sprintf("%s", a.other)) } // Expressionインターフェースを実装していれば、この構造体を // 変更せずに新しい処理を追加できる type CombineExpression struct { left, center, right Expression } // 処理を実行 func (c *CombineExpression) Show(sb *strings.Builder) { c.left.Show(sb) c.center.Show(sb) c.right.Show(sb) } func main() { // v(^_^)v e := CombineExpression{ &OtherExpression{"v("}, &CombineExpression{ &EyeExpression{"^"}, &MouthExpression{"_"}, &EyeExpression{"^"}, }, &OtherExpression{")v"}, } sb := strings.Builder{} e.Show(&sb) fmt.Println(sb.String()) } |
Ver2
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 |
package main import ( "fmt" "strings" ) // 共通のインターフェース type Expression interface { // 削除 } // Visitor -「目」 type EyeExpression struct { eye string } // Visitor -「口」 type MouthExpression struct { mouth string } // Visitor -「その他」 type OtherExpression struct { other string } // Expressionインターフェースを実装していれば、この構造体を // 変更せずに新しい処理を追加できる type CombineExpression struct { left, center, right Expression } func Show(e Expression, sb *strings.Builder) { if ee, ok := e.(*EyeExpression); ok { sb.WriteString(fmt.Sprintf("%s", ee.eye)) } else if me, ok := e.(*MouthExpression); ok { sb.WriteString(fmt.Sprintf("%s", me.mouth)) } else if oe, ok := e.(*OtherExpression); ok { sb.WriteString(fmt.Sprintf("%s", oe.other)) } else if ce, ok := e.(*CombineExpression); ok { Show(ce.left, sb) Show(ce.center, sb) Show(ce.right, sb) } } func main() { // v(^_^)v e := CombineExpression{ &OtherExpression{"v("}, &CombineExpression{ &EyeExpression{"^"}, &MouthExpression{"_"}, &EyeExpression{"^"}, }, &OtherExpression{")v"}, } sb := strings.Builder{} Show(&e, &sb) fmt.Println(sb.String()) } |
コメントを残す
コメントを投稿するにはログインしてください。