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.
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.
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();
}
}
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.
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.
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
https://github.com/iluwatar/java-design-patterns http://language-and-engineering.hatenablog.jp/entry/20120330/p1
Recommended Posts