Goでデザインパターン (Commandパターン)

Goデザインパターン

Commandパターンとは

振る舞いに関するデザインパターンの一つ。リクエストをそれに関する情報をすべて保持する独立したオブジェクトとして保持することにより、リクエストをメソッドの引数にしたり、リクエストの遅延実行、キューイング等を行うことができるようになる。

長所

  • 処理を起動するクラスを実際に処理をするクラスから分離可能。(単一責任の原則)
  • クライアント側を修正せずに新規コマンドを追加可能。
  • 命令の取り消し・再実行や遅延実行の実装が可能。
  • 単純にコマンドをまとめて複雑な命令を作成可能。

短所

  • コードが複雑化する。

利用場面

  • 操作・命令をパラメータとして利用したい場合。
  • 操作・命令の再実行・取り消し・遅延実行等を行いたい場合。

クラス図

Commandパターン

Command

ConcreteCommandに共通のインターフェースを宣言する。基本的には命令を実行するためのメソッドを一つだけ宣言する。

Concrete Command

Commandインターフェースを実装する。仕事を独立して実行せず、Reciever役にリクエストを渡す。

Receiver

Concrete Commandが命令を実行するとき対象となるクラス。Commandからリクエストを受けとり、実際の作業を実行する。

Invoker

命令の実行を行うクラス。Commandインターフェースで宣言されている、命令実行メソッドを呼び出す。

実装例

Command

package main

type Command interface {
	execute()
}

Concrete Command

package main

import (
	"fmt"
)

type InputCommand struct {
	editor *Editor
	text string
}

func NewInputCommand(text string, editor *Editor) *InputCommand {
	return &InputCommand{
		editor: editor,
		text: text,
	}
}

func (c *InputCommand) execute() {
	fmt.Printf("<< Input command >> (%s)\n", c.text)
	c.editor.InputText(c.text)
	c.editor.Show()
}
package main

import (
	"fmt"
)

type ClearCommand struct {
	editor *Editor
}

func NewClearCommand(editor *Editor) *ClearCommand {
	return &ClearCommand{editor: editor}
}

func (c *ClearCommand) execute() {
	fmt.Println("<< Clear command >>")
	c.editor.InputText("")
	c.editor.Show()
}

Receiver

package main

import (
	"fmt"
)

// Receiver
type Editor struct {
	text string
}

func NewEditor() *Editor {
	return &Editor{}
}

func (e *Editor) InputText(text string) {
	e.text = text
}

func (e *Editor) Show() {
	fmt.Printf("Editor's text: %s\n", e.text)
}

Invoker

package main

// Invoker(起動者)
// Commandインタフェースで宣言されたexecuteを呼び出し、命令を実行する
// Invokerは具象Commandには依存しない。
type App struct {
	editor *Editor
	histroy []Command
}

func NewApp(editor *Editor) *App {
	return &App{editor: editor}
}

func (a *App) ExecuteCommand(command Command) {
	a.histroy = append(a.histroy, command)
	command.execute()
}

func (a *App) Input(text string) {
	command := NewInputCommand(text, a.editor)
	a.executeCommand(command)
}

func (a *App) Undo() {
	if len(a.histroy) > 0 {
		command := a.histroy[len(a.histroy) - 1]
		command.execute()
		a.histroy = a.histroy[:len(a.histroy) - 1]
	}
}

func (a *App) Clear() {
	command := NewClearCommand(a.editor)
	a.executeCommand(command)
}

func (a *App) executeCommand(command Command) {
	command.execute()
	a.histroy = append(a.histroy, command)
}

動作確認

package main

import "fmt"

func main() {
	editor := NewEditor()
	app := NewApp(editor)

	fmt.Println("=== コマンド実行 ===")

	app.Input("Hello World")
	fmt.Println()

	app.Input("This is Command Pattern")
	fmt.Println()

	app.Clear()
	fmt.Println()

	fmt.Println("=== Undo実行 ===")

	app.Undo()
	fmt.Println()

	app.Undo()
	fmt.Println()

	app.Undo()
	fmt.Println()
}
>> go run .
=== コマンド実行 ===
<< Input command >> (Hello World)
Editor's text: Hello World

<< Input command >> (This is Command Pattern)
Editor's text: This is Command Pattern       

<< Clear command >>
Editor's text: 

=== Undo実行 ===
<< Clear command >>
Editor's text:

<< Input command >> (This is Command Pattern)
Editor's text: This is Command Pattern

<< Input command >> (Hello World)
Editor's text: Hello World

コメント

タイトルとURLをコピーしました