This commit is contained in:
Ilkka Seppälä 2019-11-16 14:48:43 +02:00
commit 26f4aa8001
99 changed files with 1599 additions and 453 deletions

41
game-loop/README.md Normal file
View File

@ -0,0 +1,41 @@
---
layout: pattern
title: Game Loop
folder: game-loop
permalink: /patterns/game-loop/
categories: Other
tags:
- Java
- Difficulty-Beginner
---
## Intent
A game loop runs continuously during gameplay. Each turn of the loop, it processes user input without blocking, updates the game state, and renders the game. It tracks the passage of time to control the rate of gameplay.
This pattern decouple the progression of game time from user input and processor speed.
## Applicability
This pattern is used in every game engine.
## Explanation
Game loop is the main process of all the game rendering threads. It drives input process, internal status update, rendering, AI and all the other processes.
There are a lot of implementations of game loop:
- Frame-based game loop
Frame-based game loop is the easiest implementation. The loop always keeps spinning for the following three processes: processInput, update and render. The problem with it is you have no control over how fast the game runs. On a fast machine, that loop will spin so fast users wont be able to see whats going on. On a slow machine, the game will crawl. If you have a part of the game thats content-heavy or does more AI or physics, the game will actually play slower there.
- Variable-step game loop
The variable-step game loop chooses a time step to advance based on how much real time passed since the last frame. The longer the frame takes, the bigger steps the game takes. It always keeps up with real time because it will take bigger and bigger steps to get there.
- Fixed-step game loop
For fixed-step game loop, a certain amount of real time has elapsed since the last turn of the game loop. This is how much game time need to be simulated for the games “now” to catch up with the players.
## Credits
* [Game Programming Patterns - Game Loop](http://gameprogrammingpatterns.com/game-loop.html)

45
game-loop/pom.xml Normal file
View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
Copyright © 2014-2019 Ilkka Seppälä
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.22.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>game-loop</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,76 @@
/*
* The MIT License
* Copyright © 2014-2019 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.gameloop;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A game loop runs continuously during gameplay. Each turn of the loop, it processes
* user input without blocking, updates the game state, and renders the game. It tracks
* the passage of time to control the rate of gameplay.
*/
public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
/**
* Each type of game loop will run for 2 seconds.
*/
private static final int GAME_LOOP_DURATION_TIME = 2000;
/**
* Program entry point.
* @param args runtime arguments
*/
public static void main(String[] args) {
try {
LOGGER.info("Start frame-based game loop:");
var frameBasedGameLoop = new FrameBasedGameLoop();
frameBasedGameLoop.run();
Thread.sleep(GAME_LOOP_DURATION_TIME);
frameBasedGameLoop.stop();
LOGGER.info("Stop frame-based game loop.");
LOGGER.info("Start variable-step game loop:");
var variableStepGameLoop = new VariableStepGameLoop();
variableStepGameLoop.run();
Thread.sleep(GAME_LOOP_DURATION_TIME);
variableStepGameLoop.stop();
LOGGER.info("Stop variable-step game loop.");
LOGGER.info("Start fixed-step game loop:");
var fixedStepGameLoop = new FixedStepGameLoop();
fixedStepGameLoop.run();
Thread.sleep(GAME_LOOP_DURATION_TIME);
fixedStepGameLoop.stop();
LOGGER.info("Stop variable-step game loop.");
} catch (InterruptedException e) {
LOGGER.error(e.getMessage());
}
}
}

View File

@ -0,0 +1,44 @@
/*
* The MIT License
* Copyright © 2014-2019 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.gameloop;
/**
* Bullet object class.
*/
public class Bullet {
private float position;
public Bullet() {
position = 0.0f;
}
public float getPosition() {
return position;
}
public void setPosition(float position) {
this.position = position;
}
}

View File

@ -0,0 +1,62 @@
/*
* The MIT License
* Copyright © 2014-2019 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.gameloop;
/**
* For fixed-step game loop, a certain amount of real time has elapsed since the
* last turn of the game loop. This is how much game time need to be simulated for
* the games now to catch up with the players.
*/
public class FixedStepGameLoop extends GameLoop {
/**
* 20 ms per frame = 50 FPS.
*/
private static final long MS_PER_FRAME = 20;
@Override
protected void processGameLoop() {
var previousTime = System.currentTimeMillis();
var lag = 0L;
while (isGameRunning()) {
var currentTime = System.currentTimeMillis();
var elapsedTime = currentTime - previousTime;
previousTime = currentTime;
lag += elapsedTime;
processInput();
while (lag >= MS_PER_FRAME) {
update();
lag -= MS_PER_FRAME;
}
render();
}
}
protected void update() {
controller.moveBullet(0.5f * MS_PER_FRAME / 1000);
}
}

View File

@ -0,0 +1,53 @@
/*
* The MIT License
* Copyright © 2014-2019 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.gameloop;
/**
* Frame-based game loop is the easiest implementation. The loop always keeps spinning
* for the following three processes: processInput, update and render. The problem with
* it is you have no control over how fast the game runs. On a fast machine, that loop
* will spin so fast users wont be able to see whats going on. On a slow machine, the
* game will crawl. If you have a part of the game thats content-heavy or does more AI
* or physics, the game will actually play slower there.
*/
public class FrameBasedGameLoop extends GameLoop {
@Override
protected void processGameLoop() {
while (isGameRunning()) {
processInput();
update();
render();
}
}
/**
* Each time when update() is invoked, a new frame is created, and the bullet will be
* moved 0.5f away from the current position.
*/
protected void update() {
controller.moveBullet(0.5f);
}
}

View File

@ -0,0 +1,61 @@
/*
* The MIT License
* Copyright © 2014-2019 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.gameloop;
/**
* Update and render objects in the game. Here we add a Bullet object to the
* game system to show how the game loop works.
*/
public class GameController {
protected final Bullet bullet;
/**
* Initialize Bullet instance.
*/
public GameController() {
bullet = new Bullet();
}
/**
* Move bullet position by the provided offset.
*
* @param offset moving offset
*/
public void moveBullet(float offset) {
var currentPosition = bullet.getPosition();
bullet.setPosition(currentPosition + offset);
}
/**
* Get current position of the bullet.
*
* @return position of bullet
*/
public float getBulletPosition() {
return bullet.getPosition();
}
}

View File

@ -0,0 +1,104 @@
/*
* The MIT License
* Copyright © 2014-2019 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.gameloop;
import java.util.Random;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Abstract class for GameLoop implementation class.
*/
public abstract class GameLoop {
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
protected volatile GameStatus status;
protected GameController controller;
private Thread gameThread;
/**
* Initialize game status to be stopped.
*/
public GameLoop() {
controller = new GameController();
status = GameStatus.STOPPED;
}
/**
* Run game loop.
*/
public void run() {
status = GameStatus.RUNNING;
gameThread = new Thread(() -> processGameLoop());
gameThread.start();
}
/**
* Stop game loop.
*/
public void stop() {
status = GameStatus.STOPPED;
}
/**
* Check if game is running or not.
*
* @return {@code true} if the game is running.
*/
public boolean isGameRunning() {
return status == GameStatus.RUNNING ? true : false;
}
/**
* Handle any user input that has happened since the last call. In order to
* simulate the situation in real-life game, here we add a random time lag.
* The time lag ranges from 50 ms to 250 ms.
*/
protected void processInput() {
try {
int lag = new Random().nextInt(200) + 50;
Thread.sleep(lag);
} catch (InterruptedException e) {
logger.error(e.getMessage());
}
}
/**
* Render game frames to screen. Here we print bullet position to simulate
* this process.
*/
protected void render() {
var position = controller.getBulletPosition();
logger.info("Current bullet position: " + position);
}
/**
* execute game loop logic.
*/
protected abstract void processGameLoop();
}

View File

@ -0,0 +1,33 @@
/*
* The MIT License
* Copyright © 2014-2019 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.gameloop;
/**
* Enum class for game status.
*/
public enum GameStatus {
RUNNING, STOPPED
}

View File

@ -0,0 +1,51 @@
/*
* The MIT License
* Copyright © 2014-2019 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.gameloop;
/**
* The variable-step game loop chooses a time step to advance based on how much
* real time passed since the last frame. The longer the frame takes, the bigger
* steps the game takes. It always keeps up with real time because it will take
* bigger and bigger steps to get there.
*/
public class VariableStepGameLoop extends GameLoop {
@Override
protected void processGameLoop() {
var lastFrameTime = System.currentTimeMillis();
while (isGameRunning()) {
processInput();
var currentFrameTime = System.currentTimeMillis();
var elapsedTime = currentFrameTime - lastFrameTime;
update(elapsedTime);
lastFrameTime = currentFrameTime;
render();
}
}
protected void update(Long elapsedTime) {
controller.moveBullet(0.5f * elapsedTime / 1000);
}
}

View File

@ -0,0 +1,39 @@
/*
* The MIT License
* Copyright © 2014-2019 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.gameloop;
import org.junit.Test;
/**
* App unit test class.
*/
public class AppTest {
@Test
public void testMain() {
String[] args = {};
new App().main(args);
}
}

View File

@ -0,0 +1,54 @@
/*
* The MIT License
* Copyright © 2014-2019 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.gameloop;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* FixedStepGameLoop unit test class.
*/
public class FixedStepGameLoopTest {
private FixedStepGameLoop gameLoop;
@Before
public void setup() {
gameLoop = new FixedStepGameLoop();
}
@After
public void tearDown() {
gameLoop = null;
}
@Test
public void testUpdate() {
gameLoop.update();
Assert.assertEquals(0.01f, gameLoop.controller.getBulletPosition(), 0);
}
}

View File

@ -0,0 +1,53 @@
/*
* The MIT License
* Copyright © 2014-2019 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.gameloop;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* FrameBasedGameLoop unit test class.
*/
public class FrameBasedGameLoopTest {
private FrameBasedGameLoop gameLoop;
@Before
public void setup() {
gameLoop = new FrameBasedGameLoop();
}
@After
public void tearDown() {
gameLoop = null;
}
@Test
public void testUpdate() {
gameLoop.update();
Assert.assertEquals(0.5f, gameLoop.controller.getBulletPosition(), 0);
}
}

View File

@ -0,0 +1,56 @@
/*
* The MIT License
* Copyright © 2014-2019 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.gameloop;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class GameControllerTest {
private GameController controller;
@Before
public void setup() {
controller = new GameController();
}
@After
public void tearDown() {
controller = null;
}
@Test
public void testMoveBullet() {
controller.moveBullet(1.5f);
Assert.assertEquals(1.5f, controller.bullet.getPosition(), 0);
}
@Test
public void testGetBulletPosition() {
Assert.assertEquals(controller.bullet.getPosition(), controller.getBulletPosition(), 0);
}
}

View File

@ -0,0 +1,71 @@
/*
* The MIT License
* Copyright © 2014-2019 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.gameloop;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* GameLoop unit test class.
*/
public class GameLoopTest {
private GameLoop gameLoop;
/**
* Create mock implementation of GameLoop.
*/
@Before
public void setup() {
gameLoop = new GameLoop() {
@Override
protected void processGameLoop() {}
};
}
@After
public void tearDown() {
gameLoop = null;
}
@Test
public void testRun() {
gameLoop.run();
Assert.assertEquals(GameStatus.RUNNING, gameLoop.status);
}
@Test
public void testStop() {
gameLoop.stop();
Assert.assertEquals(GameStatus.STOPPED, gameLoop.status);
}
@Test
public void testIsGameRunning() {
Assert.assertEquals(false, gameLoop.isGameRunning());
}
}

View File

@ -0,0 +1,54 @@
/*
* The MIT License
* Copyright © 2014-2019 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.gameloop;
import java.lang.reflect.InvocationTargetException;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* VariableStepGameLoop unit test class.
*/
public class VariableStepGameLoopTest {
private VariableStepGameLoop gameLoop;
@Before
public void setup() {
gameLoop = new VariableStepGameLoop();
}
@After
public void tearDown() {
gameLoop = null;
}
@Test
public void testUpdate() {
gameLoop.update(20L);
Assert.assertEquals(0.01f, gameLoop.controller.getBulletPosition(), 0);
}
}

View File

@ -25,27 +25,23 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* Created by Alexis on 28-Apr-17. * Created by Alexis on 28-Apr-17. With Marker interface idea is to make empty interface and extend
* With Marker interface idea is to make empty interface and extend it. * it. Basically it is just to identify the special objects from normal objects. Like in case of
* Basically it is just to identify the special objects from normal objects. * serialization , objects that need to be serialized must implement serializable interface (it is
* Like in case of serialization , objects that need to be serialized must implement serializable interface * empty interface) and down the line writeObject() method must be checking if it is a instance of
* (it is empty interface) and down the line writeObject() method must be checking * serializable or not.
* if it is a instance of serializable or not. *
* <p> * <p>Marker interface vs annotation Marker interfaces and marker annotations both have their uses,
* Marker interface vs annotation * neither of them is obsolete or always better then the other one. If you want to define a type
* Marker interfaces and marker annotations both have their uses, * that does not have any new methods associated with it, a marker interface is the way to go. If
* neither of them is obsolete or always better then the other one. * you want to mark program elements other than classes and interfaces, to allow for the possibility
* If you want to define a type that does not have any new methods associated with it, * of adding more information to the marker in the future, or to fit the marker into a framework
* a marker interface is the way to go. * that already makes heavy use of annotation types, then a marker annotation is the correct choice
* If you want to mark program elements other than classes and interfaces,
* to allow for the possibility of adding more information to the marker in the future,
* or to fit the marker into a framework that already makes heavy use of annotation types,
* then a marker annotation is the correct choice
*/ */
public class App { public class App {
/** /**
* Program entry point * Program entry point.
* *
* @param args command line args * @param args command line args
*/ */

View File

@ -25,7 +25,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* Class defining Guard * Class defining Guard.
*/ */
public class Guard implements Permission { public class Guard implements Permission {

View File

@ -22,8 +22,7 @@
*/ */
/** /**
* Interface without any methods * Interface without any methods Marker interface is based on that assumption.
* Marker interface is based on that assumption
*/ */
public interface Permission { public interface Permission {
} }

View File

@ -25,7 +25,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* Class defining Thief * Class defining Thief.
*/ */
public class Thief { public class Thief {

View File

@ -28,30 +28,37 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* <p>The <b><em>Master-Worker</em></b> pattern is used when the problem at hand can be solved by dividing into * <p>The <b><em>Master-Worker</em></b> pattern is used when the problem at hand can be solved by
* multiple parts which need to go through the same computation and may need to be aggregated to get final result. * dividing into
* Parallel processing is performed using a system consisting of a master and some number of workers, where a * multiple parts which need to go through the same computation and may need to be aggregated to get
* master divides the work among the workers, gets the result back from them and assimilates all the results to * final result. Parallel processing is performed using a system consisting of a master and some
* give final result. The only communication is between the master and the worker - none of the workers communicate * number of workers, where a master divides the work among the workers, gets the result back from
* among one another and the user only communicates with the master to get required job done.</p> * them and assimilates all the results to give final result. The only communication is between the
* <p>In our example, we have generic abstract classes {@link MasterWorker}, {@link Master} and {@link Worker} which * master and the worker - none of the workers communicate among one another and the user only
* have to be extended by the classes which will perform the specific job at hand (in this case finding transpose of * communicates with the master to get required job done.</p>
* matrix, done by {@link ArrayTransposeMasterWorker}, {@link ArrayTransposeMaster} and {@link ArrayTransposeWorker}). * <p>In our example, we have generic abstract classes {@link MasterWorker}, {@link Master} and
* The Master class divides the work into parts to be given to the workers, collects the results from the workers and * {@link Worker} which
* aggregates it when all workers have responded before returning the solution. The Worker class extends the Thread * have to be extended by the classes which will perform the specific job at hand (in this case
* class to enable parallel processing, and does the work once the data has been received from the Master. The * finding transpose of matrix, done by {@link ArrayTransposeMasterWorker}, {@link
* MasterWorker contains a reference to the Master class, gets the input from the App and passes it on to the Master. * ArrayTransposeMaster} and {@link ArrayTransposeWorker}). The Master class divides the work into
* These 3 classes define the system which computes the result. We also have 2 abstract classes {@link Input} and * parts to be given to the workers, collects the results from the workers and aggregates it when
* {@link Result}, which contain the input data and result data respectively. The Input class also has an abstract * all workers have responded before returning the solution. The Worker class extends the Thread
* method divideData which defines how the data is to be divided into segments. These classes are extended by * class to enable parallel processing, and does the work once the data has been received from the
* {@link ArrayInput} and {@link ArrayResult}.</p> * Master. The MasterWorker contains a reference to the Master class, gets the input from the App
* and passes it on to the Master. These 3 classes define the system which computes the result. We
* also have 2 abstract classes {@link Input} and {@link Result}, which contain the input data and
* result data respectively. The Input class also has an abstract method divideData which defines
* how the data is to be divided into segments. These classes are extended by {@link ArrayInput} and
* {@link ArrayResult}.</p>
*/ */
public class App { public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class); private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
/** /**
* Program entry point. * Program entry point.
*
* @param args command line args * @param args command line args
*/ */
@ -59,7 +66,7 @@ public class App {
ArrayTransposeMasterWorker mw = new ArrayTransposeMasterWorker(); ArrayTransposeMasterWorker mw = new ArrayTransposeMasterWorker();
int rows = 10; int rows = 10;
int columns = 20; int columns = 20;
int[][] inputMatrix = ArrayUtilityMethods.createRandomIntMatrix(rows,columns); int[][] inputMatrix = ArrayUtilityMethods.createRandomIntMatrix(rows, columns);
ArrayInput input = new ArrayInput(inputMatrix); ArrayInput input = new ArrayInput(inputMatrix);
ArrayResult result = (ArrayResult) mw.getResult(input); ArrayResult result = (ArrayResult) mw.getResult(input);
if (result != null) { if (result != null) {

View File

@ -27,8 +27,7 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
/** /**
*Class ArrayInput extends abstract class {@link Input} and contains data * Class ArrayInput extends abstract class {@link Input} and contains data of type int[][].
*of type int[][].
*/ */
public class ArrayInput extends Input<int[][]> { public class ArrayInput extends Input<int[][]> {

View File

@ -24,8 +24,7 @@
package com.iluwatar.masterworker; package com.iluwatar.masterworker;
/** /**
*Class ArrayResult extends abstract class {@link Result} and contains data * Class ArrayResult extends abstract class {@link Result} and contains data of type int[][].
*of type int[][].
*/ */
public class ArrayResult extends Result<int[][]> { public class ArrayResult extends Result<int[][]> {

View File

@ -23,13 +23,12 @@
package com.iluwatar.masterworker; package com.iluwatar.masterworker;
import java.util.Random;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.Random;
/** /**
*Class ArrayUtilityMethods has some utility methods for matrices and arrays. * Class ArrayUtilityMethods has some utility methods for matrices and arrays.
*/ */
public class ArrayUtilityMethods { public class ArrayUtilityMethods {
@ -37,9 +36,10 @@ public class ArrayUtilityMethods {
private static final Logger LOGGER = LoggerFactory.getLogger(ArrayUtilityMethods.class); private static final Logger LOGGER = LoggerFactory.getLogger(ArrayUtilityMethods.class);
private static final Random RANDOM = new Random(); private static final Random RANDOM = new Random();
/** /**
* Method arraysSame compares 2 arrays @param a1 and @param a2 * Method arraysSame compares 2 arrays @param a1 and @param a2 and @return whether their values
* and @return whether their values are equal (boolean). * are equal (boolean).
*/ */
public static boolean arraysSame(int[] a1, int[] a2) { public static boolean arraysSame(int[] a1, int[] a2) {
@ -61,8 +61,8 @@ public class ArrayUtilityMethods {
} }
/** /**
* Method matricesSame compares 2 matrices @param m1 and @param m2 * Method matricesSame compares 2 matrices @param m1 and @param m2 and @return whether their
* and @return whether their values are equal (boolean). * values are equal (boolean).
*/ */
public static boolean matricesSame(int[][] m1, int[][] m2) { public static boolean matricesSame(int[][] m1, int[][] m2) {
@ -83,10 +83,10 @@ public class ArrayUtilityMethods {
} }
/** /**
* Method createRandomIntMatrix creates a random matrix of size @param rows * Method createRandomIntMatrix creates a random matrix of size @param rows and @param columns.
* and @param columns @return it (int[][]). *
* @return it (int[][]).
*/ */
public static int[][] createRandomIntMatrix(int rows, int columns) { public static int[][] createRandomIntMatrix(int rows, int columns) {
int[][] matrix = new int[rows][columns]; int[][] matrix = new int[rows][columns];
for (int i = 0; i < rows; i++) { for (int i = 0; i < rows; i++) {

View File

@ -26,8 +26,9 @@ package com.iluwatar.masterworker;
import java.util.ArrayList; import java.util.ArrayList;
/** /**
*The abstract Input class, having 1 public field which contains input data, * The abstract Input class, having 1 public field which contains input data, and abstract method
*and abstract method divideData. * divideData.
*
* @param <T> T will be type of data. * @param <T> T will be type of data.
*/ */

View File

@ -24,8 +24,8 @@
package com.iluwatar.masterworker; package com.iluwatar.masterworker;
/** /**
*The abstract Result class, which contains 1 public field containing result * The abstract Result class, which contains 1 public field containing result data.
*data. *
* @param <T> T will be type of data. * @param <T> T will be type of data.
*/ */

View File

@ -27,8 +27,8 @@ import com.iluwatar.masterworker.system.systemmaster.ArrayTransposeMaster;
import com.iluwatar.masterworker.system.systemmaster.Master; import com.iluwatar.masterworker.system.systemmaster.Master;
/** /**
*Class ArrayTransposeMasterWorker extends abstract class {@link MasterWorker} and * Class ArrayTransposeMasterWorker extends abstract class {@link MasterWorker} and specifically
*specifically solves the problem of finding transpose of input array. * solves the problem of finding transpose of input array.
*/ */
public class ArrayTransposeMasterWorker extends MasterWorker { public class ArrayTransposeMasterWorker extends MasterWorker {

View File

@ -28,7 +28,7 @@ import com.iluwatar.masterworker.Result;
import com.iluwatar.masterworker.system.systemmaster.Master; import com.iluwatar.masterworker.system.systemmaster.Master;
/** /**
*The abstract MasterWorker class which contains reference to master. * The abstract MasterWorker class which contains reference to master.
*/ */
public abstract class MasterWorker { public abstract class MasterWorker {

View File

@ -23,16 +23,15 @@
package com.iluwatar.masterworker.system.systemmaster; package com.iluwatar.masterworker.system.systemmaster;
import java.util.ArrayList;
import java.util.Enumeration;
import com.iluwatar.masterworker.ArrayResult; import com.iluwatar.masterworker.ArrayResult;
import com.iluwatar.masterworker.system.systemworkers.ArrayTransposeWorker; import com.iluwatar.masterworker.system.systemworkers.ArrayTransposeWorker;
import com.iluwatar.masterworker.system.systemworkers.Worker; import com.iluwatar.masterworker.system.systemworkers.Worker;
import java.util.ArrayList;
import java.util.Enumeration;
/** /**
*Class ArrayTransposeMaster extends abstract class {@link Master} and contains * Class ArrayTransposeMaster extends abstract class {@link Master} and contains definition of
*definition of aggregateData, which will obtain final result from all * aggregateData, which will obtain final result from all data obtained and for setWorkers.
*data obtained and for setWorkers.
*/ */
public class ArrayTransposeMaster extends Master { public class ArrayTransposeMaster extends Master {
@ -43,7 +42,7 @@ public class ArrayTransposeMaster extends Master {
@Override @Override
ArrayList<Worker> setWorkers(int num) { ArrayList<Worker> setWorkers(int num) {
ArrayList<Worker> ws = new ArrayList<Worker>(num); ArrayList<Worker> ws = new ArrayList<Worker>(num);
for (int i = 0; i < num ; i++) { for (int i = 0; i < num; i++) {
ws.add(new ArrayTransposeWorker(this, i + 1)); ws.add(new ArrayTransposeWorker(this, i + 1));
//i+1 will be id //i+1 will be id
} }
@ -52,17 +51,20 @@ public class ArrayTransposeMaster extends Master {
@Override @Override
ArrayResult aggregateData() { ArrayResult aggregateData() {
//number of rows in final result is number of rows in any of obtained results obtained from workers // number of rows in final result is number of rows in any of obtained results from workers
int rows = ((ArrayResult) this.getAllResultData().get(this.getAllResultData().keys().nextElement())).data.length; int rows = ((ArrayResult) this.getAllResultData()
int columns = 0; //number of columns is sum of number of columns in all results obtained from workers .get(this.getAllResultData().keys().nextElement())).data.length;
for (Enumeration<Integer> e = this.getAllResultData().keys(); e.hasMoreElements();) { int columns =
0; //number of columns is sum of number of columns in all results obtained from workers
for (Enumeration<Integer> e = this.getAllResultData().keys(); e.hasMoreElements(); ) {
columns += ((ArrayResult) this.getAllResultData().get(e.nextElement())).data[0].length; columns += ((ArrayResult) this.getAllResultData().get(e.nextElement())).data[0].length;
} }
int[][] resultData = new int[rows][columns]; int[][] resultData = new int[rows][columns];
int columnsDone = 0; //columns aggregated so far int columnsDone = 0; //columns aggregated so far
for (int i = 0; i < this.getExpectedNumResults(); i++) { for (int i = 0; i < this.getExpectedNumResults(); i++) {
//result obtained from ith worker //result obtained from ith worker
int[][] work = ((ArrayResult) this.getAllResultData().get(this.getWorkers().get(i).getWorkerId())).data; int[][] work =
((ArrayResult) this.getAllResultData().get(this.getWorkers().get(i).getWorkerId())).data;
for (int m = 0; m < work.length; m++) { for (int m = 0; m < work.length; m++) {
//m = row number, n = columns number //m = row number, n = columns number
for (int n = 0; n < work[0].length; n++) { for (int n = 0; n < work[0].length; n++) {

View File

@ -23,18 +23,17 @@
package com.iluwatar.masterworker.system.systemmaster; package com.iluwatar.masterworker.system.systemmaster;
import java.util.ArrayList;
import java.util.Hashtable;
import com.iluwatar.masterworker.Input; import com.iluwatar.masterworker.Input;
import com.iluwatar.masterworker.Result; import com.iluwatar.masterworker.Result;
import com.iluwatar.masterworker.system.systemworkers.Worker; import com.iluwatar.masterworker.system.systemworkers.Worker;
import java.util.ArrayList;
import java.util.Hashtable;
/** /**
*The abstract Master class which contains private fields numOfWorkers * The abstract Master class which contains private fields numOfWorkers (number of workers), workers
*(number of workers), workers (arraylist of workers), expectedNumResults * (arraylist of workers), expectedNumResults (number of divisions of input data, same as expected
*(number of divisions of input data, same as expected number of results), * number of results), allResultData (hashtable of results obtained from workers, mapped by their
*allResultData (hashtable of results obtained from workers, mapped by * ids) and finalResult (aggregated from allResultData).
*their ids) and finalResult (aggregated from allResultData).
*/ */
public abstract class Master { public abstract class Master {

View File

@ -28,8 +28,8 @@ import com.iluwatar.masterworker.ArrayResult;
import com.iluwatar.masterworker.system.systemmaster.Master; import com.iluwatar.masterworker.system.systemmaster.Master;
/** /**
*Class ArrayTransposeWorker extends abstract class {@link Worker} and defines method * Class ArrayTransposeWorker extends abstract class {@link Worker} and defines method
*executeOperation(), to be performed on data received from master. * executeOperation(), to be performed on data received from master.
*/ */
public class ArrayTransposeWorker extends Worker { public class ArrayTransposeWorker extends Worker {
@ -41,12 +41,14 @@ public class ArrayTransposeWorker extends Worker {
@Override @Override
ArrayResult executeOperation() { ArrayResult executeOperation() {
//number of rows in result matrix is equal to number of columns in input matrix and vice versa //number of rows in result matrix is equal to number of columns in input matrix and vice versa
int[][] resultData = new int[((ArrayInput) this.getReceivedData()).data[0].length] ArrayInput arrayInput = (ArrayInput) this.getReceivedData();
[((ArrayInput) this.getReceivedData()).data.length]; final int rows = arrayInput.data[0].length;
for (int i = 0; i < ((ArrayInput) this.getReceivedData()).data.length; i++) { final int cols = arrayInput.data.length;
for (int j = 0; j < ((ArrayInput) this.getReceivedData()).data[0].length; j++) { int[][] resultData = new int[rows][cols];
for (int i = 0; i < cols; i++) {
for (int j = 0; j < rows; j++) {
//flipping element positions along diagonal //flipping element positions along diagonal
resultData[j][i] = ((ArrayInput) this.getReceivedData()).data[i][j]; resultData[j][i] = arrayInput.data[i][j];
} }
} }
return new ArrayResult(resultData); return new ArrayResult(resultData);

View File

@ -28,9 +28,8 @@ import com.iluwatar.masterworker.Result;
import com.iluwatar.masterworker.system.systemmaster.Master; import com.iluwatar.masterworker.system.systemmaster.Master;
/** /**
*The abstract Worker class which extends Thread class to enable parallel * The abstract Worker class which extends Thread class to enable parallel processing. Contains
*processing. Contains fields master(holding reference to master), workerId * fields master(holding reference to master), workerId (unique id) and receivedData(from master).
*(unique id) and receivedData(from master).
*/ */
public abstract class Worker extends Thread { public abstract class Worker extends Thread {

View File

@ -24,15 +24,15 @@
package com.iluwatar.mediator; package com.iluwatar.mediator;
/** /**
*
* Action enumeration. * Action enumeration.
*
*/ */
public enum Action { public enum Action {
HUNT("hunted a rabbit", "arrives for dinner"), TALE("tells a tale", "comes to listen"), GOLD( HUNT("hunted a rabbit", "arrives for dinner"),
"found gold", "takes his share of the gold"), ENEMY("spotted enemies", "runs for cover"), NONE( TALE("tells a tale", "comes to listen"),
"", ""); GOLD("found gold", "takes his share of the gold"),
ENEMY("spotted enemies", "runs for cover"),
NONE("", "");
private String title; private String title;
private String description; private String description;

View File

@ -24,31 +24,30 @@
package com.iluwatar.mediator; package com.iluwatar.mediator;
/** /**
*
* The Mediator pattern defines an object that encapsulates how a set of objects interact. This * The Mediator pattern defines an object that encapsulates how a set of objects interact. This
* pattern is considered to be a behavioral pattern due to the way it can alter the program's * pattern is considered to be a behavioral pattern due to the way it can alter the program's
* running behavior. * running behavior.
* <p> *
* Usually a program is made up of a large number of classes. So the logic and computation is * <p>Usually a program is made up of a large number of classes. So the logic and computation is
* distributed among these classes. However, as more classes are developed in a program, especially * distributed among these classes. However, as more classes are developed in a program, especially
* during maintenance and/or refactoring, the problem of communication between these classes may * during maintenance and/or refactoring, the problem of communication between these classes may
* become more complex. This makes the program harder to read and maintain. Furthermore, it can * become more complex. This makes the program harder to read and maintain. Furthermore, it can
* become difficult to change the program, since any change may affect code in several other * become difficult to change the program, since any change may affect code in several other
* classes. * classes.
* <p>
* With the Mediator pattern, communication between objects is encapsulated with a mediator object.
* Objects no longer communicate directly with each other, but instead communicate through the
* mediator. This reduces the dependencies between communicating objects, thereby lowering the
* coupling.
* <p>
* In this example the mediator encapsulates how a set of objects ({@link PartyMember}) interact.
* Instead of referring to each other directly they use the mediator ({@link Party}) interface.
* *
* <p>With the Mediator pattern, communication between objects is encapsulated with a mediator
* object. Objects no longer communicate directly with each other, but instead communicate through
* the mediator. This reduces the dependencies between communicating objects, thereby lowering the
* coupling.
*
* <p>In this example the mediator encapsulates how a set of objects ({@link PartyMember})
* interact. Instead of referring to each other directly they use the mediator ({@link Party})
* interface.
*/ */
public class App { public class App {
/** /**
* Program entry point * Program entry point.
* *
* @param args command line args * @param args command line args
*/ */

View File

@ -24,9 +24,7 @@
package com.iluwatar.mediator; package com.iluwatar.mediator;
/** /**
*
* Hobbit party member. * Hobbit party member.
*
*/ */
public class Hobbit extends PartyMemberBase { public class Hobbit extends PartyMemberBase {

View File

@ -24,9 +24,7 @@
package com.iluwatar.mediator; package com.iluwatar.mediator;
/** /**
*
* Hunter party member. * Hunter party member.
*
*/ */
public class Hunter extends PartyMemberBase { public class Hunter extends PartyMemberBase {

View File

@ -24,9 +24,7 @@
package com.iluwatar.mediator; package com.iluwatar.mediator;
/** /**
*
* Party interface. * Party interface.
*
*/ */
public interface Party { public interface Party {

View File

@ -27,9 +27,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
*
* Party implementation. * Party implementation.
*
*/ */
public class PartyImpl implements Party { public class PartyImpl implements Party {

View File

@ -24,9 +24,7 @@
package com.iluwatar.mediator; package com.iluwatar.mediator;
/** /**
*
* Interface for party members interacting with {@link Party}. * Interface for party members interacting with {@link Party}.
*
*/ */
public interface PartyMember { public interface PartyMember {

View File

@ -27,9 +27,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
*
* Abstract base class for party members. * Abstract base class for party members.
*
*/ */
public abstract class PartyMemberBase implements PartyMember { public abstract class PartyMemberBase implements PartyMember {

View File

@ -24,9 +24,7 @@
package com.iluwatar.mediator; package com.iluwatar.mediator;
/** /**
*
* Rogue party member. * Rogue party member.
*
*/ */
public class Rogue extends PartyMemberBase { public class Rogue extends PartyMemberBase {

View File

@ -24,9 +24,7 @@
package com.iluwatar.mediator; package com.iluwatar.mediator;
/** /**
*
* Wizard party member. * Wizard party member.
*
*/ */
public class Wizard extends PartyMemberBase { public class Wizard extends PartyMemberBase {

View File

@ -23,36 +23,33 @@
package com.iluwatar.memento; package com.iluwatar.memento;
import java.util.Stack;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.Stack;
/** /**
*
* The Memento pattern is a software design pattern that provides the ability to restore an object * The Memento pattern is a software design pattern that provides the ability to restore an object
* to its previous state (undo via rollback). * to its previous state (undo via rollback).
* <p> *
* The Memento pattern is implemented with three objects: the originator, a caretaker and a memento. * <p>The Memento pattern is implemented with three objects: the originator, a caretaker and a
* The originator is some object that has an internal state. The caretaker is going to do something * memento. The originator is some object that has an internal state. The caretaker is going to do
* to the originator, but wants to be able to undo the change. The caretaker first asks the * something to the originator, but wants to be able to undo the change. The caretaker first asks
* originator for a memento object. Then it does whatever operation (or sequence of operations) it * the originator for a memento object. Then it does whatever operation (or sequence of operations)
* was going to do. To roll back to the state before the operations, it returns the memento object * it was going to do. To roll back to the state before the operations, it returns the memento
* to the originator. The memento object itself is an opaque object (one which the caretaker cannot, * object to the originator. The memento object itself is an opaque object (one which the caretaker
* or should not, change). When using this pattern, care should be taken if the originator may * cannot, or should not, change). When using this pattern, care should be taken if the originator
* change other objects or resources - the memento pattern operates on a single object. * may change other objects or resources - the memento pattern operates on a single object.
* <p> *
* In this example the object ({@link Star}) gives out a "memento" ({@link StarMemento}) that * <p>In this example the object ({@link Star}) gives out a "memento" ({@link StarMemento}) that
* contains the state of the object. Later on the memento can be set back to the object restoring * contains the state of the object. Later on the memento can be set back to the object restoring
* the state. * the state.
*
*/ */
public class App { public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class); private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
/** /**
* Program entry point * Program entry point.
*/ */
public static void main(String[] args) { public static void main(String[] args) {
Stack<StarMemento> states = new Stack<>(); Stack<StarMemento> states = new Stack<>();

View File

@ -24,9 +24,7 @@
package com.iluwatar.memento; package com.iluwatar.memento;
/** /**
*
* Star uses "mementos" to store and restore state. * Star uses "mementos" to store and restore state.
*
*/ */
public class Star { public class Star {
@ -35,7 +33,7 @@ public class Star {
private int massTons; private int massTons;
/** /**
* Constructor * Constructor.
*/ */
public Star(StarType startType, int startAge, int startMass) { public Star(StarType startType, int startAge, int startMass) {
this.type = startType; this.type = startType;
@ -44,7 +42,7 @@ public class Star {
} }
/** /**
* Makes time pass for the star * Makes time pass for the star.
*/ */
public void timePasses() { public void timePasses() {
ageYears *= 2; ageYears *= 2;
@ -96,9 +94,7 @@ public class Star {
} }
/** /**
* * StarMemento implementation.
* StarMemento implementation
*
*/ */
private static class StarMementoInternal implements StarMemento { private static class StarMementoInternal implements StarMemento {

View File

@ -24,9 +24,7 @@
package com.iluwatar.memento; package com.iluwatar.memento;
/** /**
*
* External interface to memento. * External interface to memento.
*
*/ */
public interface StarMemento { public interface StarMemento {

View File

@ -24,9 +24,7 @@
package com.iluwatar.memento; package com.iluwatar.memento;
/** /**
* * StarType enumeration.
* StarType enumeration
*
*/ */
public enum StarType { public enum StarType {

View File

@ -24,26 +24,24 @@
package com.iluwatar.model.view.controller; package com.iluwatar.model.view.controller;
/** /**
*
* Model-View-Controller is a pattern for implementing user interfaces. It divides the application * Model-View-Controller is a pattern for implementing user interfaces. It divides the application
* into three interconnected parts namely the model, the view and the controller. * into three interconnected parts namely the model, the view and the controller.
* <p>
* The central component of MVC, the model, captures the behavior of the application in terms of its
* problem domain, independent of the user interface. The model directly manages the data, logic and
* rules of the application. A view can be any output representation of information, such as a chart
* or a diagram The third part, the controller, accepts input and converts it to commands for the
* model or view.
* <p>
* In this example we have a giant ({@link GiantModel}) with statuses for health, fatigue and
* nourishment. {@link GiantView} can display the giant with its current status.
* {@link GiantController} receives input affecting the model and delegates redrawing the giant to
* the view.
* *
* <p>The central component of MVC, the model, captures the behavior of the application in terms of
* its problem domain, independent of the user interface. The model directly manages the data, logic
* and rules of the application. A view can be any output representation of information, such as a
* chart or a diagram The third part, the controller, accepts input and converts it to commands for
* the model or view.
*
* <p>In this example we have a giant ({@link GiantModel}) with statuses for health, fatigue and
* nourishment. {@link GiantView} can display the giant with its current status. {@link
* GiantController} receives input affecting the model and delegates redrawing the giant to the
* view.
*/ */
public class App { public class App {
/** /**
* Program entry point * Program entry point.
* *
* @param args command line args * @param args command line args
*/ */

View File

@ -24,9 +24,7 @@
package com.iluwatar.model.view.controller; package com.iluwatar.model.view.controller;
/** /**
* * Fatigue enumeration.
* Fatigue enumeration
*
*/ */
public enum Fatigue { public enum Fatigue {

View File

@ -24,9 +24,7 @@
package com.iluwatar.model.view.controller; package com.iluwatar.model.view.controller;
/** /**
*
* GiantController can update the giant data and redraw it using the view. * GiantController can update the giant data and redraw it using the view.
*
*/ */
public class GiantController { public class GiantController {

View File

@ -24,9 +24,7 @@
package com.iluwatar.model.view.controller; package com.iluwatar.model.view.controller;
/** /**
* * GiantModel contains the giant data.
* GiantModel contains the giant data
*
*/ */
public class GiantModel { public class GiantModel {

View File

@ -27,9 +27,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* * GiantView displays the giant.
* GiantView displays the giant
*
*/ */
public class GiantView { public class GiantView {

View File

@ -24,9 +24,7 @@
package com.iluwatar.model.view.controller; package com.iluwatar.model.view.controller;
/** /**
* * Health enumeration.
* Health enumeration
*
*/ */
public enum Health { public enum Health {

View File

@ -24,9 +24,7 @@
package com.iluwatar.model.view.controller; package com.iluwatar.model.view.controller;
/** /**
* * Nourishment enumeration.
* Nourishment enumeration
*
*/ */
public enum Nourishment { public enum Nourishment {

View File

@ -24,31 +24,29 @@
package com.iluwatar.model.view.presenter; package com.iluwatar.model.view.presenter;
/** /**
* The Model-View-Presenter(MVP) architectural pattern, helps us achieve what is called "The
* separation of concerns" principle. This is accomplished by separating the application's logic
* (Model), GUIs (View), and finally the way that the user's actions update the application's logic
* (Presenter).
* *
* The Model-View-Presenter(MVP) architectural pattern, helps us achieve what is called * <p>In the following example, The {@link FileLoader} class represents the app's logic, the {@link
* "The separation of concerns" principle. This is accomplished by separating the application's * FileSelectorJFrame} is the GUI and the {@link FileSelectorPresenter} is responsible to respond to
* logic (Model), GUIs (View), and finally the way that the user's actions update the application's * users' actions.
* logic (Presenter).
* <p>
* In the following example, The {@link FileLoader} class represents the app's logic, the
* {@link FileSelectorJFrame} is the GUI and the {@link FileSelectorPresenter} is responsible to
* respond to users' actions.
* <p>
* Finally, please notice the wiring between the Presenter and the View and between the Presenter
* and the Model.
* *
* <p>Finally, please notice the wiring between the Presenter and the View and between the
* Presenter and the Model.
*/ */
public class App { public class App {
/** /**
* Program entry point * Program entry point.
* *
* @param args command line args * @param args command line args
*/ */
public static void main(String[] args) { public static void main(String[] args) {
FileLoader loader = new FileLoader(); FileLoader loader = new FileLoader();
FileSelectorJFrame jFrame = new FileSelectorJFrame(); FileSelectorJFrame frame = new FileSelectorJFrame();
FileSelectorPresenter presenter = new FileSelectorPresenter(jFrame); FileSelectorPresenter presenter = new FileSelectorPresenter(frame);
presenter.setLoader(loader); presenter.setLoader(loader);
presenter.start(); presenter.start();
} }

View File

@ -27,20 +27,19 @@ import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.FileReader; import java.io.FileReader;
import java.io.Serializable; import java.io.Serializable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* Every instance of this class represents the Model component in the Model-View-Presenter * Every instance of this class represents the Model component in the Model-View-Presenter
* architectural pattern. * architectural pattern.
* <p> *
* It is responsible for reading and loading the contents of a given file. * <p>It is responsible for reading and loading the contents of a given file.
*/ */
public class FileLoader implements Serializable { public class FileLoader implements Serializable {
/** /**
* Generated serial version UID * Generated serial version UID.
*/ */
private static final long serialVersionUID = -4745803872902019069L; private static final long serialVersionUID = -4745803872902019069L;
@ -89,6 +88,8 @@ public class FileLoader implements Serializable {
} }
/** /**
* Gets the path of the file to be loaded.
*
* @return fileName The path of the file to be loaded. * @return fileName The path of the file to be loaded.
*/ */
public String getFileName() { public String getFileName() {
@ -96,6 +97,8 @@ public class FileLoader implements Serializable {
} }
/** /**
* Returns true if the given file exists.
*
* @return True, if the file given exists, false otherwise. * @return True, if the file given exists, false otherwise.
*/ */
public boolean fileExists() { public boolean fileExists() {
@ -103,6 +106,8 @@ public class FileLoader implements Serializable {
} }
/** /**
* Returns true if the given file is loaded.
*
* @return True, if the file is loaded, false otherwise. * @return True, if the file is loaded, false otherwise.
*/ */
public boolean isLoaded() { public boolean isLoaded() {

View File

@ -26,7 +26,6 @@ package com.iluwatar.model.view.presenter;
import java.awt.Color; import java.awt.Color;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.JLabel; import javax.swing.JLabel;
@ -82,7 +81,7 @@ public class FileSelectorJFrame extends JFrame implements FileSelectorView, Acti
private JPanel panel; private JPanel panel;
/** /**
* The Presenter component that the frame will interact with * The Presenter component that the frame will interact with.
*/ */
private FileSelectorPresenter presenter; private FileSelectorPresenter presenter;

View File

@ -28,13 +28,13 @@ import java.io.Serializable;
/** /**
* Every instance of this class represents the Presenter component in the Model-View-Presenter * Every instance of this class represents the Presenter component in the Model-View-Presenter
* architectural pattern. * architectural pattern.
* <p> *
* It is responsible for reacting to the user's actions and update the View component. * <p>It is responsible for reacting to the user's actions and update the View component.
*/ */
public class FileSelectorPresenter implements Serializable { public class FileSelectorPresenter implements Serializable {
/** /**
* Generated serial version UID * Generated serial version UID.
*/ */
private static final long serialVersionUID = 1210314339075855074L; private static final long serialVersionUID = 1210314339075855074L;
@ -49,7 +49,7 @@ public class FileSelectorPresenter implements Serializable {
private FileLoader loader; private FileLoader loader;
/** /**
* Constructor * Constructor.
* *
* @param view The view component that the presenter will interact with. * @param view The view component that the presenter will interact with.
*/ */
@ -82,7 +82,7 @@ public class FileSelectorPresenter implements Serializable {
} }
/** /**
* Ok button handler * Ok button handler.
*/ */
public void confirmed() { public void confirmed() {
if (loader.getFileName() == null || loader.getFileName().equals("")) { if (loader.getFileName() == null || loader.getFileName().equals("")) {

View File

@ -26,12 +26,12 @@ package com.iluwatar.model.view.presenter;
/** /**
* Every instance of this class represents the Stub component in the Model-View-Presenter * Every instance of this class represents the Stub component in the Model-View-Presenter
* architectural pattern. * architectural pattern.
* <p> *
* The stub implements the View interface and it is useful when we want the test the reaction to * <p>The stub implements the View interface and it is useful when we want the test the reaction to
* user events, such as mouse clicks. * user events, such as mouse clicks.
* <p> *
* Since we can not test the GUI directly, the MVP pattern provides this functionality through the * <p>Since we can not test the GUI directly, the MVP pattern provides this functionality through
* View's dummy implementation, the Stub. * the View's dummy implementation, the Stub.
*/ */
public class FileSelectorStub implements FileSelectorView { public class FileSelectorStub implements FileSelectorView {
@ -61,7 +61,7 @@ public class FileSelectorStub implements FileSelectorView {
private boolean dataDisplayed; private boolean dataDisplayed;
/** /**
* Constructor * Constructor.
*/ */
public FileSelectorStub() { public FileSelectorStub() {
this.opened = false; this.opened = false;
@ -124,6 +124,8 @@ public class FileSelectorStub implements FileSelectorView {
} }
/** /**
* Returns true, if the data were displayed.
*
* @return True if the data where displayed, false otherwise. * @return True if the data where displayed, false otherwise.
*/ */
public boolean dataDisplayed() { public boolean dataDisplayed() {

View File

@ -42,6 +42,8 @@ public interface FileSelectorView extends Serializable {
void close(); void close();
/** /**
* Returns true if view is opened.
*
* @return True, if the view is opened, false otherwise. * @return True, if the view is opened, false otherwise.
*/ */
boolean isOpened(); boolean isOpened();
@ -54,6 +56,8 @@ public interface FileSelectorView extends Serializable {
void setPresenter(FileSelectorPresenter presenter); void setPresenter(FileSelectorPresenter presenter);
/** /**
* Gets presenter component.
*
* @return The presenter Component. * @return The presenter Component.
*/ */
FileSelectorPresenter getPresenter(); FileSelectorPresenter getPresenter();
@ -66,6 +70,8 @@ public interface FileSelectorView extends Serializable {
void setFileName(String name); void setFileName(String name);
/** /**
* Gets the name of file.
*
* @return The name of the file. * @return The name of the file.
*/ */
String getFileName(); String getFileName();

View File

@ -31,10 +31,9 @@ import java.io.FileNotFoundException;
* An object that applies this pattern can provide the equivalent of a namespace, providing the * An object that applies this pattern can provide the equivalent of a namespace, providing the
* initialization and finalization process of a static class or a class with static members with * initialization and finalization process of a static class or a class with static members with
* cleaner, more concise syntax and semantics. * cleaner, more concise syntax and semantics.
* <p>
* The below example demonstrates a use case for testing two different modules: File Logger and
* Console Logger
* *
* <p>The below example demonstrates a use case for testing two different modules: File Logger and
* Console Logger
*/ */
public class App { public class App {
@ -42,7 +41,7 @@ public class App {
public static ConsoleLoggerModule consoleLoggerModule; public static ConsoleLoggerModule consoleLoggerModule;
/** /**
* Following method performs the initialization * Following method performs the initialization.
* *
* @throws FileNotFoundException if program is not able to find log files (output.txt and * @throws FileNotFoundException if program is not able to find log files (output.txt and
* error.txt) * error.txt)
@ -55,7 +54,7 @@ public class App {
} }
/** /**
* Following method performs the finalization * Following method performs the finalization.
*/ */
public static void unprepare() { public static void unprepare() {
@ -65,7 +64,7 @@ public class App {
} }
/** /**
* Following method is main executor * Following method is main executor.
* *
* @param args for providing default program arguments * @param args for providing default program arguments
*/ */

View File

@ -23,16 +23,15 @@
package com.iluwatar.module; package com.iluwatar.module;
import java.io.PrintStream;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.PrintStream;
/** /**
* The ConsoleLoggerModule is responsible for showing logs on System Console * The ConsoleLoggerModule is responsible for showing logs on System Console.
* <p> *
* The below example demonstrates a Console logger module, which can print simple and error messages * <p>The below example demonstrates a Console logger module, which can print simple and error
* in two designated formats * messages in two designated formats
*/ */
public final class ConsoleLoggerModule { public final class ConsoleLoggerModule {
@ -43,10 +42,11 @@ public final class ConsoleLoggerModule {
public PrintStream output = null; public PrintStream output = null;
public PrintStream error = null; public PrintStream error = null;
private ConsoleLoggerModule() {} private ConsoleLoggerModule() {
}
/** /**
* Static method to get single instance of class * Static method to get single instance of class.
* *
* @return singleton instance of ConsoleLoggerModule * @return singleton instance of ConsoleLoggerModule
*/ */
@ -60,7 +60,7 @@ public final class ConsoleLoggerModule {
} }
/** /**
* Following method performs the initialization * Following method performs the initialization.
*/ */
public ConsoleLoggerModule prepare() { public ConsoleLoggerModule prepare() {
@ -73,7 +73,7 @@ public final class ConsoleLoggerModule {
} }
/** /**
* Following method performs the finalization * Following method performs the finalization.
*/ */
public void unprepare() { public void unprepare() {
@ -93,7 +93,7 @@ public final class ConsoleLoggerModule {
} }
/** /**
* Used to print a message * Used to print a message.
* *
* @param value will be printed on console * @param value will be printed on console
*/ */
@ -102,7 +102,7 @@ public final class ConsoleLoggerModule {
} }
/** /**
* Used to print a error message * Used to print a error message.
* *
* @param value will be printed on error console * @param value will be printed on error console
*/ */

View File

@ -23,18 +23,17 @@
package com.iluwatar.module; package com.iluwatar.module;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.PrintStream; import java.io.PrintStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* The FileLoggerModule is responsible for showing logs on File System * The FileLoggerModule is responsible for showing logs on File System.
* <p> *
* The below example demonstrates a File logger module, which can print simple and error messages in * <p>The below example demonstrates a File logger module, which can print simple and error
* two designated files * messages in two designated files
*/ */
public final class FileLoggerModule { public final class FileLoggerModule {
@ -48,10 +47,11 @@ public final class FileLoggerModule {
public PrintStream output = null; public PrintStream output = null;
public PrintStream error = null; public PrintStream error = null;
private FileLoggerModule() {} private FileLoggerModule() {
}
/** /**
* Static method to get single instance of class * Static method to get single instance of class.
* *
* @return singleton instance of FileLoggerModule * @return singleton instance of FileLoggerModule
*/ */
@ -65,7 +65,7 @@ public final class FileLoggerModule {
} }
/** /**
* Following method performs the initialization * Following method performs the initialization.
* *
* @throws FileNotFoundException if program is not able to find log files (output.txt and * @throws FileNotFoundException if program is not able to find log files (output.txt and
* error.txt) * error.txt)
@ -81,7 +81,7 @@ public final class FileLoggerModule {
} }
/** /**
* Following method performs the finalization * Following method performs the finalization.
*/ */
public void unprepare() { public void unprepare() {
@ -101,7 +101,7 @@ public final class FileLoggerModule {
} }
/** /**
* Used to print a message * Used to print a message.
* *
* @param value will be printed in file * @param value will be printed in file
*/ */
@ -110,7 +110,7 @@ public final class FileLoggerModule {
} }
/** /**
* Used to print a error message * Used to print a error message.
* *
* @param value will be printed on error file * @param value will be printed on error file
*/ */

View File

@ -23,26 +23,27 @@
package com.iluwatar.monad; package com.iluwatar.monad;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Objects; import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* The Monad pattern defines a monad structure, that enables chaining operations * The Monad pattern defines a monad structure, that enables chaining operations in pipelines and
* in pipelines and processing data step by step. * processing data step by step. Formally, monad consists of a type constructor M and two
* Formally, monad consists of a type constructor M and two operations: * operations:
* <br>bind - that takes monadic object and a function from plain object to the * <br>bind - that takes monadic object and a function from plain object to the
* monadic value and returns monadic value. * monadic value and returns monadic value.
* <br>return - that takes plain type object and returns this object wrapped in a monadic value. * <br>return - that takes plain type object and returns this object wrapped in a monadic value.
* <p> *
* In the given example, the Monad pattern is represented as a {@link Validator} that takes an instance * <p>In the given example, the Monad pattern is represented as a {@link Validator} that takes an
* of a plain object with {@link Validator#of(Object)} * instance of a plain object with {@link Validator#of(Object)} and validates it {@link
* and validates it {@link Validator#validate(Function, Predicate, String)} against given predicates. * Validator#validate(Function, Predicate, String)} against given predicates.
* <p>As a validation result {@link Validator#get()} it either returns valid object {@link Validator#t} *
* or throws a list of exceptions {@link Validator#exceptions} collected during validation. * <p>As a validation result {@link Validator#get()} it either returns valid object {@link
* Validator#t} or throws a list of exceptions {@link Validator#exceptions} collected during
* validation.
*/ */
public class App { public class App {
@ -58,6 +59,7 @@ public class App {
LOGGER.info(Validator.of(user).validate(User::getName, Objects::nonNull, "name is null") LOGGER.info(Validator.of(user).validate(User::getName, Objects::nonNull, "name is null")
.validate(User::getName, name -> !name.isEmpty(), "name is empty") .validate(User::getName, name -> !name.isEmpty(), "name is empty")
.validate(User::getEmail, email -> !email.contains("@"), "email doesn't containt '@'") .validate(User::getEmail, email -> !email.contains("@"), "email doesn't containt '@'")
.validate(User::getAge, age -> age > 20 && age < 30, "age isn't between...").get().toString()); .validate(User::getAge, age -> age > 20 && age < 30, "age isn't between...").get()
.toString());
} }
} }

View File

@ -24,7 +24,7 @@
package com.iluwatar.monad; package com.iluwatar.monad;
/** /**
* Enumeration of Types of Sex * Enumeration of Types of Sex.
*/ */
public enum Sex { public enum Sex {
MALE, FEMALE MALE, FEMALE

View File

@ -24,7 +24,7 @@
package com.iluwatar.monad; package com.iluwatar.monad;
/** /**
* User Definition * User Definition.
*/ */
public class User { public class User {
@ -34,6 +34,8 @@ public class User {
private String email; private String email;
/** /**
* Constructor.
*
* @param name - name * @param name - name
* @param age - age * @param age - age
* @param sex - sex * @param sex - sex

View File

@ -30,18 +30,18 @@ import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
/** /**
* Class representing Monad design pattern. Monad is a way of chaining operations on the * Class representing Monad design pattern. Monad is a way of chaining operations on the given
* given object together step by step. In Validator each step results in either success or * object together step by step. In Validator each step results in either success or failure
* failure indicator, giving a way of receiving each of them easily and finally getting * indicator, giving a way of receiving each of them easily and finally getting validated object or
* validated object or list of exceptions. * list of exceptions.
* *
* @param <T> Placeholder for an object. * @param <T> Placeholder for an object.
*/ */
public class Validator<T> { public class Validator<T> {
/** /**
* Object that is validated * Object that is validated.
*/ */
private final T t; private final T obj;
/** /**
* List of exception thrown during validation. * List of exception thrown during validation.
@ -50,14 +50,15 @@ public class Validator<T> {
/** /**
* Creates a monadic value of given object. * Creates a monadic value of given object.
* @param t object to be validated *
* @param obj object to be validated
*/ */
private Validator(T t) { private Validator(T obj) {
this.t = t; this.obj = obj;
} }
/** /**
* Creates validator against given object * Creates validator against given object.
* *
* @param t object to be validated * @param t object to be validated
* @param <T> object's type * @param <T> object's type
@ -68,25 +69,27 @@ public class Validator<T> {
} }
/** /**
* @param validation one argument boolean-valued function that * Checks if the validation is successful.
* represents one step of validation. Adds exception to main validation exception *
* list when single step validation ends with failure. * @param validation one argument boolean-valued function that represents one step of validation.
* Adds exception to main validation exception list when single step validation
* ends with failure.
* @param message error message when object is invalid * @param message error message when object is invalid
* @return this * @return this
*/ */
public Validator<T> validate(Predicate<T> validation, String message) { public Validator<T> validate(Predicate<T> validation, String message) {
if (!validation.test(t)) { if (!validation.test(obj)) {
exceptions.add(new IllegalStateException(message)); exceptions.add(new IllegalStateException(message));
} }
return this; return this;
} }
/** /**
* Extension for the {@link Validator#validate(Function, Predicate, String)} method, * Extension for the {@link Validator#validate(Function, Predicate, String)} method, dedicated for
* dedicated for objects, that need to be projected before requested validation. * objects, that need to be projected before requested validation.
* *
* @param projection function that gets an objects, and returns projection representing * @param projection function that gets an objects, and returns projection representing element to
* element to be validated. * be validated.
* @param validation see {@link Validator#validate(Function, Predicate, String)} * @param validation see {@link Validator#validate(Function, Predicate, String)}
* @param message see {@link Validator#validate(Function, Predicate, String)} * @param message see {@link Validator#validate(Function, Predicate, String)}
* @param <U> see {@link Validator#validate(Function, Predicate, String)} * @param <U> see {@link Validator#validate(Function, Predicate, String)}
@ -105,7 +108,7 @@ public class Validator<T> {
*/ */
public T get() throws IllegalStateException { public T get() throws IllegalStateException {
if (exceptions.isEmpty()) { if (exceptions.isEmpty()) {
return t; return obj;
} }
IllegalStateException e = new IllegalStateException(); IllegalStateException e = new IllegalStateException();
exceptions.forEach(e::addSuppressed); exceptions.forEach(e::addSuppressed);

View File

@ -24,27 +24,21 @@
package com.iluwatar.monostate; package com.iluwatar.monostate;
/** /**
*
* The MonoState pattern ensures that all instances of the class will have the same state. This can * The MonoState pattern ensures that all instances of the class will have the same state. This can
* be used a direct replacement of the Singleton pattern. * be used a direct replacement of the Singleton pattern.
* *
* <p> * <p>In the following example, The {@link LoadBalancer} class represents the app's logic. It
* In the following example, The {@link LoadBalancer} class represents the app's logic. It contains * contains a series of Servers, which can handle requests of type {@link Request}. Two instances of
* a series of Servers, which can handle requests of type {@link Request}. Two instances of
* LoadBalacer are created. When a request is made to a server via the first LoadBalancer the state * LoadBalacer are created. When a request is made to a server via the first LoadBalancer the state
* change in the first load balancer affects the second. So if the first LoadBalancer selects the * change in the first load balancer affects the second. So if the first LoadBalancer selects the
* Server 1, the second LoadBalancer on a new request will select the Second server. If a third * Server 1, the second LoadBalancer on a new request will select the Second server. If a third
* LoadBalancer is created and a new request is made to it, then it will select the third server as * LoadBalancer is created and a new request is made to it, then it will select the third server as
* the second load balancer has already selected the second server. * the second load balancer has already selected the second server.
* <p>
* .
*
*/ */
public class App { public class App {
/** /**
* Program entry point * Program entry point.
* *
* @param args command line args * @param args command line args
*/ */

View File

@ -31,7 +31,6 @@ import java.util.List;
* receiving a new Request, it delegates the call to the servers in a Round Robin Fashion. Since all * receiving a new Request, it delegates the call to the servers in a Round Robin Fashion. Since all
* instances of the class share the same state, all instances will delegate to the same server on * instances of the class share the same state, all instances will delegate to the same server on
* receiving a new Request. * receiving a new Request.
*
*/ */
public class LoadBalancer { public class LoadBalancer {
@ -40,13 +39,13 @@ public class LoadBalancer {
static { static {
int id = 0; int id = 0;
for (int port : new int[] {8080, 8081, 8082, 8083, 8084}) { for (int port : new int[]{8080, 8081, 8082, 8083, 8084}) {
SERVERS.add(new Server("localhost", port, ++id)); SERVERS.add(new Server("localhost", port, ++id));
} }
} }
/** /**
* Add new server * Add new server.
*/ */
public final void addServer(Server server) { public final void addServer(Server server) {
synchronized (SERVERS) { synchronized (SERVERS) {
@ -64,7 +63,7 @@ public class LoadBalancer {
} }
/** /**
* Handle request * Handle request.
*/ */
public synchronized void serverRequest(Request request) { public synchronized void serverRequest(Request request) {
if (lastServedId >= SERVERS.size()) { if (lastServedId >= SERVERS.size()) {

View File

@ -24,9 +24,7 @@
package com.iluwatar.monostate; package com.iluwatar.monostate;
/** /**
*
* The Request class. A {@link Server} can handle an instance of a Request. * The Request class. A {@link Server} can handle an instance of a Request.
*
*/ */
public class Request { public class Request {

View File

@ -27,10 +27,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
*
* The Server class. Each Server sits behind a LoadBalancer which delegates the call to the servers * The Server class. Each Server sits behind a LoadBalancer which delegates the call to the servers
* in a simplistic Round Robin fashion. * in a simplistic Round Robin fashion.
*
*/ */
public class Server { public class Server {
@ -41,7 +39,7 @@ public class Server {
public final int id; public final int id;
/** /**
* Constructor * Constructor.
*/ */
public Server(String host, int port, int id) { public Server(String host, int port, int id) {
this.host = host; this.host = host;

View File

@ -27,25 +27,23 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
*
* Whereas Singleton design pattern introduces single globally accessible object the Multiton * Whereas Singleton design pattern introduces single globally accessible object the Multiton
* pattern defines many globally accessible objects. The client asks for the correct instance from * pattern defines many globally accessible objects. The client asks for the correct instance from
* the Multiton by passing an enumeration as parameter. * the Multiton by passing an enumeration as parameter.
* <p>
* There is more than one way to implement the multiton design pattern. In the first example
* {@link Nazgul} is the Multiton and we can ask single {@link Nazgul} from it using {@link NazgulName}.
* The {@link Nazgul}s are statically initialized and stored in concurrent hash map.
* <p>
* In the enum implementation {@link NazgulEnum} is the multiton. It is static and mutable because
* of the way java supports enums.
* *
* <p>There is more than one way to implement the multiton design pattern. In the first example
* {@link Nazgul} is the Multiton and we can ask single {@link Nazgul} from it using {@link
* NazgulName}. The {@link Nazgul}s are statically initialized and stored in concurrent hash map.
*
* <p>In the enum implementation {@link NazgulEnum} is the multiton. It is static and mutable
* because of the way java supports enums.
*/ */
public class App { public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class); private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
/** /**
* Program entry point * Program entry point.
* *
* @param args command line args * @param args command line args
*/ */

View File

@ -27,9 +27,7 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
/** /**
*
* Nazgul is a Multiton class. Nazgul instances can be queried using {@link #getInstance} method. * Nazgul is a Multiton class. Nazgul instances can be queried using {@link #getInstance} method.
*
*/ */
public final class Nazgul { public final class Nazgul {

View File

@ -24,8 +24,7 @@
package com.iluwatar.multiton; package com.iluwatar.multiton;
/** /**
* enum based multiton implementation * enum based multiton implementation.
*
*/ */
public enum NazgulEnum { public enum NazgulEnum {

View File

@ -24,9 +24,7 @@
package com.iluwatar.multiton; package com.iluwatar.multiton;
/** /**
*
* Each Nazgul has different {@link NazgulName}. * Each Nazgul has different {@link NazgulName}.
*
*/ */
public enum NazgulName { public enum NazgulName {

View File

@ -23,19 +23,17 @@
package com.iluwatar.mute; package com.iluwatar.mute;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.sql.SQLException; import java.sql.SQLException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* Mute pattern is utilized when we need to suppress an exception due to an API flaw or in * Mute pattern is utilized when we need to suppress an exception due to an API flaw or in situation
* situation when all we can do to handle the exception is to log it. * when all we can do to handle the exception is to log it. This pattern should not be used
* This pattern should not be used everywhere. It is very important to logically handle the * everywhere. It is very important to logically handle the exceptions in a system, but some
* exceptions in a system, but some situations like the ones described above require this pattern, * situations like the ones described above require this pattern, so that we don't need to repeat
* so that we don't need to repeat
* <pre> * <pre>
* <code> * <code>
* try { * try {
@ -45,7 +43,6 @@ import java.sql.SQLException;
* } * }
* </code> * </code>
* </pre> every time we need to ignore an exception. * </pre> every time we need to ignore an exception.
*
*/ */
public class App { public class App {

View File

@ -25,12 +25,12 @@ package com.iluwatar.mute;
/** /**
* A runnable which may throw exception on execution. * A runnable which may throw exception on execution.
*
*/ */
@FunctionalInterface @FunctionalInterface
public interface CheckedRunnable { public interface CheckedRunnable {
/** /**
* Same as {@link Runnable#run()} with a possibility of exception in execution. * Same as {@link Runnable#run()} with a possibility of exception in execution.
*
* @throws Exception if any exception occurs. * @throws Exception if any exception occurs.
*/ */
void run() throws Exception; void run() throws Exception;

View File

@ -32,14 +32,15 @@ import java.io.IOException;
public final class Mute { public final class Mute {
// The constructor is never meant to be called. // The constructor is never meant to be called.
private Mute() {} private Mute() {
}
/** /**
* Executes the <code>runnable</code> and throws the exception occurred within a {@link AssertionError}. * Executes the <code>runnable</code> and throws the exception occurred within a {@link
* This method should be utilized to mute the operations that are guaranteed not to throw an exception. * AssertionError}. This method should be utilized to mute the operations that are guaranteed not
* For instance {@link ByteArrayOutputStream#write(byte[])} declares in it's signature that it can throw * to throw an exception. For instance {@link ByteArrayOutputStream#write(byte[])} declares in
* an {@link IOException}, but in reality it cannot. This is because the bulk write method is not overridden * it's signature that it can throw an {@link IOException}, but in reality it cannot. This is
* in {@link ByteArrayOutputStream}. * because the bulk write method is not overridden in {@link ByteArrayOutputStream}.
* *
* @param runnable a runnable that should never throw an exception on execution. * @param runnable a runnable that should never throw an exception on execution.
*/ */
@ -52,10 +53,10 @@ public final class Mute {
} }
/** /**
* Executes the <code>runnable</code> and logs the exception occurred on {@link System#err}. * Executes the <code>runnable</code> and logs the exception occurred on {@link System#err}. This
* This method should be utilized to mute the operations about which most you can do is log. * method should be utilized to mute the operations about which most you can do is log. For
* For instance while closing a connection to database, or cleaning up a resource, * instance while closing a connection to database, or cleaning up a resource, all you can do is
* all you can do is log the exception occurred. * log the exception occurred.
* *
* @param runnable a runnable that may throw an exception on execution. * @param runnable a runnable that may throw an exception on execution.
*/ */

View File

@ -26,9 +26,8 @@ package com.iluwatar.mute;
import java.io.Closeable; import java.io.Closeable;
/** /**
* Represents any resource that the application might acquire and that must be closed * Represents any resource that the application might acquire and that must be closed after it is
* after it is utilized. Example of such resources can be a database connection, open * utilized. Example of such resources can be a database connection, open files, sockets.
* files, sockets.
*/ */
public interface Resource extends Closeable { public interface Resource extends Closeable {

View File

@ -25,19 +25,17 @@ package com.iluwatar.mutex;
/** /**
* A Mutex prevents multiple threads from accessing a resource simultaneously. * A Mutex prevents multiple threads from accessing a resource simultaneously.
* <p> *
* In this example we have two thieves who are taking beans from a jar. * <p>In this example we have two thieves who are taking beans from a jar. Only one thief can take
* Only one thief can take a bean at a time. This is ensured by a Mutex lock * a bean at a time. This is ensured by a Mutex lock which must be acquired in order to access the
* which must be acquired in order to access the jar. Each thief attempts to * jar. Each thief attempts to acquire the lock, take a bean and then release the lock. If the lock
* acquire the lock, take a bean and then release the lock. If the lock has * has already been acquired, the thief will be prevented from continuing (blocked) until the lock
* already been acquired, the thief will be prevented from continuing (blocked) * has been released. The thieves stop taking beans once there are no beans left to take.
* until the lock has been released. The thieves stop taking beans once there
* are no beans left to take.
*/ */
public class App { public class App {
/** /**
* main method * main method.
*/ */
public static void main(String[] args) { public static void main(String[] args) {
Mutex mutex = new Mutex(); Mutex mutex = new Mutex();

View File

@ -24,9 +24,8 @@
package com.iluwatar.mutex; package com.iluwatar.mutex;
/** /**
* A Jar has a resource of beans which can only be accessed by a single Thief * A Jar has a resource of beans which can only be accessed by a single Thief (thread) at any one
* (thread) at any one time. A Mutex lock is used to prevent more than one Thief * time. A Mutex lock is used to prevent more than one Thief taking a bean simultaneously.
* taking a bean simultaneously.
*/ */
public class Jar { public class Jar {

View File

@ -34,16 +34,15 @@ public class Mutex implements Lock {
private Object owner; private Object owner;
/** /**
* Returns the current owner of the Mutex, or null if available * Returns the current owner of the Mutex, or null if available.
*/ */
public Object getOwner() { public Object getOwner() {
return owner; return owner;
} }
/** /**
* Method called by a thread to acquire the lock. If the lock has already * Method called by a thread to acquire the lock. If the lock has already been acquired this will
* been acquired this will wait until the lock has been released to * wait until the lock has been released to re-attempt the acquire.
* re-attempt the acquire.
*/ */
@Override @Override
public synchronized void acquire() throws InterruptedException { public synchronized void acquire() throws InterruptedException {

View File

@ -27,8 +27,8 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
/** /**
* Thief is a class which continually tries to acquire a jar and take a bean * Thief is a class which continually tries to acquire a jar and take a bean from it. When the jar
* from it. When the jar is empty the thief stops. * is empty the thief stops.
*/ */
public class Thief extends Thread { public class Thief extends Thread {
@ -40,7 +40,7 @@ public class Thief extends Thread {
private final String name; private final String name;
/** /**
* The jar * The jar.
*/ */
private final Jar jar; private final Jar jar;
@ -50,8 +50,7 @@ public class Thief extends Thread {
} }
/** /**
* In the run method the thief repeatedly tries to take a bean until none * In the run method the thief repeatedly tries to take a bean until none are left.
* are left.
*/ */
@Override @Override
public void run() { public void run() {

View File

@ -31,7 +31,7 @@ Use the Specification pattern when
Real world example Real world example
> There is a pool of different creatures and we often need to select some subset of them. We can write our search specification such as "creatures that can fly" and give it to the party that will perform the filtering. > There is a pool of different creatures and we often need to select some subset of them. We can write our search specification such as "creatures that can fly" or "creatures heavier than 500 kilograms" and give it to the party that will perform the filtering.
In Plain Words In Plain Words
@ -43,7 +43,8 @@ Wikipedia says
**Programmatic Example** **Programmatic Example**
If we look at our creature pool example from above, we have a set of creatures with certain properties. If we look at our creature pool example from above, we have a set of creatures with certain properties.\
Those properties can be part of a pre-defined, limited set (represented here by the enums Size, Movement and Color); but they can also be discrete (e.g. the mass of a Creature). In this case, it is more appropriate to use what we call "parameterized specification", where the property value can be given as an argument when the Creature is created, allowing for more flexibility.
```java ```java
public interface Creature { public interface Creature {
@ -51,6 +52,7 @@ public interface Creature {
Size getSize(); Size getSize();
Movement getMovement(); Movement getMovement();
Color getColor(); Color getColor();
Mass getMass();
} }
``` ```
@ -60,7 +62,7 @@ And dragon implementation looks like this.
public class Dragon extends AbstractCreature { public class Dragon extends AbstractCreature {
public Dragon() { public Dragon() {
super("Dragon", Size.LARGE, Movement.FLYING, Color.RED); super("Dragon", Size.LARGE, Movement.FLYING, Color.RED, new Mass(39300.0));
} }
} }
``` ```
@ -83,6 +85,24 @@ public class MovementSelector implements Predicate<Creature> {
} }
``` ```
On the other hand, we selecting creatures heavier than a chosen amount, we use MassGreaterThanSelector.
```java
public class MassGreaterThanSelector implements Predicate<Creature> {
private final Mass mass;
public MassGreaterThanSelector(double mass) {
this.mass = new Mass(mass);
}
@Override
public boolean test(Creature t) {
return t.getMass().greaterThan(mass);
}
}
```
With these building blocks in place, we can perform a search for red and flying creatures like this. With these building blocks in place, we can perform a search for red and flying creatures like this.
```java ```java
@ -90,6 +110,13 @@ With these building blocks in place, we can perform a search for red and flying
.filter(new ColorSelector(Color.RED).and(new MovementSelector(Movement.FLYING))).collect(Collectors.toList()); .filter(new ColorSelector(Color.RED).and(new MovementSelector(Movement.FLYING))).collect(Collectors.toList());
``` ```
But we could also use our paramterized selector like this.
```java
List<Creature> heavyCreatures = creatures.stream()
.filter(new MassGreaterThanSelector(500.0).collect(Collectors.toList());
```
## Related patterns ## Related patterns
* Repository * Repository

View File

@ -31,8 +31,11 @@ import com.iluwatar.specification.creature.Octopus;
import com.iluwatar.specification.creature.Shark; import com.iluwatar.specification.creature.Shark;
import com.iluwatar.specification.creature.Troll; import com.iluwatar.specification.creature.Troll;
import com.iluwatar.specification.property.Color; import com.iluwatar.specification.property.Color;
import com.iluwatar.specification.property.Mass;
import com.iluwatar.specification.property.Movement; import com.iluwatar.specification.property.Movement;
import com.iluwatar.specification.selector.ColorSelector; import com.iluwatar.specification.selector.ColorSelector;
import com.iluwatar.specification.selector.MassGreaterThanSelector;
import com.iluwatar.specification.selector.MassSmallerThanOrEqSelector;
import com.iluwatar.specification.selector.MovementSelector; import com.iluwatar.specification.selector.MovementSelector;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -61,6 +64,8 @@ public class App {
// initialize creatures list // initialize creatures list
List<Creature> creatures = List.of(new Goblin(), new Octopus(), new Dragon(), new Shark(), List<Creature> creatures = List.of(new Goblin(), new Octopus(), new Dragon(), new Shark(),
new Troll(), new KillerBee()); new Troll(), new KillerBee());
// so-called "hard-coded" specification
LOGGER.info("Demonstrating hard-coded specification :");
// find all walking creatures // find all walking creatures
LOGGER.info("Find all walking creatures"); LOGGER.info("Find all walking creatures");
List<Creature> walkingCreatures = List<Creature> walkingCreatures =
@ -79,5 +84,19 @@ public class App {
.filter(new ColorSelector(Color.RED).and(new MovementSelector(Movement.FLYING))) .filter(new ColorSelector(Color.RED).and(new MovementSelector(Movement.FLYING)))
.collect(Collectors.toList()); .collect(Collectors.toList());
redAndFlyingCreatures.forEach(c -> LOGGER.info(c.toString())); redAndFlyingCreatures.forEach(c -> LOGGER.info(c.toString()));
// so-called "parameterized" specification
LOGGER.info("Demonstrating parameterized specification :");
// find all creatures heavier than 500kg
LOGGER.info("Find all creatures heavier than 600kg");
List<Creature> heavyCreatures =
creatures.stream().filter(new MassGreaterThanSelector(600.0))
.collect(Collectors.toList());
heavyCreatures.forEach(c -> LOGGER.info(c.toString()));
// find all creatures heavier than 500kg
LOGGER.info("Find all creatures lighter than or weighing exactly 500kg");
List<Creature> lightCreatures =
creatures.stream().filter(new MassSmallerThanOrEqSelector(500.0))
.collect(Collectors.toList());
lightCreatures.forEach(c -> LOGGER.info(c.toString()));
} }
} }

View File

@ -24,6 +24,7 @@
package com.iluwatar.specification.creature; package com.iluwatar.specification.creature;
import com.iluwatar.specification.property.Color; import com.iluwatar.specification.property.Color;
import com.iluwatar.specification.property.Mass;
import com.iluwatar.specification.property.Movement; import com.iluwatar.specification.property.Movement;
import com.iluwatar.specification.property.Size; import com.iluwatar.specification.property.Size;
@ -36,20 +37,23 @@ public abstract class AbstractCreature implements Creature {
private Size size; private Size size;
private Movement movement; private Movement movement;
private Color color; private Color color;
private Mass mass;
/** /**
* Constructor. * Constructor.
*/ */
public AbstractCreature(String name, Size size, Movement movement, Color color) { public AbstractCreature(String name, Size size, Movement movement, Color color, Mass mass) {
this.name = name; this.name = name;
this.size = size; this.size = size;
this.movement = movement; this.movement = movement;
this.color = color; this.color = color;
this.mass = mass;
} }
@Override @Override
public String toString() { public String toString() {
return String.format("%s [size=%s, movement=%s, color=%s]", name, size, movement, color); return String.format("%s [size=%s, movement=%s, color=%s, mass=%s]",
name, size, movement, color, mass);
} }
@Override @Override
@ -71,4 +75,9 @@ public abstract class AbstractCreature implements Creature {
public Color getColor() { public Color getColor() {
return color; return color;
} }
@Override
public Mass getMass() {
return mass;
}
} }

View File

@ -24,6 +24,7 @@
package com.iluwatar.specification.creature; package com.iluwatar.specification.creature;
import com.iluwatar.specification.property.Color; import com.iluwatar.specification.property.Color;
import com.iluwatar.specification.property.Mass;
import com.iluwatar.specification.property.Movement; import com.iluwatar.specification.property.Movement;
import com.iluwatar.specification.property.Size; import com.iluwatar.specification.property.Size;
@ -39,4 +40,6 @@ public interface Creature {
Movement getMovement(); Movement getMovement();
Color getColor(); Color getColor();
Mass getMass();
} }

View File

@ -24,6 +24,7 @@
package com.iluwatar.specification.creature; package com.iluwatar.specification.creature;
import com.iluwatar.specification.property.Color; import com.iluwatar.specification.property.Color;
import com.iluwatar.specification.property.Mass;
import com.iluwatar.specification.property.Movement; import com.iluwatar.specification.property.Movement;
import com.iluwatar.specification.property.Size; import com.iluwatar.specification.property.Size;
@ -33,6 +34,10 @@ import com.iluwatar.specification.property.Size;
public class Dragon extends AbstractCreature { public class Dragon extends AbstractCreature {
public Dragon() { public Dragon() {
super("Dragon", Size.LARGE, Movement.FLYING, Color.RED); this(new Mass(39300.0));
}
public Dragon(Mass mass) {
super("Dragon", Size.LARGE, Movement.FLYING, Color.RED, mass);
} }
} }

View File

@ -24,6 +24,7 @@
package com.iluwatar.specification.creature; package com.iluwatar.specification.creature;
import com.iluwatar.specification.property.Color; import com.iluwatar.specification.property.Color;
import com.iluwatar.specification.property.Mass;
import com.iluwatar.specification.property.Movement; import com.iluwatar.specification.property.Movement;
import com.iluwatar.specification.property.Size; import com.iluwatar.specification.property.Size;
@ -33,6 +34,10 @@ import com.iluwatar.specification.property.Size;
public class Goblin extends AbstractCreature { public class Goblin extends AbstractCreature {
public Goblin() { public Goblin() {
super("Goblin", Size.SMALL, Movement.WALKING, Color.GREEN); this(new Mass(30.0));
}
public Goblin(Mass mass) {
super("Goblin", Size.SMALL, Movement.WALKING, Color.GREEN, mass);
} }
} }

View File

@ -24,6 +24,7 @@
package com.iluwatar.specification.creature; package com.iluwatar.specification.creature;
import com.iluwatar.specification.property.Color; import com.iluwatar.specification.property.Color;
import com.iluwatar.specification.property.Mass;
import com.iluwatar.specification.property.Movement; import com.iluwatar.specification.property.Movement;
import com.iluwatar.specification.property.Size; import com.iluwatar.specification.property.Size;
@ -33,6 +34,10 @@ import com.iluwatar.specification.property.Size;
public class KillerBee extends AbstractCreature { public class KillerBee extends AbstractCreature {
public KillerBee() { public KillerBee() {
super("KillerBee", Size.SMALL, Movement.FLYING, Color.LIGHT); this(new Mass(6.7));
}
public KillerBee(Mass mass) {
super("KillerBee", Size.SMALL, Movement.FLYING, Color.LIGHT, mass);
} }
} }

View File

@ -24,6 +24,7 @@
package com.iluwatar.specification.creature; package com.iluwatar.specification.creature;
import com.iluwatar.specification.property.Color; import com.iluwatar.specification.property.Color;
import com.iluwatar.specification.property.Mass;
import com.iluwatar.specification.property.Movement; import com.iluwatar.specification.property.Movement;
import com.iluwatar.specification.property.Size; import com.iluwatar.specification.property.Size;
@ -33,6 +34,10 @@ import com.iluwatar.specification.property.Size;
public class Octopus extends AbstractCreature { public class Octopus extends AbstractCreature {
public Octopus() { public Octopus() {
super("Octopus", Size.NORMAL, Movement.SWIMMING, Color.DARK); this(new Mass(12.0));
}
public Octopus(Mass mass) {
super("Octopus", Size.NORMAL, Movement.SWIMMING, Color.DARK, mass);
} }
} }

View File

@ -24,6 +24,7 @@
package com.iluwatar.specification.creature; package com.iluwatar.specification.creature;
import com.iluwatar.specification.property.Color; import com.iluwatar.specification.property.Color;
import com.iluwatar.specification.property.Mass;
import com.iluwatar.specification.property.Movement; import com.iluwatar.specification.property.Movement;
import com.iluwatar.specification.property.Size; import com.iluwatar.specification.property.Size;
@ -33,6 +34,10 @@ import com.iluwatar.specification.property.Size;
public class Shark extends AbstractCreature { public class Shark extends AbstractCreature {
public Shark() { public Shark() {
super("Shark", Size.NORMAL, Movement.SWIMMING, Color.LIGHT); this(new Mass(500.0));
}
public Shark(Mass mass) {
super("Shark", Size.NORMAL, Movement.SWIMMING, Color.LIGHT, mass);
} }
} }

View File

@ -24,6 +24,7 @@
package com.iluwatar.specification.creature; package com.iluwatar.specification.creature;
import com.iluwatar.specification.property.Color; import com.iluwatar.specification.property.Color;
import com.iluwatar.specification.property.Mass;
import com.iluwatar.specification.property.Movement; import com.iluwatar.specification.property.Movement;
import com.iluwatar.specification.property.Size; import com.iluwatar.specification.property.Size;
@ -33,6 +34,10 @@ import com.iluwatar.specification.property.Size;
public class Troll extends AbstractCreature { public class Troll extends AbstractCreature {
public Troll() { public Troll() {
super("Troll", Size.LARGE, Movement.WALKING, Color.DARK); this(new Mass(4000.0));
}
public Troll(Mass mass) {
super("Troll", Size.LARGE, Movement.WALKING, Color.DARK, mass);
} }
} }

View File

@ -24,7 +24,7 @@
package com.iluwatar.specification.property; package com.iluwatar.specification.property;
/** /**
* <p>Color property.</p> * Color property.
*/ */
public enum Color { public enum Color {

View File

@ -0,0 +1,65 @@
/*
* The MIT License
* Copyright © 2014-2019 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.specification.property;
/** Mass property. */
public class Mass {
private double value;
private String title;
public Mass(double value) {
this.value = value;
this.title = value + "kg"; // Implicit call to Double.toString(value)
}
public final boolean greaterThan(Mass other) {
return this.value > other.value;
}
public final boolean smallerThan(Mass other) {
return this.value < other.value;
}
public final boolean greaterThanOrEq(Mass other) {
return this.value >= other.value;
}
public final boolean smallerThanOrEq(Mass other) {
return this.value <= other.value;
}
@Override
public String toString() {
return title;
}
@Override
public final boolean equals(Object obj) {
if (!(obj instanceof Mass)) {
return false;
}
return ((Mass) obj).value == this.value;
}
}

View File

@ -0,0 +1,44 @@
/*
* The MIT License
* Copyright © 2014-2019 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.specification.selector;
import com.iluwatar.specification.creature.Creature;
import com.iluwatar.specification.property.Mass;
import java.util.function.Predicate;
/** Mass selector for values greater than the parameter. */
public class MassGreaterThanSelector implements Predicate<Creature> {
private final Mass mass;
/** The use of a double as a parameter will spare some typing when instantiating this class. */
public MassGreaterThanSelector(double mass) {
this.mass = new Mass(mass);
}
@Override
public boolean test(Creature t) {
return t.getMass().greaterThan(mass);
}
}

View File

@ -0,0 +1,44 @@
/*
* The MIT License
* Copyright © 2014-2019 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.specification.selector;
import com.iluwatar.specification.creature.Creature;
import com.iluwatar.specification.property.Mass;
import java.util.function.Predicate;
/** Mass selector for values smaller or equal to the parameter. */
public class MassSmallerThanOrEqSelector implements Predicate<Creature> {
private final Mass mass;
/** The use of a double as a parameter will spare some typing when instantiating this class. */
public MassSmallerThanOrEqSelector(double mass) {
this.mass = new Mass(mass);
}
@Override
public boolean test(Creature t) {
return t.getMass().smallerThanOrEq(mass);
}
}

View File

@ -24,6 +24,7 @@
package com.iluwatar.specification.creature; package com.iluwatar.specification.creature;
import com.iluwatar.specification.property.Color; import com.iluwatar.specification.property.Color;
import com.iluwatar.specification.property.Mass;
import com.iluwatar.specification.property.Movement; import com.iluwatar.specification.property.Movement;
import com.iluwatar.specification.property.Size; import com.iluwatar.specification.property.Size;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
@ -47,12 +48,12 @@ public class CreatureTest {
*/ */
public static Collection<Object[]> dataProvider() { public static Collection<Object[]> dataProvider() {
return List.of( return List.of(
new Object[]{new Dragon(), "Dragon", Size.LARGE, Movement.FLYING, Color.RED}, new Object[]{new Dragon(), "Dragon", Size.LARGE, Movement.FLYING, Color.RED, new Mass(39300.0)},
new Object[]{new Goblin(), "Goblin", Size.SMALL, Movement.WALKING, Color.GREEN}, new Object[]{new Goblin(), "Goblin", Size.SMALL, Movement.WALKING, Color.GREEN, new Mass(30.0)},
new Object[]{new KillerBee(), "KillerBee", Size.SMALL, Movement.FLYING, Color.LIGHT}, new Object[]{new KillerBee(), "KillerBee", Size.SMALL, Movement.FLYING, Color.LIGHT, new Mass(6.7)},
new Object[]{new Octopus(), "Octopus", Size.NORMAL, Movement.SWIMMING, Color.DARK}, new Object[]{new Octopus(), "Octopus", Size.NORMAL, Movement.SWIMMING, Color.DARK, new Mass(12.0)},
new Object[]{new Shark(), "Shark", Size.NORMAL, Movement.SWIMMING, Color.LIGHT}, new Object[]{new Shark(), "Shark", Size.NORMAL, Movement.SWIMMING, Color.LIGHT, new Mass(500.0)},
new Object[]{new Troll(), "Troll", Size.LARGE, Movement.WALKING, Color.DARK} new Object[]{new Troll(), "Troll", Size.LARGE, Movement.WALKING, Color.DARK, new Mass(4000.0)}
); );
} }
@ -82,11 +83,17 @@ public class CreatureTest {
@ParameterizedTest @ParameterizedTest
@MethodSource("dataProvider") @MethodSource("dataProvider")
public void testToString(Creature testedCreature, String name, Size size, Movement movement, Color color) { public void testGetMass(Creature testedCreature, String name, Size size, Movement movement, Color color, Mass mass) {
assertEquals(mass, testedCreature.getMass());
}
@ParameterizedTest
@MethodSource("dataProvider")
public void testToString(Creature testedCreature, String name, Size size, Movement movement, Color color, Mass mass) {
final String toString = testedCreature.toString(); final String toString = testedCreature.toString();
assertNotNull(toString); assertNotNull(toString);
assertEquals( assertEquals(
String.format("%s [size=%s, movement=%s, color=%s]", name, size, movement, color), String.format("%s [size=%s, movement=%s, color=%s, mass=%s]", name, size, movement, color, mass),
toString toString
); );
} }

View File

@ -0,0 +1,50 @@
/*
* The MIT License
* Copyright © 2014-2019 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.specification.selector;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.iluwatar.specification.creature.Creature;
import com.iluwatar.specification.property.Mass;
import org.junit.jupiter.api.Test;
public class MassSelectorTest {
/** Verify if the mass selector gives the correct results */
@Test
public void testMass() {
final Creature lightCreature = mock(Creature.class);
when(lightCreature.getMass()).thenReturn(new Mass(50.0));
final Creature heavyCreature = mock(Creature.class);
when(heavyCreature.getMass()).thenReturn(new Mass(2500.0));
final MassSmallerThanOrEqSelector lightSelector = new MassSmallerThanOrEqSelector(500.0);
assertTrue(lightSelector.test(lightCreature));
assertFalse(lightSelector.test(heavyCreature));
}
}