diff --git a/pom.xml b/pom.xml index d330bbfcb..020c784c6 100644 --- a/pom.xml +++ b/pom.xml @@ -60,6 +60,7 @@ 2.3.1 2.3.2 1.3.2 + 1.19.0 abstract-factory @@ -180,8 +181,8 @@ bytecode leader-election data-locality + subclass-sandbox circuit-breaker - @@ -317,6 +318,12 @@ javassist 3.25.0-GA + + com.github.stefanbirkner + system-rules + ${system-rules.version} + test + diff --git a/subclass-sandbox/README.md b/subclass-sandbox/README.md new file mode 100644 index 000000000..674991f08 --- /dev/null +++ b/subclass-sandbox/README.md @@ -0,0 +1,29 @@ + +--- +layout: pattern +title: Subclass Sandbox +folder: subclass-sandbox +permalink: /patterns/subclass-sandbox/ +categories: Other +tags: + - Java + - Difficulty-Beginner +--- + +## Intent +The subclass sandbox pattern describes a basic idea, while not having a lot of detailed mechanics. You will need the pattern when you have several similar subclasses. If you have to make a tiny change, then change the base class, while all subclasses shouldn't have to be touched. So the base class has to be able to provide all of the operations a derived class needs to perform. + +## Applicability +The Subclass Sandbox pattern is a very simple, common pattern lurking in lots of codebases, even outside of games. If you have a non-virtual protected method laying around, you’re probably already using something like this. Subclass Sandbox is a good fit when: + +- You have a base class with a number of derived classes. + +- The base class is able to provide all of the operations that a derived class may need to perform. + +- There is behavioral overlap in the subclasses and you want to make it easier to share code between them. + +- You want to minimize coupling between those derived classes and the rest of the program. + +## Credits + +* [Game Programming Patterns - Subclass Sandbox]([http://gameprogrammingpatterns.com/subclass-sandbox.html](http://gameprogrammingpatterns.com/subclass-sandbox.html)) \ No newline at end of file diff --git a/subclass-sandbox/pom.xml b/subclass-sandbox/pom.xml new file mode 100644 index 000000000..8016ba471 --- /dev/null +++ b/subclass-sandbox/pom.xml @@ -0,0 +1,49 @@ + + + + + java-design-patterns + com.iluwatar + 1.22.0-SNAPSHOT + + 4.0.0 + + subclass-sandbox + + + + junit + junit + + + com.github.stefanbirkner + system-rules + + + + \ No newline at end of file diff --git a/subclass-sandbox/src/main/java/com/iluwatar/subclasssandbox/App.java b/subclass-sandbox/src/main/java/com/iluwatar/subclasssandbox/App.java new file mode 100644 index 000000000..2e1f41c05 --- /dev/null +++ b/subclass-sandbox/src/main/java/com/iluwatar/subclasssandbox/App.java @@ -0,0 +1,53 @@ +/** + * The MIT License + * Copyright (c) 2014-2016 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.subclasssandbox; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The subclass sandbox pattern describes a basic idea, while not having a lot + * of detailed mechanics. You will need the pattern when you have several similar + * subclasses. If you have to make a tiny change, then change the base class, + * while all subclasses shouldn't have to be touched. So the base class has to be + * able to provide all of the operations a derived class needs to perform. + */ +public class App { + + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + + /** + * Entry point of the main program. + * @param args Program runtime arguments. + */ + public static void main(String[] args) { + LOGGER.info("Use superpower: sky launch"); + var skyLaunch = new SkyLaunch(); + skyLaunch.activate(); + LOGGER.info("Use superpower: ground dive"); + var groundDive = new GroundDive(); + groundDive.activate(); + } + +} diff --git a/subclass-sandbox/src/main/java/com/iluwatar/subclasssandbox/GroundDive.java b/subclass-sandbox/src/main/java/com/iluwatar/subclasssandbox/GroundDive.java new file mode 100644 index 000000000..f284e125f --- /dev/null +++ b/subclass-sandbox/src/main/java/com/iluwatar/subclasssandbox/GroundDive.java @@ -0,0 +1,44 @@ +/** + * The MIT License + * Copyright (c) 2014-2016 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.subclasssandbox; + +import org.slf4j.LoggerFactory; + +/** + * GroundDive superpower. + */ +public class GroundDive extends Superpower { + + public GroundDive() { + super(); + logger = LoggerFactory.getLogger(GroundDive.class); + } + + @Override + protected void activate() { + move(0, 0, -20); + playSound("GROUNDDIVE_SOUND", 5); + spawnParticles("GROUNDDIVE_PARTICLE", 20); + } +} diff --git a/subclass-sandbox/src/main/java/com/iluwatar/subclasssandbox/SkyLaunch.java b/subclass-sandbox/src/main/java/com/iluwatar/subclasssandbox/SkyLaunch.java new file mode 100644 index 000000000..16c94bd0c --- /dev/null +++ b/subclass-sandbox/src/main/java/com/iluwatar/subclasssandbox/SkyLaunch.java @@ -0,0 +1,44 @@ +/** + * The MIT License + * Copyright (c) 2014-2016 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.subclasssandbox; + +import org.slf4j.LoggerFactory; + +/** + * SkyLaunch superpower. + */ +public class SkyLaunch extends Superpower { + + public SkyLaunch() { + super(); + logger = LoggerFactory.getLogger(SkyLaunch.class); + } + + @Override + protected void activate() { + move(0, 0, 20); + playSound("SKYLAUNCH_SOUND", 1); + spawnParticles("SKYLAUNCH_PARTICLE", 100); + } +} diff --git a/subclass-sandbox/src/main/java/com/iluwatar/subclasssandbox/Superpower.java b/subclass-sandbox/src/main/java/com/iluwatar/subclasssandbox/Superpower.java new file mode 100644 index 000000000..d4875d586 --- /dev/null +++ b/subclass-sandbox/src/main/java/com/iluwatar/subclasssandbox/Superpower.java @@ -0,0 +1,69 @@ +/** + * The MIT License + * Copyright (c) 2014-2016 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.subclasssandbox; + +import org.slf4j.Logger; + +/** + * Superpower abstract class. In this class the basic operations of all types of + * superpowers are provided as protected methods. + */ +public abstract class Superpower { + + protected Logger logger; + + /** + * Subclass of superpower should implement this sandbox method by calling the + * methods provided in this super class. + */ + protected abstract void activate(); + + /** + * Move to (x, y, z). + * @param x X coordinate. + * @param y Y coordinate. + * @param z Z coordinate. + */ + protected void move(double x, double y, double z) { + logger.info("Move to ( " + x + ", " + y + ", " + z + " )"); + } + + /** + * Play sound effect for the superpower. + * @param soundName Sound name. + * @param volumn Value of volumn. + */ + protected void playSound(String soundName, int volumn) { + logger.info("Play " + soundName + " with volumn " + volumn); + } + + /** + * Spawn particles for the superpower. + * @param particleType Particle type. + * @param count Count of particles to be spawned. + */ + protected void spawnParticles(String particleType, int count) { + logger.info("Spawn " + count + " particle with type " + particleType); + } +} diff --git a/subclass-sandbox/src/test/java/com/iluwatar/subclasssandbox/AppTest.java b/subclass-sandbox/src/test/java/com/iluwatar/subclasssandbox/AppTest.java new file mode 100644 index 000000000..207c71ef9 --- /dev/null +++ b/subclass-sandbox/src/test/java/com/iluwatar/subclasssandbox/AppTest.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014-2016 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.subclasssandbox; + +import org.junit.Test; + +/** + * App unit tests. + */ +public class AppTest { + + @Test + public void testMain() { + String[] args = {}; + App.main(args); + } +} diff --git a/subclass-sandbox/src/test/java/com/iluwatar/subclasssandbox/GroundDiveTest.java b/subclass-sandbox/src/test/java/com/iluwatar/subclasssandbox/GroundDiveTest.java new file mode 100644 index 000000000..7b4645742 --- /dev/null +++ b/subclass-sandbox/src/test/java/com/iluwatar/subclasssandbox/GroundDiveTest.java @@ -0,0 +1,92 @@ +/** + * The MIT License + * Copyright (c) 2014-2016 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.subclasssandbox; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.SystemOutRule; + +/** + * GroundDive unit tests. + */ +public class GroundDiveTest { + + @Rule + public SystemOutRule log = new SystemOutRule().enableLog(); + + @Test + public void testMove() { + log.clearLog(); + var groundDive = new GroundDive(); + groundDive.move(1.0, 1.0, 1.0); + var outputLog = getLogContent(log.getLog()); + var expectedLog = "Move to ( 1.0, 1.0, 1.0 )"; + Assert.assertEquals(outputLog, expectedLog); + } + + @Test + public void testPlaySound() { + log.clearLog(); + var groundDive = new GroundDive(); + groundDive.playSound("SOUND_NAME", 1); + var outputLog = getLogContent(log.getLog()); + var expectedLog = "Play SOUND_NAME with volumn 1"; + Assert.assertEquals(outputLog, expectedLog); + } + + @Test + public void testSpawnParticles() { + log.clearLog(); + var groundDive = new GroundDive(); + groundDive.spawnParticles("PARTICLE_TYPE", 100); + final var outputLog = getLogContent(log.getLog()); + final var expectedLog = "Spawn 100 particle with type PARTICLE_TYPE"; + Assert.assertEquals(outputLog, expectedLog); + } + + @Test + public void testActivate() { + log.clearLog(); + var groundDive = new GroundDive(); + groundDive.activate();; + String[] logs = log.getLog().split("\n"); + final var expectedSize = 3; + final var log1 = logs[0].split("-")[1].trim() + " -" + logs[0].split("-")[2].trim(); + final var expectedLog1 = "Move to ( 0.0, 0.0, -20.0 )"; + final var log2 = getLogContent(logs[1]); + final var expectedLog2 = "Play GROUNDDIVE_SOUND with volumn 5"; + final var log3 = getLogContent(logs[2]); + final var expectedLog3 = "Spawn 20 particle with type GROUNDDIVE_PARTICLE"; + Assert.assertEquals(logs.length, expectedSize); + Assert.assertEquals(log1, expectedLog1); + Assert.assertEquals(log2, expectedLog2); + Assert.assertEquals(log3, expectedLog3); + } + + private String getLogContent(String log) { + return log.split("-")[1].trim(); + } + +} diff --git a/subclass-sandbox/src/test/java/com/iluwatar/subclasssandbox/SkyLaunchTest.java b/subclass-sandbox/src/test/java/com/iluwatar/subclasssandbox/SkyLaunchTest.java new file mode 100644 index 000000000..5ba84c0b5 --- /dev/null +++ b/subclass-sandbox/src/test/java/com/iluwatar/subclasssandbox/SkyLaunchTest.java @@ -0,0 +1,91 @@ +/** + * The MIT License + * Copyright (c) 2014-2016 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.subclasssandbox; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.SystemOutRule; + +/** + * SkyLaunch unit tests. + */ +public class SkyLaunchTest { + + @Rule + public SystemOutRule log = new SystemOutRule().enableLog(); + + @Test + public void testMove() { + log.clearLog(); + var skyLaunch = new SkyLaunch(); + skyLaunch.move(1.0, 1.0, 1.0); + var outputLog = getLogContent(log.getLog()); + var expectedLog = "Move to ( 1.0, 1.0, 1.0 )"; + Assert.assertEquals(outputLog, expectedLog); + } + + @Test + public void testPlaySound() { + log.clearLog(); + var skyLaunch = new SkyLaunch(); + skyLaunch.playSound("SOUND_NAME", 1); + var outputLog = getLogContent(log.getLog()); + var expectedLog = "Play SOUND_NAME with volumn 1"; + Assert.assertEquals(outputLog, expectedLog); + } + + @Test + public void testSpawnParticles() { + log.clearLog(); + var skyLaunch = new SkyLaunch(); + skyLaunch.spawnParticles("PARTICLE_TYPE", 100); + var outputLog = getLogContent(log.getLog()); + var expectedLog = "Spawn 100 particle with type PARTICLE_TYPE"; + Assert.assertEquals(outputLog, expectedLog); + } + + @Test + public void testActivate() { + log.clearLog(); + var skyLaunch = new SkyLaunch(); + skyLaunch.activate();; + String[] logs = log.getLog().split("\n"); + final var expectedSize = 3; + final var log1 = getLogContent(logs[0]); + final var expectedLog1 = "Move to ( 0.0, 0.0, 20.0 )"; + final var log2 = getLogContent(logs[1]); + final var expectedLog2 = "Play SKYLAUNCH_SOUND with volumn 1"; + final var log3 = getLogContent(logs[2]); + final var expectedLog3 = "Spawn 100 particle with type SKYLAUNCH_PARTICLE"; + Assert.assertEquals(logs.length, expectedSize); + Assert.assertEquals(log1, expectedLog1); + Assert.assertEquals(log2, expectedLog2); + Assert.assertEquals(log3, expectedLog3); + } + + private String getLogContent(String log) { + return log.split("-")[1].trim(); + } +}