forget for get

覚えるために忘れる

Mementoパターン

状態を保存しておいて、復元できるようにしておく。
undo,redo,history,snapshot

復元するためにインスタンスの内部情報を公開すると、カプセル化の破壊になるので、
インタフェースを使い分ける。

Originator(作成者):Gamer
自分の現在の状態を保存したいときにMementoをつくる。
以前のMementoを渡されると、その状態に戻る。
Memento(記念品):Memento
Originatorの情報を持っているが、誰にでも公開はしない。
広いインタフェース→オブジェクトを元に戻すために内部情報を得られるメソッド。Originatorだけが使える。
狭いインタフェース→Caretakerに見せるもの。
Caretaker(世話をする人):Main
Originatorの状態を保存したいときに、Originatorに伝える。
OriginatorはMementoをCaretakerに渡す。
CaretakerはMementoの狭いインタフェースしか使えないので、Mementoの内部情報をいじることはできない。

Memento.php

class Memento {
    private $money;
    private $fruits = [];
    function __construct($money) {
        $this->money = $money;
    }
    function getMoney() {
        return $this->money;
    }
    function addFruit($fruit) {
        $this->fruits[] = $fruit;
    }
    function getFruits() {
        return $this->fruits;
    }
}

Gamer.php

class Gamer {
    private $money;
    private $fruits = [];
    private $fruitNames = ["夕張メロン","すいか","もも","なし"];
    function __construct($money) {
        $this->money = $money;
    }
    function getMoney() {
        return $this->money;
    }
    function bet() {
        $dice = rand(1, 6);
        if ($dice == 1) {
            $this->money += 100;
            echo "所持金が増えました\n";
        } else if ($dice == 2) {
            $this->money /= 2;
            echo "所持金が半分になりました\n";
        } else if ($dice == 6) {
            $f = $this->getFruit();
            echo "フルーツ(" . $f . ")をもらいました\n";
            $this->fruits[] = $f;
        } else {
            echo "何も起こりませんでした\n";
        }
    }
    function createMemento() {
        $m = new Memento($this->money);
        foreach ($this->fruits as $f) {
            if (strpos($f, "おいしい") !== false) {
                $m->addFruit($f);
            }
        }
        return $m;
    }
    function restoreMemento(Memento $memento) {
        $this->money = $memento->getMoney();
        $this->fruits = $memento->getFruits();
    }
    function toString() {
        return "[money = " . $this->money 
        . ", fruits = " . implode(",", $this->fruits) . "]";
    }
    private function getFruit() {
        $prefix = rand(0, 1) ? "おいしい" : "";
        return $prefix . $this->fruitNames[rand(0, count($this->fruitNames) - 1)];
    }
}

Main.php

require "../autoload.php";
$gamer = new Gamer(100);
$memento = $gamer->createMemento();
for ($i=1;$i<=100;$i++) {
    echo $i . ":" . $gamer->toString() . "\n";
    $gamer->bet();
    echo "所持金:" . $gamer->getMoney() . "\n";
    if ($gamer->getMoney() > $memento->getMoney()) {
        echo " (保存)\n";
        $memento = $gamer->createMemento();
    } else if ($gamer->getMoney() < $memento->getMoney() / 2) {
        echo " (復元)\n";
        $gamer->restoreMemento($memento);
    }
    sleep(1);
}