デザインパターン（Design Pattern）#Strategy

設計を意識したコードが書けるようになる為に、デザインパターン修行しました。
他のDesign Patternもちょくちょく出していきます。

## 前置き
* [増補改訂版Java言語で学ぶデザインパターン入門](http://www.hyuki.com/dp/)をJavaからPythonにしてます。（Pythonは3.4.2）
* [githubにコード置いてあります（まだ動かないものもある）](https://github.com/Nirperm/Design-Pattern)

デザインパターンをどういう時に、何を、どう使うのかを理解することが一先ずの目標。
（Javaというか静的型付言語は初めてで、且つpython歴もそんなに長くないので、Pythonistaぽっくないところがあると思います。ご指摘ございましたらご教授ください。）

今回は、振る舞いに関するパターンStrategy。

## Strategyとは

アルゴリズムを実装した部分がごっそりと交換できるようになっています。アルゴリズムを切り替え、同じ問題を別の方法で解くのを容易にするパターン。

それは、Strategyパターンでは、アルゴリズムの部分を他の部分と意識的に分離しているので。そしてアルゴリズムとのインタフェース(API)の部分だけを規定します。そして、プログラムから委譲によってアルゴリズムを利用する。委譲というゆるやかな結びつきを使うことによって、アルゴリズムを容易に切り替えることができます。

多くの振る舞いが 複数の条件文として現れている場合にStrategyパターンが使えるでしょう。

## 概要
ここで作るサンプルプログラムは、コンピュータで「じゃんけん」を行うものです。
じゃんけんの「戦略」として、2つの方法を考えました。1つは「勝ったら次も同じ手を出す」という方法（WinningStrategy）、もう1つは「1回前の手から次の手を確率的に計算する」という方法（ProbStrategy）です。

## 全体のクラス図
![](https://cacoo.com/diagrams/B5k3skDe33hODh87-CB2E5.png?t=1432683276423)

```hand.py
class Hand():
    HANDVALUE_ROCK = 0
    HANDVALUE_SCISSORS = 1
    HANDVALUE_PAPER = 2
    NAMES = ['グー', 'チョキ', 'パー']
    HANDS = [HANDVALUE_ROCK,
             HANDVALUE_SCISSORS,
             HANDVALUE_PAPER]

    def __init__(self, handvalue):
        self.__handvalue = handvalue

    def get_hand(self, handvalue):
        return self.HANDS[self.__handvalue]

    def is_stronger_than(self, h):
        return self.__fight(h) == 1

    def is_weaker_than(self, h):
        return self.__fight(h) == -1

    def __fight(self, h):
        if self.__handvalue == h.__handvalue:
            return 0
        elif (self.__handvalue + 1) % 3 == h.__handvalue:
            return 1
        else:
            return -1

    def to_string(self):
        return self.NAMES[self.__handvalue]
```
Handクラスはじゃんけんの「手」を表すクラスです。クラスの内部ではグーは0、チョキは1、パーは2という表現をしています。これを手の値を表すフィールド（handvalue）で保存します。

Handクラスのインスタンスは3つしか作られません。最初に3つのインスタンスが作られ、配列のHANDSに保存されています。

クラスメソッドget_handを使って、インスタンスを得ることが出来ます。手の値を引数で与えると、インスタンスが戻り値となります。

is_stronger_thanとis_weaker_thanは手の強さを比較するものです。2つの手hand1とhand2がある時に使います。

このクラスの内部で実際に手の強さを判定しているのは、fightというメソッドです。強さの判定では手の値を使っています。ここで使われている`(self.__handvalue + 1) % 3 == h.__handvalue` はselfの手の値に1を加えたものがhの手の値（selfがグーならhはチョキ、selfがチョキならhはパー、selfがパーならhはグー）になっているなら、selfはhより強いということになります。演算子%を使って3の剰余を取ってるのは、パー（2）に1を加えたときに、グー（0）になって欲しいからです。
<br>

```strategy.py
from abc import ABCMeta, abstractmethod


class Strategy(metaclass=ABCMeta):

    @abstractmethod
    def next_hand():
        pass

    @abstractmethod
    def study(win):
        pass
```
Strategyインターフェースは、じゃんけんの「戦略」のための抽象メソッドを集めたものです。

next_handは「次に出す手を得る」ためのメソッドです。このメソッドが呼ばれると、Strategyインタフェースを実装するクラスは「次の一手」を決めます。

studyは、「さっき出した手によって勝ったかどうか」を学習するためのメソッドです。直前のnext_handメソッド呼び出しで勝った場合には、study(True)として呼び出します。負けた場合には、study(False)として呼び出します。これらによって、Strategyインタフェースを実装するクラスは、自分の内部状態を変化させ、次回以降のnext_handメソッドの戻り値を決定する材料とするのです。
<br>

```winning_strategy.py
import random
from hand import Hand
from strategy import Strategy


class WinningStrategy(Strategy):

    __won = False
    __prev_hand = 0

    def __init__(self, seed):
        self.__rand = seed

    def next_hand(self):
        if not(self.__won):
            self.__prev_hand = Hand(self.__rand).get_hand(random.randint(0, 3))
        return self.__prev_hand

    def study(self, win):
        self.__won = win
```
WinningStrategyクラスは、Strategyインタフェースを実装するクラスの1つです。Strategyインタフェースを実装しているということは、next_handとstudyという2つのメソッドを実装していることになります。

このクラスは、前回の勝負に勝ったならば、次も同じ手（グーならグー、パーならパー）を出す戦略をとります。もしも前回の勝負に負けたらなば、次の手は乱数を使って決定します。

randフィールドは、このクラスが乱数を必要とするときに使う乱数を保持します。

wonフィールドは、前回勝負の結果を保持します。勝ったならTrue、負けたならFalseになります。

prev_handフィールドは、前回の勝負で出した手を保持します。

```prob_strategy.py
import random
from hand import Hand
from strategy import Strategy


class ProbStrategy(Strategy):

    __prev_hand_value = 0
    __current_hand_value = 0
    __history = [[1, 1, 1], [1, 1, 1], [1, 1, 1]]

    def __init__(self, seed):
        self.__rand = seed

    def next_hand(self):
        bet = random.randint(0, self.__get_sum(self.__current_hand_value))
        hand_value = 0
        if bet < self.__history[self.__current_hand_value][0]:
            hand_value = 0
        elif bet < self.__history[self.__current_hand_value][0] + \
                self.__history[self.__current_hand_value][1]:
            hand_value = 1
        else:
            hand_value = 2

        self.__prev_hand_value = self.__current_hand_value
        self.__current_hand_value = hand_value

        return Hand(hand_value).get_hand(hand_value)

    def __get_sum(self, hv):
        total = 0
        for i in range(0, 3):
            total += self.__history[hv][i]
        return total

    def study(self, win):
        if win:
            self.__history[self.__prev_hand_value][self.__current_hand_value] \
                += 1
        else:
            self.__history[self.__prev_hand_value][(self.__current_hand_value + 1) % 3] \
                += 1
            self.__history[self.__prev_hand_value][(self.__current_hand_value + 2) % 3] \
                += 1
```
ProbStrategyクラスは、もう1つの具体的な「戦略」です。次の手はいつも乱数で決定するのですが、過去の勝ち負けの履歴を使って、それぞれの手を出す確立を変えています。

historyフィールドが、過去の勝敗を反映した確率計算のための表になっています。
historyはintegerの2次元配列で、各次元の添え字は次のような意味を持ちます。

history[前回に出した手][今回出す手]
この式の値が大きければ大きいほど、過去の勝率が高い、ということになります。

詳しく書くと。
history[0][0]  グー、グーと自分が出したときの過去の勝ち数
history[0][1]　グー、チョキと自分が出したときの過去の勝ち数
history[0][2]　グー、パーと自分が出したときの過去の勝ち数

前回、自分がグーを出したとします。そのとき、次に自分が何を出すかを、上記の`histroy[0][0]、histroy[0][1]、histroy[0][2]`の値から確率で計算してます。要するに、この3つの式の値を足し（get_sumメソッド）、0からその数を計算し、そしてそれを元に次の手を決めます（next_handメソッド）

例えば、
history[0][0]の値が3
history[0][1]の値が5
history[0][2]の値が7
の場合。この時、グー、チョキ、パーを出す割合を3:5:7として次の手を決めます。0以上15未満（15は3+5+7の値）の乱数値を得て、
0以上3未満ならグー
3以上8未満ならチョキ
8以上15未満ならパー
とします。

studyメソッドは、next_handメソッドで返した手の勝敗を元に、historyフィールドの内容を更新します。
<br>

```player.py
class Player():

    __wincount = 0
    __losecount = 0
    __gamecount = 0

    def __init__(self, name, strategy):
        self.__name = name
        self.__strategy = strategy

    def next_hand(self):
        return self.__strategy.next_hand()

    def win(self):
        self.__strategy.study(True)
        self.__wincount += 1
        self.__gamecount += 1

    def lose(self):
        self.__strategy.study(False)
        self.__losecount += 1
        self.__gamecount += 1

    def even(self):
        self.__gamecount += 1

    def to_stirng(self):
        return '[{0}: {1} games {2} win {3} lose]'.format(self.__name,
                                                          self.__gamecount,
                                                          self.__wincount,
                                                          self.__losecount)
```
Playerクラスは、じゃんけんを行う人を表すクラスです。
Playerクラスは「名前」と「戦略」を与えられてインスタンスを作ります。
next_handメソッドは次の手を得るためのものですが、実際に次の手を決定するのは自分の「戦略｣です。戦略のnext_handメソッドの戻り値が、そのままPlayerのnext_handメソッドの戻り値となります。next_handメソッドは、自分の行うべき処理をStrategyに「委譲」しています。

勝ったり（win）、負けたり（lose）、引き分けたり（even）した勝負の結果を次回の勝負に生かすために、Playerクラスは、startegyフィールドを通してstudyメソッドを呼び出します。studyメソッドを使って戦略の内部状態を変化させるます。wincount、losecount、geamecountは、プレイヤーの勝ち数を記録するものです。


```main.py
import random
import sys
from winning_strategy import WinningStrategy
from prob_strategy import ProbStrategy
from player import Player
from hand import Hand


def main():
    try:
        if int(sys.argv[1]) >= 3:
            seed1 = random.randint(0, 2)
        else:
            seed1 = int(sys.argv[1])

        if int(sys.argv[2]) >= 3:
            seed2 = random.randint(0, 2)
        else:
            seed2 = int(sys.argv[2])

        player1 = Player('Taro', WinningStrategy(seed1))
        player2 = Player('Hana', ProbStrategy(seed2))
        for i in range(0, 10):  # 10000
            next_hand1 = Hand(player1.next_hand())
            next_hand2 = Hand(player2.next_hand())
            if next_hand1.is_stronger_than(next_hand2):
                print('Winner : {0}'.format(player1.to_stirng()))
                player1.win()
                player2.lose()
            elif next_hand2.is_stronger_than(next_hand1):
                print('Winner : {0}'.format(player2.to_stirng()))
                player1.lose()
                player2.win()
            else:
                print('Even ...')
                player1.even()
                player2.even()

        print('Total result:')
        print(player1.to_stirng())
        print(player2.to_stirng())

    except IndexError:
        print('Check args size, does not work')
        print('usage: python main random_seed1 random_seed2')
        print('Example: python main.py 314 15')

if __name__ == "__main__":
    main()
```

実行結果

```
python main.py 21  3
Winner : [Hana: 0 games 0 win 0 lose]
Even ...
Winner : [Hana: 2 games 1 win 0 lose]
Winner : [Taro: 3 games 0 win 2 lose]
Even ...
Winner : [Taro: 5 games 1 win 2 lose]
Even ...
Winner : [Hana: 7 games 2 win 2 lose]
Winner : [Taro: 8 games 2 win 3 lose]
Winner : [Hana: 9 games 3 win 3 lose]
Total result:
[Taro: 10 games 3 win 4 lose]
[Hana: 10 games 4 win 3 lose]

```

## まとめ
Strategyパターンは、アルゴリズムの部分を他の部分と意識的に分離します。委譲というゆるやかな結びつきを使っているので、インタフェース部分を変更しないようにすれば、アルゴリズムの切り替えが簡単にできます。


## 参考
* [サルでもわかる 逆引きデザインパターン](http://www.nulab.co.jp/designPatterns/designPatterns1/designPatterns1-2.html)
* [TECHSCOREデザインパターン](http://www.techscore.com/tech/DesignPattern/foundation/foundation1.html/)
* [Wikipedia Software design pattern]([http://en.wikipedia.org/wiki/Software_design_pattern)
* [Wikipedia デザインパターン](http://goo.gl/lRi5)
* [Ruby 2.0.0で学ぶ、14個のデザインパターンを作りました[GoF][Design Pattern]](http://morizyun.github.io/blog/ruby-design-pattern-matome-mokuzi/)
* [Rubyによるデザインパターン5原則](http://qiita.com/kidach1/items/4b63de9ad5a97726c50c)