[JAVA] I used the Mediator pattern to express a river crossing puzzle.

My recent full-time job is Uncle Operation Improvement. I am re-studying design patterns.

To better understand the Mediator pattern As a sample code, I think it's the Nth decoction, but ... I used the river crossing puzzle as the code.

It's not a program that finds the answer to a river crossing puzzle It is a program that simulates a river crossing puzzle.

If you make a mistake in recognizing Mediator, If you like, I would appreciate it if you could point it out.

environment

River crossing puzzle

That is the famous one. It is the one that crosses from this shore to the other shore by boat.

I think there are various characters that appear, This time, I chose farmers, wolves, goats, and cabbage.

Only farmers can row a boat. Only the farmer and another character can board the ship. If a wolf or goat is left behind when there is no farmer on the same bank, the wolf will eat the goat. If you leave the goat and cabbage when there is no farmer on the same bank, the goat will eat the cabbage.

How can I safely cross the other side of the river? That is.

Premise

Farmer.java, Chabbage.java, Goat.java, Wolf.java, It inherits the following:

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;
        }
    }

}

Also, Farmer.java only, Implements the cross (Character with Character) method.

Farmer.java


public class Farmer extends CharacterBase {
    public void cross(Character withCharacter) {
        if (withCharacter == null) {
            throw new IllegalArgumentException("Please specify the character to cross with.");
        }

        if (withCharacter instanceof Farmer) {
            throw new IllegalArgumentException("You cannot specify a farmer.");
        }

        if (!this.getPosition().equals(withCharacter.getPosition())) {
            throw new IllegalArgumentException("Only characters in the same position can cross together.");

        }

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

Without Mediator

Because there is no class that knows all the characters Check the status one by one in the execution class, I have to update my character's stats.

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);
        //This shore: wolf, cabbage
        //The other side: farmer, goat
        updateStatus();

        farmer.cross();
        //Kishi: Farmer, wolf, cabbage
        //The other side: Goat
        updateStatus();

        farmer.cross(cabbage);
        //This shore: wolf
        //The other side: farmer, goat, cabbage
        updateStatus();

        farmer.cross(goat);
        //This shore: farmers, goats, wolves
        //The other side: cabbage
        updateStatus();

        farmer.cross(wolf);
        //This shore: goat
        //The other side: farmers, wolves, cabbage
        updateStatus();

        farmer.cross();
        //This shore: farmer, goat
        //The other side: wolf, cabbage
        updateStatus();

        farmer.cross(goat);
        //This shore:
        //The other side: farmers, wolves, goats, cabbage
        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);
        }
    }
}


I think you can still express the river crossing puzzle, It's hard to read and it's hard to test the updateStatus method. I don't think the code is in very good condition.

If you have a Mediator

Here, prepare a Mediator that knows the entire character.

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("Please specify the character to cross with.");
        }

        if (withCharacter instanceof Farmer) {
            throw new IllegalArgumentException("You cannot specify a farmer.");
        }

        if (!character.getPosition().equals(withCharacter.getPosition())) {
            throw new IllegalArgumentException("Only characters in the same position can cross together.");

        }

        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);
        }

    }
}


Because I know all the characters You can execute the updateStatus method to update the life and death of each character.

Also, along with that CharacterBase.java, the parent class of each character, Cross () method, cross (Character) method of Farmer.java, Change it to run via 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);
    }
}

Then the execution class will It will be as follows.

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);     //This shore: wolf, cabbage The other shore: farmer, goat
        farmer.cross();         //This shore: farmer, wolf, cabbage Other shore: goat
        farmer.cross(cabbage);  //This shore: wolf The other shore: farmer, goat, cabbage
        farmer.cross(goat);     //This shore: farmer, goat, wolf Other shore: cabbage
        farmer.cross(wolf);     //This shore: Goat The other shore: Farmer, wolf, cabbage
        farmer.cross();         //This shore: farmer, goat The other shore: wolf, cabbage
        farmer.cross(goat);     //This shore: The other shore: Farmers, wolves, goats, cabbage
    }
}

Therefore, the logic can be separated from the execution class, When testing the logic, you just need to test the Mediator, I think it's a relatively good code.

result

I was able to program a river crossing puzzle. https://github.com/naokiur/design-pattern-sample/tree/master/src/main/java/jp/ne/naokiur/design/pattern/mediator

I used it as a reference

https://github.com/iluwatar/java-design-patterns http://language-and-engineering.hatenablog.jp/entry/20120330/p1

Recommended Posts

I used the Mediator pattern to express a river crossing puzzle.
I tried to summarize the methods used
I tried to implement the Iterator pattern
The code I used to connect Rails 3 to PostgreSQL 10
I tried to decorate the simple calendar a little
I want to add a delete function to the comment function
I want to call a method and count the number
I want to give a class name to the select attribute
I tried to express the result of before and after of Date class with a number line
I tried to illuminate the Christmas tree in a life game
I made a gem to post the text of org-mode to qiita
[Java] I tried to make a maze by the digging method ♪
I made a tool to output the difference of CSV file
I tried to explain the method
The story I wanted to unzip
[Rails] I learned about migration files! (Adding a column to the table)
A story I was addicted to when testing the API using MockMVC
I can't build if I set the build destination to a simulator with XCode12!
It was a life I wanted to reset the thread-safe associative counter
I used Docker to solidify the template to be developed with spring boot.
I thought about the best way to create a ValueObject in Ruby