こんにちは、KOUKIです。
この記事では、デザインパターンの一つであるCommandパターンについて紹介します。
<目次>
デザインパターンまとめ
シチュエーション
Commandパターンは、オブジェクト(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 |
package main import "fmt" type Calculator struct { result int } func NewCalculator() *Calculator { return &Calculator{0} } func (c *Calculator) Add(value int) { c.result += value } func (c *Calculator) Subtract(value int) { c.result -= value } func (c *Calculator) Multiply(value int) { c.result *= value } func (c *Calculator) Divide(value int) { c.result /= value } |
Calculator構造体は、Add/Subtract/Multiply/Divideの4つのメソッドを実装しています。通常であれば、下記のようにインスタンス化をして、それぞれのメソッドを呼び出すことになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
func main() { calculator := NewCalculator() // Add calculator.Add(40) fmt.Println(calculator.result) // Subtract calculator.Subtract(10) fmt.Println(calculator.result) // Multiply calculator.Multiply(30) fmt.Println(calculator.result) // Divide calculator.Divide(50) fmt.Println(calculator.result) } |
1 2 3 4 5 |
$ go run main.go 40 30 900 18 |
Commandパターンの適用
Commandパターンは、オブジェクト(インスタンス)への要求自体をオブジェクトにするパターンです。ここでの要求は、計算処理(Add/Subtract/Multiply/Divide)を指します。
シチュエーションのサンプルコードに、Commandパターンを適用してみましょう。
Commandインターフェースの実装
まず、Commandインターフェースを実装します。
1 2 3 |
type Command interface { Call() } |
Command Actionの実装
次に、Action(要求)を実装します。
1 2 3 4 5 6 7 8 |
type Action int const ( Add Action = iota Subtract Multiply Divide ) |
Command構造体の実装
次に、Command構造体を実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
type CalculatorCommand struct { calc *Calculator action Action value int } func NewCalculatorCommand( calc *Calculator, action Action, value int) *CalculatorCommand { return &CalculatorCommand{ calc: calc, action: action, value: value, } } |
Callメソッドの実装
ここが、本記事のポイントです。
1 2 3 4 5 6 7 8 9 10 11 12 |
func (c *CalculatorCommand) Call() { switch c.action { case Add: c.calc.Add(c.value) case Subtract: c.calc.Subtract(c.value) case Multiply: c.calc.Multiply(c.value) case Divide: c.calc.Divide(c.value) } } |
ご覧の通り、Actionごとにswitch文で処理分岐をしています。
また、CalculatorCommand構造体は、Calculator構造体をパラメータとして持つので、Add/Subtract/Multiply/Divideの4つのメソッドを呼び出すことが可能です。
使ってみよう
ここまで実装したコードを使ってみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
func main() { calculator := NewCalculator() // Add cmd := NewCalculatorCommand(calculator, Add, 40) cmd.Call() fmt.Println(calculator.result) // Subtract cmd2 := NewCalculatorCommand(calculator, Subtract, 10) cmd2.Call() fmt.Println(calculator.result) // Multiply cmd3 := NewCalculatorCommand(calculator, Multiply, 30) cmd3.Call() fmt.Println(calculator.result) // Divide cmd4 := NewCalculatorCommand(calculator, Divide, 50) cmd4.Call() fmt.Println(calculator.result) } |
NewCalculatorCommandコンストラクタで、Commandをインスタンス化し、Callメソッドを介して処理を呼び出しています。
プログラムを実行してみましょう。
1 2 3 4 5 |
$ go run main.go 40 30 900 18 |
OKですね。
オブジェクトから直接処理を呼び出すのではなく、Commandインスタンスを介して処理を呼び出せました。
Undo Commandパターン
説明は以上!という感じなのですが、このままだと内容が薄いので、Undo Command(計算キャンセル)も実装してみます。
まず、CommandインターフェースにUndoを追加します。
1 2 3 4 |
type Command interface { Call() Undo() } |
次に、CalculatorCommand構造体にUndoメソッドを実装します。
1 2 3 4 5 6 7 8 9 10 11 12 |
func (c *CalculatorCommand) Undo() { switch c.action { case Add: c.calc.Subtract(c.value) case Subtract: c.calc.Add(c.value) case Multiply: c.calc.Divide(c.value) case Divide: c.calc.Multiply(c.value) } } |
これをmain関数から呼び出してみましょう。
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 |
func main() { calculator := NewCalculator() // Add cmd := NewCalculatorCommand(calculator, Add, 40) cmd.Call() fmt.Println(calculator.result) // cmd.Undo() // fmt.Println(calculator.result) // Subtract cmd2 := NewCalculatorCommand(calculator, Subtract, 10) cmd2.Call() fmt.Println(calculator.result) // cmd2.Undo() // fmt.Println(calculator.result) // Multiply cmd3 := NewCalculatorCommand(calculator, Multiply, 30) cmd3.Call() fmt.Println(calculator.result) // cmd3.Undo() // fmt.Println(calculator.result) // Divide cmd4 := NewCalculatorCommand(calculator, Divide, 50) cmd4.Call() fmt.Println(calculator.result) cmd4.Undo() fmt.Println(calculator.result) } |
cmd4だけUndo(計算キャンセル)しました。
1 2 3 4 5 6 |
$ go run main.go 40 30 900 18 <<<<< 割った数値 900 <<<<<< 900に戻っている(Undo) |
OKですね。
機能追加とその役割がわかりやすい気がしますね。
まとめ
今回のサンプルは、シンプルすぎるが故に、Commandパターンで実装するメリットがわかりづらいかったかもしれません。
しかし、オブジェクト(インスタンス)への要求は、特定のCommandを介して行うという縛りを作ると何かしらのメリットを得られるかもしれません。
例えば、システムをShutdownしたい場合、Shutdownコマンドを送ることになると思いますが、Command構造体の要求からのみ有効にする、みたいな使い方もできると思います。
コマンドラインツールを作るときも活躍しそうですね。
それでは、また!
次回
次回は、CommandパターンのComposite Commandを学習します。
Go言語まとめ
ソースコード
Command
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 91 92 93 94 95 |
package main import "fmt" type Calculator struct { result int } func NewCalculator() *Calculator { return &Calculator{0} } func (c *Calculator) Add(value int) { c.result += value } func (c *Calculator) Subtract(value int) { c.result -= value } func (c *Calculator) Multiply(value int) { c.result *= value } func (c *Calculator) Divide(value int) { c.result /= value } // Command Pattern type Command interface { Call() } type Action int const ( Add Action = iota Subtract Multiply Divide ) type CalculatorCommand struct { calc *Calculator action Action value int } func NewCalculatorCommand( calc *Calculator, action Action, value int) *CalculatorCommand { return &CalculatorCommand{ calc: calc, action: action, value: value, } } func (c *CalculatorCommand) Call() { switch c.action { case Add: c.calc.Add(c.value) case Subtract: c.calc.Subtract(c.value) case Multiply: c.calc.Multiply(c.value) case Divide: c.calc.Divide(c.value) } } func main() { calculator := NewCalculator() // Add cmd := NewCalculatorCommand(calculator, Add, 40) cmd.Call() fmt.Println(calculator.result) // Subtract cmd2 := NewCalculatorCommand(calculator, Subtract, 10) cmd2.Call() fmt.Println(calculator.result) // Multiply cmd3 := NewCalculatorCommand(calculator, Multiply, 30) cmd3.Call() fmt.Println(calculator.result) // Divide cmd4 := NewCalculatorCommand(calculator, Divide, 50) cmd4.Call() fmt.Println(calculator.result) } |
Command + Undo
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 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
package main import "fmt" type Calculator struct { result int } func NewCalculator() *Calculator { return &Calculator{0} } func (c *Calculator) Add(value int) { c.result += value } func (c *Calculator) Subtract(value int) { c.result -= value } func (c *Calculator) Multiply(value int) { c.result *= value } func (c *Calculator) Divide(value int) { c.result /= value } // Command Pattern type Command interface { Call() Undo() } type Action int const ( Add Action = iota Subtract Multiply Divide ) type CalculatorCommand struct { calc *Calculator action Action value int } func NewCalculatorCommand( calc *Calculator, action Action, value int) *CalculatorCommand { return &CalculatorCommand{ calc: calc, action: action, value: value, } } func (c *CalculatorCommand) Call() { switch c.action { case Add: c.calc.Add(c.value) case Subtract: c.calc.Subtract(c.value) case Multiply: c.calc.Multiply(c.value) case Divide: c.calc.Divide(c.value) } } func (c *CalculatorCommand) Undo() { switch c.action { case Add: c.calc.Subtract(c.value) case Subtract: c.calc.Add(c.value) case Multiply: c.calc.Divide(c.value) case Divide: c.calc.Multiply(c.value) } } func main() { calculator := NewCalculator() // Add cmd := NewCalculatorCommand(calculator, Add, 40) cmd.Call() fmt.Println(calculator.result) // cmd.Undo() // fmt.Println(calculator.result) // Subtract cmd2 := NewCalculatorCommand(calculator, Subtract, 10) cmd2.Call() fmt.Println(calculator.result) // cmd2.Undo() // fmt.Println(calculator.result) // Multiply cmd3 := NewCalculatorCommand(calculator, Multiply, 30) cmd3.Call() fmt.Println(calculator.result) // cmd3.Undo() // fmt.Println(calculator.result) // Divide cmd4 := NewCalculatorCommand(calculator, Divide, 50) cmd4.Call() fmt.Println(calculator.result) cmd4.Undo() fmt.Println(calculator.result) } |
コメントを残す
コメントを投稿するにはログインしてください。