Mediatorパターンを使って、川渡りパズルを表現した。

最近のフルタイムは、運用改善おじさんです。
デザインパターンを勉強し直しています。

Mediatorパターンの理解を深めるために、
サンプルコードとして、N番煎じかと思いますが…川渡りパズルをコードにしました。

川渡りパズルの解答を割り出すプログラムではなく、
川渡りパズルをシミュレートするプログラムです。

Mediatorについて、認識の誤りがありましたら、
もし良ければ、ご指摘頂けると幸いです。

# 環境
* MacBook Pro (Retina 13-inch、Early 2015)
* OS X El Capitan 10.11.6
* Java 1.8.0_102
* Eclipse Mars.2 Release (4.5.2)

# 川渡りパズル
かの有名なアレです。
こちらの岸から、船を使って、向こうの岸へ渡るアレです。

登場キャラクターは、色々あるかと思いますが、
今回は、農夫、オオカミ、ヤギ、キャベツにしました。

船を漕げるのは農夫のみ。
船に乗れるのは、農夫と、もう1キャラクターのみ。
同じ岸に農夫がいないときに、オオカミ、ヤギを残してしまうと、オオカミはヤギを食べてしまう。
同じ岸に農夫がいないときに、ヤギ、キャベツを残してしまうと、ヤギはキャベツを食べてしまう。

すべてを無事に、向こう岸に渡るには、どうしたらよいのか？ というアレです。

# 前提
Farmer.java, Chabbage.java, Goat.java, Wolf.javaは、
以下を継承しています。

```CharacterBase.java
public abstract class CharacterBase implements Character {
    private Position position;
    private Status status;

    public CharacterBase() {
        this.position = Position.FRONT;
        this.status = Status.ALIVE;
    }

    @Override
    public Status getStatus() {
        return this.status;
    }

    @Override
    public void setStatus(Status status) {
        this.status = status;
    }

    @Override
    public Position getPosition() {
        return this.position;
    }

    @Override
    public void setPosition(Position position) {
        this.position = position;
    }

    @Override
    public void cross() {
        switch (this.getPosition()) {
        case FRONT:
            this.setPosition(Position.BEYOND);

            break;

        case BEYOND:
            this.setPosition(Position.FRONT);

            break;

        default:
            break;
        }
    }

}

```

また、Farmer.javaのみ、
cross(Character withCharacter)メソッドを実装しています。

```Farmer.java
public class Farmer extends CharacterBase {
    public void cross(Character withCharacter) {
        if (withCharacter == null) {
            throw new IllegalArgumentException("一緒に渡るキャラクターを指定して下さい。");
        }

        if (withCharacter instanceof Farmer) {
            throw new IllegalArgumentException("農夫を指定することはできません。");
        }

        if (!this.getPosition().equals(withCharacter.getPosition())) {
            throw new IllegalArgumentException("同じ位置にいるキャラクターのみ、一緒に渡ることができます。");

        }

        this.cross();
        withCharacter.cross();
    }
}

```

# Mediatorがいない場合
各キャラクターを全て知っているクラスが居ないため、
実行クラス内で、逐一状況を確認し、
キャラクターのステータスを更新しなければならなくなってしまいました。

```CrossRiverExecutor.java

public class CrossRiverExecutor {
    private Farmer farmer;
    private Wolf wolf;
    private Goat goat;
    private Cabbage cabbage;

    public CrossRiverExecutor(Farmer farmer, Wolf wolf, Goat goat, Cabbage cabbage) {
        this.farmer = farmer;
        this.wolf = wolf;
        this.goat = goat;
        this.cabbage = cabbage;
    }

    public void execute() {
        farmer.cross(goat);
        // こちらの岸：オオカミ、キャベツ
        // 向こう岸：農夫、ヤギ
        updateStatus();

        farmer.cross();
        // こちらの岸：農夫、オオカミ、キャベツ
        // 向こう岸：ヤギ
        updateStatus();

        farmer.cross(cabbage);
        // こちらの岸：オオカミ
        // 向こう岸：農夫、ヤギ、キャベツ
        updateStatus();

        farmer.cross(goat);
        // こちらの岸：農夫、ヤギ、オオカミ
        // 向こう岸：キャベツ
        updateStatus();

        farmer.cross(wolf);
        // こちらの岸：ヤギ
        // 向こう岸：農夫、オオカミ、キャベツ
        updateStatus();

        farmer.cross();
        // こちらの岸：農夫、ヤギ
        // 向こう岸：オオカミ、キャベツ
        updateStatus();

        farmer.cross(goat);
        // こちらの岸：
        // 向こう岸：農夫、オオカミ、ヤギ、キャベツ
        updateStatus();
    }


    private void updateStatus() {
        if (!farmer.getPosition().equals(goat.getPosition()) && wolf.getPosition().equals(goat.getPosition())) {
            goat.setStatus(Status.DEAD);
        }

        if (!farmer.getPosition().equals(cabbage.getPosition()) && cabbage.getPosition().equals(goat.getPosition())) {
            cabbage.setStatus(Status.DEAD);
        }
    }
}


```

これでも、川渡りパズルを表現できているとは思いますが、
読みにくく、また、updateStatusメソッドをテストしにくい状態になってしまっています。
あまり良いコードの状態とはいえないと思います。

# Mediatorがいる場合
ここで、キャラクター全体を知る、Mediatorを用意します。

```CrossRiverMediator.java

public class CrossRiverMediator implements CrossRiver {
    private Farmer farmer;
    private Wolf wolf;
    private Goat goat;
    private Cabbage cabbage;

    public CrossRiverMediator(Farmer farmer, Wolf wolf, Goat goat, Cabbage cabbage) {
        this.farmer = farmer;
        this.wolf = wolf;
        this.goat = goat;
        this.cabbage = cabbage;
    }

    @Override
    public void cross(Character character) {

        switch (character.getPosition()) {
        case FRONT:
            character.setPosition(Position.BEYOND);

            break;

        case BEYOND:
            character.setPosition(Position.FRONT);

            break;

        default:
            break;
        }
    }

    @Override
    public void cross(Farmer character, Character withCharacter) {
        if (withCharacter == null) {
            throw new IllegalArgumentException("一緒に渡るキャラクターを指定して下さい。");
        }

        if (withCharacter instanceof Farmer) {
            throw new IllegalArgumentException("農夫を指定することはできません。");
        }

        if (!character.getPosition().equals(withCharacter.getPosition())) {
            throw new IllegalArgumentException("同じ位置にいるキャラクターのみ、一緒に渡ることができます。");

        }

        character.cross();
        withCharacter.cross();
        updateStatus();
    }

    private void updateStatus() {
        if (!farmer.getPosition().equals(goat.getPosition()) && wolf.getPosition().equals(goat.getPosition())) {
            goat.setStatus(Status.DEAD);
        }

        if (!farmer.getPosition().equals(cabbage.getPosition()) && cabbage.getPosition().equals(goat.getPosition())) {
            cabbage.setStatus(Status.DEAD);
        }

    }
}


```

キャラクター全員を知っているため、
各キャラクターの生死を更新する、updateStatusメソッドを実行することができます。

また、それに伴い、
各キャラクターの親クラスである、CharacterBase.javaと、
Farmer.javaかのcross()メソッド、cross(Character)メソッドを、
Mediator経由に実行するように変更します。

```CharacterBase.java

public abstract class CharacterBase implements Character {
    protected CrossRiver crossRiver;
    private Position position;
    private Status status;

    public CharacterBase() {
        this.position = Position.FRONT;
        this.status = Status.ALIVE;
    }

    @Override
    public void setMediator(CrossRiver crossRiver) {
        this.crossRiver = crossRiver;
    }

    @Override
    public Status getStatus() {
        return this.status;
    }

    @Override
    public void setStatus(Status status) {
        this.status = status;
    }

    @Override
    public Position getPosition() {
        return this.position;
    }

    @Override
    public void setPosition(Position position) {
        this.position = position;
    }

    @Override
    public void cross() {
        this.crossRiver.cross(this);
    }
}

```

```Farmer.java

public class Farmer extends CharacterBase {
    public void cross(Character withCharacter) {
        this.crossRiver.cross(this, withCharacter);
    }
}
```

そうすると、実行クラスは、
以下のようになります。

```CrossRiverExecutor.java

public class CrossRiverExecutor {
    private Farmer farmer;
    private Wolf wolf;
    private Goat goat;
    private Cabbage cabbage;
    private CrossRiverMediator crossRiver;

    public CrossRiverExecutor() {
        farmer = new Farmer();
        wolf = new Wolf();
        goat = new Goat();
        cabbage = new Cabbage();

        crossRiver = new CrossRiverMediator(farmer, wolf, goat, cabbage);
        farmer.setMediator(crossRiver);
        wolf.setMediator(crossRiver);
        goat.setMediator(crossRiver);
        cabbage.setMediator(crossRiver);
    }

    public void execute() {
        farmer.cross(goat);     // こちらの岸：オオカミ、キャベツ      向こう岸：農夫、ヤギ
        farmer.cross();         // こちらの岸：農夫、オオカミ、キャベツ 向こう岸：ヤギ
        farmer.cross(cabbage);  // こちらの岸：オオカミ              向こう岸：農夫、ヤギ、キャベツ
        farmer.cross(goat);     // こちらの岸：農夫、ヤギ、オオカミ    向こう岸：キャベツ
        farmer.cross(wolf);     // こちらの岸：ヤギ                 向こう岸：農夫、オオカミ、キャベツ
        farmer.cross();         // こちらの岸：農夫、ヤギ            向こう岸：オオカミ、キャベツ
        farmer.cross(goat);     // こちらの岸：                    向こう岸：農夫、オオカミ、ヤギ、キャベツ
    }
}

```

よって、実行クラスからロジックを切り離すことができ、
ロジックをテストする際は、Mediatorをテストすればよい状態になり、
比較的マシなコードの状態になったのではと思います。

# 結果
川渡りパズルをプログラムにすることができました。
https://github.com/naokiur/design-pattern-sample/tree/master/src/main/java/jp/ne/naokiur/design/pattern/mediator

# 参考にさせて頂きました
https://github.com/iluwatar/java-design-patterns
http://language-and-engineering.hatenablog.jp/entry/20120330/p1