Strategyパターン

最終更新:2020/05/11


この記事の内容は

java言語で学ぶデザインパターン

を参考にGo言語に置き換えた内容になっております。

わかりにくい箇所は私の責任であり、java言語で学ぶデザインパターンのせいではありません。

目次

  1. Strategyパターンとは
  2. Strategyパターンの構成
  3. サンプルコード
  4. サンプルコードの説明
  5. サンプルコードの実行

1. Strategyパターンとは

Strategyパターンは、アルゴリズム部分を切り替えるのを容易にするパターンです。

2. Strategyパターンの構成

Strategyパターンは以下の役割で構成します。

Strategy

戦略を利用するための役割をもちます。

Strategyはインターフェースで定めます。

ConcreateStrategy

Strategyインターフェースを具体的に実装します。

Context

Strategyを利用する役割を担います。

ConcreateStrategyのインスタンスを持って、必要に応じて利用します。

3. サンプルコード

package strategy import ( "math/rand" ) type Hand struct { HandValue int } func NewHand(num int) Hand { a := num % 3 return Hand{ HandValue: a, } } // Strategy type Strategy interface { NextHand() Hand Study(bool) } // ConcreateStrategy type WinningStrategy struct { Rand *rand.Rand Hand Hand Won bool } func NewWinningStrategy(num int64) Strategy { strategy := &WinningStrategy{ Rand: rand.New(rand.NewSource(num)), Hand: NewHand(int(num)), Won: false, } return strategy } func (s *WinningStrategy) NextHand() Hand { if !s.Won { s.Hand = NewHand(s.Rand.Int()) } return s.Hand } func (s *WinningStrategy) Study(win bool) { s.Won = win } // ConcreateStrategy type SecondStrategy struct { Rand *rand.Rand Hand Hand Won bool } func NewSecondStrategy(num int64) Strategy { strategy := &SecondStrategy{ Rand: rand.New(rand.NewSource(num)), Hand: NewHand(int(num)), Won: false, } return strategy } func (s *SecondStrategy) NextHand() Hand { if s.Won { s.Hand = NewHand(s.Rand.Int() + 1) } return s.Hand } func (s *SecondStrategy) Study(win bool) { s.Won = !win } // Context type Player struct { Name string Stg Strategy } func NewPlayer(Name string, stra Strategy) Player { player := Player{ Name: Name, Stg: stra, } return player } func (p *Player) NextHand() Hand { return p.Stg.NextHand() } func (p *Player) win() { p.Stg.Study(true) }

4. サンプルコードの説明

上記のコードの構成は以下の通りです。

Strategy

Strategyの役割は、戦略を利用するためのメソッドを集めて定義することです。

サンプルコードだとStrategyインターフェースが担います。

// Strategy type Strategy interface { NextHand() Hand Study(bool) }

ConcreateStrategy

ConcreateStrategyはStrategyを具体的に実装します。

サンプルコードだとWinningStrategy構造体とNewSecondStrategy構造体が役割を担います。

初期化時に、Strategyインターフェースを返すのがポイントです。

func NewWinningStrategy(num int64) Strategy { strategy := &WinningStrategy{ Rand: rand.New(rand.NewSource(num)), Hand: NewHand(int(num)), Won: false, } return strategy } func NewSecondStrategy(num int64) Strategy { strategy := &SecondStrategy{ Rand: rand.New(rand.NewSource(num)), Hand: NewHand(int(num)), Won: false, } return strategy }

Context

ContextはStrategyを利用する役割を担います。

ConcreateStrategyのインスタンスを持って、必要に応じて利用します。

サンプルコードだとPlayer構造体が役割を担います。

type Player struct { Name string Stg Strategy } func NewPlayer(Name string, stra Strategy) Player { player := Player{ Name: Name, Stg: stra, } return player }

初期化時に、Strategyインターフェースを受け取るのがポイントです。

5. サンプルコードの実行

上記のコードをmain関数で呼び出します。

package main import ( "fmt" "github.com/java-lang-programming/marsa/strategy" ) func main() { player1 := strategy.NewPlayer("Taro", strategy.NewWinningStrategy(1)) player2 := strategy.NewPlayer("Sato", strategy.NewSecondStrategy(1)) result1 := player1.NextHand() result2 := player2.NextHand() fmt.Println(result1.HandValue) fmt.Println(result2.HandValue) }

NewPlayerの第二引数で、NewWinningStrategyとNewSecondStrategyの両方を受け取れています。

player1 := strategy.NewPlayer("Taro", strategy.NewWinningStrategy(1)) player2 := strategy.NewPlayer("Sato", strategy.NewSecondStrategy(1))

この理由は、引数がStrategyインタフェースなので、Strategyインタフェースを利用しているWinningStrategyとSecondStrategyが使えるからです。

こうすることでPleyerは、引数がWinningStrategyかSecondStrategyを意識しないでStrategyのメソッドを呼び出して使うことができます。

実行すると以下が出力されます。

# go run main.go 2 1