diff --git a/.gitignore b/.gitignore index 5d2cd77e1..589d3fb13 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,9 @@ target .idea *.iml *.swp -datanucleus.log \ No newline at end of file +datanucleus.log +/bin/ +/bin/ +/bin/ + +data-mapper/src/main/resources/log4j.xml \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 60693c5cc..deb436cd2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,4 +15,4 @@ after_success: - mvn clean test jacoco:report coveralls:report - bash update-ghpages.sh -sudo: false +sudo: false # route the build to the container-based infrastructure for a faster build diff --git a/CODE_COVERAGE.md b/CODE_COVERAGE.md new file mode 100644 index 000000000..589c7ad79 --- /dev/null +++ b/CODE_COVERAGE.md @@ -0,0 +1,13 @@ +# Code Coverage Report generation + +To generate the code coverage report, execute the following command: +> mvn clean verify + +This will generate code coverage report in each of the modules. In order to view the same, open the following file in your browser. +> target/site/jacoco/index.html + +Please note that the above folder is created under each of the modules. For example: +* adapter/target/site/jacoco/index.html +* busniess-delegate/target/site/jacoco/index.html + + diff --git a/CONTRIBUTING.MD b/CONTRIBUTING.MD new file mode 100644 index 000000000..39087fbf1 --- /dev/null +++ b/CONTRIBUTING.MD @@ -0,0 +1,4 @@ +This is great you have something to contribute! + +Before going any further please read the [wiki](https://github.com/iluwatar/java-design-patterns/wiki) +with conventions and rules we used for this project. diff --git a/LICENSE.md b/LICENSE.md index d1f75f80a..e73cf6618 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014 Ilkka Seppälä +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 diff --git a/README.md b/README.md index ac3aadd67..811d6a17a 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ that smart and dearly wants an empty line before a heading to be able to display it as such, e.g. website) --> -# Design pattern samples in Java +# Design patterns implemented in Java [![Build status](https://travis-ci.org/iluwatar/java-design-patterns.svg?branch=master)](https://travis-ci.org/iluwatar/java-design-patterns) [![Coverage Status](https://coveralls.io/repos/iluwatar/java-design-patterns/badge.svg?branch=master)](https://coveralls.io/r/iluwatar/java-design-patterns?branch=master) -[![Coverity Scan Build Status](https://scan.coverity.com/projects/5634/badge.svg)](https://scan.coverity.com/projects/5634) +[![License MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/LICENSE.md) [![Join the chat at https://gitter.im/iluwatar/java-design-patterns](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) # Introduction @@ -40,19 +40,7 @@ patterns by any of the following approaches # How to contribute -If you are willing to contribute to the project you will find the relevant information in our [developer wiki](https://github.com/iluwatar/java-design-patterns/wiki). - -# Credits - -* [Effective Java (2nd Edition)](http://www.amazon.com/Effective-Java-Edition-Joshua-Bloch/dp/0321356683) -* [Java Generics and Collections](http://www.amazon.com/Java-Generics-Collections-Maurice-Naftalin/dp/0596527756/) -* [Let's Modify the Objects-First Approach into Design-Patterns-First](http://edu.pecinovsky.cz/papers/2006_ITiCSE_Design_Patterns_First.pdf) -* [Pattern Languages of Program Design](http://www.amazon.com/Pattern-Languages-Program-Design-Coplien/dp/0201607344/ref=sr_1_1) -* [Presentation Tier Patterns](http://www.javagyan.com/tutorials/corej2eepatterns/presentation-tier-patterns) -* [Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions](http://www.amazon.com/Functional-Programming-Java-Harnessing-Expressions/dp/1937785467/ref=sr_1_1) -* [Patterns of Enterprise Application Architecture](http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420) -* [Spring Data](http://www.amazon.com/Spring-Data-Mark-Pollack/dp/1449323952/ref=sr_1_1) -* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) +If you are willing to contribute to the project you will find the relevant information in our [developer wiki](https://github.com/iluwatar/java-design-patterns/wiki). We will help you and answer your questions in the [Gitter chatroom](https://gitter.im/iluwatar/java-design-patterns). # License diff --git a/abstract-document/README.md b/abstract-document/README.md new file mode 100644 index 000000000..bf28ff999 --- /dev/null +++ b/abstract-document/README.md @@ -0,0 +1,31 @@ +--- +layout: pattern +title: Abstract Document +folder: abstract-document +permalink: /patterns/abstract-document/ +categories: Structural +tags: + - Java + - Difficulty-Intermediate +--- + +## Intent +Achieve flexibility of untyped languages and keep the type-safety + +![alt text](./etc/abstract-document-base.png "Abstract Document Base") + +![alt text](./etc/abstract-document.png "Abstract Document Traits and Domain") + + +## Applicability +Use the Abstract Document Pattern when + +* there is a need to add new properties on the fly +* you want a flexible way to organize domain in tree like structure +* you want more loosely coupled system + + +## Credits + +* [Wikipedia: Abstract Document Pattern](https://en.wikipedia.org/wiki/Abstract_Document_Pattern) +* [Martin Fowler: Dealing with properties](http://martinfowler.com/apsupp/properties.pdf) \ No newline at end of file diff --git a/abstract-document/etc/abstract-document-base.png b/abstract-document/etc/abstract-document-base.png new file mode 100644 index 000000000..13345dbb8 Binary files /dev/null and b/abstract-document/etc/abstract-document-base.png differ diff --git a/abstract-document/etc/abstract-document-base.ucls b/abstract-document/etc/abstract-document-base.ucls new file mode 100644 index 000000000..bfe927ed9 --- /dev/null +++ b/abstract-document/etc/abstract-document-base.ucls @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/abstract-document/etc/abstract-document.png b/abstract-document/etc/abstract-document.png new file mode 100644 index 000000000..98d186f7e Binary files /dev/null and b/abstract-document/etc/abstract-document.png differ diff --git a/abstract-document/etc/abstract-document.ucls b/abstract-document/etc/abstract-document.ucls new file mode 100644 index 000000000..ad97457fd --- /dev/null +++ b/abstract-document/etc/abstract-document.ucls @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/abstract-document/pom.xml b/abstract-document/pom.xml new file mode 100644 index 000000000..b7a348d26 --- /dev/null +++ b/abstract-document/pom.xml @@ -0,0 +1,42 @@ + + + + 4.0.0 + + java-design-patterns + com.iluwatar + 1.13.0-SNAPSHOT + + abstract-document + + + junit + junit + test + + + \ No newline at end of file diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java new file mode 100644 index 000000000..4bf8f0d14 --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java @@ -0,0 +1,72 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractdocument; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Stream; + +/** + * Abstract implementation of Document interface + */ +public abstract class AbstractDocument implements Document { + + private final Map properties; + + protected AbstractDocument(Map properties) { + Objects.requireNonNull(properties, "properties map is required"); + this.properties = properties; + } + + @Override + public Void put(String key, Object value) { + properties.put(key, value); + return null; + } + + @Override + public Object get(String key) { + return properties.get(key); + } + + @Override + public Stream children(String key, Function, T> constructor) { + Optional>> any = Stream.of(get(key)).filter(el -> el != null) + .map(el -> (List>) el).findAny(); + return any.isPresent() ? any.get().stream().map(constructor) : Stream.empty(); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(getClass().getName()).append("["); + properties.entrySet() + .forEach(e -> builder.append("[").append(e.getKey()).append(" : ").append(e.getValue()).append("]")); + builder.append("]"); + return builder.toString(); + } + +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java new file mode 100644 index 000000000..d7758b6f7 --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java @@ -0,0 +1,88 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractdocument; + +import com.iluwatar.abstractdocument.domain.Car; +import com.iluwatar.abstractdocument.domain.HasModel; +import com.iluwatar.abstractdocument.domain.HasParts; +import com.iluwatar.abstractdocument.domain.HasPrice; +import com.iluwatar.abstractdocument.domain.HasType; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * The Abstract Document pattern enables handling additional, non-static + * properties. This pattern uses concept of traits to enable type safety and + * separate properties of different classes into set of interfaces. + *

+ *

+ * In Abstract Document pattern,({@link AbstractDocument}) fully implements + * {@link Document}) interface. Traits are then defined to enable access to + * properties in usual, static way. + */ +public class App { + + /** + * Executes the App + */ + public App() { + System.out.println("Constructing parts and car"); + + Map carProperties = new HashMap<>(); + carProperties.put(HasModel.PROPERTY, "300SL"); + carProperties.put(HasPrice.PROPERTY, 10000L); + + Map wheelProperties = new HashMap<>(); + wheelProperties.put(HasType.PROPERTY, "wheel"); + wheelProperties.put(HasModel.PROPERTY, "15C"); + wheelProperties.put(HasPrice.PROPERTY, 100L); + + Map doorProperties = new HashMap<>(); + doorProperties.put(HasType.PROPERTY, "door"); + doorProperties.put(HasModel.PROPERTY, "Lambo"); + doorProperties.put(HasPrice.PROPERTY, 300L); + + carProperties.put(HasParts.PROPERTY, Arrays.asList(wheelProperties, doorProperties)); + + Car car = new Car(carProperties); + + System.out.println("Here is our car:"); + System.out.println("-> model: " + car.getModel().get()); + System.out.println("-> price: " + car.getPrice().get()); + System.out.println("-> parts: "); + car.getParts().forEach(p -> System.out + .println("\t" + p.getType().get() + "/" + p.getModel().get() + "/" + p.getPrice().get())); + } + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + new App(); + } + +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java new file mode 100644 index 000000000..7705f37eb --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java @@ -0,0 +1,59 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractdocument; + +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Stream; + +/** + * Document interface + */ +public interface Document { + + /** + * Puts the value related to the key + * + * @param key element key + * @param value element value + * @return Void + */ + Void put(String key, Object value); + + /** + * Gets the value for the key + * + * @param key element key + * @return value or null + */ + Object get(String key); + + /** + * Gets the stream of child documents + * + * @param key element key + * @param constructor constructor of child class + * @return child documents + */ + Stream children(String key, Function, T> constructor); +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java new file mode 100644 index 000000000..e29ee63da --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractdocument.domain; + +import java.util.Map; + +import com.iluwatar.abstractdocument.AbstractDocument; + +/** + * Car entity + */ +public class Car extends AbstractDocument implements HasModel, HasPrice, HasParts { + + public Car(Map properties) { + super(properties); + } + +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java new file mode 100644 index 000000000..fbd8c0d7f --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractdocument.domain; + +import java.util.Optional; + +import com.iluwatar.abstractdocument.Document; + +/** + * HasModel trait for static access to 'model' property + */ +public interface HasModel extends Document { + + String PROPERTY = "model"; + + default Optional getModel() { + return Optional.ofNullable((String) get(PROPERTY)); + } + +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java new file mode 100644 index 000000000..581702cc9 --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractdocument.domain; + +import java.util.stream.Stream; + +import com.iluwatar.abstractdocument.Document; + +/** + * HasParts trait for static access to 'parts' property + */ +public interface HasParts extends Document { + + String PROPERTY = "parts"; + + default Stream getParts() { + return children(PROPERTY, Part::new); + } + +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java new file mode 100644 index 000000000..3d1d0e3e7 --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractdocument.domain; + +import java.util.Optional; + +import com.iluwatar.abstractdocument.Document; + +/** + * HasPrice trait for static access to 'price' property + */ +public interface HasPrice extends Document { + + String PROPERTY = "price"; + + default Optional getPrice() { + return Optional.ofNullable((Number) get(PROPERTY)); + } + +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java new file mode 100644 index 000000000..b0f292bb6 --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractdocument.domain; + +import com.iluwatar.abstractdocument.Document; + +import java.util.Optional; + +/** + * HasType trait for static access to 'type' property + */ +public interface HasType extends Document { + + String PROPERTY = "type"; + + default Optional getType() { + return Optional.ofNullable((String) get(PROPERTY)); + } + +} diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java new file mode 100644 index 000000000..e42f099d9 --- /dev/null +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractdocument.domain; + +import java.util.Map; + +import com.iluwatar.abstractdocument.AbstractDocument; + +/** + * Part entity + */ +public class Part extends AbstractDocument implements HasType, HasModel, HasPrice { + + public Part(Map properties) { + super(properties); + } + +} diff --git a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java new file mode 100644 index 000000000..b6467e232 --- /dev/null +++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java @@ -0,0 +1,88 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractdocument; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertNotNull; + +/** + * AbstractDocument test class + */ +public class AbstractDocumentTest { + + private static final String KEY = "key"; + private static final String VALUE = "value"; + + private class DocumentImplementation extends AbstractDocument { + + DocumentImplementation(Map properties) { + super(properties); + } + } + + private DocumentImplementation document = new DocumentImplementation(new HashMap<>()); + + @Test + public void shouldPutAndGetValue() { + document.put(KEY, VALUE); + assertEquals(VALUE, document.get(KEY)); + } + + @Test + public void shouldRetrieveChildren() { + Map child1 = new HashMap<>(); + Map child2 = new HashMap<>(); + List> children = Arrays.asList(child1, child2); + + document.put(KEY, children); + + Stream childrenStream = document.children(KEY, DocumentImplementation::new); + assertNotNull(children); + assertEquals(2, childrenStream.count()); + } + + @Test + public void shouldRetrieveEmptyStreamForNonExistingChildren() { + Stream children = document.children(KEY, DocumentImplementation::new); + assertNotNull(children); + assertEquals(0, children.count()); + } + + @Test + public void shouldIncludePropsInToString() { + Map props = new HashMap<>(); + props.put(KEY, VALUE); + DocumentImplementation document = new DocumentImplementation(props); + assertNotNull(document.toString().contains(KEY)); + assertNotNull(document.toString().contains(VALUE)); + } + +} diff --git a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java new file mode 100644 index 000000000..787ae3aa6 --- /dev/null +++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractdocument; + +import org.junit.Test; + +/** + * Simple App test + */ +public class AppTest { + + @Test + public void shouldExecuteAppWithoutException() { + App.main(null); + } + +} diff --git a/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java b/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java new file mode 100644 index 000000000..437244a3d --- /dev/null +++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java @@ -0,0 +1,77 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractdocument; + +import com.iluwatar.abstractdocument.domain.Car; +import com.iluwatar.abstractdocument.domain.HasModel; +import com.iluwatar.abstractdocument.domain.HasParts; +import com.iluwatar.abstractdocument.domain.HasPrice; +import com.iluwatar.abstractdocument.domain.HasType; +import com.iluwatar.abstractdocument.domain.Part; +import org.junit.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import static junit.framework.TestCase.assertEquals; + +/** + * Test for Part and Car + */ +public class DomainTest { + + private static final String TEST_PART_TYPE = "test-part-type"; + private static final String TEST_PART_MODEL = "test-part-model"; + private static final long TEST_PART_PRICE = 0L; + + private static final String TEST_CAR_MODEL = "test-car-model"; + private static final long TEST_CAR_PRICE = 1L; + + @Test + public void shouldConstructPart() { + Map partProperties = new HashMap<>(); + partProperties.put(HasType.PROPERTY, TEST_PART_TYPE); + partProperties.put(HasModel.PROPERTY, TEST_PART_MODEL); + partProperties.put(HasPrice.PROPERTY, TEST_PART_PRICE); + Part part = new Part(partProperties); + + assertEquals(TEST_PART_TYPE, part.getType().get()); + assertEquals(TEST_PART_MODEL, part.getModel().get()); + assertEquals(TEST_PART_PRICE, part.getPrice().get()); + } + + @Test + public void shouldConstructCar() { + Map carProperties = new HashMap<>(); + carProperties.put(HasModel.PROPERTY, TEST_CAR_MODEL); + carProperties.put(HasPrice.PROPERTY, TEST_CAR_PRICE); + carProperties.put(HasParts.PROPERTY, Arrays.asList(new HashMap<>(), new HashMap<>())); + Car car = new Car(carProperties); + + assertEquals(TEST_CAR_MODEL, car.getModel().get()); + assertEquals(TEST_CAR_PRICE, car.getPrice().get()); + assertEquals(2, car.getParts().count()); + } + +} diff --git a/abstract-factory/index.md b/abstract-factory/README.md similarity index 81% rename from abstract-factory/index.md rename to abstract-factory/README.md index 7db699a99..485599b98 100644 --- a/abstract-factory/index.md +++ b/abstract-factory/README.md @@ -7,24 +7,30 @@ categories: Creational tags: - Java - Gang Of Four + - Difficulty-Intermediate --- -**Intent:** Provide an interface for creating families of related or dependent +## Also known as +Kit + +## Intent +Provide an interface for creating families of related or dependent objects without specifying their concrete classes. ![alt text](./etc/abstract-factory_1.png "Abstract Factory") -**Applicability:** Use the Abstract Factory pattern when +## Applicability +Use the Abstract Factory pattern when * a system should be independent of how its products are created, composed and represented * a system should be configured with one of multiple families of products * a family of related product objects is designed to be used together, and you need to enforce this constraint * you want to provide a class library of products, and you want to reveal just their interfaces, not their implementations -**Real world examples:** +## Real world examples * [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/abstract-factory/pom.xml b/abstract-factory/pom.xml index bb9df6bc9..ec0f700a5 100644 --- a/abstract-factory/pom.xml +++ b/abstract-factory/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT abstract-factory diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java index 618b98c52..aae396f1d 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java @@ -1,82 +1,115 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractfactory; - /** * - * The Abstract Factory pattern provides a way to encapsulate a group of individual - * factories that have a common theme without specifying their concrete classes. In - * normal usage, the client software creates a concrete implementation of the abstract - * factory and then uses the generic interface of the factory to create the concrete - * objects that are part of the theme. The client does not know (or care) which - * concrete objects it gets from each of these internal factories, since it uses only - * the generic interfaces of their products. This pattern separates the details of - * implementation of a set of objects from their general usage and relies on object - * composition, as object creation is implemented in methods exposed in the factory - * interface. + * The Abstract Factory pattern provides a way to encapsulate a group of individual factories that have a common theme + * without specifying their concrete classes. In normal usage, the client software creates a concrete implementation of + * the abstract factory and then uses the generic interface of the factory to create the concrete objects that are part + * of the theme. The client does not know (or care) which concrete objects it gets from each of these internal + * factories, since it uses only the generic interfaces of their products. This pattern separates the details of + * implementation of a set of objects from their general usage and relies on object composition, as object creation is + * implemented in methods exposed in the factory interface. *

- * The essence of the Abstract Factory pattern is a factory interface - * ({@link KingdomFactory}) and its implementations ({@link ElfKingdomFactory}, - * {@link OrcKingdomFactory}). The example uses both concrete implementations to - * create a king, a castle and an army. + * The essence of the Abstract Factory pattern is a factory interface ({@link KingdomFactory}) and its implementations ( + * {@link ElfKingdomFactory}, {@link OrcKingdomFactory}). The example uses both concrete implementations to create a + * king, a castle and an army. * */ public class App { - private King king; - private Castle castle; - private Army army; + private King king; + private Castle castle; + private Army army; + + /** + * Creates kingdom + */ + public void createKingdom(final KingdomFactory factory) { + setKing(factory.createKing()); + setCastle(factory.createCastle()); + setArmy(factory.createArmy()); + } + + King getKing(final KingdomFactory factory) { + return factory.createKing(); + } + + public King getKing() { + return king; + } + + private void setKing(final King king) { + this.king = king; + } + + Castle getCastle(final KingdomFactory factory) { + return factory.createCastle(); + } + + public Castle getCastle() { + return castle; + } + + private void setCastle(final Castle castle) { + this.castle = castle; + } + + Army getArmy(final KingdomFactory factory) { + return factory.createArmy(); + } + + public Army getArmy() { + return army; + } + + private void setArmy(final Army army) { + this.army = army; + } + + /** + * Program entry point + * + * @param args + * command line args + */ + public static void main(String[] args) { + + App app = new App(); + + System.out.println("Elf Kingdom"); + app.createKingdom(new ElfKingdomFactory()); + System.out.println(app.getArmy().getDescription()); + System.out.println(app.getCastle().getDescription()); + System.out.println(app.getKing().getDescription()); + + System.out.println("\nOrc Kingdom"); + app.createKingdom(new OrcKingdomFactory()); + System.out.println(app.getArmy().getDescription()); + System.out.println(app.getCastle().getDescription()); + System.out.println(app.getKing().getDescription()); + + } - /** - * Creates kingdom - * @param factory - */ - public void createKingdom(final KingdomFactory factory) { - setKing(factory.createKing()); - setCastle(factory.createCastle()); - setArmy(factory.createArmy()); - } - - ElfKingdomFactory getElfKingdomFactory() { - return new ElfKingdomFactory(); - } - - OrcKingdomFactory getOrcKingdomFactory() { - return new OrcKingdomFactory(); - } - - King getKing(final KingdomFactory factory) { - return factory.createKing(); - } - - Castle getCastle(final KingdomFactory factory) { - return factory.createCastle(); - } - - Army getArmy(final KingdomFactory factory) { - return factory.createArmy(); - } - - public King getKing() { - return king; - } - - private void setKing(final King king) { - this.king = king; - } - - public Castle getCastle() { - return castle; - } - - private void setCastle(final Castle castle) { - this.castle = castle; - } - - public Army getArmy() { - return army; - } - - private void setArmy(final Army army) { - this.army = army; - } } diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Army.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Army.java index 333b5c2cd..d9e7f9989 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Army.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Army.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractfactory; /** @@ -7,5 +29,5 @@ package com.iluwatar.abstractfactory; */ public interface Army { - String getDescription(); + String getDescription(); } diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Castle.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Castle.java index 0290cb67c..adea2327e 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Castle.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Castle.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractfactory; /** @@ -7,5 +29,5 @@ package com.iluwatar.abstractfactory; */ public interface Castle { - String getDescription(); + String getDescription(); } diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfArmy.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfArmy.java index 410f46951..2969a8615 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfArmy.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfArmy.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractfactory; /** @@ -7,10 +29,10 @@ package com.iluwatar.abstractfactory; */ public class ElfArmy implements Army { - static final String DESCRIPTION = "This is the Elven Army!"; + static final String DESCRIPTION = "This is the Elven Army!"; - @Override - public String getDescription() { - return DESCRIPTION; - } + @Override + public String getDescription() { + return DESCRIPTION; + } } diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfCastle.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfCastle.java index fe2e9a0e7..5321bfeba 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfCastle.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfCastle.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractfactory; /** @@ -7,10 +29,10 @@ package com.iluwatar.abstractfactory; */ public class ElfCastle implements Castle { - static final String DESCRIPTION = "This is the Elven castle!"; + static final String DESCRIPTION = "This is the Elven castle!"; - @Override - public String getDescription() { - return DESCRIPTION; - } + @Override + public String getDescription() { + return DESCRIPTION; + } } diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKing.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKing.java index 66571ee01..1eb892e6f 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKing.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKing.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractfactory; /** @@ -7,10 +29,10 @@ package com.iluwatar.abstractfactory; */ public class ElfKing implements King { - static final String DESCRIPTION = "This is the Elven king!"; - - @Override - public String getDescription() { - return DESCRIPTION; - } + static final String DESCRIPTION = "This is the Elven king!"; + + @Override + public String getDescription() { + return DESCRIPTION; + } } diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKingdomFactory.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKingdomFactory.java index 0d62fa5f2..9d48ab25f 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKingdomFactory.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKingdomFactory.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractfactory; /** @@ -7,16 +29,16 @@ package com.iluwatar.abstractfactory; */ public class ElfKingdomFactory implements KingdomFactory { - public Castle createCastle() { - return new ElfCastle(); - } + public Castle createCastle() { + return new ElfCastle(); + } - public King createKing() { - return new ElfKing(); - } + public King createKing() { + return new ElfKing(); + } - public Army createArmy() { - return new ElfArmy(); - } + public Army createArmy() { + return new ElfArmy(); + } } diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/King.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/King.java index c7b9a867c..ec1cff4d1 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/King.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/King.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractfactory; /** @@ -7,5 +29,5 @@ package com.iluwatar.abstractfactory; */ public interface King { - String getDescription(); + String getDescription(); } diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/KingdomFactory.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/KingdomFactory.java index 00bcd1755..d8258fd8b 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/KingdomFactory.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/KingdomFactory.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractfactory; /** @@ -7,10 +29,10 @@ package com.iluwatar.abstractfactory; */ public interface KingdomFactory { - Castle createCastle(); + Castle createCastle(); - King createKing(); + King createKing(); - Army createArmy(); + Army createArmy(); } diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcArmy.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcArmy.java index 108700511..261ad37c9 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcArmy.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcArmy.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractfactory; /** @@ -7,10 +29,10 @@ package com.iluwatar.abstractfactory; */ public class OrcArmy implements Army { - static final String DESCRIPTION = "This is the Orc Army!"; + static final String DESCRIPTION = "This is the Orc Army!"; - @Override - public String getDescription() { - return DESCRIPTION; - } + @Override + public String getDescription() { + return DESCRIPTION; + } } diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcCastle.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcCastle.java index 5012f9200..cb2a92652 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcCastle.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcCastle.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractfactory; /** @@ -7,10 +29,10 @@ package com.iluwatar.abstractfactory; */ public class OrcCastle implements Castle { - static final String DESCRIPTION = "This is the Orc castle!"; + static final String DESCRIPTION = "This is the Orc castle!"; - @Override - public String getDescription() { - return DESCRIPTION; - } + @Override + public String getDescription() { + return DESCRIPTION; + } } diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKing.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKing.java index a5657d4e4..ba7576492 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKing.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKing.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractfactory; /** @@ -7,10 +29,10 @@ package com.iluwatar.abstractfactory; */ public class OrcKing implements King { -static final String DESCRIPTION = "This is the Orc king!"; - - @Override - public String getDescription() { - return DESCRIPTION; - } + static final String DESCRIPTION = "This is the Orc king!"; + + @Override + public String getDescription() { + return DESCRIPTION; + } } diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKingdomFactory.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKingdomFactory.java index 4fdea6656..2d740cf0d 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKingdomFactory.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKingdomFactory.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractfactory; /** @@ -7,16 +29,15 @@ package com.iluwatar.abstractfactory; */ public class OrcKingdomFactory implements KingdomFactory { - public Castle createCastle() { - return new OrcCastle(); - } + public Castle createCastle() { + return new OrcCastle(); + } - public King createKing() { - return new OrcKing(); - } - - public Army createArmy() { - return new OrcArmy(); - } + public King createKing() { + return new OrcKing(); + } + public Army createArmy() { + return new OrcArmy(); + } } diff --git a/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java new file mode 100644 index 000000000..216f0443a --- /dev/null +++ b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java @@ -0,0 +1,100 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractfactory; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Before; +import org.junit.Test; + +public class AbstractFactoryTest { + + private App app = new App(); + private KingdomFactory elfFactory; + private KingdomFactory orcFactory; + + @Before + public void setUp() { + elfFactory = new ElfKingdomFactory(); + orcFactory = new OrcKingdomFactory(); + } + + @Test + public void king() { + final King elfKing = app.getKing(elfFactory); + assertTrue(elfKing instanceof ElfKing); + assertEquals(ElfKing.DESCRIPTION, elfKing.getDescription()); + final King orcKing = app.getKing(orcFactory); + assertTrue(orcKing instanceof OrcKing); + assertEquals(OrcKing.DESCRIPTION, orcKing.getDescription()); + } + + @Test + public void castle() { + final Castle elfCastle = app.getCastle(elfFactory); + assertTrue(elfCastle instanceof ElfCastle); + assertEquals(ElfCastle.DESCRIPTION, elfCastle.getDescription()); + final Castle orcCastle = app.getCastle(orcFactory); + assertTrue(orcCastle instanceof OrcCastle); + assertEquals(OrcCastle.DESCRIPTION, orcCastle.getDescription()); + } + + @Test + public void army() { + final Army elfArmy = app.getArmy(elfFactory); + assertTrue(elfArmy instanceof ElfArmy); + assertEquals(ElfArmy.DESCRIPTION, elfArmy.getDescription()); + final Army orcArmy = app.getArmy(orcFactory); + assertTrue(orcArmy instanceof OrcArmy); + assertEquals(OrcArmy.DESCRIPTION, orcArmy.getDescription()); + } + + @Test + public void createElfKingdom() { + app.createKingdom(elfFactory); + final King king = app.getKing(); + final Castle castle = app.getCastle(); + final Army army = app.getArmy(); + assertTrue(king instanceof ElfKing); + assertEquals(ElfKing.DESCRIPTION, king.getDescription()); + assertTrue(castle instanceof ElfCastle); + assertEquals(ElfCastle.DESCRIPTION, castle.getDescription()); + assertTrue(army instanceof ElfArmy); + assertEquals(ElfArmy.DESCRIPTION, army.getDescription()); + } + + @Test + public void createOrcKingdom() { + app.createKingdom(orcFactory); + final King king = app.getKing(); + final Castle castle = app.getCastle(); + final Army army = app.getArmy(); + assertTrue(king instanceof OrcKing); + assertEquals(OrcKing.DESCRIPTION, king.getDescription()); + assertTrue(castle instanceof OrcCastle); + assertEquals(OrcCastle.DESCRIPTION, castle.getDescription()); + assertTrue(army instanceof OrcArmy); + assertEquals(OrcArmy.DESCRIPTION, army.getDescription()); + } +} diff --git a/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java index 4d3659245..a965284f7 100644 --- a/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java +++ b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java @@ -1,77 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 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.abstractfactory; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import org.junit.Before; import org.junit.Test; -public class AppTest { +import java.io.IOException; - private App app = new App(); - private KingdomFactory elfFactory; - private KingdomFactory orcFactory; - - @Before - public void setUp() { - elfFactory = app.getElfKingdomFactory(); - orcFactory = app.getOrcKingdomFactory(); - } - - @Test - public void king() { - final King elfKing = app.getKing(elfFactory); - assertTrue(elfKing instanceof ElfKing); - assertEquals(ElfKing.DESCRIPTION, elfKing.getDescription()); - final King orcKing = app.getKing(orcFactory); - assertTrue(orcKing instanceof OrcKing); - assertEquals(OrcKing.DESCRIPTION, orcKing.getDescription()); - } - - @Test - public void castle() { - final Castle elfCastle = app.getCastle(elfFactory); - assertTrue(elfCastle instanceof ElfCastle); - assertEquals(ElfCastle.DESCRIPTION, elfCastle.getDescription()); - final Castle orcCastle = app.getCastle(orcFactory); - assertTrue(orcCastle instanceof OrcCastle); - assertEquals(OrcCastle.DESCRIPTION, orcCastle.getDescription()); - } - - @Test - public void army() { - final Army elfArmy = app.getArmy(elfFactory); - assertTrue(elfArmy instanceof ElfArmy); - assertEquals(ElfArmy.DESCRIPTION, elfArmy.getDescription()); - final Army orcArmy = app.getArmy(orcFactory); - assertTrue(orcArmy instanceof OrcArmy); - assertEquals(OrcArmy.DESCRIPTION, orcArmy.getDescription()); - } - - @Test - public void createElfKingdom() { - app.createKingdom(elfFactory); - final King king = app.getKing(); - final Castle castle = app.getCastle(); - final Army army = app.getArmy(); - assertTrue(king instanceof ElfKing); - assertEquals(ElfKing.DESCRIPTION, king.getDescription()); - assertTrue(castle instanceof ElfCastle); - assertEquals(ElfCastle.DESCRIPTION, castle.getDescription()); - assertTrue(army instanceof ElfArmy); - assertEquals(ElfArmy.DESCRIPTION, army.getDescription()); - } - - @Test - public void createOrcKingdom() { - app.createKingdom(orcFactory); - final King king = app.getKing(); - final Castle castle = app.getCastle(); - final Army army = app.getArmy(); - assertTrue(king instanceof OrcKing); - assertEquals(OrcKing.DESCRIPTION, king.getDescription()); - assertTrue(castle instanceof OrcCastle); - assertEquals(OrcCastle.DESCRIPTION, castle.getDescription()); - assertTrue(army instanceof OrcArmy); - assertEquals(OrcArmy.DESCRIPTION, army.getDescription()); - } +/** + * Tests that Abstract Factory example runs without errors. + */ +public class AppTest { + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } } diff --git a/adapter/index.md b/adapter/README.md similarity index 72% rename from adapter/index.md rename to adapter/README.md index be9a87228..ea3baa7fa 100644 --- a/adapter/index.md +++ b/adapter/README.md @@ -7,24 +7,31 @@ categories: Structural tags: - Java - Gang Of Four + - Difficulty-Beginner --- -**Intent:** Convert the interface of a class into another interface the clients +## Also known as +Wrapper + +## Intent +Convert the interface of a class into another interface the clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces. -![alt text](./etc/adapter_1.png "Adapter") +![alt text](./etc/adapter.png "Adapter") -**Applicability:** Use the Adapter pattern when +## Applicability +Use the Adapter pattern when * you want to use an existing class, and its interface does not match the one you need * you want to create a reusable class that cooperates with unrelated or unforeseen classes, that is, classes that don't necessarily have compatible interfaces * you need to use several existing subclasses, but it's impractical to adapt their interface by subclassing every one. An object adapter can adapt the interface of its parent class. -**Real world examples:** +## Real world examples * [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) +* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) diff --git a/adapter/etc/adapter.png b/adapter/etc/adapter.png index 511bb5880..f43358b04 100644 Binary files a/adapter/etc/adapter.png and b/adapter/etc/adapter.png differ diff --git a/adapter/etc/adapter.ucls b/adapter/etc/adapter.ucls index 8c09f0399..290ff544e 100644 --- a/adapter/etc/adapter.ucls +++ b/adapter/etc/adapter.ucls @@ -1,61 +1,61 @@ - - - + + + - - + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - + + - - - - - - - - + + + + - + + + + + - - + + diff --git a/adapter/etc/adapter_1.png b/adapter/etc/adapter_1.png deleted file mode 100644 index 64eb34b84..000000000 Binary files a/adapter/etc/adapter_1.png and /dev/null differ diff --git a/adapter/pom.xml b/adapter/pom.xml index d07d26b94..2c99796f4 100644 --- a/adapter/pom.xml +++ b/adapter/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT adapter @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/adapter/src/main/java/com/iluwatar/adapter/App.java b/adapter/src/main/java/com/iluwatar/adapter/App.java index ed036b391..29c313b59 100644 --- a/adapter/src/main/java/com/iluwatar/adapter/App.java +++ b/adapter/src/main/java/com/iluwatar/adapter/App.java @@ -1,31 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 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.adapter; /** + * An adapter helps two incompatible interfaces to work together. This is the real world definition + * for an adapter. Interfaces may be incompatible but the inner functionality should suit the need. + * The Adapter design pattern allows otherwise incompatible classes to work together by converting + * the interface of one class into an interface expected by the clients. * - * An adapter helps two incompatible interfaces to work together. This is the real - * world definition for an adapter. Interfaces may be incompatible but the inner - * functionality should suit the need. The Adapter design pattern allows otherwise - * incompatible classes to work together by converting the interface of one class - * into an interface expected by the clients. *

- * There are two variations of the Adapter pattern: The class adapter implements - * the adaptee's interface whereas the object adapter uses composition to - * contain the adaptee in the adapter object. This example uses the object - * adapter approach. + * There are two variations of the Adapter pattern: The class adapter implements the adaptee's + * interface whereas the object adapter uses composition to contain the adaptee in the adapter + * object. This example uses the object adapter approach. + * *

- * The Adapter ({@link GnomeEngineer}) converts the interface of the target class - * ({@link GoblinGlider}) into a suitable one expected by the client - * ({@link GnomeEngineeringManager}). - * + * The Adapter ({@link BattleFishingBoat}) converts the interface of the adaptee class ( + * {@link FishingBoat}) into a suitable one expected by the client ( {@link BattleShip} ). + * + *

+ * The story of this implementation is this.
+ * Pirates are coming! we need a {@link BattleShip} to fight! We have a {@link FishingBoat} and our + * captain. We have no time to make up a new ship! we need to reuse this {@link FishingBoat}. The + * captain needs a battleship which can fire and move. The spec is in {@link BattleShip}. We will + * use the Adapter pattern to reuse {@link FishingBoat}. + * */ public class App { - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { - Engineer manager = new GnomeEngineeringManager(); - manager.operateDevice(); - } + /** + * Program entry point. + * + * @param args command line args + */ + public static void main(String[] args) { + Captain captain = new Captain(new BattleFishingBoat()); + captain.move(); + captain.fire(); + } } diff --git a/adapter/src/main/java/com/iluwatar/adapter/BattleFishingBoat.java b/adapter/src/main/java/com/iluwatar/adapter/BattleFishingBoat.java new file mode 100644 index 000000000..a591818fe --- /dev/null +++ b/adapter/src/main/java/com/iluwatar/adapter/BattleFishingBoat.java @@ -0,0 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014 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.adapter; + +/** + * + * Adapter class. Adapts the interface of the device ({@link FishingBoat}) into {@link BattleShip} + * interface expected by the client ({@link Captain}).
+ * In this case we added a new function fire to suit the interface. We are reusing the + * {@link FishingBoat} without changing itself. The Adapter class can just map the functions of the + * Adaptee or add, delete features of the Adaptee. + * + */ +public class BattleFishingBoat implements BattleShip { + + private FishingBoat boat; + + public BattleFishingBoat() { + boat = new FishingBoat(); + } + + @Override + public void fire() { + System.out.println("fire!"); + } + + @Override + public void move() { + boat.sail(); + } +} diff --git a/adapter/src/main/java/com/iluwatar/adapter/BattleShip.java b/adapter/src/main/java/com/iluwatar/adapter/BattleShip.java new file mode 100644 index 000000000..6a29a5034 --- /dev/null +++ b/adapter/src/main/java/com/iluwatar/adapter/BattleShip.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * Copyright (c) 2014 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.adapter; + +/** + * The interface expected by the client.
+ * A Battleship can fire and move. + * + */ +public interface BattleShip { + + void fire(); + + void move(); + +} diff --git a/adapter/src/main/java/com/iluwatar/adapter/Captain.java b/adapter/src/main/java/com/iluwatar/adapter/Captain.java new file mode 100644 index 000000000..34f783cd4 --- /dev/null +++ b/adapter/src/main/java/com/iluwatar/adapter/Captain.java @@ -0,0 +1,55 @@ +/** + * The MIT License + * Copyright (c) 2014 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.adapter; + +/** + * The Captain uses {@link BattleShip} to fight.
+ * This is the client in the pattern. + */ +public class Captain implements BattleShip { + + private BattleShip battleship; + + public Captain() { + + } + + public Captain(BattleShip battleship) { + this.battleship = battleship; + } + + public void setBattleship(BattleShip battleship) { + this.battleship = battleship; + } + + @Override + public void fire() { + battleship.fire(); + } + + @Override + public void move() { + battleship.move(); + } + +} diff --git a/adapter/src/main/java/com/iluwatar/adapter/Engineer.java b/adapter/src/main/java/com/iluwatar/adapter/Engineer.java deleted file mode 100644 index 7478b5b69..000000000 --- a/adapter/src/main/java/com/iluwatar/adapter/Engineer.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.iluwatar.adapter; - -/** - * - * Engineers can operate devices. - * - */ -public interface Engineer { - - void operateDevice(); - -} diff --git a/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java b/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java new file mode 100644 index 000000000..307437038 --- /dev/null +++ b/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 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.adapter; + +/** + * + * Device class (adaptee in the pattern). We want to reuse this class + * + */ +public class FishingBoat { + + public void sail() { + System.out.println("The Boat is moving to that place"); + } + + public void fish() { + System.out.println("fishing ..."); + } + +} diff --git a/adapter/src/main/java/com/iluwatar/adapter/GnomeEngineer.java b/adapter/src/main/java/com/iluwatar/adapter/GnomeEngineer.java deleted file mode 100644 index 35cbc9573..000000000 --- a/adapter/src/main/java/com/iluwatar/adapter/GnomeEngineer.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.iluwatar.adapter; - -/** - * - * Adapter class. Adapts the interface of the device ({@link GoblinGlider}) into - * {@link Engineer} interface expected by the client ({@link GnomeEngineeringManager}). - * - */ -public class GnomeEngineer implements Engineer { - - private GoblinGlider glider; - - public GnomeEngineer() { - glider = new GoblinGlider(); - } - - @Override - public void operateDevice() { - glider.attachGlider(); - glider.gainSpeed(); - glider.takeOff(); - } - -} diff --git a/adapter/src/main/java/com/iluwatar/adapter/GnomeEngineeringManager.java b/adapter/src/main/java/com/iluwatar/adapter/GnomeEngineeringManager.java deleted file mode 100644 index d95065b88..000000000 --- a/adapter/src/main/java/com/iluwatar/adapter/GnomeEngineeringManager.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.iluwatar.adapter; - -/** - * - * GnomeEngineering manager uses {@link Engineer} to operate devices. - * - */ -public class GnomeEngineeringManager implements Engineer { - - private Engineer engineer; - - public GnomeEngineeringManager() { - engineer = new GnomeEngineer(); - } - - @Override - public void operateDevice() { - engineer.operateDevice(); - } -} diff --git a/adapter/src/main/java/com/iluwatar/adapter/GoblinGlider.java b/adapter/src/main/java/com/iluwatar/adapter/GoblinGlider.java deleted file mode 100644 index ff1dbeb8d..000000000 --- a/adapter/src/main/java/com/iluwatar/adapter/GoblinGlider.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.iluwatar.adapter; - -/** - * - * Device class (adaptee in the pattern). - * - */ -public class GoblinGlider { - - public void attachGlider() { - System.out.println("Glider attached."); - } - - public void gainSpeed() { - System.out.println("Gaining speed."); - } - - public void takeOff() { - System.out.println("Lift-off!"); - } -} diff --git a/adapter/src/test/java/com/iluwatar/adapter/AdapterPatternTest.java b/adapter/src/test/java/com/iluwatar/adapter/AdapterPatternTest.java new file mode 100644 index 000000000..263c9ab02 --- /dev/null +++ b/adapter/src/test/java/com/iluwatar/adapter/AdapterPatternTest.java @@ -0,0 +1,83 @@ +/** + * The MIT License + * Copyright (c) 2014 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.adapter; + +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +/** + * Test class + * + */ +public class AdapterPatternTest { + + private Map beans; + + private static final String BATTLESHIP_BEAN = "engineer"; + + private static final String CAPTAIN_BEAN = "captain"; + + /** + * This method runs before the test execution and sets the bean objects in the beans Map. + */ + @Before + public void setup() { + beans = new HashMap<>(); + + BattleFishingBoat battleFishingBoat = spy(new BattleFishingBoat()); + beans.put(BATTLESHIP_BEAN, battleFishingBoat); + + Captain captain = new Captain(); + captain.setBattleship((BattleFishingBoat) beans.get(BATTLESHIP_BEAN)); + beans.put(CAPTAIN_BEAN, captain); + } + + /** + * This test asserts that when we use the move() method on a captain bean(client), it is + * internally calling move method on the battleship object. The Adapter ({@link BattleFishingBoat} + * ) converts the interface of the target class ( {@link FishingBoat}) into a suitable one + * expected by the client ({@link Captain} ). + */ + @Test + public void testAdapter() { + BattleShip captain = (BattleShip) beans.get(CAPTAIN_BEAN); + + // when captain moves + captain.move(); + + // the captain internally calls the battleship object to move + BattleShip battleship = (BattleShip) beans.get(BATTLESHIP_BEAN); + verify(battleship).move(); + + // same with above with firing + captain.fire(); + verify(battleship).fire(); + + } +} diff --git a/adapter/src/test/java/com/iluwatar/adapter/AppTest.java b/adapter/src/test/java/com/iluwatar/adapter/AppTest.java index 3d877815a..4f5fcd91d 100644 --- a/adapter/src/test/java/com/iluwatar/adapter/AppTest.java +++ b/adapter/src/test/java/com/iluwatar/adapter/AppTest.java @@ -1,19 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 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.adapter; import org.junit.Test; -import com.iluwatar.adapter.App; +import java.io.IOException; /** - * - * Application test - * + * Tests that Adapter example runs without errors. */ public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } } diff --git a/aggregator-microservices/README.md b/aggregator-microservices/README.md new file mode 100644 index 000000000..e65f26d9a --- /dev/null +++ b/aggregator-microservices/README.md @@ -0,0 +1,29 @@ +--- +layout: pattern +title: Aggregator Microservices +folder: aggregator-microservices +permalink: /patterns/aggregator-microservices/ +categories: Architectural +tags: +- Java +- Spring +--- + +## Intent + +The user makes a single call to the Aggregator, and the aggregator then calls each relevant microservice and collects +the data, apply business logic to it, and further publish is as a REST Endpoint. +More variations of the aggregator are: +- Proxy Microservice Design Pattern: A different microservice is called upon the business need. +- Chained Microservice Design Pattern: In this case each microservice is dependent/ chained to a series +of other microservices. + +![alt text](./etc/aggregator-microservice.png "Aggregator Microservice") + +## Applicability + +Use the Aggregator Microservices pattern when you need a unified API for various microservices, regardless the client device. + +## Credits + +* [Microservice Design Patterns](http://blog.arungupta.me/microservice-design-patterns/) diff --git a/aggregator-microservices/aggregator-service/pom.xml b/aggregator-microservices/aggregator-service/pom.xml new file mode 100644 index 000000000..169d0da94 --- /dev/null +++ b/aggregator-microservices/aggregator-service/pom.xml @@ -0,0 +1,88 @@ + + + + + aggregator-microservices + com.iluwatar + 1.13.0-SNAPSHOT + + 4.0.0 + + aggregator-service + jar + + + + + org.springframework.boot + spring-boot-dependencies + + + + + + org.springframework + spring-webmvc + + + org.springframework.boot + spring-boot-starter-web + + + junit + junit + test + + + org.mockito + mockito-core + test + + + org.apache.httpcomponents + httpclient + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + \ No newline at end of file diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Aggregator.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Aggregator.java new file mode 100644 index 000000000..639349db9 --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Aggregator.java @@ -0,0 +1,58 @@ +/** + * The MIT License + * Copyright (c) 2014 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.aggregator.microservices; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +/** + * The aggregator aggregates calls on various micro-services, collects + * data and further publishes them under a REST endpoint. + */ +@RestController +public class Aggregator { + + + @Resource + private ProductInformationClient informationClient; + + @Resource + private ProductInventoryClient inventoryClient; + + + /** + * Retrieves product data. + * + * @return a Product. + */ + @RequestMapping("/product") + public Product getProduct() { + Product product = new Product(); + product.setTitle(informationClient.getProductTitle()); + product.setProductInventories(inventoryClient.getProductInventories()); + return product; + } + +} diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/App.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/App.java new file mode 100644 index 000000000..25bb9ee6f --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/App.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 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.aggregator.microservices; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + SpringApplication.run(App.class, args); + } +} diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Product.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Product.java new file mode 100644 index 000000000..edf0c121d --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Product.java @@ -0,0 +1,57 @@ +/** + * The MIT License + * Copyright (c) 2014 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.aggregator.microservices; + +/** + * Encapsulates all the data for a Product that clients will request. + */ +public class Product { + + /** + * The title of the product. + */ + private String title; + + + /** + * The inventories of the product. + */ + private int productInventories; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public int getProductInventories() { + return productInventories; + } + + public void setProductInventories(int productInventories) { + this.productInventories = productInventories; + } + +} diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInformationClient.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInformationClient.java new file mode 100644 index 000000000..863db4759 --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInformationClient.java @@ -0,0 +1,32 @@ +/** + * The MIT License + * Copyright (c) 2014 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.aggregator.microservices; + +/** + * Interface for the Information micro-service. + */ +public interface ProductInformationClient { + + String getProductTitle(); + +} diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInformationClientImpl.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInformationClientImpl.java new file mode 100644 index 000000000..1c5c1527c --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInformationClientImpl.java @@ -0,0 +1,53 @@ +/** + * The MIT License + * Copyright (c) 2014 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.aggregator.microservices; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +/** + * An adapter to communicate with information micro-service. + */ +@Component +public class ProductInformationClientImpl implements ProductInformationClient { + + @Override + public String getProductTitle() { + String response = null; + try (CloseableHttpClient httpClient = HttpClients.createDefault()) { + HttpGet httpGet = new HttpGet("http://localhost:51515/information"); + try (CloseableHttpResponse httpResponse = httpClient.execute(httpGet)) { + response = EntityUtils.toString(httpResponse.getEntity()); + } + } catch (IOException e) { + e.printStackTrace(); + } + return response; + } +} diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClient.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClient.java new file mode 100644 index 000000000..22be900b3 --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClient.java @@ -0,0 +1,31 @@ +/** + * The MIT License + * Copyright (c) 2014 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.aggregator.microservices; + +/** + * Interface to Inventory micro-service. + */ +public interface ProductInventoryClient { + + int getProductInventories(); +} diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClientImpl.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClientImpl.java new file mode 100644 index 000000000..14d0a32c4 --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClientImpl.java @@ -0,0 +1,53 @@ +/** + * The MIT License + * Copyright (c) 2014 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.aggregator.microservices; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +/** + * An adapter to communicate with inventory micro-service. + */ +@Component +public class ProductInventoryClientImpl implements ProductInventoryClient { + + @Override + public int getProductInventories() { + String response = "0"; + try (CloseableHttpClient httpClient = HttpClients.createDefault()) { + HttpGet httpGet = new HttpGet("http://localhost:51516/inventories"); + try (CloseableHttpResponse httpResponse = httpClient.execute(httpGet)) { + response = EntityUtils.toString(httpResponse.getEntity()); + } + } catch (IOException e) { + e.printStackTrace(); + } + return Integer.parseInt(response); + } +} diff --git a/aggregator-microservices/aggregator-service/src/main/resources/application.properties b/aggregator-microservices/aggregator-service/src/main/resources/application.properties new file mode 100644 index 000000000..69f581712 --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/main/resources/application.properties @@ -0,0 +1,24 @@ +# +# The MIT License +# Copyright (c) 2014 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. +# + +server.port=50004 \ No newline at end of file diff --git a/aggregator-microservices/aggregator-service/src/test/java/com/iluwatar/aggregator/microservices/AggregatorTest.java b/aggregator-microservices/aggregator-service/src/test/java/com/iluwatar/aggregator/microservices/AggregatorTest.java new file mode 100644 index 000000000..95d36fe25 --- /dev/null +++ b/aggregator-microservices/aggregator-service/src/test/java/com/iluwatar/aggregator/microservices/AggregatorTest.java @@ -0,0 +1,67 @@ +/** + * The MIT License + * Copyright (c) 2014 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.aggregator.microservices; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; + +public class AggregatorTest { + + @InjectMocks + private Aggregator aggregator; + + @Mock + private ProductInformationClient informationClient; + + @Mock + private ProductInventoryClient inventoryClient; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + } + + /** + * Tests getting the data for a desktop client + */ + @Test + public void testGetProduct() { + String title = "The Product Title."; + int inventories = 5; + + when(informationClient.getProductTitle()).thenReturn(title); + when(inventoryClient.getProductInventories()).thenReturn(inventories); + + Product testProduct = aggregator.getProduct(); + + assertEquals(title, testProduct.getTitle()); + assertEquals(inventories, testProduct.getProductInventories()); + } + +} \ No newline at end of file diff --git a/aggregator-microservices/etc/aggregator-microservice.png b/aggregator-microservices/etc/aggregator-microservice.png new file mode 100644 index 000000000..ad344a7e1 Binary files /dev/null and b/aggregator-microservices/etc/aggregator-microservice.png differ diff --git a/aggregator-microservices/information-microservice/pom.xml b/aggregator-microservices/information-microservice/pom.xml new file mode 100644 index 000000000..986540344 --- /dev/null +++ b/aggregator-microservices/information-microservice/pom.xml @@ -0,0 +1,79 @@ + + + + + aggregator-microservices + com.iluwatar + 1.13.0-SNAPSHOT + + 4.0.0 + + information-microservice + jar + + + + + org.springframework.boot + spring-boot-dependencies + + + + + + org.springframework + spring-webmvc + + + org.springframework.boot + spring-boot-starter-web + + + junit + junit + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + diff --git a/aggregator-microservices/information-microservice/src/main/java/com/iluwatar/information/microservice/InformationApplication.java b/aggregator-microservices/information-microservice/src/main/java/com/iluwatar/information/microservice/InformationApplication.java new file mode 100644 index 000000000..c93219c17 --- /dev/null +++ b/aggregator-microservices/information-microservice/src/main/java/com/iluwatar/information/microservice/InformationApplication.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 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.information.microservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * Inventory Application starts container (Spring Boot) and exposes the Inventory micro-service. + */ +@SpringBootApplication +public class InformationApplication { + + public static void main(String[] args) { + SpringApplication.run(InformationApplication.class, args); + } +} diff --git a/aggregator-microservices/information-microservice/src/main/java/com/iluwatar/information/microservice/InformationController.java b/aggregator-microservices/information-microservice/src/main/java/com/iluwatar/information/microservice/InformationController.java new file mode 100644 index 000000000..37ec45c1b --- /dev/null +++ b/aggregator-microservices/information-microservice/src/main/java/com/iluwatar/information/microservice/InformationController.java @@ -0,0 +1,41 @@ +/** + * The MIT License + * Copyright (c) 2014 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.information.microservice; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class InformationController { + + /** + * Endpoint to retrieve a product's informations. + * + * @return product inventory. + */ + @RequestMapping(value = "/information", method = RequestMethod.GET) + public String getProductTitle() { + return "The Product Title."; + } +} diff --git a/aggregator-microservices/information-microservice/src/main/resources/application.properties b/aggregator-microservices/information-microservice/src/main/resources/application.properties new file mode 100644 index 000000000..3c8452f1f --- /dev/null +++ b/aggregator-microservices/information-microservice/src/main/resources/application.properties @@ -0,0 +1,24 @@ +# +# The MIT License +# Copyright (c) 2014 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. +# + +server.port=51515 \ No newline at end of file diff --git a/aggregator-microservices/information-microservice/src/test/java/com/iluwatar/information/microservice/InformationControllerTest.java b/aggregator-microservices/information-microservice/src/test/java/com/iluwatar/information/microservice/InformationControllerTest.java new file mode 100644 index 000000000..9c3a6b98d --- /dev/null +++ b/aggregator-microservices/information-microservice/src/test/java/com/iluwatar/information/microservice/InformationControllerTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 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.information.microservice; + +import org.junit.Assert; +import org.junit.Test; + +public class InformationControllerTest { + + @Test + public void shouldGetProductTitle() { + InformationController infoController = new InformationController(); + + String title = infoController.getProductTitle(); + + Assert.assertEquals("The Product Title.", title); + } + +} \ No newline at end of file diff --git a/aggregator-microservices/inventory-microservice/pom.xml b/aggregator-microservices/inventory-microservice/pom.xml new file mode 100644 index 000000000..f8844dd39 --- /dev/null +++ b/aggregator-microservices/inventory-microservice/pom.xml @@ -0,0 +1,79 @@ + + + + + aggregator-microservices + com.iluwatar + 1.13.0-SNAPSHOT + + 4.0.0 + + inventory-microservice + jar + + + + + org.springframework.boot + spring-boot-dependencies + + + + + + org.springframework + spring-webmvc + + + org.springframework.boot + spring-boot-starter-web + + + junit + junit + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + \ No newline at end of file diff --git a/aggregator-microservices/inventory-microservice/src/main/java/com/iluwatar/inventory/microservice/InventoryApplication.java b/aggregator-microservices/inventory-microservice/src/main/java/com/iluwatar/inventory/microservice/InventoryApplication.java new file mode 100644 index 000000000..3e2cf9e60 --- /dev/null +++ b/aggregator-microservices/inventory-microservice/src/main/java/com/iluwatar/inventory/microservice/InventoryApplication.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 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.inventory.microservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * Inventory Application starts container (Spring Boot) and exposes the Inventory micro-service. + */ +@SpringBootApplication +public class InventoryApplication { + + public static void main(String[] args) { + SpringApplication.run(InventoryApplication.class, args); + } + +} diff --git a/aggregator-microservices/inventory-microservice/src/main/java/com/iluwatar/inventory/microservice/InventoryController.java b/aggregator-microservices/inventory-microservice/src/main/java/com/iluwatar/inventory/microservice/InventoryController.java new file mode 100644 index 000000000..483e28bf3 --- /dev/null +++ b/aggregator-microservices/inventory-microservice/src/main/java/com/iluwatar/inventory/microservice/InventoryController.java @@ -0,0 +1,42 @@ +/** + * The MIT License + * Copyright (c) 2014 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.inventory.microservice; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class InventoryController { + + /** + * Endpoint to retrieve a product's inventories. + * + * @return product inventory. + */ + @RequestMapping(value = "/inventories", method = RequestMethod.GET) + public int getProductInventories() { + return 5; + } + +} diff --git a/aggregator-microservices/inventory-microservice/src/main/resources/application.properties b/aggregator-microservices/inventory-microservice/src/main/resources/application.properties new file mode 100644 index 000000000..36f7589fc --- /dev/null +++ b/aggregator-microservices/inventory-microservice/src/main/resources/application.properties @@ -0,0 +1,24 @@ +# +# The MIT License +# Copyright (c) 2014 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. +# + +server.port=51516 \ No newline at end of file diff --git a/aggregator-microservices/inventory-microservice/src/test/java/com/iluwatar/inventory/microservice/InventoryControllerTest.java b/aggregator-microservices/inventory-microservice/src/test/java/com/iluwatar/inventory/microservice/InventoryControllerTest.java new file mode 100644 index 000000000..787ec0e61 --- /dev/null +++ b/aggregator-microservices/inventory-microservice/src/test/java/com/iluwatar/inventory/microservice/InventoryControllerTest.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 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.inventory.microservice; + +import org.junit.Assert; +import org.junit.Test; + +public class InventoryControllerTest { + + @Test + public void testGetProductInventories() throws Exception { + InventoryController inventoryController = new InventoryController(); + + int numberOfInventories = inventoryController.getProductInventories(); + + Assert.assertEquals(5, numberOfInventories); + } +} \ No newline at end of file diff --git a/aggregator-microservices/pom.xml b/aggregator-microservices/pom.xml new file mode 100644 index 000000000..0133e9ea4 --- /dev/null +++ b/aggregator-microservices/pom.xml @@ -0,0 +1,43 @@ + + + + + java-design-patterns + com.iluwatar + 1.13.0-SNAPSHOT + + 4.0.0 + aggregator-microservices + pom + + + information-microservice + aggregator-service + inventory-microservice + + \ No newline at end of file diff --git a/api-gateway/README.md b/api-gateway/README.md new file mode 100644 index 000000000..23014ae0b --- /dev/null +++ b/api-gateway/README.md @@ -0,0 +1,30 @@ +--- +layout: pattern +title: API Gateway +folder: api-gateway +permalink: /patterns/api-gateway/ +categories: Architectural +tags: +- Java +- Difficulty-Intermediate +- Spring +--- + +## Intent + +Aggregate calls to microservices in a single location: the API Gateway. The user makes a single +call to the API Gateway, and the API Gateway then calls each relevant microservice. + +![alt text](./etc/api-gateway.png "API Gateway") + +## Applicability + +Use the API Gateway pattern when + +* you're also using the Microservices pattern and need a single point of aggregation for your +microservice calls + +## Credits + +* [microservices.io - API Gateway](http://microservices.io/patterns/apigateway.html) +* [NGINX - Building Microservices: Using an API Gateway](https://www.nginx.com/blog/building-microservices-using-an-api-gateway/) diff --git a/api-gateway/api-gateway-service/pom.xml b/api-gateway/api-gateway-service/pom.xml new file mode 100644 index 000000000..87a1ebb5e --- /dev/null +++ b/api-gateway/api-gateway-service/pom.xml @@ -0,0 +1,87 @@ + + + + + api-gateway + com.iluwatar + 1.13.0-SNAPSHOT + + 4.0.0 + api-gateway-service + jar + + + + + org.springframework.boot + spring-boot-dependencies + + + + + + org.springframework + spring-webmvc + + + org.springframework.boot + spring-boot-starter-web + + + junit + junit + test + + + org.mockito + mockito-core + test + + + org.apache.httpcomponents + httpclient + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + \ No newline at end of file diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ApiGateway.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ApiGateway.java new file mode 100644 index 000000000..60f0b6d54 --- /dev/null +++ b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ApiGateway.java @@ -0,0 +1,64 @@ +/** + * The MIT License + * Copyright (c) 2014 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.api.gateway; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +/** + * The ApiGateway aggregates calls to microservices based on the needs of the individual clients. + */ +@RestController +public class ApiGateway { + + @Resource + private ImageClient imageClient; + + @Resource + private PriceClient priceClient; + + /** + * Retrieves product information that desktop clients need + * @return Product information for clients on a desktop + */ + @RequestMapping("/desktop") + public DesktopProduct getProductDesktop() { + DesktopProduct desktopProduct = new DesktopProduct(); + desktopProduct.setImagePath(imageClient.getImagePath()); + desktopProduct.setPrice(priceClient.getPrice()); + return desktopProduct; + } + + /** + * Retrieves product information that mobile clients need + * @return Product information for clients on a mobile device + */ + @RequestMapping("/mobile") + public MobileProduct getProductMobile() { + MobileProduct mobileProduct = new MobileProduct(); + mobileProduct.setPrice(priceClient.getPrice()); + return mobileProduct; + } +} diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/App.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/App.java new file mode 100644 index 000000000..9a644a0f7 --- /dev/null +++ b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/App.java @@ -0,0 +1,66 @@ +/** + * The MIT License + * Copyright (c) 2014 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.api.gateway; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * With the Microservices pattern, a client may need data from multiple different microservices. + * If the client called each microservice directly, that could contribute to longer load times, + * since the client would have to make a network request for each microservice called. Moreover, + * having the client call each microservice directly ties the client to that microservice - if the + * internal implementations of the microservices change (for example, if two microservices are + * combined sometime in the future) or if the location (host and port) of a microservice changes, + * then every client that makes use of those microservices must be updated. + * + *

+ * The intent of the API Gateway pattern is to alleviate some of these issues. In the API Gateway + * pattern, an additional entity (the API Gateway) is placed between the client and the + * microservices. The job of the API Gateway is to aggregate the calls to the microservices. + * Rather than the client calling each microservice individually, the client calls the API Gateway + * a single time. The API Gateway then calls each of the microservices that the client needs. + * + *

+ * This implementation shows what the API Gateway pattern could look like for an e-commerce site. + * The {@link ApiGateway} makes calls to the Image and Price microservices using the + * {@link ImageClientImpl} and {@link PriceClientImpl} respectively. Customers viewing the site on a + * desktop device can see both price information and an image of a product, so the {@link ApiGateway} + * calls both of the microservices and aggregates the data in the {@link DesktopProduct} model. + * However, mobile users only see price information; they do not see a product image. For mobile + * users, the {@link ApiGateway} only retrieves price information, which it uses to populate the + * {@link MobileProduct}. + */ +@SpringBootApplication +public class App { + + /** + * Program entry point + * + * @param args + * command line args + */ + public static void main(String[] args) { + SpringApplication.run(App.class, args); + } +} diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java new file mode 100644 index 000000000..0a04db402 --- /dev/null +++ b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/DesktopProduct.java @@ -0,0 +1,54 @@ +/** + * The MIT License + * Copyright (c) 2014 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.api.gateway; + +/** + * Encapsulates all of the information that a desktop client needs to display a product. + */ +public class DesktopProduct { + /** + * The price of the product + */ + private String price; + + /** + * The path to the image of the product + */ + private String imagePath; + + public String getPrice() { + return price; + } + + public void setPrice(String price) { + this.price = price; + } + + public String getImagePath() { + return imagePath; + } + + public void setImagePath(String imagePath) { + this.imagePath = imagePath; + } +} diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClient.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClient.java new file mode 100644 index 000000000..935dab778 --- /dev/null +++ b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClient.java @@ -0,0 +1,30 @@ +/** + * The MIT License + * Copyright (c) 2014 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.api.gateway; + +/** + * An interface used to communicate with the Image microservice + */ +public interface ImageClient { + String getImagePath(); +} diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java new file mode 100644 index 000000000..64fde1e66 --- /dev/null +++ b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ImageClientImpl.java @@ -0,0 +1,56 @@ +/** + * The MIT License + * Copyright (c) 2014 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.api.gateway; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +/** + * An adapter to communicate with the Image microservice + */ +@Component +public class ImageClientImpl implements ImageClient{ + /** + * Makes a simple HTTP Get request to the Image microservice + * @return The path to the image + */ + @Override + public String getImagePath() { + String response = null; + try (CloseableHttpClient httpClient = HttpClients.createDefault()) { + HttpGet httpGet = new HttpGet("http://localhost:50005/image-path"); + try (CloseableHttpResponse httpResponse = httpClient.execute(httpGet)) { + response = EntityUtils.toString(httpResponse.getEntity()); + } + } catch (IOException e) { + e.printStackTrace(); + } + return response; + } +} diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/MobileProduct.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/MobileProduct.java new file mode 100644 index 000000000..db358b781 --- /dev/null +++ b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/MobileProduct.java @@ -0,0 +1,41 @@ +/** + * The MIT License + * Copyright (c) 2014 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.api.gateway; + +/** + * Encapsulates all of the information that mobile client needs to display a product. + */ +public class MobileProduct { + /** + * The price of the product + */ + private String price; + + public String getPrice() { + return price; + } + + public void setPrice(String price) { + this.price = price; + } +} diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClient.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClient.java new file mode 100644 index 000000000..44497b997 --- /dev/null +++ b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClient.java @@ -0,0 +1,30 @@ +/** + * The MIT License + * Copyright (c) 2014 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.api.gateway; + +/** + * An interface used to communicate with the Price microservice + */ +public interface PriceClient { + String getPrice(); +} diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java new file mode 100644 index 000000000..bdc7cb00b --- /dev/null +++ b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/PriceClientImpl.java @@ -0,0 +1,56 @@ +/** + * The MIT License + * Copyright (c) 2014 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.api.gateway; + +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.util.EntityUtils; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +/** + * An adapter to communicate with the Price microservice + */ +@Component +public class PriceClientImpl implements PriceClient{ + /** + * Makes a simple HTTP Get request to the Price microservice + * @return The price of the product + */ + @Override + public String getPrice() { + String response = null; + try (CloseableHttpClient httpClient = HttpClients.createDefault()) { + HttpGet httpGet = new HttpGet("http://localhost:50006/price"); + try (CloseableHttpResponse httpResponse = httpClient.execute(httpGet)) { + response = EntityUtils.toString(httpResponse.getEntity()); + } + } catch (IOException e) { + e.printStackTrace(); + } + return response; + } +} diff --git a/api-gateway/api-gateway-service/src/main/resources/application.properties b/api-gateway/api-gateway-service/src/main/resources/application.properties new file mode 100644 index 000000000..69f581712 --- /dev/null +++ b/api-gateway/api-gateway-service/src/main/resources/application.properties @@ -0,0 +1,24 @@ +# +# The MIT License +# Copyright (c) 2014 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. +# + +server.port=50004 \ No newline at end of file diff --git a/api-gateway/api-gateway-service/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java b/api-gateway/api-gateway-service/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java new file mode 100644 index 000000000..d1a210f05 --- /dev/null +++ b/api-gateway/api-gateway-service/src/test/java/com/iluwatar/api/gateway/ApiGatewayTest.java @@ -0,0 +1,78 @@ +/** + * The MIT License + * Copyright (c) 2014 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.api.gateway; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.when; + +public class ApiGatewayTest { + + @InjectMocks + private ApiGateway apiGateway; + + @Mock + private ImageClient imageClient; + + @Mock + private PriceClient priceClient; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + } + + /** + * Tests getting the data for a desktop client + */ + @Test + public void testGetProductDesktop() { + String imagePath = "/product-image.png"; + String price = "20"; + when(imageClient.getImagePath()).thenReturn(imagePath); + when(priceClient.getPrice()).thenReturn(price); + + DesktopProduct desktopProduct = apiGateway.getProductDesktop(); + + assertEquals(price, desktopProduct.getPrice()); + assertEquals(imagePath, desktopProduct.getImagePath()); + } + + /** + * Tests getting the data for a mobile client + */ + @Test + public void testGetProductMobile() { + String price = "20"; + when(priceClient.getPrice()).thenReturn(price); + + MobileProduct mobileProduct = apiGateway.getProductMobile(); + + assertEquals(price, mobileProduct.getPrice()); + } +} diff --git a/api-gateway/etc/api-gateway.png b/api-gateway/etc/api-gateway.png new file mode 100644 index 000000000..bb3ec2e2e Binary files /dev/null and b/api-gateway/etc/api-gateway.png differ diff --git a/api-gateway/etc/api-gateway.ucls b/api-gateway/etc/api-gateway.ucls new file mode 100644 index 000000000..4a74c2108 --- /dev/null +++ b/api-gateway/etc/api-gateway.ucls @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api-gateway/image-microservice/etc/image-microservice.png b/api-gateway/image-microservice/etc/image-microservice.png new file mode 100644 index 000000000..340285a14 Binary files /dev/null and b/api-gateway/image-microservice/etc/image-microservice.png differ diff --git a/api-gateway/image-microservice/etc/image-microservice.ucls b/api-gateway/image-microservice/etc/image-microservice.ucls new file mode 100644 index 000000000..d3520768f --- /dev/null +++ b/api-gateway/image-microservice/etc/image-microservice.ucls @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api-gateway/image-microservice/pom.xml b/api-gateway/image-microservice/pom.xml new file mode 100644 index 000000000..c29932fae --- /dev/null +++ b/api-gateway/image-microservice/pom.xml @@ -0,0 +1,79 @@ + + + + + api-gateway + com.iluwatar + 1.13.0-SNAPSHOT + + + 4.0.0 + image-microservice + jar + + + + + org.springframework.boot + spring-boot-dependencies + + + + + + org.springframework + spring-webmvc + + + org.springframework.boot + spring-boot-starter-web + + + junit + junit + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + \ No newline at end of file diff --git a/api-gateway/image-microservice/src/main/java/com/iluwatar/image/microservice/ImageApplication.java b/api-gateway/image-microservice/src/main/java/com/iluwatar/image/microservice/ImageApplication.java new file mode 100644 index 000000000..b4cb3d250 --- /dev/null +++ b/api-gateway/image-microservice/src/main/java/com/iluwatar/image/microservice/ImageApplication.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 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.image.microservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * ImageApplication starts up Spring Boot, exposing endpoints for the Image microservice through + * the {@link ImageController}. + */ +@SpringBootApplication +public class ImageApplication { + + /** + * Microservice entry point + * @param args + * command line args + */ + public static void main(String[] args) { + SpringApplication.run(ImageApplication.class, args); + } +} diff --git a/api-gateway/image-microservice/src/main/java/com/iluwatar/image/microservice/ImageController.java b/api-gateway/image-microservice/src/main/java/com/iluwatar/image/microservice/ImageController.java new file mode 100644 index 000000000..16faf5b70 --- /dev/null +++ b/api-gateway/image-microservice/src/main/java/com/iluwatar/image/microservice/ImageController.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 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.image.microservice; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +/** + * Exposes the Image microservice's endpoints + */ +@RestController +public class ImageController { + + /** + * An endpoint for a user to retrieve an image path + * @return An image path + */ + @RequestMapping(value = "/image-path", method = RequestMethod.GET) + public String getImagePath() { + return "/product-image.png"; + } +} diff --git a/api-gateway/image-microservice/src/main/resources/application.properties b/api-gateway/image-microservice/src/main/resources/application.properties new file mode 100644 index 000000000..aeda0c24c --- /dev/null +++ b/api-gateway/image-microservice/src/main/resources/application.properties @@ -0,0 +1,24 @@ +# +# The MIT License +# Copyright (c) 2014 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. +# + +server.port=50005 \ No newline at end of file diff --git a/api-gateway/image-microservice/src/test/java/com/iluwatar/image/microservice/ImageControllerTest.java b/api-gateway/image-microservice/src/test/java/com/iluwatar/image/microservice/ImageControllerTest.java new file mode 100644 index 000000000..cf0c099c1 --- /dev/null +++ b/api-gateway/image-microservice/src/test/java/com/iluwatar/image/microservice/ImageControllerTest.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 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.image.microservice; + +import org.junit.Assert; +import org.junit.Test; + +public class ImageControllerTest { + @Test + public void testGetImagePath() { + ImageController imageController = new ImageController(); + + String imagePath = imageController.getImagePath(); + + Assert.assertEquals("/product-image.png", imagePath); + } +} diff --git a/api-gateway/pom.xml b/api-gateway/pom.xml new file mode 100644 index 000000000..471ffda7d --- /dev/null +++ b/api-gateway/pom.xml @@ -0,0 +1,43 @@ + + + + + java-design-patterns + com.iluwatar + 1.13.0-SNAPSHOT + + 4.0.0 + api-gateway + pom + + + image-microservice + price-microservice + api-gateway-service + + \ No newline at end of file diff --git a/api-gateway/price-microservice/etc/price-microservice.png b/api-gateway/price-microservice/etc/price-microservice.png new file mode 100644 index 000000000..7c5716025 Binary files /dev/null and b/api-gateway/price-microservice/etc/price-microservice.png differ diff --git a/api-gateway/price-microservice/etc/price-microservice.ucls b/api-gateway/price-microservice/etc/price-microservice.ucls new file mode 100644 index 000000000..5878395b6 --- /dev/null +++ b/api-gateway/price-microservice/etc/price-microservice.ucls @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/api-gateway/price-microservice/pom.xml b/api-gateway/price-microservice/pom.xml new file mode 100644 index 000000000..24e9663aa --- /dev/null +++ b/api-gateway/price-microservice/pom.xml @@ -0,0 +1,79 @@ + + + + + api-gateway + com.iluwatar + 1.13.0-SNAPSHOT + + + 4.0.0 + price-microservice + jar + + + + + org.springframework.boot + spring-boot-dependencies + + + + + + org.springframework + spring-webmvc + + + org.springframework.boot + spring-boot-starter-web + + + junit + junit + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + \ No newline at end of file diff --git a/api-gateway/price-microservice/src/main/java/com/iluwatar/price/microservice/PriceApplication.java b/api-gateway/price-microservice/src/main/java/com/iluwatar/price/microservice/PriceApplication.java new file mode 100644 index 000000000..e777c7c2b --- /dev/null +++ b/api-gateway/price-microservice/src/main/java/com/iluwatar/price/microservice/PriceApplication.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 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.price.microservice; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * PriceApplication starts up Spring Boot, exposing endpoints for the Price microservice through + * the {@link PriceController}. + */ +@SpringBootApplication +public class PriceApplication { + + /** + * Microservice entry point + * @param args + * command line args + */ + public static void main(String[] args) { + SpringApplication.run(PriceApplication.class, args); + } +} diff --git a/api-gateway/price-microservice/src/main/java/com/iluwatar/price/microservice/PriceController.java b/api-gateway/price-microservice/src/main/java/com/iluwatar/price/microservice/PriceController.java new file mode 100644 index 000000000..ec43618fa --- /dev/null +++ b/api-gateway/price-microservice/src/main/java/com/iluwatar/price/microservice/PriceController.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 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.price.microservice; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +/** + * Exposes the Price microservice's endpoints + */ +@RestController +public class PriceController { + + /** + * An endpoint for a user to retrieve a product's price + * @return A product's price + */ + @RequestMapping(value = "/price", method = RequestMethod.GET) + public String getPrice() { + return "20"; + } +} diff --git a/api-gateway/price-microservice/src/main/resources/application.properties b/api-gateway/price-microservice/src/main/resources/application.properties new file mode 100644 index 000000000..f0b479dfd --- /dev/null +++ b/api-gateway/price-microservice/src/main/resources/application.properties @@ -0,0 +1,24 @@ +# +# The MIT License +# Copyright (c) 2014 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. +# + +server.port=50006 \ No newline at end of file diff --git a/api-gateway/price-microservice/src/test/java/com/iluwatar/price/microservice/PriceControllerTest.java b/api-gateway/price-microservice/src/test/java/com/iluwatar/price/microservice/PriceControllerTest.java new file mode 100644 index 000000000..c289549f2 --- /dev/null +++ b/api-gateway/price-microservice/src/test/java/com/iluwatar/price/microservice/PriceControllerTest.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 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.price.microservice; + +import org.junit.Assert; +import org.junit.Test; + +public class PriceControllerTest { + @Test + public void testgetPrice() { + PriceController priceController = new PriceController(); + + String price = priceController.getPrice(); + + Assert.assertEquals("20", price); + } +} diff --git a/async-method-invocation/index.md b/async-method-invocation/README.md similarity index 83% rename from async-method-invocation/index.md rename to async-method-invocation/README.md index dfcee0208..2d99820c5 100644 --- a/async-method-invocation/index.md +++ b/async-method-invocation/README.md @@ -4,24 +4,30 @@ title: Async Method Invocation folder: async-method-invocation permalink: /patterns/async-method-invocation/ categories: Concurrency -tags: Java +tags: + - Java + - Difficulty-Intermediate + - Functional + - Reactive --- -**Intent:** Asynchronous method invocation is pattern where the calling thread +## Intent +Asynchronous method invocation is pattern where the calling thread is not blocked while waiting results of tasks. The pattern provides parallel processing of multiple independent tasks and retrieving the results via callbacks or waiting until everything is done. ![alt text](./etc/async-method-invocation.png "Async Method Invocation") -**Applicability:** Use async method invocation pattern when +## Applicability +Use async method invocation pattern when * you have multiple independent tasks that can run in parallel * you need to improve the performance of a group of sequential tasks * you have limited amount of processing capacity or long running tasks and the caller should not wait the tasks to be ready -**Real world examples:** +## Real world examples * [FutureTask](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/FutureTask.html), [CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html) and [ExecutorService](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html) (Java) * [Task-based Asynchronous Pattern](https://msdn.microsoft.com/en-us/library/hh873175.aspx) (.NET) diff --git a/async-method-invocation/pom.xml b/async-method-invocation/pom.xml index d5e0e2b3e..d912df965 100644 --- a/async-method-invocation/pom.xml +++ b/async-method-invocation/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT async-method-invocation @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java index 688d8482f..0a56dc166 100644 --- a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java +++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.async.method.invocation; import java.util.concurrent.Callable; @@ -5,22 +27,22 @@ import java.util.concurrent.Callable; /** * This application demonstrates the async method invocation pattern. Key parts of the pattern are * AsyncResult which is an intermediate container for an asynchronously evaluated value, - * AsyncCallback which can be provided to be executed on task completion and - * AsyncExecutor that manages the execution of the async tasks. + * AsyncCallback which can be provided to be executed on task completion and AsyncExecutor + * that manages the execution of the async tasks. *

- * The main method shows example flow of async invocations. The main thread starts multiple tasks with - * variable durations and then continues its own work. When the main thread has done it's job it collects - * the results of the async tasks. Two of the tasks are handled with callbacks, meaning the callbacks are - * executed immediately when the tasks complete. + * The main method shows example flow of async invocations. The main thread starts multiple tasks with variable + * durations and then continues its own work. When the main thread has done it's job it collects the results of the + * async tasks. Two of the tasks are handled with callbacks, meaning the callbacks are executed immediately when the + * tasks complete. *

- * Noteworthy difference of thread usage between the async results and callbacks is that the async results - * are collected in the main thread but the callbacks are executed within the worker threads. This should be - * noted when working with thread pools. + * Noteworthy difference of thread usage between the async results and callbacks is that the async results are collected + * in the main thread but the callbacks are executed within the worker threads. This should be noted when working with + * thread pools. *

- * Java provides its own implementations of async method invocation pattern. FutureTask, CompletableFuture - * and ExecutorService are the real world implementations of this pattern. But due to the nature of parallel - * programming, the implementations are not trivial. This example does not take all possible scenarios into - * account but rather provides a simple version that helps to understand the pattern. + * Java provides its own implementations of async method invocation pattern. FutureTask, CompletableFuture and + * ExecutorService are the real world implementations of this pattern. But due to the nature of parallel programming, + * the implementations are not trivial. This example does not take all possible scenarios into account but rather + * provides a simple version that helps to understand the pattern. * * @see AsyncResult * @see AsyncCallback @@ -32,66 +54,72 @@ import java.util.concurrent.Callable; */ public class App { - public static void main(String[] args) throws Exception { - // construct a new executor that will run async tasks - AsyncExecutor executor = new ThreadAsyncExecutor(); + /** + * Program entry point + */ + public static void main(String[] args) throws Exception { + // construct a new executor that will run async tasks + AsyncExecutor executor = new ThreadAsyncExecutor(); - // start few async tasks with varying processing times, two last with callback handlers - AsyncResult asyncResult1 = executor.startProcess(lazyval(10, 500)); - AsyncResult asyncResult2 = executor.startProcess(lazyval("test", 300)); - AsyncResult asyncResult3 = executor.startProcess(lazyval(50L, 700)); - AsyncResult asyncResult4 = executor.startProcess(lazyval(20, 400), callback("Callback result 4")); - AsyncResult asyncResult5 = executor.startProcess(lazyval("callback", 600), callback("Callback result 5")); + // start few async tasks with varying processing times, two last with callback handlers + AsyncResult asyncResult1 = executor.startProcess(lazyval(10, 500)); + AsyncResult asyncResult2 = executor.startProcess(lazyval("test", 300)); + AsyncResult asyncResult3 = executor.startProcess(lazyval(50L, 700)); + AsyncResult asyncResult4 = executor.startProcess(lazyval(20, 400), callback("Callback result 4")); + AsyncResult asyncResult5 = executor.startProcess(lazyval("callback", 600), callback("Callback result 5")); - // emulate processing in the current thread while async tasks are running in their own threads - Thread.sleep(350); // Oh boy I'm working hard here - log("Some hard work done"); + // emulate processing in the current thread while async tasks are running in their own threads + Thread.sleep(350); // Oh boy I'm working hard here + log("Some hard work done"); - // wait for completion of the tasks - Integer result1 = executor.endProcess(asyncResult1); - String result2 = executor.endProcess(asyncResult2); - Long result3 = executor.endProcess(asyncResult3); - asyncResult4.await(); - asyncResult5.await(); + // wait for completion of the tasks + Integer result1 = executor.endProcess(asyncResult1); + String result2 = executor.endProcess(asyncResult2); + Long result3 = executor.endProcess(asyncResult3); + asyncResult4.await(); + asyncResult5.await(); - // log the results of the tasks, callbacks log immediately when complete - log("Result 1: " + result1); - log("Result 2: " + result2); - log("Result 3: " + result3); - } + // log the results of the tasks, callbacks log immediately when complete + log("Result 1: " + result1); + log("Result 2: " + result2); + log("Result 3: " + result3); + } - /** - * Creates a callable that lazily evaluates to given value with artificial delay. - * - * @param value value to evaluate - * @param delayMillis artificial delay in milliseconds - * @return new callable for lazy evaluation - */ - private static Callable lazyval(T value, long delayMillis) { - return () -> { - Thread.sleep(delayMillis); - log("Task completed with: " + value); - return value; - }; - } + /** + * Creates a callable that lazily evaluates to given value with artificial delay. + * + * @param value + * value to evaluate + * @param delayMillis + * artificial delay in milliseconds + * @return new callable for lazy evaluation + */ + private static Callable lazyval(T value, long delayMillis) { + return () -> { + Thread.sleep(delayMillis); + log("Task completed with: " + value); + return value; + }; + } - /** - * Creates a simple callback that logs the complete status of the async result. - * - * @param name callback name - * @return new async callback - */ - private static AsyncCallback callback(String name) { - return (value, ex) -> { - if (ex.isPresent()) { - log(name + " failed: " + ex.map(Exception::getMessage).orElse("")); - } else { - log(name + ": " + value); - } - }; - } + /** + * Creates a simple callback that logs the complete status of the async result. + * + * @param name + * callback name + * @return new async callback + */ + private static AsyncCallback callback(String name) { + return (value, ex) -> { + if (ex.isPresent()) { + log(name + " failed: " + ex.map(Exception::getMessage).orElse("")); + } else { + log(name + ": " + value); + } + }; + } - private static void log(String msg) { - System.out.println(String.format("[%1$-10s] - %2$s", Thread.currentThread().getName(), msg)); - } + private static void log(String msg) { + System.out.println(String.format("[%1$-10s] - %2$s", Thread.currentThread().getName(), msg)); + } } diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncCallback.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncCallback.java index 46556a48e..2fddba683 100644 --- a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncCallback.java +++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncCallback.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.async.method.invocation; import java.util.Optional; @@ -11,12 +33,11 @@ import java.util.Optional; */ public interface AsyncCallback { - /** - * Complete handler which is executed when async task is completed or fails execution. - * - * @param value the evaluated value from async task, undefined when execution fails - * @param ex empty value if execution succeeds, some exception if executions fails - */ - void onComplete(T value, Optional ex); - + /** + * Complete handler which is executed when async task is completed or fails execution. + * + * @param value the evaluated value from async task, undefined when execution fails + * @param ex empty value if execution succeeds, some exception if executions fails + */ + void onComplete(T value, Optional ex); } diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java index 5c5098487..eb1afa4f3 100644 --- a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java +++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.async.method.invocation; import java.util.concurrent.Callable; @@ -10,33 +32,32 @@ import java.util.concurrent.ExecutionException; */ public interface AsyncExecutor { - /** - * Starts processing of an async task. Returns immediately with async result. - * - * @param task task to be executed asynchronously - * @return async result for the task - */ - AsyncResult startProcess(Callable task); + /** + * Starts processing of an async task. Returns immediately with async result. + * + * @param task task to be executed asynchronously + * @return async result for the task + */ + AsyncResult startProcess(Callable task); - /** - * Starts processing of an async task. Returns immediately with async result. Executes callback - * when the task is completed. - * - * @param task task to be executed asynchronously - * @param callback callback to be executed on task completion - * @return async result for the task - */ - AsyncResult startProcess(Callable task, AsyncCallback callback); - - /** - * Ends processing of an async task. Blocks the current thread if necessary and returns the - * evaluated value of the completed task. - * - * @param asyncResult async result of a task - * @return evaluated value of the completed task - * @throws ExecutionException if execution has failed, containing the root cause - * @throws InterruptedException if the execution is interrupted - */ - T endProcess(AsyncResult asyncResult) throws ExecutionException, InterruptedException; + /** + * Starts processing of an async task. Returns immediately with async result. Executes callback + * when the task is completed. + * + * @param task task to be executed asynchronously + * @param callback callback to be executed on task completion + * @return async result for the task + */ + AsyncResult startProcess(Callable task, AsyncCallback callback); + /** + * Ends processing of an async task. Blocks the current thread if necessary and returns the + * evaluated value of the completed task. + * + * @param asyncResult async result of a task + * @return evaluated value of the completed task + * @throws ExecutionException if execution has failed, containing the root cause + * @throws InterruptedException if the execution is interrupted + */ + T endProcess(AsyncResult asyncResult) throws ExecutionException, InterruptedException; } diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java index 405bec251..bcd97adbc 100644 --- a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java +++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.async.method.invocation; import java.util.concurrent.ExecutionException; @@ -5,31 +27,29 @@ import java.util.concurrent.ExecutionException; /** * * AsyncResult interface - * - * @param */ public interface AsyncResult { - /** - * Status of the async task execution. - * - * @return true if execution is completed or failed - */ - boolean isCompleted(); + /** + * Status of the async task execution. + * + * @return true if execution is completed or failed + */ + boolean isCompleted(); - /** - * Gets the value of completed async task. - * - * @return evaluated value or throws ExecutionException if execution has failed - * @throws ExecutionException if execution has failed, containing the root cause - * @throws IllegalStateException if execution is not completed - */ - T getValue() throws ExecutionException; + /** + * Gets the value of completed async task. + * + * @return evaluated value or throws ExecutionException if execution has failed + * @throws ExecutionException if execution has failed, containing the root cause + * @throws IllegalStateException if execution is not completed + */ + T getValue() throws ExecutionException; - /** - * Blocks the current thread until the async task is completed. - * - * @throws InterruptedException if the execution is interrupted - */ - void await() throws InterruptedException; + /** + * Blocks the current thread until the async task is completed. + * + * @throws InterruptedException if the execution is interrupted + */ + void await() throws InterruptedException; } diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java index a3ed51af3..7f96d9ab7 100644 --- a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java +++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.async.method.invocation; import java.util.Optional; @@ -12,116 +34,117 @@ import java.util.concurrent.atomic.AtomicInteger; */ public class ThreadAsyncExecutor implements AsyncExecutor { - /** Index for thread naming */ - private final AtomicInteger idx = new AtomicInteger(0); + /** Index for thread naming */ + private final AtomicInteger idx = new AtomicInteger(0); - @Override - public AsyncResult startProcess(Callable task) { - return startProcess(task, null); - } + @Override + public AsyncResult startProcess(Callable task) { + return startProcess(task, null); + } - @Override - public AsyncResult startProcess(Callable task, AsyncCallback callback) { - CompletableResult result = new CompletableResult<>(callback); - new Thread(() -> { - try { - result.setValue(task.call()); - } catch (Exception ex) { - result.setException(ex); - } - }, "executor-" + idx.incrementAndGet()).start(); - return result; - } + @Override + public AsyncResult startProcess(Callable task, AsyncCallback callback) { + CompletableResult result = new CompletableResult<>(callback); + new Thread(() -> { + try { + result.setValue(task.call()); + } catch (Exception ex) { + result.setException(ex); + } + } , "executor-" + idx.incrementAndGet()).start(); + return result; + } - @Override - public T endProcess(AsyncResult asyncResult) throws ExecutionException, InterruptedException { - if (asyncResult.isCompleted()) { - return asyncResult.getValue(); - } else { - asyncResult.await(); - return asyncResult.getValue(); - } - } + @Override + public T endProcess(AsyncResult asyncResult) throws ExecutionException, InterruptedException { + if (asyncResult.isCompleted()) { + return asyncResult.getValue(); + } else { + asyncResult.await(); + return asyncResult.getValue(); + } + } - /** - * Simple implementation of async result that allows completing it successfully with a value - * or exceptionally with an exception. A really simplified version from its real life cousins - * FutureTask and CompletableFuture. - * - * @see java.util.concurrent.FutureTask - * @see java.util.concurrent.CompletableFuture - */ - private static class CompletableResult implements AsyncResult { + /** + * Simple implementation of async result that allows completing it successfully with a value or exceptionally with an + * exception. A really simplified version from its real life cousins FutureTask and CompletableFuture. + * + * @see java.util.concurrent.FutureTask + * @see java.util.concurrent.CompletableFuture + */ + private static class CompletableResult implements AsyncResult { - static final int RUNNING = 1; - static final int FAILED = 2; - static final int COMPLETED = 3; + static final int RUNNING = 1; + static final int FAILED = 2; + static final int COMPLETED = 3; - final Object lock; - final Optional> callback; + final Object lock; + final Optional> callback; - volatile int state = RUNNING; - T value; - Exception exception; + volatile int state = RUNNING; + T value; + Exception exception; - CompletableResult(AsyncCallback callback) { - this.lock = new Object(); - this.callback = Optional.ofNullable(callback); - } + CompletableResult(AsyncCallback callback) { + this.lock = new Object(); + this.callback = Optional.ofNullable(callback); + } - /** - * Sets the value from successful execution and executes callback if available. Notifies - * any thread waiting for completion. - * - * @param value value of the evaluated task - */ - void setValue(T value) { - this.value = value; - this.state = COMPLETED; - this.callback.ifPresent(ac -> ac.onComplete(value, Optional.empty())); - synchronized (lock) { - lock.notifyAll(); - } - } + /** + * Sets the value from successful execution and executes callback if available. Notifies any thread waiting for + * completion. + * + * @param value + * value of the evaluated task + */ + void setValue(T value) { + this.value = value; + this.state = COMPLETED; + this.callback.ifPresent(ac -> ac.onComplete(value, Optional.empty())); + synchronized (lock) { + lock.notifyAll(); + } + } - /** - * Sets the exception from failed execution and executes callback if available. Notifies - * any thread waiting for completion. - * - * @param exception exception of the failed task - */ - void setException(Exception exception) { - this.exception = exception; - this.state = FAILED; - this.callback.ifPresent(ac -> ac.onComplete(null, Optional.of(exception))); - synchronized (lock) { - lock.notifyAll(); - } - } + /** + * Sets the exception from failed execution and executes callback if available. Notifies any thread waiting for + * completion. + * + * @param exception + * exception of the failed task + */ + void setException(Exception exception) { + this.exception = exception; + this.state = FAILED; + this.callback.ifPresent(ac -> ac.onComplete(null, Optional.of(exception))); + synchronized (lock) { + lock.notifyAll(); + } + } - @Override - public boolean isCompleted() { - return (state > RUNNING); - } + @Override + public boolean isCompleted() { + return state > RUNNING; + } - @Override - public T getValue() throws ExecutionException { - if (state == COMPLETED) { - return value; - } else if (state == FAILED) { - throw new ExecutionException(exception); - } else { - throw new IllegalStateException("Execution not completed yet"); - } - } + @Override + public T getValue() throws ExecutionException { + if (state == COMPLETED) { + return value; + } else if (state == FAILED) { + throw new ExecutionException(exception); + } else { + throw new IllegalStateException("Execution not completed yet"); + } + } - @Override - public void await() throws InterruptedException { - synchronized (lock) { - if (!isCompleted()) { - lock.wait(); - } - } - } - } + @Override + public void await() throws InterruptedException { + synchronized (lock) { + if (!isCompleted()) { + lock.wait(); + } + } + } + } } diff --git a/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java index 5faec5356..117a75f2a 100644 --- a/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java +++ b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.async.method.invocation; import org.junit.Test; @@ -9,10 +31,9 @@ import org.junit.Test; */ public class AppTest { - @Test - public void test() throws Exception { - String[] args = {}; - App.main(args); - } - + @Test + public void test() throws Exception { + String[] args = {}; + App.main(args); + } } diff --git a/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java new file mode 100644 index 000000000..b4a23222a --- /dev/null +++ b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java @@ -0,0 +1,312 @@ +/** + * The MIT License + * Copyright (c) 2014 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.async.method.invocation; + +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Matchers; + +import java.util.Optional; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +/** + * Date: 12/6/15 - 10:49 AM + * + * @author Jeroen Meulemeester + */ +public class ThreadAsyncExecutorTest { + + /** + * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} + */ + @Test(timeout = 3000) + public void testSuccessfulTaskWithoutCallback() throws Exception { + // Instantiate a new executor and start a new 'null' task ... + final ThreadAsyncExecutor executor = new ThreadAsyncExecutor(); + + final Object result = new Object(); + final Callable task = mock(Callable.class); + when(task.call()).thenReturn(result); + + final AsyncResult asyncResult = executor.startProcess(task); + assertNotNull(asyncResult); + asyncResult.await(); // Prevent timing issues, and wait until the result is available + assertTrue(asyncResult.isCompleted()); + + // Our task should only execute once ... + verify(task, times(1)).call(); + + // ... and the result should be exactly the same object + assertSame(result, asyncResult.getValue()); + } + + /** + * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} + */ + @Test(timeout = 3000) + public void testSuccessfulTaskWithCallback() throws Exception { + // Instantiate a new executor and start a new 'null' task ... + final ThreadAsyncExecutor executor = new ThreadAsyncExecutor(); + + final Object result = new Object(); + final Callable task = mock(Callable.class); + when(task.call()).thenReturn(result); + + final AsyncCallback callback = mock(AsyncCallback.class); + final AsyncResult asyncResult = executor.startProcess(task, callback); + assertNotNull(asyncResult); + asyncResult.await(); // Prevent timing issues, and wait until the result is available + assertTrue(asyncResult.isCompleted()); + + // Our task should only execute once ... + verify(task, times(1)).call(); + + // ... same for the callback, we expect our object + final ArgumentCaptor> optionalCaptor = ArgumentCaptor.forClass((Class) Optional.class); + verify(callback, times(1)).onComplete(eq(result), optionalCaptor.capture()); + + final Optional optionalException = optionalCaptor.getValue(); + assertNotNull(optionalException); + assertFalse(optionalException.isPresent()); + + // ... and the result should be exactly the same object + assertSame(result, asyncResult.getValue()); + } + + /** + * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} when a task takes a while + * to execute + */ + @Test(timeout = 5000) + public void testLongRunningTaskWithoutCallback() throws Exception { + // Instantiate a new executor and start a new 'null' task ... + final ThreadAsyncExecutor executor = new ThreadAsyncExecutor(); + + final Object result = new Object(); + final Callable task = mock(Callable.class); + when(task.call()).thenAnswer(i -> { + Thread.sleep(1500); + return result; + }); + + final AsyncResult asyncResult = executor.startProcess(task); + assertNotNull(asyncResult); + assertFalse(asyncResult.isCompleted()); + + try { + asyncResult.getValue(); + fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task"); + } catch (IllegalStateException e) { + assertNotNull(e.getMessage()); + } + + // Our task should only execute once, but it can take a while ... + verify(task, timeout(3000).times(1)).call(); + + // Prevent timing issues, and wait until the result is available + asyncResult.await(); + assertTrue(asyncResult.isCompleted()); + verifyNoMoreInteractions(task); + + // ... and the result should be exactly the same object + assertSame(result, asyncResult.getValue()); + } + + /** + * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when a task + * takes a while to execute + */ + @Test(timeout = 5000) + public void testLongRunningTaskWithCallback() throws Exception { + // Instantiate a new executor and start a new 'null' task ... + final ThreadAsyncExecutor executor = new ThreadAsyncExecutor(); + + final Object result = new Object(); + final Callable task = mock(Callable.class); + when(task.call()).thenAnswer(i -> { + Thread.sleep(1500); + return result; + }); + + final AsyncCallback callback = mock(AsyncCallback.class); + final AsyncResult asyncResult = executor.startProcess(task, callback); + assertNotNull(asyncResult); + assertFalse(asyncResult.isCompleted()); + + verifyZeroInteractions(callback); + + try { + asyncResult.getValue(); + fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task"); + } catch (IllegalStateException e) { + assertNotNull(e.getMessage()); + } + + // Our task should only execute once, but it can take a while ... + verify(task, timeout(3000).times(1)).call(); + + final ArgumentCaptor> optionalCaptor = ArgumentCaptor.forClass((Class) Optional.class); + verify(callback, timeout(3000).times(1)).onComplete(eq(result), optionalCaptor.capture()); + + final Optional optionalException = optionalCaptor.getValue(); + assertNotNull(optionalException); + assertFalse(optionalException.isPresent()); + + // Prevent timing issues, and wait until the result is available + asyncResult.await(); + assertTrue(asyncResult.isCompleted()); + verifyNoMoreInteractions(task, callback); + + // ... and the result should be exactly the same object + assertSame(result, asyncResult.getValue()); + } + + /** + * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} when a task takes a while + * to execute, while waiting on the result using {@link ThreadAsyncExecutor#endProcess(AsyncResult)} + */ + @Test(timeout = 5000) + public void testEndProcess() throws Exception { + // Instantiate a new executor and start a new 'null' task ... + final ThreadAsyncExecutor executor = new ThreadAsyncExecutor(); + + final Object result = new Object(); + final Callable task = mock(Callable.class); + when(task.call()).thenAnswer(i -> { + Thread.sleep(1500); + return result; + }); + + final AsyncResult asyncResult = executor.startProcess(task); + assertNotNull(asyncResult); + assertFalse(asyncResult.isCompleted()); + + try { + asyncResult.getValue(); + fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task"); + } catch (IllegalStateException e) { + assertNotNull(e.getMessage()); + } + + assertSame(result, executor.endProcess(asyncResult)); + verify(task, times(1)).call(); + assertTrue(asyncResult.isCompleted()); + + // Calling end process a second time while already finished should give the same result + assertSame(result, executor.endProcess(asyncResult)); + verifyNoMoreInteractions(task); + } + + /** + * Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable)} when the callable is 'null' + */ + @Test(timeout = 3000) + public void testNullTask() throws Exception { + // Instantiate a new executor and start a new 'null' task ... + final ThreadAsyncExecutor executor = new ThreadAsyncExecutor(); + final AsyncResult asyncResult = executor.startProcess(null); + + assertNotNull("The AsyncResult should not be 'null', even though the task was 'null'.", asyncResult); + asyncResult.await(); // Prevent timing issues, and wait until the result is available + assertTrue(asyncResult.isCompleted()); + + try { + asyncResult.getValue(); + fail("Expected ExecutionException with NPE as cause"); + } catch (final ExecutionException e) { + assertNotNull(e.getMessage()); + assertNotNull(e.getCause()); + assertEquals(NullPointerException.class, e.getCause().getClass()); + } + + } + + /** + * Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when the + * callable is 'null', but the asynchronous callback is provided + */ + @Test(timeout = 3000) + public void testNullTaskWithCallback() throws Exception { + // Instantiate a new executor and start a new 'null' task ... + final ThreadAsyncExecutor executor = new ThreadAsyncExecutor(); + final AsyncCallback callback = mock(AsyncCallback.class); + final AsyncResult asyncResult = executor.startProcess(null, callback); + + assertNotNull("The AsyncResult should not be 'null', even though the task was 'null'.", asyncResult); + asyncResult.await(); // Prevent timing issues, and wait until the result is available + assertTrue(asyncResult.isCompleted()); + + final ArgumentCaptor> optionalCaptor = ArgumentCaptor.forClass((Class) Optional.class); + verify(callback, times(1)).onComplete(Matchers.isNull(), optionalCaptor.capture()); + + final Optional optionalException = optionalCaptor.getValue(); + assertNotNull(optionalException); + assertTrue(optionalException.isPresent()); + + final Exception exception = optionalException.get(); + assertNotNull(exception); + assertEquals(NullPointerException.class, exception.getClass()); + + try { + asyncResult.getValue(); + fail("Expected ExecutionException with NPE as cause"); + } catch (final ExecutionException e) { + assertNotNull(e.getMessage()); + assertNotNull(e.getCause()); + assertEquals(NullPointerException.class, e.getCause().getClass()); + } + + } + + /** + * Test used to verify the behaviour of {@link ThreadAsyncExecutor#startProcess(Callable, AsyncCallback)} when both + * the callable and the asynchronous callback are 'null' + */ + @Test(timeout = 3000) + public void testNullTaskWithNullCallback() throws Exception { + // Instantiate a new executor and start a new 'null' task ... + final ThreadAsyncExecutor executor = new ThreadAsyncExecutor(); + final AsyncResult asyncResult = executor.startProcess(null, null); + + assertNotNull("The AsyncResult should not be 'null', even though the task and callback were 'null'.", asyncResult); + asyncResult.await(); // Prevent timing issues, and wait until the result is available + assertTrue(asyncResult.isCompleted()); + + try { + asyncResult.getValue(); + fail("Expected ExecutionException with NPE as cause"); + } catch (final ExecutionException e) { + assertNotNull(e.getMessage()); + assertNotNull(e.getCause()); + assertEquals(NullPointerException.class, e.getCause().getClass()); + } + + } + +} \ No newline at end of file diff --git a/bridge/index.md b/bridge/README.md similarity index 87% rename from bridge/index.md rename to bridge/README.md index 1ad969183..49dad14e4 100644 --- a/bridge/index.md +++ b/bridge/README.md @@ -7,15 +7,20 @@ categories: Structural tags: - Java - Gang Of Four + - Difficulty-Intermediate --- -**Intent:** Decouple an abstraction from its implementation so that the two can -vary independently. +## Also known as +Handle/Body +## Intent +Decouple an abstraction from its implementation so that the two can +vary independently. ![alt text](./etc/bridge.png "Bridge") -**Applicability:** Use the Bridge pattern when +## Applicability +Use the Bridge pattern when * you want to avoid a permanent binding between an abstraction and its implementation. This might be the case, for example, when the implementation must be selected or switched at run-time. * both the abstractions and their implementations should be extensible by subclassing. In this case, the Bridge pattern lets you combine the different abstractions and implementations and extend them independently @@ -23,6 +28,6 @@ vary independently. * you have a proliferation of classes. Such a class hierarchy indicates the need for splitting an object into two parts. Rumbaugh uses the term "nested generalizations" to refer to such class hierarchies * you want to share an implementation among multiple objects (perhaps using reference counting), and this fact should be hidden from the client. A simple example is Coplien's String class, in which multiple objects can share the same string representation. -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/bridge/pom.xml b/bridge/pom.xml index 533074d72..a7a3883c8 100644 --- a/bridge/pom.xml +++ b/bridge/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT bridge @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/bridge/src/main/java/com/iluwatar/bridge/App.java b/bridge/src/main/java/com/iluwatar/bridge/App.java index 972f49b2f..27b37b6b3 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/App.java +++ b/bridge/src/main/java/com/iluwatar/bridge/App.java @@ -1,42 +1,61 @@ +/** + * The MIT License + * Copyright (c) 2014 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.bridge; /** * - * The Bridge pattern can also be thought of as two layers of abstraction. With Bridge, - * you can decouple an abstraction from its implementation so that the two can vary independently. + * The Bridge pattern can also be thought of as two layers of abstraction. With Bridge, you can + * decouple an abstraction from its implementation so that the two can vary independently. *

- * In Bridge pattern both abstraction ({@link MagicWeapon}) and implementation - * ({@link MagicWeaponImpl}) have their own class hierarchies. The interface of the - * implementations can be changed without affecting the clients. + * In Bridge pattern both abstraction ({@link MagicWeapon}) and implementation ( + * {@link MagicWeaponImpl}) have their own class hierarchies. The interface of the implementations + * can be changed without affecting the clients. * */ public class App { - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { - BlindingMagicWeapon blindingMagicWeapon = new BlindingMagicWeapon( - new Excalibur()); - blindingMagicWeapon.wield(); - blindingMagicWeapon.blind(); - blindingMagicWeapon.swing(); - blindingMagicWeapon.unwield(); + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + BlindingMagicWeapon blindingMagicWeapon = new BlindingMagicWeapon(new Excalibur()); + blindingMagicWeapon.wield(); + blindingMagicWeapon.blind(); + blindingMagicWeapon.swing(); + blindingMagicWeapon.unwield(); - FlyingMagicWeapon flyingMagicWeapon = new FlyingMagicWeapon( - new Mjollnir()); - flyingMagicWeapon.wield(); - flyingMagicWeapon.fly(); - flyingMagicWeapon.swing(); - flyingMagicWeapon.unwield(); + FlyingMagicWeapon flyingMagicWeapon = new FlyingMagicWeapon(new Mjollnir()); + flyingMagicWeapon.wield(); + flyingMagicWeapon.fly(); + flyingMagicWeapon.swing(); + flyingMagicWeapon.unwield(); - SoulEatingMagicWeapon soulEatingMagicWeapon = new SoulEatingMagicWeapon( - new Stormbringer()); - soulEatingMagicWeapon.wield(); - soulEatingMagicWeapon.swing(); - soulEatingMagicWeapon.eatSoul(); - soulEatingMagicWeapon.unwield(); - - } + SoulEatingMagicWeapon soulEatingMagicWeapon = new SoulEatingMagicWeapon(new Stormbringer()); + soulEatingMagicWeapon.wield(); + soulEatingMagicWeapon.swing(); + soulEatingMagicWeapon.eatSoul(); + soulEatingMagicWeapon.unwield(); + } } diff --git a/bridge/src/main/java/com/iluwatar/bridge/BlindingMagicWeapon.java b/bridge/src/main/java/com/iluwatar/bridge/BlindingMagicWeapon.java index bf4bac8f9..045039ef0 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/BlindingMagicWeapon.java +++ b/bridge/src/main/java/com/iluwatar/bridge/BlindingMagicWeapon.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.bridge; /** @@ -7,32 +29,31 @@ package com.iluwatar.bridge; */ public class BlindingMagicWeapon extends MagicWeapon { - public BlindingMagicWeapon(BlindingMagicWeaponImpl imp) { - super(imp); - } + public BlindingMagicWeapon(BlindingMagicWeaponImpl imp) { + super(imp); + } - @Override - public BlindingMagicWeaponImpl getImp() { - return (BlindingMagicWeaponImpl) imp; - } + @Override + public BlindingMagicWeaponImpl getImp() { + return (BlindingMagicWeaponImpl) imp; + } - @Override - public void wield() { - getImp().wieldImp(); - } + @Override + public void wield() { + getImp().wieldImp(); + } - @Override - public void swing() { - getImp().swingImp(); - } + @Override + public void swing() { + getImp().swingImp(); + } - @Override - public void unwield() { - getImp().unwieldImp(); - } - - public void blind() { - getImp().blindImp(); - } + @Override + public void unwield() { + getImp().unwieldImp(); + } + public void blind() { + getImp().blindImp(); + } } diff --git a/bridge/src/main/java/com/iluwatar/bridge/BlindingMagicWeaponImpl.java b/bridge/src/main/java/com/iluwatar/bridge/BlindingMagicWeaponImpl.java index 31dffb042..83f31e709 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/BlindingMagicWeaponImpl.java +++ b/bridge/src/main/java/com/iluwatar/bridge/BlindingMagicWeaponImpl.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.bridge; /** @@ -7,6 +29,6 @@ package com.iluwatar.bridge; */ public abstract class BlindingMagicWeaponImpl extends MagicWeaponImpl { - public abstract void blindImp(); + public abstract void blindImp(); } diff --git a/bridge/src/main/java/com/iluwatar/bridge/Excalibur.java b/bridge/src/main/java/com/iluwatar/bridge/Excalibur.java index 9f7078139..2523b3557 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/Excalibur.java +++ b/bridge/src/main/java/com/iluwatar/bridge/Excalibur.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.bridge; /** @@ -7,25 +29,23 @@ package com.iluwatar.bridge; */ public class Excalibur extends BlindingMagicWeaponImpl { - @Override - public void wieldImp() { - System.out.println("wielding Excalibur"); - } + @Override + public void wieldImp() { + System.out.println("wielding Excalibur"); + } - @Override - public void swingImp() { - System.out.println("swinging Excalibur"); - } + @Override + public void swingImp() { + System.out.println("swinging Excalibur"); + } - @Override - public void unwieldImp() { - System.out.println("unwielding Excalibur"); - } - - @Override - public void blindImp() { - System.out - .println("bright light streams from Excalibur blinding the enemy"); - } + @Override + public void unwieldImp() { + System.out.println("unwielding Excalibur"); + } + @Override + public void blindImp() { + System.out.println("bright light streams from Excalibur blinding the enemy"); + } } diff --git a/bridge/src/main/java/com/iluwatar/bridge/FlyingMagicWeapon.java b/bridge/src/main/java/com/iluwatar/bridge/FlyingMagicWeapon.java index 542e7d97e..0988c179c 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/FlyingMagicWeapon.java +++ b/bridge/src/main/java/com/iluwatar/bridge/FlyingMagicWeapon.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.bridge; /** @@ -7,31 +29,31 @@ package com.iluwatar.bridge; */ public class FlyingMagicWeapon extends MagicWeapon { - public FlyingMagicWeapon(FlyingMagicWeaponImpl imp) { - super(imp); - } + public FlyingMagicWeapon(FlyingMagicWeaponImpl imp) { + super(imp); + } - public FlyingMagicWeaponImpl getImp() { - return (FlyingMagicWeaponImpl) imp; - } + public FlyingMagicWeaponImpl getImp() { + return (FlyingMagicWeaponImpl) imp; + } - @Override - public void wield() { - getImp().wieldImp(); - } + @Override + public void wield() { + getImp().wieldImp(); + } - @Override - public void swing() { - getImp().swingImp(); - } + @Override + public void swing() { + getImp().swingImp(); + } - @Override - public void unwield() { - getImp().unwieldImp(); - } + @Override + public void unwield() { + getImp().unwieldImp(); + } - public void fly() { - getImp().flyImp(); - } + public void fly() { + getImp().flyImp(); + } } diff --git a/bridge/src/main/java/com/iluwatar/bridge/FlyingMagicWeaponImpl.java b/bridge/src/main/java/com/iluwatar/bridge/FlyingMagicWeaponImpl.java index 8b7a10f2f..4bdb8801b 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/FlyingMagicWeaponImpl.java +++ b/bridge/src/main/java/com/iluwatar/bridge/FlyingMagicWeaponImpl.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.bridge; /** @@ -7,6 +29,6 @@ package com.iluwatar.bridge; */ public abstract class FlyingMagicWeaponImpl extends MagicWeaponImpl { - public abstract void flyImp(); + public abstract void flyImp(); } diff --git a/bridge/src/main/java/com/iluwatar/bridge/MagicWeapon.java b/bridge/src/main/java/com/iluwatar/bridge/MagicWeapon.java index b2b82839c..038da7c4f 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/MagicWeapon.java +++ b/bridge/src/main/java/com/iluwatar/bridge/MagicWeapon.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.bridge; /** @@ -7,20 +29,19 @@ package com.iluwatar.bridge; */ public abstract class MagicWeapon { - protected MagicWeaponImpl imp; + protected MagicWeaponImpl imp; - public MagicWeapon(MagicWeaponImpl imp) { - this.imp = imp; - } + public MagicWeapon(MagicWeaponImpl imp) { + this.imp = imp; + } - public abstract void wield(); + public abstract void wield(); - public abstract void swing(); + public abstract void swing(); - public abstract void unwield(); - - public MagicWeaponImpl getImp() { - return imp; - } + public abstract void unwield(); + public MagicWeaponImpl getImp() { + return imp; + } } diff --git a/bridge/src/main/java/com/iluwatar/bridge/MagicWeaponImpl.java b/bridge/src/main/java/com/iluwatar/bridge/MagicWeaponImpl.java index bd2a3b8d7..01f91825b 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/MagicWeaponImpl.java +++ b/bridge/src/main/java/com/iluwatar/bridge/MagicWeaponImpl.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.bridge; /** @@ -7,10 +29,10 @@ package com.iluwatar.bridge; */ public abstract class MagicWeaponImpl { - public abstract void wieldImp(); + public abstract void wieldImp(); - public abstract void swingImp(); + public abstract void swingImp(); - public abstract void unwieldImp(); + public abstract void unwieldImp(); } diff --git a/bridge/src/main/java/com/iluwatar/bridge/Mjollnir.java b/bridge/src/main/java/com/iluwatar/bridge/Mjollnir.java index 887173add..0cc31b471 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/Mjollnir.java +++ b/bridge/src/main/java/com/iluwatar/bridge/Mjollnir.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.bridge; /** @@ -7,25 +29,23 @@ package com.iluwatar.bridge; */ public class Mjollnir extends FlyingMagicWeaponImpl { - @Override - public void wieldImp() { - System.out.println("wielding Mjollnir"); - } + @Override + public void wieldImp() { + System.out.println("wielding Mjollnir"); + } - @Override - public void swingImp() { - System.out.println("swinging Mjollnir"); - } + @Override + public void swingImp() { + System.out.println("swinging Mjollnir"); + } - @Override - public void unwieldImp() { - System.out.println("unwielding Mjollnir"); - } - - @Override - public void flyImp() { - System.out - .println("Mjollnir hits the enemy in the air and returns back to the owner's hand"); - } + @Override + public void unwieldImp() { + System.out.println("unwielding Mjollnir"); + } + @Override + public void flyImp() { + System.out.println("Mjollnir hits the enemy in the air and returns back to the owner's hand"); + } } diff --git a/bridge/src/main/java/com/iluwatar/bridge/SoulEatingMagicWeapon.java b/bridge/src/main/java/com/iluwatar/bridge/SoulEatingMagicWeapon.java index 3310b6488..d62f7644b 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/SoulEatingMagicWeapon.java +++ b/bridge/src/main/java/com/iluwatar/bridge/SoulEatingMagicWeapon.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.bridge; /** @@ -7,32 +29,32 @@ package com.iluwatar.bridge; */ public class SoulEatingMagicWeapon extends MagicWeapon { - public SoulEatingMagicWeapon(SoulEatingMagicWeaponImpl imp) { - super(imp); - } + public SoulEatingMagicWeapon(SoulEatingMagicWeaponImpl imp) { + super(imp); + } - @Override - public SoulEatingMagicWeaponImpl getImp() { - return (SoulEatingMagicWeaponImpl) imp; - } + @Override + public SoulEatingMagicWeaponImpl getImp() { + return (SoulEatingMagicWeaponImpl) imp; + } - @Override - public void wield() { - getImp().wieldImp(); - } + @Override + public void wield() { + getImp().wieldImp(); + } - @Override - public void swing() { - getImp().swingImp(); - } + @Override + public void swing() { + getImp().swingImp(); + } - @Override - public void unwield() { - getImp().unwieldImp(); - } + @Override + public void unwield() { + getImp().unwieldImp(); + } - public void eatSoul() { - getImp().eatSoulImp(); - } + public void eatSoul() { + getImp().eatSoulImp(); + } } diff --git a/bridge/src/main/java/com/iluwatar/bridge/SoulEatingMagicWeaponImpl.java b/bridge/src/main/java/com/iluwatar/bridge/SoulEatingMagicWeaponImpl.java index 9ec112031..ad8bec48f 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/SoulEatingMagicWeaponImpl.java +++ b/bridge/src/main/java/com/iluwatar/bridge/SoulEatingMagicWeaponImpl.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.bridge; /** @@ -7,6 +29,6 @@ package com.iluwatar.bridge; */ public abstract class SoulEatingMagicWeaponImpl extends MagicWeaponImpl { - public abstract void eatSoulImp(); + public abstract void eatSoulImp(); } diff --git a/bridge/src/main/java/com/iluwatar/bridge/Stormbringer.java b/bridge/src/main/java/com/iluwatar/bridge/Stormbringer.java index 589156fe3..91cad9cc2 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/Stormbringer.java +++ b/bridge/src/main/java/com/iluwatar/bridge/Stormbringer.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.bridge; /** @@ -7,24 +29,23 @@ package com.iluwatar.bridge; */ public class Stormbringer extends SoulEatingMagicWeaponImpl { - @Override - public void wieldImp() { - System.out.println("wielding Stormbringer"); - } + @Override + public void wieldImp() { + System.out.println("wielding Stormbringer"); + } - @Override - public void swingImp() { - System.out.println("swinging Stormbringer"); - } + @Override + public void swingImp() { + System.out.println("swinging Stormbringer"); + } - @Override - public void unwieldImp() { - System.out.println("unwielding Stormbringer"); - } - - @Override - public void eatSoulImp() { - System.out.println("Stormbringer devours the enemy's soul"); - } + @Override + public void unwieldImp() { + System.out.println("unwielding Stormbringer"); + } + @Override + public void eatSoulImp() { + System.out.println("Stormbringer devours the enemy's soul"); + } } diff --git a/bridge/src/test/java/com/iluwatar/bridge/AppTest.java b/bridge/src/test/java/com/iluwatar/bridge/AppTest.java index b53111c8e..1b9b63d7f 100644 --- a/bridge/src/test/java/com/iluwatar/bridge/AppTest.java +++ b/bridge/src/test/java/com/iluwatar/bridge/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.bridge; import org.junit.Test; -import com.iluwatar.bridge.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.bridge.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/bridge/src/test/java/com/iluwatar/bridge/BlindingMagicWeaponTest.java b/bridge/src/test/java/com/iluwatar/bridge/BlindingMagicWeaponTest.java new file mode 100644 index 000000000..54435555e --- /dev/null +++ b/bridge/src/test/java/com/iluwatar/bridge/BlindingMagicWeaponTest.java @@ -0,0 +1,55 @@ +/** + * The MIT License + * Copyright (c) 2014 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.bridge; + +import org.junit.Test; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +/** + * Date: 12/6/15 - 11:15 PM + * + * @author Jeroen Meulemeester + */ +public class BlindingMagicWeaponTest extends MagicWeaponTest { + + /** + * Invoke all possible actions on the weapon and check if the actions are executed on the actual + * underlying weapon implementation. + */ + @Test + public void testExcalibur() throws Exception { + final Excalibur excalibur = spy(new Excalibur()); + final BlindingMagicWeapon blindingMagicWeapon = new BlindingMagicWeapon(excalibur); + + testBasicWeaponActions(blindingMagicWeapon, excalibur); + + blindingMagicWeapon.blind(); + verify(excalibur, times(1)).blindImp(); + verifyNoMoreInteractions(excalibur); + } + +} diff --git a/bridge/src/test/java/com/iluwatar/bridge/FlyingMagicWeaponTest.java b/bridge/src/test/java/com/iluwatar/bridge/FlyingMagicWeaponTest.java new file mode 100644 index 000000000..9053a7bb0 --- /dev/null +++ b/bridge/src/test/java/com/iluwatar/bridge/FlyingMagicWeaponTest.java @@ -0,0 +1,55 @@ +/** + * The MIT License + * Copyright (c) 2014 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.bridge; + +import org.junit.Test; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +/** + * Date: 12/6/15 - 11:26 PM + * + * @author Jeroen Meulemeester + */ +public class FlyingMagicWeaponTest extends MagicWeaponTest { + + /** + * Invoke all possible actions on the weapon and check if the actions are executed on the actual + * underlying weapon implementation. + */ + @Test + public void testMjollnir() throws Exception { + final Mjollnir mjollnir = spy(new Mjollnir()); + final FlyingMagicWeapon flyingMagicWeapon = new FlyingMagicWeapon(mjollnir); + + testBasicWeaponActions(flyingMagicWeapon, mjollnir); + + flyingMagicWeapon.fly(); + verify(mjollnir, times(1)).flyImp(); + verifyNoMoreInteractions(mjollnir); + } + +} \ No newline at end of file diff --git a/bridge/src/test/java/com/iluwatar/bridge/MagicWeaponTest.java b/bridge/src/test/java/com/iluwatar/bridge/MagicWeaponTest.java new file mode 100644 index 000000000..6408fd256 --- /dev/null +++ b/bridge/src/test/java/com/iluwatar/bridge/MagicWeaponTest.java @@ -0,0 +1,64 @@ +/** + * The MIT License + * Copyright (c) 2014 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.bridge; + +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +/** + * Date: 12/6/15 - 11:28 PM + * + * @author Jeroen Meulemeester + */ +public abstract class MagicWeaponTest { + + /** + * Invoke the basic actions of the given weapon, and test if the underlying weapon implementation + * is invoked + * + * @param weaponImpl The spied weapon implementation where actions are bridged to + * @param weapon The weapon, handled by the app + */ + protected final void testBasicWeaponActions(final MagicWeapon weapon, + final MagicWeaponImpl weaponImpl) { + assertNotNull(weapon); + assertNotNull(weaponImpl); + assertNotNull(weapon.getImp()); + + weapon.swing(); + verify(weaponImpl, times(1)).swingImp(); + verifyNoMoreInteractions(weaponImpl); + + weapon.wield(); + verify(weaponImpl, times(1)).wieldImp(); + verifyNoMoreInteractions(weaponImpl); + + weapon.unwield(); + verify(weaponImpl, times(1)).unwieldImp(); + verifyNoMoreInteractions(weaponImpl); + + } + +} diff --git a/bridge/src/test/java/com/iluwatar/bridge/SoulEatingMagicWeaponTest.java b/bridge/src/test/java/com/iluwatar/bridge/SoulEatingMagicWeaponTest.java new file mode 100644 index 000000000..0bd0a6b8d --- /dev/null +++ b/bridge/src/test/java/com/iluwatar/bridge/SoulEatingMagicWeaponTest.java @@ -0,0 +1,55 @@ +/** + * The MIT License + * Copyright (c) 2014 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.bridge; + +import org.junit.Test; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +/** + * Date: 12/6/15 - 11:43 PM + * + * @author Jeroen Meulemeester + */ +public class SoulEatingMagicWeaponTest extends MagicWeaponTest { + + /** + * Invoke all possible actions on the weapon and check if the actions are executed on the actual + * underlying weapon implementation. + */ + @Test + public void testStormBringer() throws Exception { + final Stormbringer stormbringer = spy(new Stormbringer()); + final SoulEatingMagicWeapon soulEatingMagicWeapon = new SoulEatingMagicWeapon(stormbringer); + + testBasicWeaponActions(soulEatingMagicWeapon, stormbringer); + + soulEatingMagicWeapon.eatSoul(); + verify(stormbringer, times(1)).eatSoulImp(); + verifyNoMoreInteractions(stormbringer); + } + +} \ No newline at end of file diff --git a/builder/index.md b/builder/README.md similarity index 76% rename from builder/index.md rename to builder/README.md index f350638d2..5d1f3d24d 100644 --- a/builder/index.md +++ b/builder/README.md @@ -7,24 +7,28 @@ categories: Creational tags: - Java - Gang Of Four + - Difficulty-Intermediate --- -**Intent:** Separate the construction of a complex object from its +## Intent +Separate the construction of a complex object from its representation so that the same construction process can create different representations. ![alt text](./etc/builder_1.png "Builder") -**Applicability:** Use the Builder pattern when +## Applicability +Use the Builder pattern when * the algorithm for creating a complex object should be independent of the parts that make up the object and how they're assembled * the construction process must allow different representations for the object that's constructed -**Real world examples:** +## Real world examples * [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html) * [Apache Camel builders](https://github.com/apache/camel/tree/0e195428ee04531be27a0b659005e3aa8d159d23/camel-core/src/main/java/org/apache/camel/builder) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) +* [Effective Java (2nd Edition)](http://www.amazon.com/Effective-Java-Edition-Joshua-Bloch/dp/0321356683) diff --git a/builder/etc/builder.ucls b/builder/etc/builder.ucls index 06a83ced7..073c5cea7 100644 --- a/builder/etc/builder.ucls +++ b/builder/etc/builder.ucls @@ -28,7 +28,7 @@ - + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT builder diff --git a/builder/src/main/java/com/iluwatar/builder/App.java b/builder/src/main/java/com/iluwatar/builder/App.java index b6131c3ab..d421de7b6 100644 --- a/builder/src/main/java/com/iluwatar/builder/App.java +++ b/builder/src/main/java/com/iluwatar/builder/App.java @@ -1,55 +1,77 @@ +/** + * The MIT License + * Copyright (c) 2014 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.builder; -import com.iluwatar. builder.Hero.HeroBuilder; +import com.iluwatar.builder.Hero.Builder; /** * - * The intention of the Builder pattern is to find a solution to the telescoping - * constructor anti-pattern. The telescoping constructor anti-pattern occurs when the - * increase of object constructor parameter combination leads to an exponential list - * of constructors. Instead of using numerous constructors, the builder pattern uses - * another object, a builder, that receives each initialization parameter step by step - * and then returns the resulting constructed object at once. + * The intention of the Builder pattern is to find a solution to the telescoping constructor + * anti-pattern. The telescoping constructor anti-pattern occurs when the increase of object + * constructor parameter combination leads to an exponential list of constructors. Instead of using + * numerous constructors, the builder pattern uses another object, a builder, that receives each + * initialization parameter step by step and then returns the resulting constructed object at once. *

- * The Builder pattern has another benefit. It can be used for objects that contain - * flat data (html code, SQL query, X.509 certificate...), that is to say, data that - * can't be easily edited. This type of data cannot be edited step by step and must - * be edited at once. The best way to construct such an object is to use a builder - * class. + * The Builder pattern has another benefit. It can be used for objects that contain flat data (html + * code, SQL query, X.509 certificate...), that is to say, data that can't be easily edited. This + * type of data cannot be edited step by step and must be edited at once. The best way to construct + * such an object is to use a builder class. *

- * In this example we have the Builder pattern variation as described by Joshua Bloch in - * Effective Java 2nd Edition. + * In this example we have the Builder pattern variation as described by Joshua Bloch in Effective + * Java 2nd Edition. *

- * We want to build {@link Hero} objects, but its construction is complex because of the - * many parameters needed. To aid the user we introduce {@link HeroBuilder} class. - * {@link HeroBuilder} takes the minimum parameters to build {@link Hero} object in its - * constructor. After that additional configuration for the {@link Hero} object can be - * done using the fluent {@link HeroBuilder} interface. When configuration is ready the - * build method is called to receive the final {@link Hero} object. + * We want to build {@link Hero} objects, but its construction is complex because of the many + * parameters needed. To aid the user we introduce {@link Builder} class. {@link Hero.Builder} + * takes the minimum parameters to build {@link Hero} object in its constructor. After that + * additional configuration for the {@link Hero} object can be done using the fluent + * {@link Builder} interface. When configuration is ready the build method is called to receive + * the final {@link Hero} object. * */ public class App { - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { - Hero mage = new HeroBuilder(Profession.MAGE, "Riobard") - .withHairColor(HairColor.BLACK).withWeapon(Weapon.DAGGER) - .build(); - System.out.println(mage); + Hero mage = + new Hero.Builder(Profession.MAGE, "Riobard").withHairColor(HairColor.BLACK) + .withWeapon(Weapon.DAGGER).build(); + System.out.println(mage); - Hero warrior = new HeroBuilder(Profession.WARRIOR, "Amberjill") - .withHairColor(HairColor.BLOND) - .withHairType(HairType.LONG_CURLY).withArmor(Armor.CHAIN_MAIL) - .withWeapon(Weapon.SWORD).build(); - System.out.println(warrior); + Hero warrior = + new Hero.Builder(Profession.WARRIOR, "Amberjill").withHairColor(HairColor.BLOND) + .withHairType(HairType.LONG_CURLY).withArmor(Armor.CHAIN_MAIL).withWeapon(Weapon.SWORD) + .build(); + System.out.println(warrior); - Hero thief = new HeroBuilder(Profession.THIEF, "Desmond") - .withHairType(HairType.BALD).withWeapon(Weapon.BOW).build(); - System.out.println(thief); + Hero thief = + new Hero.Builder(Profession.THIEF, "Desmond").withHairType(HairType.BALD) + .withWeapon(Weapon.BOW).build(); + System.out.println(thief); - } + } } diff --git a/builder/src/main/java/com/iluwatar/builder/Armor.java b/builder/src/main/java/com/iluwatar/builder/Armor.java index 95fcba43b..a0a4fe582 100644 --- a/builder/src/main/java/com/iluwatar/builder/Armor.java +++ b/builder/src/main/java/com/iluwatar/builder/Armor.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.builder; /** @@ -7,16 +29,16 @@ package com.iluwatar.builder; */ public enum Armor { - CLOTHES("clothes"), LEATHER("leather"), CHAIN_MAIL("chain mail"), PLATE_MAIL("plate mail"); + CLOTHES("clothes"), LEATHER("leather"), CHAIN_MAIL("chain mail"), PLATE_MAIL("plate mail"); - private String title; + private String title; - Armor(String title) { - this.title = title; - } + Armor(String title) { + this.title = title; + } - @Override - public String toString() { - return title; - } + @Override + public String toString() { + return title; + } } diff --git a/builder/src/main/java/com/iluwatar/builder/HairColor.java b/builder/src/main/java/com/iluwatar/builder/HairColor.java index dd8ec2ecc..3c3195cc6 100644 --- a/builder/src/main/java/com/iluwatar/builder/HairColor.java +++ b/builder/src/main/java/com/iluwatar/builder/HairColor.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.builder; /** @@ -7,11 +29,11 @@ package com.iluwatar.builder; */ public enum HairColor { - WHITE, BLOND, RED, BROWN, BLACK; + WHITE, BLOND, RED, BROWN, BLACK; - @Override - public String toString() { - return name().toLowerCase(); - } + @Override + public String toString() { + return name().toLowerCase(); + } } diff --git a/builder/src/main/java/com/iluwatar/builder/HairType.java b/builder/src/main/java/com/iluwatar/builder/HairType.java index ea49c0470..8d15b0f30 100644 --- a/builder/src/main/java/com/iluwatar/builder/HairType.java +++ b/builder/src/main/java/com/iluwatar/builder/HairType.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.builder; /** @@ -7,16 +29,17 @@ package com.iluwatar.builder; */ public enum HairType { - BALD("bald"), SHORT("short"), CURLY("curly"), LONG_STRAIGHT("long straight"), LONG_CURLY("long curly"); + BALD("bald"), SHORT("short"), CURLY("curly"), LONG_STRAIGHT("long straight"), LONG_CURLY( + "long curly"); - private String title; + private String title; - HairType(String title) { - this.title = title; - } + HairType(String title) { + this.title = title; + } - @Override - public String toString() { - return title; - } + @Override + public String toString() { + return title; + } } diff --git a/builder/src/main/java/com/iluwatar/builder/Hero.java b/builder/src/main/java/com/iluwatar/builder/Hero.java index 05304bf09..25445851a 100644 --- a/builder/src/main/java/com/iluwatar/builder/Hero.java +++ b/builder/src/main/java/com/iluwatar/builder/Hero.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.builder; /** @@ -5,125 +27,123 @@ package com.iluwatar.builder; * Hero, the class with many parameters. * */ -public class Hero { +public final class Hero { - private final Profession profession; - private final String name; - private final HairType hairType; - private final HairColor hairColor; - private final Armor armor; - private final Weapon weapon; + private final Profession profession; + private final String name; + private final HairType hairType; + private final HairColor hairColor; + private final Armor armor; + private final Weapon weapon; - public Profession getProfession() { - return profession; - } + private Hero(Builder builder) { + this.profession = builder.profession; + this.name = builder.name; + this.hairColor = builder.hairColor; + this.hairType = builder.hairType; + this.weapon = builder.weapon; + this.armor = builder.armor; + } - public String getName() { - return name; - } + public Profession getProfession() { + return profession; + } - public HairType getHairType() { - return hairType; - } + public String getName() { + return name; + } - public HairColor getHairColor() { - return hairColor; - } + public HairType getHairType() { + return hairType; + } - public Armor getArmor() { - return armor; - } + public HairColor getHairColor() { + return hairColor; + } - public Weapon getWeapon() { - return weapon; - } + public Armor getArmor() { + return armor; + } - @Override - public String toString() { + public Weapon getWeapon() { + return weapon; + } - StringBuilder sb = new StringBuilder(); - sb.append("This is a "); - sb.append(profession); - sb.append(" named "); - sb.append(name); - if (hairColor != null || hairType != null) { - sb.append(" with "); - if (hairColor != null) { - sb.append(hairColor); - sb.append(" "); - } - if (hairType != null) { - sb.append(hairType); - sb.append(" "); - } - sb.append(hairType != HairType.BALD ? "hair" : "head"); - } - if (armor != null) { - sb.append(" wearing "); - sb.append(armor); - } - if (weapon != null) { - sb.append(" and wielding a "); - sb.append(weapon); - } - sb.append("."); - return sb.toString(); - } + @Override + public String toString() { - private Hero(HeroBuilder builder) { - this.profession = builder.profession; - this.name = builder.name; - this.hairColor = builder.hairColor; - this.hairType = builder.hairType; - this.weapon = builder.weapon; - this.armor = builder.armor; - } + StringBuilder sb = new StringBuilder(); + sb.append("This is a ") + .append(profession) + .append(" named ") + .append(name); + if (hairColor != null || hairType != null) { + sb.append(" with "); + if (hairColor != null) { + sb.append(hairColor).append(' '); + } + if (hairType != null) { + sb.append(hairType).append(' '); + } + sb.append(hairType != HairType.BALD ? "hair" : "head"); + } + if (armor != null) { + sb.append(" wearing ").append(armor); + } + if (weapon != null) { + sb.append(" and wielding a ").append(weapon); + } + sb.append('.'); + return sb.toString(); + } - /** - * - * The builder class. - * - */ - public static class HeroBuilder { + /** + * + * The builder class. + * + */ + public static class Builder { - private final Profession profession; - private final String name; - private HairType hairType; - private HairColor hairColor; - private Armor armor; - private Weapon weapon; + private final Profession profession; + private final String name; + private HairType hairType; + private HairColor hairColor; + private Armor armor; + private Weapon weapon; - public HeroBuilder(Profession profession, String name) { - if (profession == null || name == null) { - throw new IllegalArgumentException( - "profession and name can not be null"); - } - this.profession = profession; - this.name = name; - } + /** + * Constructor + */ + public Builder(Profession profession, String name) { + if (profession == null || name == null) { + throw new IllegalArgumentException("profession and name can not be null"); + } + this.profession = profession; + this.name = name; + } - public HeroBuilder withHairType(HairType hairType) { - this.hairType = hairType; - return this; - } + public Builder withHairType(HairType hairType) { + this.hairType = hairType; + return this; + } - public HeroBuilder withHairColor(HairColor hairColor) { - this.hairColor = hairColor; - return this; - } + public Builder withHairColor(HairColor hairColor) { + this.hairColor = hairColor; + return this; + } - public HeroBuilder withArmor(Armor armor) { - this.armor = armor; - return this; - } + public Builder withArmor(Armor armor) { + this.armor = armor; + return this; + } - public HeroBuilder withWeapon(Weapon weapon) { - this.weapon = weapon; - return this; - } + public Builder withWeapon(Weapon weapon) { + this.weapon = weapon; + return this; + } - public Hero build() { - return new Hero(this); - } - } + public Hero build() { + return new Hero(this); + } + } } diff --git a/builder/src/main/java/com/iluwatar/builder/Profession.java b/builder/src/main/java/com/iluwatar/builder/Profession.java index c9a7cc4e9..7be5b999e 100644 --- a/builder/src/main/java/com/iluwatar/builder/Profession.java +++ b/builder/src/main/java/com/iluwatar/builder/Profession.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.builder; /** @@ -7,11 +29,10 @@ package com.iluwatar.builder; */ public enum Profession { - WARRIOR, THIEF, MAGE, PRIEST; - - @Override - public String toString() { - return name().toLowerCase(); - } + WARRIOR, THIEF, MAGE, PRIEST; + @Override + public String toString() { + return name().toLowerCase(); + } } diff --git a/builder/src/main/java/com/iluwatar/builder/Weapon.java b/builder/src/main/java/com/iluwatar/builder/Weapon.java index af71c596d..97d347d24 100644 --- a/builder/src/main/java/com/iluwatar/builder/Weapon.java +++ b/builder/src/main/java/com/iluwatar/builder/Weapon.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.builder; /** @@ -7,11 +29,10 @@ package com.iluwatar.builder; */ public enum Weapon { - DAGGER, SWORD, AXE, WARHAMMER, BOW; - - @Override - public String toString() { - return name().toLowerCase(); - } + DAGGER, SWORD, AXE, WARHAMMER, BOW; + @Override + public String toString() { + return name().toLowerCase(); + } } diff --git a/builder/src/test/java/com/iluwatar/builder/AppTest.java b/builder/src/test/java/com/iluwatar/builder/AppTest.java index acae1ca70..e83db9f9f 100644 --- a/builder/src/test/java/com/iluwatar/builder/AppTest.java +++ b/builder/src/test/java/com/iluwatar/builder/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.builder; import org.junit.Test; -import com.iluwatar. builder.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar. builder.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/builder/src/test/java/com/iluwatar/builder/HeroTest.java b/builder/src/test/java/com/iluwatar/builder/HeroTest.java new file mode 100644 index 000000000..b0a73a9d0 --- /dev/null +++ b/builder/src/test/java/com/iluwatar/builder/HeroTest.java @@ -0,0 +1,78 @@ +/** + * The MIT License + * Copyright (c) 2014 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.builder; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/6/15 - 11:01 PM + * + * @author Jeroen Meulemeester + */ +public class HeroTest { + + /** + * Test if we get the expected exception when trying to create a hero without a profession + */ + @Test(expected = IllegalArgumentException.class) + public void testMissingProfession() throws Exception { + new Hero.Builder(null, "Sir without a job"); + } + + /** + * Test if we get the expected exception when trying to create a hero without a name + */ + @Test(expected = IllegalArgumentException.class) + public void testMissingName() throws Exception { + new Hero.Builder(Profession.THIEF, null); + } + + /** + * Test if the hero build by the builder has the correct attributes, as requested + */ + @Test + public void testBuildHero() throws Exception { + final String heroName = "Sir Lancelot"; + + final Hero hero = new Hero.Builder(Profession.WARRIOR, heroName) + .withArmor(Armor.CHAIN_MAIL) + .withWeapon(Weapon.SWORD) + .withHairType(HairType.LONG_CURLY) + .withHairColor(HairColor.BLOND) + .build(); + + assertNotNull(hero); + assertNotNull(hero.toString()); + assertEquals(Profession.WARRIOR, hero.getProfession()); + assertEquals(heroName, hero.getName()); + assertEquals(Armor.CHAIN_MAIL, hero.getArmor()); + assertEquals(Weapon.SWORD, hero.getWeapon()); + assertEquals(HairType.LONG_CURLY, hero.getHairType()); + assertEquals(HairColor.BLOND, hero.getHairColor()); + + } + +} \ No newline at end of file diff --git a/business-delegate/index.md b/business-delegate/README.md similarity index 67% rename from business-delegate/index.md rename to business-delegate/README.md index a55febaf9..e6e249122 100644 --- a/business-delegate/index.md +++ b/business-delegate/README.md @@ -4,18 +4,26 @@ title: Business Delegate folder: business-delegate permalink: /patterns/business-delegate/ categories: Business Tier -tags: Java +tags: + - Java + - Difficulty-Intermediate --- -**Intent:** The Business Delegate pattern adds an abstraction layer between +## Intent +The Business Delegate pattern adds an abstraction layer between presentation and business tiers. By using the pattern we gain loose coupling between the tiers and encapsulate knowledge about how to locate, connect to, and interact with the business objects that make up the application. ![alt text](./etc/business-delegate.png "Business Delegate") -**Applicability:** Use the Business Delegate pattern when +## Applicability +Use the Business Delegate pattern when * you want loose coupling between presentation and business tiers * you want to orchestrate calls to multiple business services * you want to encapsulate service lookups and service calls + +## Credits + +* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) diff --git a/business-delegate/pom.xml b/business-delegate/pom.xml index 84ae64a17..c6f7e0c37 100644 --- a/business-delegate/pom.xml +++ b/business-delegate/pom.xml @@ -1,4 +1,28 @@ + @@ -6,7 +30,7 @@ com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT business-delegate @@ -15,5 +39,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/App.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/App.java index eea7608eb..e9a264322 100644 --- a/business-delegate/src/main/java/com/iluwatar/business/delegate/App.java +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/App.java @@ -1,35 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 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.business.delegate; /** + * The Business Delegate pattern adds an abstraction layer between the presentation and business + * tiers. By using the pattern we gain loose coupling between the tiers. The Business Delegate + * encapsulates knowledge about how to locate, connect to, and interact with the business objects + * that make up the application. * - * The Business Delegate pattern adds an abstraction layer between the presentation and business tiers. - * By using the pattern we gain loose coupling between the tiers. The Business Delegate encapsulates - * knowledge about how to locate, connect to, and interact with the business objects that make up - * the application. - *

- * Some of the services the Business Delegate uses are instantiated directly, and some can be retrieved - * through service lookups. The Business Delegate itself may contain business logic too potentially tying - * together multiple service calls, exception handling, retrying etc. - *

- * In this example the client ({@link Client}) utilizes a business delegate ({@link BusinessDelegate}) to execute a task. - * The Business Delegate then selects the appropriate service and makes the service call. - * + *

Some of the services the Business Delegate uses are instantiated directly, and some can be + * retrieved through service lookups. The Business Delegate itself may contain business logic too + * potentially tying together multiple service calls, exception handling, retrying etc. + * + *

In this example the client ({@link Client}) utilizes a business delegate ( + * {@link BusinessDelegate}) to execute a task. The Business Delegate then selects the appropriate + * service and makes the service call. */ public class App { - - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { - - BusinessDelegate businessDelegate = new BusinessDelegate(); - businessDelegate.setServiceType(ServiceType.EJB); - Client client = new Client(businessDelegate); - client.doTask(); + /** + * Program entry point. + * + * @param args command line args + */ + public static void main(String[] args) { - businessDelegate.setServiceType(ServiceType.JMS); - client.doTask(); - } + BusinessDelegate businessDelegate = new BusinessDelegate(); + BusinessLookup businessLookup = new BusinessLookup(); + businessLookup.setEjbService(new EjbService()); + businessLookup.setJmsService(new JmsService()); + + businessDelegate.setLookupService(businessLookup); + businessDelegate.setServiceType(ServiceType.EJB); + + Client client = new Client(businessDelegate); + client.doTask(); + + businessDelegate.setServiceType(ServiceType.JMS); + client.doTask(); + } } diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessDelegate.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessDelegate.java index cf0809b97..81aa9a0f1 100644 --- a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessDelegate.java +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessDelegate.java @@ -1,22 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 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.business.delegate; /** - * * BusinessDelegate separates the presentation and business tiers - * */ public class BusinessDelegate { - - private BusinessLookup lookupService = new BusinessLookup(); - private BusinessService businessService; - private ServiceType serviceType; - public void setServiceType(ServiceType serviceType) { - this.serviceType = serviceType; - } + private BusinessLookup lookupService; + private BusinessService businessService; + private ServiceType serviceType; - public void doTask() { - businessService = lookupService.getBusinessService(serviceType); - businessService.doProcessing(); - } + public void setLookupService(BusinessLookup businessLookup) { + this.lookupService = businessLookup; + } + + public void setServiceType(ServiceType serviceType) { + this.serviceType = serviceType; + } + + public void doTask() { + businessService = lookupService.getBusinessService(serviceType); + businessService.doProcessing(); + } } diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessLookup.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessLookup.java index 6a5f2d504..7e1045e7f 100644 --- a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessLookup.java +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessLookup.java @@ -1,17 +1,53 @@ +/** + * The MIT License + * Copyright (c) 2014 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.business.delegate; /** - * - * Class for performing service lookups - * + * Class for performing service lookups. */ public class BusinessLookup { - public BusinessService getBusinessService(ServiceType serviceType) { - if (serviceType.equals(ServiceType.EJB)) { - return new EjbService(); - } else { - return new JmsService(); - } - } + private EjbService ejbService; + + private JmsService jmsService; + + /** + * @param serviceType Type of service instance to be returned. + * @return Service instance. + */ + public BusinessService getBusinessService(ServiceType serviceType) { + if (serviceType.equals(ServiceType.EJB)) { + return ejbService; + } else { + return jmsService; + } + } + + public void setJmsService(JmsService jmsService) { + this.jmsService = jmsService; + } + + public void setEjbService(EjbService ejbService) { + this.ejbService = ejbService; + } } diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessService.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessService.java index 7e39745d5..f5784964a 100644 --- a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessService.java +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessService.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.business.delegate; /** @@ -7,5 +29,5 @@ package com.iluwatar.business.delegate; */ public interface BusinessService { - void doProcessing(); + void doProcessing(); } diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/Client.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/Client.java index 2dc0cc662..8c6bf59a1 100644 --- a/business-delegate/src/main/java/com/iluwatar/business/delegate/Client.java +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/Client.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.business.delegate; /** @@ -7,13 +29,13 @@ package com.iluwatar.business.delegate; */ public class Client { - private BusinessDelegate businessDelegate; + private BusinessDelegate businessDelegate; - public Client(BusinessDelegate businessDelegate) { - this.businessDelegate = businessDelegate; - } + public Client(BusinessDelegate businessDelegate) { + this.businessDelegate = businessDelegate; + } - public void doTask() { - businessDelegate.doTask(); - } + public void doTask() { + businessDelegate.doTask(); + } } diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/EjbService.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/EjbService.java index bd03db45d..7296f63b4 100644 --- a/business-delegate/src/main/java/com/iluwatar/business/delegate/EjbService.java +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/EjbService.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.business.delegate; /** @@ -7,8 +29,8 @@ package com.iluwatar.business.delegate; */ public class EjbService implements BusinessService { - @Override - public void doProcessing() { - System.out.println("EjbService is now processing"); - } + @Override + public void doProcessing() { + System.out.println("EjbService is now processing"); + } } diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/JmsService.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/JmsService.java index 37425755a..5b71ce57d 100644 --- a/business-delegate/src/main/java/com/iluwatar/business/delegate/JmsService.java +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/JmsService.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.business.delegate; /** @@ -7,8 +29,8 @@ package com.iluwatar.business.delegate; */ public class JmsService implements BusinessService { - @Override - public void doProcessing() { - System.out.println("JmsService is now processing"); - } + @Override + public void doProcessing() { + System.out.println("JmsService is now processing"); + } } diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/ServiceType.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/ServiceType.java index e26d71ae6..06d0a42b9 100644 --- a/business-delegate/src/main/java/com/iluwatar/business/delegate/ServiceType.java +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/ServiceType.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.business.delegate; /** @@ -6,6 +28,6 @@ package com.iluwatar.business.delegate; * */ public enum ServiceType { - - EJB, JMS; + + EJB, JMS; } diff --git a/business-delegate/src/test/java/com/iluwatar/business/delegate/AppTest.java b/business-delegate/src/test/java/com/iluwatar/business/delegate/AppTest.java index 7ce63c2b4..1767b7ac5 100644 --- a/business-delegate/src/test/java/com/iluwatar/business/delegate/AppTest.java +++ b/business-delegate/src/test/java/com/iluwatar/business/delegate/AppTest.java @@ -1,19 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 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.business.delegate; import org.junit.Test; -import com.iluwatar.business.delegate.App; +import java.io.IOException; /** - * - * Application test - * + * Tests that Business Delegate example runs without errors. */ public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } } diff --git a/business-delegate/src/test/java/com/iluwatar/business/delegate/BusinessDelegateTest.java b/business-delegate/src/test/java/com/iluwatar/business/delegate/BusinessDelegateTest.java new file mode 100644 index 000000000..aec4ea14b --- /dev/null +++ b/business-delegate/src/test/java/com/iluwatar/business/delegate/BusinessDelegateTest.java @@ -0,0 +1,100 @@ +/** + * The MIT License + * Copyright (c) 2014 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.business.delegate; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import org.junit.Before; +import org.junit.Test; + +/** + * The Business Delegate pattern adds an abstraction layer between the presentation and business + * tiers. By using the pattern we gain loose coupling between the tiers. The Business Delegate + * encapsulates knowledge about how to locate, connect to, and interact with the business objects + * that make up the application. + * + *

Some of the services the Business Delegate uses are instantiated directly, and some can be + * retrieved through service lookups. The Business Delegate itself may contain business logic too + * potentially tying together multiple service calls, exception handling, retrying etc. + */ +public class BusinessDelegateTest { + + private EjbService ejbService; + + private JmsService jmsService; + + private BusinessLookup businessLookup; + + private BusinessDelegate businessDelegate; + + /** + * This method sets up the instance variables of this test class. It is executed before the + * execution of every test. + */ + @Before + public void setup() { + ejbService = spy(new EjbService()); + jmsService = spy(new JmsService()); + + businessLookup = spy(new BusinessLookup()); + businessLookup.setEjbService(ejbService); + businessLookup.setJmsService(jmsService); + + businessDelegate = spy(new BusinessDelegate()); + businessDelegate.setLookupService(businessLookup); + } + + /** + * In this example the client ({@link Client}) utilizes a business delegate ( + * {@link BusinessDelegate}) to execute a task. The Business Delegate then selects the appropriate + * service and makes the service call. + */ + @Test + public void testBusinessDelegate() { + + // setup a client object + Client client = new Client(businessDelegate); + + // set the service type + businessDelegate.setServiceType(ServiceType.EJB); + + // action + client.doTask(); + + // verifying that the businessDelegate was used by client during doTask() method. + verify(businessDelegate).doTask(); + verify(ejbService).doProcessing(); + + // set the service type + businessDelegate.setServiceType(ServiceType.JMS); + + // action + client.doTask(); + + // verifying that the businessDelegate was used by client during doTask() method. + verify(businessDelegate, times(2)).doTask(); + verify(jmsService).doProcessing(); + } +} diff --git a/caching/index.md b/caching/README.md similarity index 81% rename from caching/index.md rename to caching/README.md index f79f13e42..2b89d0559 100644 --- a/caching/index.md +++ b/caching/README.md @@ -6,19 +6,23 @@ permalink: /patterns/caching/ categories: Other tags: - Java + - Difficulty-Intermediate + - Performance --- -**Intent:** To avoid expensive re-acquisition of resources by not releasing +## Intent +To avoid expensive re-acquisition of resources by not releasing the resources immediately after their use. The resources retain their identity, are kept in some fast-access storage, and are re-used to avoid having to acquire them again. ![alt text](./etc/caching.png "Caching") -**Applicability:** Use the Caching pattern(s) when +## Applicability +Use the Caching pattern(s) when * Repetitious acquisition, initialization, and release of the same resource causes unnecessary performance overhead. -**Credits** +## Credits * [Write-through, write-around, write-back: Cache explained](http://www.computerweekly.com/feature/Write-through-write-around-write-back-Cache-explained) * [Read-Through, Write-Through, Write-Behind, and Refresh-Ahead Caching](https://docs.oracle.com/cd/E15357_01/coh.360/e15723/cache_rtwtwbra.htm#COHDG5177) diff --git a/caching/pom.xml b/caching/pom.xml index d2284a5f1..c20842a89 100644 --- a/caching/pom.xml +++ b/caching/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT caching diff --git a/caching/src/main/java/com/iluwatar/caching/App.java b/caching/src/main/java/com/iluwatar/caching/App.java index c7f55db70..8e5a84085 100644 --- a/caching/src/main/java/com/iluwatar/caching/App.java +++ b/caching/src/main/java/com/iluwatar/caching/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.caching; /** @@ -21,7 +43,7 @@ package com.iluwatar.caching; * application data. The cache itself is implemented as an internal (Java) data structure. It adopts * a Least-Recently-Used (LRU) strategy for evicting data from itself when its full. The three * strategies are individually tested. The testing of the cache is restricted towards saving and - * querying of user accounts from the underlying data store ( {@link DBManager}). The main class ( + * querying of user accounts from the underlying data store ( {@link DbManager}). The main class ( * {@link App} is not aware of the underlying mechanics of the application (i.e. save and query) and * whether the data is coming from the cache or the DB (i.e. separation of concern). The AppManager * ({@link AppManager}) handles the transaction of data to-and-from the underlying data store @@ -43,7 +65,7 @@ public class App { * @param args command line args */ public static void main(String[] args) { - AppManager.initDB(false); // VirtualDB (instead of MongoDB) was used in running the JUnit tests + AppManager.initDb(false); // VirtualDB (instead of MongoDB) was used in running the JUnit tests // and the App class to avoid Maven compilation errors. Set flag to // true to run the tests with MongoDB (provided that MongoDB is // installed and socket connection is open). @@ -65,8 +87,8 @@ public class App { AppManager.save(userAccount1); System.out.println(AppManager.printCacheContent()); - userAccount1 = AppManager.find("001"); - userAccount1 = AppManager.find("001"); + AppManager.find("001"); + AppManager.find("001"); } /** @@ -80,15 +102,15 @@ public class App { AppManager.save(userAccount2); System.out.println(AppManager.printCacheContent()); - userAccount2 = AppManager.find("002"); + AppManager.find("002"); System.out.println(AppManager.printCacheContent()); userAccount2 = AppManager.find("002"); userAccount2.setUserName("Jane G."); AppManager.save(userAccount2); System.out.println(AppManager.printCacheContent()); - userAccount2 = AppManager.find("002"); + AppManager.find("002"); System.out.println(AppManager.printCacheContent()); - userAccount2 = AppManager.find("002"); + AppManager.find("002"); } /** @@ -106,12 +128,12 @@ public class App { AppManager.save(userAccount4); AppManager.save(userAccount5); System.out.println(AppManager.printCacheContent()); - userAccount3 = AppManager.find("003"); + AppManager.find("003"); System.out.println(AppManager.printCacheContent()); UserAccount userAccount6 = new UserAccount("006", "Yasha", "She is an only child."); AppManager.save(userAccount6); System.out.println(AppManager.printCacheContent()); - userAccount4 = AppManager.find("004"); + AppManager.find("004"); System.out.println(AppManager.printCacheContent()); } } diff --git a/caching/src/main/java/com/iluwatar/caching/AppManager.java b/caching/src/main/java/com/iluwatar/caching/AppManager.java index 08132e327..2967c759f 100644 --- a/caching/src/main/java/com/iluwatar/caching/AppManager.java +++ b/caching/src/main/java/com/iluwatar/caching/AppManager.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.caching; import java.text.ParseException; @@ -11,28 +33,34 @@ import java.text.ParseException; * CacheStore class. * */ -public class AppManager { +public final class AppManager { private static CachingPolicy cachingPolicy; + private AppManager() { + } + /** * * Developer/Tester is able to choose whether the application should use MongoDB as its underlying * data storage or a simple Java data structure to (temporarily) store the data/objects during * runtime. */ - public static void initDB(boolean useMongoDB) { - if (useMongoDB) { + public static void initDb(boolean useMongoDb) { + if (useMongoDb) { try { - DBManager.connect(); + DbManager.connect(); } catch (ParseException e) { e.printStackTrace(); } } else { - DBManager.createVirtualDB(); + DbManager.createVirtualDb(); } } + /** + * Initialize caching policy + */ public static void initCachingPolicy(CachingPolicy policy) { cachingPolicy = policy; if (cachingPolicy == CachingPolicy.BEHIND) { @@ -50,15 +78,21 @@ public class AppManager { CacheStore.initCapacity(capacity); } - public static UserAccount find(String userID) { + /** + * Find user account + */ + public static UserAccount find(String userId) { if (cachingPolicy == CachingPolicy.THROUGH || cachingPolicy == CachingPolicy.AROUND) { - return CacheStore.readThrough(userID); + return CacheStore.readThrough(userId); } else if (cachingPolicy == CachingPolicy.BEHIND) { - return CacheStore.readThroughWithWriteBackPolicy(userID); + return CacheStore.readThroughWithWriteBackPolicy(userId); } return null; } + /** + * Save user account + */ public static void save(UserAccount userAccount) { if (cachingPolicy == CachingPolicy.THROUGH) { CacheStore.writeThrough(userAccount); diff --git a/caching/src/main/java/com/iluwatar/caching/CacheStore.java b/caching/src/main/java/com/iluwatar/caching/CacheStore.java index 2041ac14f..5903f8219 100644 --- a/caching/src/main/java/com/iluwatar/caching/CacheStore.java +++ b/caching/src/main/java/com/iluwatar/caching/CacheStore.java @@ -1,6 +1,28 @@ +/** + * The MIT License + * Copyright (c) 2014 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.caching; -import java.util.ArrayList; +import java.util.List; /** * @@ -9,73 +31,99 @@ import java.util.ArrayList; */ public class CacheStore { - static LRUCache cache = null; + static LruCache cache; - public static void initCapacity(int capacity) { - if (null == cache) - cache = new LRUCache(capacity); - else - cache.setCapacity(capacity); + private CacheStore() { } - public static UserAccount readThrough(String userID) { - if (cache.contains(userID)) { + /** + * Init cache capacity + */ + public static void initCapacity(int capacity) { + if (null == cache) { + cache = new LruCache(capacity); + } else { + cache.setCapacity(capacity); + } + } + + /** + * Get user account using read-through cache + */ + public static UserAccount readThrough(String userId) { + if (cache.contains(userId)) { System.out.println("# Cache Hit!"); - return cache.get(userID); + return cache.get(userId); } System.out.println("# Cache Miss!"); - UserAccount userAccount = DBManager.readFromDB(userID); - cache.set(userID, userAccount); + UserAccount userAccount = DbManager.readFromDb(userId); + cache.set(userId, userAccount); return userAccount; } + /** + * Get user account using write-through cache + */ public static void writeThrough(UserAccount userAccount) { - if (cache.contains(userAccount.getUserID())) { - DBManager.updateDB(userAccount); + if (cache.contains(userAccount.getUserId())) { + DbManager.updateDb(userAccount); } else { - DBManager.writeToDB(userAccount); + DbManager.writeToDb(userAccount); } - cache.set(userAccount.getUserID(), userAccount); + cache.set(userAccount.getUserId(), userAccount); } + /** + * Get user account using write-around cache + */ public static void writeAround(UserAccount userAccount) { - if (cache.contains(userAccount.getUserID())) { - DBManager.updateDB(userAccount); - cache.invalidate(userAccount.getUserID()); // Cache data has been updated -- remove older + if (cache.contains(userAccount.getUserId())) { + DbManager.updateDb(userAccount); + cache.invalidate(userAccount.getUserId()); // Cache data has been updated -- remove older // version from cache. } else { - DBManager.writeToDB(userAccount); + DbManager.writeToDb(userAccount); } } - public static UserAccount readThroughWithWriteBackPolicy(String userID) { - if (cache.contains(userID)) { + /** + * Get user account using read-through cache with write-back policy + */ + public static UserAccount readThroughWithWriteBackPolicy(String userId) { + if (cache.contains(userId)) { System.out.println("# Cache Hit!"); - return cache.get(userID); + return cache.get(userId); } System.out.println("# Cache Miss!"); - UserAccount userAccount = DBManager.readFromDB(userID); + UserAccount userAccount = DbManager.readFromDb(userId); if (cache.isFull()) { System.out.println("# Cache is FULL! Writing LRU data to DB..."); - UserAccount toBeWrittenToDB = cache.getLRUData(); - DBManager.upsertDB(toBeWrittenToDB); + UserAccount toBeWrittenToDb = cache.getLruData(); + DbManager.upsertDb(toBeWrittenToDb); } - cache.set(userID, userAccount); + cache.set(userId, userAccount); return userAccount; } + /** + * Set user account + */ public static void writeBehind(UserAccount userAccount) { - if (cache.isFull() && !cache.contains(userAccount.getUserID())) { + if (cache.isFull() && !cache.contains(userAccount.getUserId())) { System.out.println("# Cache is FULL! Writing LRU data to DB..."); - UserAccount toBeWrittenToDB = cache.getLRUData(); - DBManager.upsertDB(toBeWrittenToDB); + UserAccount toBeWrittenToDb = cache.getLruData(); + DbManager.upsertDb(toBeWrittenToDb); } - cache.set(userAccount.getUserID(), userAccount); + cache.set(userAccount.getUserId(), userAccount); } + /** + * Clears cache + */ public static void clearCache() { - if (null != cache) + if (null != cache) { cache.clear(); + } } /** @@ -83,16 +131,20 @@ public class CacheStore { */ public static void flushCache() { System.out.println("# flushCache..."); - if (null == cache) + if (null == cache) { return; - ArrayList listOfUserAccounts = cache.getCacheDataInListForm(); + } + List listOfUserAccounts = cache.getCacheDataInListForm(); for (UserAccount userAccount : listOfUserAccounts) { - DBManager.upsertDB(userAccount); + DbManager.upsertDb(userAccount); } } + /** + * Print user accounts + */ public static String print() { - ArrayList listOfUserAccounts = cache.getCacheDataInListForm(); + List listOfUserAccounts = cache.getCacheDataInListForm(); StringBuilder sb = new StringBuilder(); sb.append("\n--CACHE CONTENT--\n"); for (UserAccount userAccount : listOfUserAccounts) { diff --git a/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java b/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java index 314cfaa36..490113baa 100644 --- a/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java +++ b/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.caching; /** diff --git a/caching/src/main/java/com/iluwatar/caching/DBManager.java b/caching/src/main/java/com/iluwatar/caching/DbManager.java similarity index 52% rename from caching/src/main/java/com/iluwatar/caching/DBManager.java rename to caching/src/main/java/com/iluwatar/caching/DbManager.java index 07a5daeac..c12461d0c 100644 --- a/caching/src/main/java/com/iluwatar/caching/DBManager.java +++ b/caching/src/main/java/com/iluwatar/caching/DbManager.java @@ -1,7 +1,30 @@ +/** + * The MIT License + * Copyright (c) 2014 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.caching; import java.text.ParseException; import java.util.HashMap; +import java.util.Map; import org.bson.Document; @@ -21,29 +44,42 @@ import com.mongodb.client.model.UpdateOptions; * during runtime (createVirtualDB()).

* */ -public class DBManager { +public final class DbManager { private static MongoClient mongoClient; private static MongoDatabase db; private static boolean useMongoDB; - private static HashMap virtualDB; + private static Map virtualDB; - public static void createVirtualDB() { - useMongoDB = false; - virtualDB = new HashMap(); + private DbManager() { } + /** + * Create DB + */ + public static void createVirtualDb() { + useMongoDB = false; + virtualDB = new HashMap<>(); + } + + /** + * Connect to DB + */ public static void connect() throws ParseException { useMongoDB = true; mongoClient = new MongoClient(); db = mongoClient.getDatabase("test"); } - public static UserAccount readFromDB(String userID) { + /** + * Read user account from DB + */ + public static UserAccount readFromDb(String userId) { if (!useMongoDB) { - if (virtualDB.containsKey(userID)) - return virtualDB.get(userID); + if (virtualDB.containsKey(userId)) { + return virtualDB.get(userId); + } return null; } if (null == db) { @@ -54,18 +90,20 @@ public class DBManager { } } FindIterable iterable = - db.getCollection("user_accounts").find(new Document("userID", userID)); - if (iterable == null) + db.getCollection("user_accounts").find(new Document("userID", userId)); + if (iterable == null) { return null; + } Document doc = iterable.first(); - UserAccount userAccount = - new UserAccount(userID, doc.getString("userName"), doc.getString("additionalInfo")); - return userAccount; + return new UserAccount(userId, doc.getString("userName"), doc.getString("additionalInfo")); } - public static void writeToDB(UserAccount userAccount) { + /** + * Write user account to DB + */ + public static void writeToDb(UserAccount userAccount) { if (!useMongoDB) { - virtualDB.put(userAccount.getUserID(), userAccount); + virtualDB.put(userAccount.getUserId(), userAccount); return; } if (null == db) { @@ -76,13 +114,16 @@ public class DBManager { } } db.getCollection("user_accounts").insertOne( - new Document("userID", userAccount.getUserID()).append("userName", + new Document("userID", userAccount.getUserId()).append("userName", userAccount.getUserName()).append("additionalInfo", userAccount.getAdditionalInfo())); } - public static void updateDB(UserAccount userAccount) { + /** + * Update DB + */ + public static void updateDb(UserAccount userAccount) { if (!useMongoDB) { - virtualDB.put(userAccount.getUserID(), userAccount); + virtualDB.put(userAccount.getUserId(), userAccount); return; } if (null == db) { @@ -93,7 +134,7 @@ public class DBManager { } } db.getCollection("user_accounts").updateOne( - new Document("userID", userAccount.getUserID()), + new Document("userID", userAccount.getUserId()), new Document("$set", new Document("userName", userAccount.getUserName()).append( "additionalInfo", userAccount.getAdditionalInfo()))); } @@ -102,9 +143,9 @@ public class DBManager { * * Insert data into DB if it does not exist. Else, update it. */ - public static void upsertDB(UserAccount userAccount) { + public static void upsertDb(UserAccount userAccount) { if (!useMongoDB) { - virtualDB.put(userAccount.getUserID(), userAccount); + virtualDB.put(userAccount.getUserId(), userAccount); return; } if (null == db) { @@ -115,8 +156,8 @@ public class DBManager { } } db.getCollection("user_accounts").updateOne( - new Document("userID", userAccount.getUserID()), - new Document("$set", new Document("userID", userAccount.getUserID()).append("userName", + new Document("userID", userAccount.getUserId()), + new Document("$set", new Document("userID", userAccount.getUserId()).append("userName", userAccount.getUserName()).append("additionalInfo", userAccount.getAdditionalInfo())), new UpdateOptions().upsert(true)); } diff --git a/caching/src/main/java/com/iluwatar/caching/LRUCache.java b/caching/src/main/java/com/iluwatar/caching/LRUCache.java deleted file mode 100644 index 872f97256..000000000 --- a/caching/src/main/java/com/iluwatar/caching/LRUCache.java +++ /dev/null @@ -1,146 +0,0 @@ -package com.iluwatar.caching; - -import java.util.ArrayList; -import java.util.HashMap; - -/** - * - * Data structure/implementation of the application's cache. The data structure consists of a hash - * table attached with a doubly linked-list. The linked-list helps in capturing and maintaining the - * LRU data in the cache. When a data is queried (from the cache), added (to the cache), or updated, - * the data is moved to the front of the list to depict itself as the most-recently-used data. The - * LRU data is always at the end of the list. - * - */ -public class LRUCache { - - class Node { - String userID; - UserAccount userAccount; - Node previous; - Node next; - - public Node(String userID, UserAccount userAccount) { - this.userID = userID; - this.userAccount = userAccount; - } - } - - int capacity; - HashMap cache = new HashMap(); - Node head = null; - Node end = null; - - public LRUCache(int capacity) { - this.capacity = capacity; - } - - public UserAccount get(String userID) { - if (cache.containsKey(userID)) { - Node node = cache.get(userID); - remove(node); - setHead(node); - return node.userAccount; - } - return null; - } - - /** - * - * Remove node from linked list. - */ - public void remove(Node node) { - if (node.previous != null) { - node.previous.next = node.next; - } else { - head = node.next; - } - if (node.next != null) { - node.next.previous = node.previous; - } else { - end = node.previous; - } - } - - /** - * - * Move node to the front of the list. - */ - public void setHead(Node node) { - node.next = head; - node.previous = null; - if (head != null) - head.previous = node; - head = node; - if (end == null) - end = head; - } - - public void set(String userID, UserAccount userAccount) { - if (cache.containsKey(userID)) { - Node old = cache.get(userID); - old.userAccount = userAccount; - remove(old); - setHead(old); - } else { - Node newNode = new Node(userID, userAccount); - if (cache.size() >= capacity) { - System.out.println("# Cache is FULL! Removing " + end.userID + " from cache..."); - cache.remove(end.userID); // remove LRU data from cache. - remove(end); - setHead(newNode); - } else { - setHead(newNode); - } - cache.put(userID, newNode); - } - } - - public boolean contains(String userID) { - return cache.containsKey(userID); - } - - public void invalidate(String userID) { - System.out.println("# " + userID + " has been updated! Removing older version from cache..."); - Node toBeRemoved = cache.get(userID); - remove(toBeRemoved); - cache.remove(userID); - } - - public boolean isFull() { - return cache.size() >= capacity; - } - - public UserAccount getLRUData() { - return end.userAccount; - } - - public void clear() { - head = null; - end = null; - cache.clear(); - } - - /** - * - * Returns cache data in list form. - */ - public ArrayList getCacheDataInListForm() { - ArrayList listOfCacheData = new ArrayList(); - Node temp = head; - while (temp != null) { - listOfCacheData.add(temp.userAccount); - temp = temp.next; - } - return listOfCacheData; - } - - public void setCapacity(int newCapacity) { - if (capacity > newCapacity) { - clear(); // Behavior can be modified to accommodate for decrease in cache size. For now, we'll - // just clear the cache. - } else { - this.capacity = newCapacity; - } - } -} diff --git a/caching/src/main/java/com/iluwatar/caching/LruCache.java b/caching/src/main/java/com/iluwatar/caching/LruCache.java new file mode 100644 index 000000000..5c5549afd --- /dev/null +++ b/caching/src/main/java/com/iluwatar/caching/LruCache.java @@ -0,0 +1,187 @@ +/** + * The MIT License + * Copyright (c) 2014 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.caching; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * + * Data structure/implementation of the application's cache. The data structure consists of a hash + * table attached with a doubly linked-list. The linked-list helps in capturing and maintaining the + * LRU data in the cache. When a data is queried (from the cache), added (to the cache), or updated, + * the data is moved to the front of the list to depict itself as the most-recently-used data. The + * LRU data is always at the end of the list. + * + */ +public class LruCache { + + class Node { + String userId; + UserAccount userAccount; + Node previous; + Node next; + + public Node(String userId, UserAccount userAccount) { + this.userId = userId; + this.userAccount = userAccount; + } + } + + int capacity; + Map cache = new HashMap<>(); + Node head; + Node end; + + public LruCache(int capacity) { + this.capacity = capacity; + } + + /** + * Get user account + */ + public UserAccount get(String userId) { + if (cache.containsKey(userId)) { + Node node = cache.get(userId); + remove(node); + setHead(node); + return node.userAccount; + } + return null; + } + + /** + * + * Remove node from linked list. + */ + public void remove(Node node) { + if (node.previous != null) { + node.previous.next = node.next; + } else { + head = node.next; + } + if (node.next != null) { + node.next.previous = node.previous; + } else { + end = node.previous; + } + } + + /** + * + * Move node to the front of the list. + */ + public void setHead(Node node) { + node.next = head; + node.previous = null; + if (head != null) { + head.previous = node; + } + head = node; + if (end == null) { + end = head; + } + } + + /** + * Set user account + */ + public void set(String userId, UserAccount userAccount) { + if (cache.containsKey(userId)) { + Node old = cache.get(userId); + old.userAccount = userAccount; + remove(old); + setHead(old); + } else { + Node newNode = new Node(userId, userAccount); + if (cache.size() >= capacity) { + System.out.println("# Cache is FULL! Removing " + end.userId + " from cache..."); + cache.remove(end.userId); // remove LRU data from cache. + remove(end); + setHead(newNode); + } else { + setHead(newNode); + } + cache.put(userId, newNode); + } + } + + public boolean contains(String userId) { + return cache.containsKey(userId); + } + + /** + * Invalidate cache for user + */ + public void invalidate(String userId) { + System.out.println("# " + userId + " has been updated! Removing older version from cache..."); + Node toBeRemoved = cache.get(userId); + remove(toBeRemoved); + cache.remove(userId); + } + + public boolean isFull() { + return cache.size() >= capacity; + } + + public UserAccount getLruData() { + return end.userAccount; + } + + /** + * Clear cache + */ + public void clear() { + head = null; + end = null; + cache.clear(); + } + + /** + * + * Returns cache data in list form. + */ + public List getCacheDataInListForm() { + ArrayList listOfCacheData = new ArrayList<>(); + Node temp = head; + while (temp != null) { + listOfCacheData.add(temp.userAccount); + temp = temp.next; + } + return listOfCacheData; + } + + /** + * Set cache capacity + */ + public void setCapacity(int newCapacity) { + if (capacity > newCapacity) { + clear(); // Behavior can be modified to accommodate for decrease in cache size. For now, we'll + // just clear the cache. + } else { + this.capacity = newCapacity; + } + } +} diff --git a/caching/src/main/java/com/iluwatar/caching/UserAccount.java b/caching/src/main/java/com/iluwatar/caching/UserAccount.java index eff0878ad..e2444ad72 100644 --- a/caching/src/main/java/com/iluwatar/caching/UserAccount.java +++ b/caching/src/main/java/com/iluwatar/caching/UserAccount.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.caching; /** @@ -6,22 +28,25 @@ package com.iluwatar.caching; * */ public class UserAccount { - private String userID; + private String userId; private String userName; private String additionalInfo; - public UserAccount(String userID, String userName, String additionalInfo) { - this.userID = userID; + /** + * Constructor + */ + public UserAccount(String userId, String userName, String additionalInfo) { + this.userId = userId; this.userName = userName; this.additionalInfo = additionalInfo; } - public String getUserID() { - return userID; + public String getUserId() { + return userId; } - public void setUserID(String userID) { - this.userID = userID; + public void setUserId(String userId) { + this.userId = userId; } public String getUserName() { @@ -42,6 +67,6 @@ public class UserAccount { @Override public String toString() { - return userID + ", " + userName + ", " + additionalInfo; + return userId + ", " + userName + ", " + additionalInfo; } } diff --git a/caching/src/test/java/com/iluwatar/caching/AppTest.java b/caching/src/test/java/com/iluwatar/caching/AppTest.java index ce5cddf08..90ed1c275 100644 --- a/caching/src/test/java/com/iluwatar/caching/AppTest.java +++ b/caching/src/test/java/com/iluwatar/caching/AppTest.java @@ -1,41 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 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.caching; -import org.junit.Before; import org.junit.Test; +import java.io.IOException; + /** - * - * Application test - * + * Tests that Caching example runs without errors. */ public class AppTest { - App app; - - /** - * Setup of application test includes: initializing DB connection and cache size/capacity. - */ - @Before - public void setUp() { - AppManager.initDB(false); // VirtualDB (instead of MongoDB) was used in running the JUnit tests - // to avoid Maven compilation errors. Set flag to true to run the - // tests with MongoDB (provided that MongoDB is installed and socket - // connection is open). - AppManager.initCacheCapacity(3); - app = new App(); - } - @Test - public void testReadAndWriteThroughStrategy() { - app.useReadAndWriteThroughStrategy(); - } - - @Test - public void testReadThroughAndWriteAroundStrategy() { - app.useReadThroughAndWriteAroundStrategy(); - } - - @Test - public void testReadThroughAndWriteBehindStrategy() { - app.useReadThroughAndWriteBehindStrategy(); + public void test() throws IOException { + String[] args = {}; + App.main(args); } } diff --git a/caching/src/test/java/com/iluwatar/caching/CachingTest.java b/caching/src/test/java/com/iluwatar/caching/CachingTest.java new file mode 100644 index 000000000..19262a3b6 --- /dev/null +++ b/caching/src/test/java/com/iluwatar/caching/CachingTest.java @@ -0,0 +1,63 @@ +/** + * The MIT License + * Copyright (c) 2014 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.caching; + +import org.junit.Before; +import org.junit.Test; + +/** + * + * Application test + * + */ +public class CachingTest { + App app; + + /** + * Setup of application test includes: initializing DB connection and cache size/capacity. + */ + @Before + public void setUp() { + AppManager.initDb(false); // VirtualDB (instead of MongoDB) was used in running the JUnit tests + // to avoid Maven compilation errors. Set flag to true to run the + // tests with MongoDB (provided that MongoDB is installed and socket + // connection is open). + AppManager.initCacheCapacity(3); + app = new App(); + } + + @Test + public void testReadAndWriteThroughStrategy() { + app.useReadAndWriteThroughStrategy(); + } + + @Test + public void testReadThroughAndWriteAroundStrategy() { + app.useReadThroughAndWriteAroundStrategy(); + } + + @Test + public void testReadThroughAndWriteBehindStrategy() { + app.useReadThroughAndWriteBehindStrategy(); + } +} diff --git a/callback/README.md b/callback/README.md new file mode 100644 index 000000000..a408fd5e0 --- /dev/null +++ b/callback/README.md @@ -0,0 +1,28 @@ +--- +layout: pattern +title: Callback +folder: callback +permalink: /patterns/callback/ +categories: Other +tags: + - Java + - Difficulty-Beginner + - Functional + - Idiom +--- + +## Intent +Callback is a piece of executable code that is passed as an +argument to other code, which is expected to call back (execute) the argument +at some convenient time. + +![alt text](./etc/callback.png "Callback") + +## Applicability +Use the Callback pattern when + +* when some arbitrary synchronous or asynchronous action must be performed after execution of some defined activity. + +## Real world examples + +* [CyclicBarrier](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html#CyclicBarrier%28int,%20java.lang.Runnable%29) constructor can accept callback that will be triggered every time when barrier is tripped. diff --git a/callback/index.md b/callback/index.md deleted file mode 100644 index b724f1edc..000000000 --- a/callback/index.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -layout: pattern -title: Callback -folder: callback -permalink: /patterns/callback/ -categories: Other -tags: Java ---- - -**Intent:** Callback is a piece of executable code that is passed as an -argument to other code, which is expected to call back (execute) the argument -at some convenient time. - -![alt text](./etc/callback.png "Callback") - -**Applicability:** Use the Callback pattern when - -* when some arbitrary synchronous or asynchronous action must be performed after execution of some defined activity. - -**Real world examples:** - -* [CyclicBarrier] (http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html#CyclicBarrier%28int,%20java.lang.Runnable%29) constructor can accept callback that will be triggered every time when barrier is tripped. diff --git a/callback/pom.xml b/callback/pom.xml index 7e66f6e2b..4fe17d143 100644 --- a/callback/pom.xml +++ b/callback/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT callback diff --git a/callback/src/main/java/com/iluwatar/callback/App.java b/callback/src/main/java/com/iluwatar/callback/App.java index 513a32415..bc1b2890e 100644 --- a/callback/src/main/java/com/iluwatar/callback/App.java +++ b/callback/src/main/java/com/iluwatar/callback/App.java @@ -1,21 +1,47 @@ +/** + * The MIT License + * Copyright (c) 2014 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.callback; /** * - * Callback pattern is more native for functional languages where functions are treated as first-class citizens. - * Prior to Java 8 callbacks can be simulated using simple (alike command) interfaces. + * Callback pattern is more native for functional languages where functions are treated as + * first-class citizens. Prior to Java 8 callbacks can be simulated using simple (alike command) + * interfaces. * */ public class App { - public static void main(String[] args) { - Task task = new SimpleTask(); - Callback callback = new Callback() { - @Override - public void call() { - System.out.println("I'm done now."); - } - }; - task.executeWith(callback); - } + /** + * Program entry point + */ + public static void main(String[] args) { + Task task = new SimpleTask(); + Callback callback = new Callback() { + @Override + public void call() { + System.out.println("I'm done now."); + } + }; + task.executeWith(callback); + } } diff --git a/callback/src/main/java/com/iluwatar/callback/Callback.java b/callback/src/main/java/com/iluwatar/callback/Callback.java index 81a421c0d..78932a24e 100644 --- a/callback/src/main/java/com/iluwatar/callback/Callback.java +++ b/callback/src/main/java/com/iluwatar/callback/Callback.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.callback; /** @@ -7,5 +29,5 @@ package com.iluwatar.callback; */ public interface Callback { - public void call(); + void call(); } diff --git a/callback/src/main/java/com/iluwatar/callback/LambdasApp.java b/callback/src/main/java/com/iluwatar/callback/LambdasApp.java new file mode 100644 index 000000000..78ca63e0b --- /dev/null +++ b/callback/src/main/java/com/iluwatar/callback/LambdasApp.java @@ -0,0 +1,41 @@ +/** + * The MIT License + * Copyright (c) 2014 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.callback; + +/** + * + * This example generates the exact same output as {@link App} however the callback has been + * defined as a Lambdas expression. + * + */ +public class LambdasApp { + + /** + * Program entry point + */ + public static void main(String[] args) { + Task task = new SimpleTask(); + Callback c = () -> System.out.println("I'm done now."); + task.executeWith(c); + } +} diff --git a/callback/src/main/java/com/iluwatar/callback/SimpleTask.java b/callback/src/main/java/com/iluwatar/callback/SimpleTask.java index 70b844ce3..2a7385607 100644 --- a/callback/src/main/java/com/iluwatar/callback/SimpleTask.java +++ b/callback/src/main/java/com/iluwatar/callback/SimpleTask.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.callback; /** @@ -7,9 +29,8 @@ package com.iluwatar.callback; */ public class SimpleTask extends Task { - @Override - public void execute() { - System.out.println("Perform some important activity and after call the callback method."); - } - + @Override + public void execute() { + System.out.println("Perform some important activity and after call the callback method."); + } } diff --git a/callback/src/main/java/com/iluwatar/callback/Task.java b/callback/src/main/java/com/iluwatar/callback/Task.java index db4b66dc5..9580c91cb 100644 --- a/callback/src/main/java/com/iluwatar/callback/Task.java +++ b/callback/src/main/java/com/iluwatar/callback/Task.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.callback; /** @@ -7,12 +29,15 @@ package com.iluwatar.callback; */ public abstract class Task { - public final void executeWith(Callback callback) { - execute(); - if (callback != null) { - callback.call(); - } - } + /** + * Execute with callback + */ + public final void executeWith(Callback callback) { + execute(); + if (callback != null) { + callback.call(); + } + } - public abstract void execute(); + public abstract void execute(); } diff --git a/callback/src/test/java/com/iluwatar/callback/AppTest.java b/callback/src/test/java/com/iluwatar/callback/AppTest.java index 0f7a6f45e..b7ab3fe75 100644 --- a/callback/src/test/java/com/iluwatar/callback/AppTest.java +++ b/callback/src/test/java/com/iluwatar/callback/AppTest.java @@ -1,39 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 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.callback; import org.junit.Test; -import static org.junit.Assert.assertEquals; +import java.io.IOException; /** - * Add a field as a counter. Every time the callback method is called increment this - * field. Unit test checks that the field is being incremented. - * - * Could be done with mock objects as well where the call method call is verified. + * Tests that Callback example runs without errors. */ public class AppTest { - - private Integer callingCount = 0; - - @Test - public void test() { - Callback callback = new Callback() { - @Override - public void call() { - callingCount++; - } - }; - - Task task = new SimpleTask(); - - assertEquals("Initial calling count of 0", new Integer(0), callingCount); - - task.executeWith(callback); - - assertEquals("Callback called once", new Integer(1), callingCount); - - task.executeWith(callback); - - assertEquals("Callback called twice", new Integer(2), callingCount); - - } + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } } diff --git a/callback/src/test/java/com/iluwatar/callback/CallbackTest.java b/callback/src/test/java/com/iluwatar/callback/CallbackTest.java new file mode 100644 index 000000000..b48ac36ff --- /dev/null +++ b/callback/src/test/java/com/iluwatar/callback/CallbackTest.java @@ -0,0 +1,79 @@ +/** + * The MIT License + * Copyright (c) 2014 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.callback; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Add a field as a counter. Every time the callback method is called increment this field. Unit + * test checks that the field is being incremented. + * + * Could be done with mock objects as well where the call method call is verified. + */ +public class CallbackTest { + + private Integer callingCount = 0; + + @Test + public void test() { + Callback callback = new Callback() { + @Override + public void call() { + callingCount++; + } + }; + + Task task = new SimpleTask(); + + assertEquals("Initial calling count of 0", new Integer(0), callingCount); + + task.executeWith(callback); + + assertEquals("Callback called once", new Integer(1), callingCount); + + task.executeWith(callback); + + assertEquals("Callback called twice", new Integer(2), callingCount); + + } + + @Test + public void testWithLambdasExample() { + Callback callback = () -> callingCount++; + + Task task = new SimpleTask(); + + assertEquals("Initial calling count of 0", new Integer(0), callingCount); + + task.executeWith(callback); + + assertEquals("Callback called once", new Integer(1), callingCount); + + task.executeWith(callback); + + assertEquals("Callback called twice", new Integer(2), callingCount); + + } +} diff --git a/chain/index.md b/chain/README.md similarity index 85% rename from chain/index.md rename to chain/README.md index 9be376324..ef18f6f64 100644 --- a/chain/index.md +++ b/chain/README.md @@ -7,25 +7,28 @@ categories: Behavioral tags: - Java - Gang Of Four + - Difficulty-Intermediate --- -**Intent:** Avoid coupling the sender of a request to its receiver by giving +## Intent +Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it. ![alt text](./etc/chain_1.png "Chain of Responsibility") -**Applicability:** Use Chain of Responsibility when +## Applicability +Use Chain of Responsibility when * more than one object may handle a request, and the handler isn't known a priori. The handler should be ascertained automatically * you want to issue a request to one of several objects without specifying the receiver explicitly * the set of objects that can handle a request should be specified dynamically -**Real world examples:** +## Real world examples * [java.util.logging.Logger#log()](http://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#log%28java.util.logging.Level,%20java.lang.String%29) * [Apache Commons Chain](https://commons.apache.org/proper/commons-chain/index.html) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/chain/pom.xml b/chain/pom.xml index 80591a477..ee3b92401 100644 --- a/chain/pom.xml +++ b/chain/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT chain diff --git a/chain/src/main/java/com/iluwatar/chain/App.java b/chain/src/main/java/com/iluwatar/chain/App.java index 4d3ca69db..cd6a200de 100644 --- a/chain/src/main/java/com/iluwatar/chain/App.java +++ b/chain/src/main/java/com/iluwatar/chain/App.java @@ -1,32 +1,53 @@ +/** + * The MIT License + * Copyright (c) 2014 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.chain; /** * - * The Chain of Responsibility pattern is a design pattern consisting of command - * objects and a series of processing objects. Each processing object contains - * logic that defines the types of command objects that it can handle; the rest are - * passed to the next processing object in the chain. A mechanism also exists for - * adding new processing objects to the end of this chain. + * The Chain of Responsibility pattern is a design pattern consisting of command objects and a + * series of processing objects. Each processing object contains logic that defines the types of + * command objects that it can handle; the rest are passed to the next processing object in the + * chain. A mechanism also exists for adding new processing objects to the end of this chain. *

- * In this example we organize the request handlers ({@link RequestHandler}) into a - * chain where each handler has a chance to act on the request on its turn. Here - * the king ({@link OrcKing}) makes requests and the military orcs ({@link OrcCommander}, - * {@link OrcOfficer}, {@link OrcSoldier}) form the handler chain. + * In this example we organize the request handlers ({@link RequestHandler}) into a chain where each + * handler has a chance to act on the request on its turn. Here the king ({@link OrcKing}) makes + * requests and the military orcs ({@link OrcCommander}, {@link OrcOfficer}, {@link OrcSoldier}) + * form the handler chain. * */ public class App { - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { - OrcKing king = new OrcKing(); - king.makeRequest(new Request(RequestType.DEFEND_CASTLE, "defend castle")); - king.makeRequest(new Request(RequestType.TORTURE_PRISONER, - "torture prisoner")); - king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax")); + OrcKing king = new OrcKing(); + king.makeRequest(new Request(RequestType.DEFEND_CASTLE, "defend castle")); + king.makeRequest(new Request(RequestType.TORTURE_PRISONER, "torture prisoner")); + king.makeRequest(new Request(RequestType.COLLECT_TAX, "collect tax")); - } + } } diff --git a/chain/src/main/java/com/iluwatar/chain/OrcCommander.java b/chain/src/main/java/com/iluwatar/chain/OrcCommander.java index fb5134e01..a1f2bf284 100644 --- a/chain/src/main/java/com/iluwatar/chain/OrcCommander.java +++ b/chain/src/main/java/com/iluwatar/chain/OrcCommander.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.chain; /** @@ -7,21 +29,22 @@ package com.iluwatar.chain; */ public class OrcCommander extends RequestHandler { - public OrcCommander(RequestHandler handler) { - super(handler); - } + public OrcCommander(RequestHandler handler) { + super(handler); + } - @Override - public void handleRequest(Request req) { - if (req.getRequestType().equals(RequestType.DEFEND_CASTLE)) { - printHandling(req); - } else { - super.handleRequest(req); - } - } + @Override + public void handleRequest(Request req) { + if (req.getRequestType().equals(RequestType.DEFEND_CASTLE)) { + printHandling(req); + req.markHandled(); + } else { + super.handleRequest(req); + } + } - @Override - public String toString() { - return "Orc commander"; - } + @Override + public String toString() { + return "Orc commander"; + } } diff --git a/chain/src/main/java/com/iluwatar/chain/OrcKing.java b/chain/src/main/java/com/iluwatar/chain/OrcKing.java index b39959935..3b13c4a53 100644 --- a/chain/src/main/java/com/iluwatar/chain/OrcKing.java +++ b/chain/src/main/java/com/iluwatar/chain/OrcKing.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.chain; /** @@ -7,18 +29,18 @@ package com.iluwatar.chain; */ public class OrcKing { - RequestHandler chain; + RequestHandler chain; - public OrcKing() { - buildChain(); - } + public OrcKing() { + buildChain(); + } - private void buildChain() { - chain = new OrcCommander(new OrcOfficer(new OrcSoldier(null))); - } + private void buildChain() { + chain = new OrcCommander(new OrcOfficer(new OrcSoldier(null))); + } - public void makeRequest(Request req) { - chain.handleRequest(req); - } + public void makeRequest(Request req) { + chain.handleRequest(req); + } } diff --git a/chain/src/main/java/com/iluwatar/chain/OrcOfficer.java b/chain/src/main/java/com/iluwatar/chain/OrcOfficer.java index 131cb1101..ea35722c1 100644 --- a/chain/src/main/java/com/iluwatar/chain/OrcOfficer.java +++ b/chain/src/main/java/com/iluwatar/chain/OrcOfficer.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.chain; /** @@ -7,22 +29,23 @@ package com.iluwatar.chain; */ public class OrcOfficer extends RequestHandler { - public OrcOfficer(RequestHandler handler) { - super(handler); - } + public OrcOfficer(RequestHandler handler) { + super(handler); + } - @Override - public void handleRequest(Request req) { - if (req.getRequestType().equals(RequestType.TORTURE_PRISONER)) { - printHandling(req); - } else { - super.handleRequest(req); - } - } + @Override + public void handleRequest(Request req) { + if (req.getRequestType().equals(RequestType.TORTURE_PRISONER)) { + printHandling(req); + req.markHandled(); + } else { + super.handleRequest(req); + } + } - @Override - public String toString() { - return "Orc officer"; - } + @Override + public String toString() { + return "Orc officer"; + } } diff --git a/chain/src/main/java/com/iluwatar/chain/OrcSoldier.java b/chain/src/main/java/com/iluwatar/chain/OrcSoldier.java index a681dfb77..d7601bf4f 100644 --- a/chain/src/main/java/com/iluwatar/chain/OrcSoldier.java +++ b/chain/src/main/java/com/iluwatar/chain/OrcSoldier.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.chain; /** @@ -7,21 +29,22 @@ package com.iluwatar.chain; */ public class OrcSoldier extends RequestHandler { - public OrcSoldier(RequestHandler handler) { - super(handler); - } + public OrcSoldier(RequestHandler handler) { + super(handler); + } - @Override - public void handleRequest(Request req) { - if (req.getRequestType().equals(RequestType.COLLECT_TAX)) { - printHandling(req); - } else { - super.handleRequest(req); - } - } + @Override + public void handleRequest(Request req) { + if (req.getRequestType().equals(RequestType.COLLECT_TAX)) { + printHandling(req); + req.markHandled(); + } else { + super.handleRequest(req); + } + } - @Override - public String toString() { - return "Orc soldier"; - } + @Override + public String toString() { + return "Orc soldier"; + } } diff --git a/chain/src/main/java/com/iluwatar/chain/Request.java b/chain/src/main/java/com/iluwatar/chain/Request.java index 558ee65d1..399eddb36 100644 --- a/chain/src/main/java/com/iluwatar/chain/Request.java +++ b/chain/src/main/java/com/iluwatar/chain/Request.java @@ -1,38 +1,100 @@ +/** + * The MIT License + * Copyright (c) 2014 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.chain; +import java.util.Objects; + /** - * * Request - * */ public class Request { - private String requestDescription; - private RequestType requestType; + /** + * The type of this request, used by each item in the chain to see if they should or can handle + * this particular request + */ + private final RequestType requestType; - public Request(RequestType requestType, String requestDescription) { - this.setRequestType(requestType); - this.setRequestDescription(requestDescription); - } + /** + * A description of the request + */ + private final String requestDescription; - public String getRequestDescription() { - return requestDescription; - } + /** + * Indicates if the request is handled or not. A request can only switch state from unhandled to + * handled, there's no way to 'unhandle' a request + */ + private boolean handled; - public void setRequestDescription(String requestDescription) { - this.requestDescription = requestDescription; - } + /** + * Create a new request of the given type and accompanied description. + * + * @param requestType The type of request + * @param requestDescription The description of the request + */ + public Request(final RequestType requestType, final String requestDescription) { + this.requestType = Objects.requireNonNull(requestType); + this.requestDescription = Objects.requireNonNull(requestDescription); + } - public RequestType getRequestType() { - return requestType; - } + /** + * Get a description of the request + * + * @return A human readable description of the request + */ + public String getRequestDescription() { + return requestDescription; + } - public void setRequestType(RequestType requestType) { - this.requestType = requestType; - } + /** + * Get the type of this request, used by each person in the chain of command to see if they should + * or can handle this particular request + * + * @return The request type + */ + public RequestType getRequestType() { + return requestType; + } + + /** + * Mark the request as handled + */ + public void markHandled() { + this.handled = true; + } + + /** + * Indicates if this request is handled or not + * + * @return true when the request is handled, false if not + */ + public boolean isHandled() { + return this.handled; + } + + @Override + public String toString() { + return getRequestDescription(); + } - @Override - public String toString() { - return getRequestDescription(); - } } diff --git a/chain/src/main/java/com/iluwatar/chain/RequestHandler.java b/chain/src/main/java/com/iluwatar/chain/RequestHandler.java index 5570c20ce..78d68e5dc 100644 --- a/chain/src/main/java/com/iluwatar/chain/RequestHandler.java +++ b/chain/src/main/java/com/iluwatar/chain/RequestHandler.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.chain; /** @@ -7,22 +29,25 @@ package com.iluwatar.chain; */ public abstract class RequestHandler { - private RequestHandler next; + private RequestHandler next; - public RequestHandler(RequestHandler next) { - this.next = next; - } + public RequestHandler(RequestHandler next) { + this.next = next; + } - public void handleRequest(Request req) { - if (next != null) { - next.handleRequest(req); - } - } + /** + * Request handler + */ + public void handleRequest(Request req) { + if (next != null) { + next.handleRequest(req); + } + } - protected void printHandling(Request req) { - System.out.println(this + " handling request \"" + req + "\""); - } + protected void printHandling(Request req) { + System.out.println(this + " handling request \"" + req + "\""); + } - @Override - public abstract String toString(); + @Override + public abstract String toString(); } diff --git a/chain/src/main/java/com/iluwatar/chain/RequestType.java b/chain/src/main/java/com/iluwatar/chain/RequestType.java index 9ad975d2f..ff6dcff4d 100644 --- a/chain/src/main/java/com/iluwatar/chain/RequestType.java +++ b/chain/src/main/java/com/iluwatar/chain/RequestType.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.chain; /** @@ -7,6 +29,6 @@ package com.iluwatar.chain; */ public enum RequestType { - DEFEND_CASTLE, TORTURE_PRISONER, COLLECT_TAX + DEFEND_CASTLE, TORTURE_PRISONER, COLLECT_TAX } diff --git a/chain/src/test/java/com/iluwatar/chain/AppTest.java b/chain/src/test/java/com/iluwatar/chain/AppTest.java index aa52e60e2..93a8a4096 100644 --- a/chain/src/test/java/com/iluwatar/chain/AppTest.java +++ b/chain/src/test/java/com/iluwatar/chain/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.chain; import org.junit.Test; -import com.iluwatar.chain.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.chain.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/chain/src/test/java/com/iluwatar/chain/OrcKingTest.java b/chain/src/test/java/com/iluwatar/chain/OrcKingTest.java new file mode 100644 index 000000000..be3650613 --- /dev/null +++ b/chain/src/test/java/com/iluwatar/chain/OrcKingTest.java @@ -0,0 +1,59 @@ +/** + * The MIT License + * Copyright (c) 2014 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.chain; + +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +/** + * Date: 12/6/15 - 9:29 PM + * + * @author Jeroen Meulemeester + */ +public class OrcKingTest { + + /** + * All possible requests + */ + private static final Request[] REQUESTS = new Request[]{ + new Request(RequestType.DEFEND_CASTLE, "Don't let the barbarians enter my castle!!"), + new Request(RequestType.TORTURE_PRISONER, "Don't just stand there, tickle him!"), + new Request(RequestType.COLLECT_TAX, "Don't steal, the King hates competition ..."), + }; + + @Test + public void testMakeRequest() throws Exception { + final OrcKing king = new OrcKing(); + + for (final Request request : REQUESTS) { + king.makeRequest(request); + assertTrue( + "Expected all requests from King to be handled, but [" + request + "] was not!", + request.isHandled() + ); + } + + } + +} \ No newline at end of file diff --git a/checkstyle-suppressions.xml b/checkstyle-suppressions.xml new file mode 100644 index 000000000..60de4a697 --- /dev/null +++ b/checkstyle-suppressions.xml @@ -0,0 +1,33 @@ + + + + + + + + + diff --git a/checkstyle.xml b/checkstyle.xml index 0ff943d95..19b6d2ec4 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -1,4 +1,28 @@ + @@ -25,8 +49,10 @@ + + - + @@ -48,7 +74,7 @@ - + @@ -61,7 +87,7 @@ - + @@ -86,9 +112,6 @@ - - - @@ -97,42 +120,19 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - - - - - - @@ -180,11 +172,6 @@ - - - - @@ -195,7 +182,7 @@ - + @@ -205,4 +192,5 @@ + diff --git a/command/index.md b/command/README.md similarity index 88% rename from command/index.md rename to command/README.md index c9790819f..a5478394c 100644 --- a/command/index.md +++ b/command/README.md @@ -7,15 +7,22 @@ categories: Behavioral tags: - Java - Gang Of Four + - Difficulty-Intermediate + - Functional --- -**Intent:** Encapsulate a request as an object, thereby letting you +## Also known as +Action, Transaction + +## Intent +Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations. ![alt text](./etc/command.png "Command") -**Applicability:** Use the Command pattern when you want to +## Applicability +Use the Command pattern when you want to * parameterize objects by an action to perform. You can express such parameterization in a procedural language with a callback function, that is, a function that's registered somewhere to be called at a later point. Commands are an object-oriented replacement for callbacks. * specify, queue, and execute requests at different times. A Command object can have a lifetime independent of the original request. If the receiver of a request can be represented in an address space-independent way, then you can transfer a command object for the request to a different process and fulfill the request there @@ -23,16 +30,17 @@ support undoable operations. * support logging changes so that they can be reapplied in case of a system crash. By augmenting the Command interface with load and store operations, you can keep a persistent log of changes. Recovering from a crash involves reloading logged commands from disk and re-executing them with the execute operation * structure a system around high-level operations build on primitive operations. Such a structure is common in information systems that support transactions. A transaction encapsulates a set of changes to data. The Command pattern offers a way to model transactions. Commands have a common interface, letting you invoke all transactions the same way. The pattern also makes it easy to extend the system with new transactions -**Typical Use Case:** +## Typical Use Case * to keep a history of requests * implement callback functionality * implement the undo functionality -**Real world examples:** +## Real world examples * [java.lang.Runnable](http://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html) +* [Netflix Hystrix](https://github.com/Netflix/Hystrix/wiki) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/command/pom.xml b/command/pom.xml index 22f1c256b..a27a56e54 100644 --- a/command/pom.xml +++ b/command/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT command @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/command/src/main/java/com/iluwatar/command/App.java b/command/src/main/java/com/iluwatar/command/App.java index b421b683b..93f9c376a 100644 --- a/command/src/main/java/com/iluwatar/command/App.java +++ b/command/src/main/java/com/iluwatar/command/App.java @@ -1,53 +1,77 @@ +/** + * The MIT License + * Copyright (c) 2014 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.command; /** * - * The Command pattern is a behavioral design pattern in which an object is used to encapsulate all information - * needed to perform an action or trigger an event at a later time. This information includes the method name, - * the object that owns the method and values for the method parameters. + * The Command pattern is a behavioral design pattern in which an object is used to encapsulate all + * information needed to perform an action or trigger an event at a later time. This information + * includes the method name, the object that owns the method and values for the method parameters. *

- * Four terms always associated with the command pattern are command, receiver, invoker and client. A command - * object (spell) knows about the receiver (target) and invokes a method of the receiver. Values for parameters of - * the receiver method are stored in the command. The receiver then does the work. An invoker object (wizard) - * knows how to execute a command, and optionally does bookkeeping about the command execution. The invoker - * does not know anything about a concrete command, it knows only about command interface. Both an invoker object - * and several command objects are held by a client object (app). The client decides which commands to execute at - * which points. To execute a command, it passes the command object to the invoker object. + * Four terms always associated with the command pattern are command, receiver, invoker and client. + * A command object (spell) knows about the receiver (target) and invokes a method of the receiver. + * Values for parameters of the receiver method are stored in the command. The receiver then does + * the work. An invoker object (wizard) knows how to execute a command, and optionally does + * bookkeeping about the command execution. The invoker does not know anything about a concrete + * command, it knows only about command interface. Both an invoker object and several command + * objects are held by a client object (app). The client decides which commands to execute at which + * points. To execute a command, it passes the command object to the invoker object. *

- * In other words, in this example the wizard casts spells on the goblin. The wizard keeps track of the previous - * spells cast, so it is easy to undo them. In addition, the wizard keeps track of the spells undone, so they - * can be redone. + * In other words, in this example the wizard casts spells on the goblin. The wizard keeps track of + * the previous spells cast, so it is easy to undo them. In addition, the wizard keeps track of the + * spells undone, so they can be redone. * * */ public class App { - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { - Wizard wizard = new Wizard(); - Goblin goblin = new Goblin(); + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + Wizard wizard = new Wizard(); + Goblin goblin = new Goblin(); - goblin.printStatus(); + goblin.printStatus(); - wizard.castSpell(new ShrinkSpell(), goblin); - goblin.printStatus(); + wizard.castSpell(new ShrinkSpell(), goblin); + goblin.printStatus(); - wizard.castSpell(new InvisibilitySpell(), goblin); - goblin.printStatus(); + wizard.castSpell(new InvisibilitySpell(), goblin); + goblin.printStatus(); - wizard.undoLastSpell(); - goblin.printStatus(); + wizard.undoLastSpell(); + goblin.printStatus(); - wizard.undoLastSpell(); - goblin.printStatus(); + wizard.undoLastSpell(); + goblin.printStatus(); - wizard.redoLastSpell(); - goblin.printStatus(); + wizard.redoLastSpell(); + goblin.printStatus(); - wizard.redoLastSpell(); - goblin.printStatus(); - } + wizard.redoLastSpell(); + goblin.printStatus(); + } } diff --git a/command/src/main/java/com/iluwatar/command/Command.java b/command/src/main/java/com/iluwatar/command/Command.java index 9ffeed24d..c7b0c43bb 100644 --- a/command/src/main/java/com/iluwatar/command/Command.java +++ b/command/src/main/java/com/iluwatar/command/Command.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.command; /** @@ -7,13 +29,13 @@ package com.iluwatar.command; */ public abstract class Command { - public abstract void execute(Target target); + public abstract void execute(Target target); - public abstract void undo(); + public abstract void undo(); - public abstract void redo(); + public abstract void redo(); - @Override - public abstract String toString(); + @Override + public abstract String toString(); } diff --git a/command/src/main/java/com/iluwatar/command/Goblin.java b/command/src/main/java/com/iluwatar/command/Goblin.java index 7d0804b0c..9e21cdefd 100644 --- a/command/src/main/java/com/iluwatar/command/Goblin.java +++ b/command/src/main/java/com/iluwatar/command/Goblin.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.command; /** @@ -7,14 +29,14 @@ package com.iluwatar.command; */ public class Goblin extends Target { - public Goblin() { - setSize(Size.NORMAL); - setVisibility(Visibility.VISIBLE); - } + public Goblin() { + setSize(Size.NORMAL); + setVisibility(Visibility.VISIBLE); + } - @Override - public String toString() { - return "Goblin"; - } + @Override + public String toString() { + return "Goblin"; + } } diff --git a/command/src/main/java/com/iluwatar/command/InvisibilitySpell.java b/command/src/main/java/com/iluwatar/command/InvisibilitySpell.java index b72c34cc2..9435e7245 100644 --- a/command/src/main/java/com/iluwatar/command/InvisibilitySpell.java +++ b/command/src/main/java/com/iluwatar/command/InvisibilitySpell.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.command; /** @@ -7,30 +29,30 @@ package com.iluwatar.command; */ public class InvisibilitySpell extends Command { - private Target target; + private Target target; - @Override - public void execute(Target target) { - target.setVisibility(Visibility.INVISIBLE); - this.target = target; - } + @Override + public void execute(Target target) { + target.setVisibility(Visibility.INVISIBLE); + this.target = target; + } - @Override - public void undo() { - if (target != null) { - target.setVisibility(Visibility.VISIBLE); - } - } + @Override + public void undo() { + if (target != null) { + target.setVisibility(Visibility.VISIBLE); + } + } - @Override - public void redo() { - if (target != null) { - target.setVisibility(Visibility.INVISIBLE); - } - } + @Override + public void redo() { + if (target != null) { + target.setVisibility(Visibility.INVISIBLE); + } + } - @Override - public String toString() { - return "Invisibility spell"; - } + @Override + public String toString() { + return "Invisibility spell"; + } } diff --git a/command/src/main/java/com/iluwatar/command/ShrinkSpell.java b/command/src/main/java/com/iluwatar/command/ShrinkSpell.java index f36438082..e88447353 100644 --- a/command/src/main/java/com/iluwatar/command/ShrinkSpell.java +++ b/command/src/main/java/com/iluwatar/command/ShrinkSpell.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.command; /** @@ -7,32 +29,32 @@ package com.iluwatar.command; */ public class ShrinkSpell extends Command { - private Size oldSize; - private Target target; + private Size oldSize; + private Target target; - @Override - public void execute(Target target) { - oldSize = target.getSize(); - target.setSize(Size.SMALL); - this.target = target; - } + @Override + public void execute(Target target) { + oldSize = target.getSize(); + target.setSize(Size.SMALL); + this.target = target; + } - @Override - public void undo() { - if (oldSize != null && target != null) { - Size temp = target.getSize(); - target.setSize(oldSize); - oldSize = temp; - } - } + @Override + public void undo() { + if (oldSize != null && target != null) { + Size temp = target.getSize(); + target.setSize(oldSize); + oldSize = temp; + } + } - @Override - public void redo() { - undo(); - } + @Override + public void redo() { + undo(); + } - @Override - public String toString() { - return "Shrink spell"; - } + @Override + public String toString() { + return "Shrink spell"; + } } diff --git a/command/src/main/java/com/iluwatar/command/Size.java b/command/src/main/java/com/iluwatar/command/Size.java index a9c20bd59..d33a19453 100644 --- a/command/src/main/java/com/iluwatar/command/Size.java +++ b/command/src/main/java/com/iluwatar/command/Size.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.command; /** @@ -7,16 +29,16 @@ package com.iluwatar.command; */ public enum Size { - SMALL("small"), NORMAL("normal"), LARGE("large"), UNDEFINED(""); - - private String title; + SMALL("small"), NORMAL("normal"), LARGE("large"), UNDEFINED(""); - Size(String title) { - this.title = title; - } + private String title; - @Override - public String toString() { - return title; - } + Size(String title) { + this.title = title; + } + + @Override + public String toString() { + return title; + } } diff --git a/command/src/main/java/com/iluwatar/command/Target.java b/command/src/main/java/com/iluwatar/command/Target.java index 6ea2681d3..2e49ab663 100644 --- a/command/src/main/java/com/iluwatar/command/Target.java +++ b/command/src/main/java/com/iluwatar/command/Target.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.command; /** @@ -7,32 +29,35 @@ package com.iluwatar.command; */ public abstract class Target { - private Size size; + private Size size; - private Visibility visibility; + private Visibility visibility; - public Size getSize() { - return size; - } + public Size getSize() { + return size; + } - public void setSize(Size size) { - this.size = size; - } + public void setSize(Size size) { + this.size = size; + } - public Visibility getVisibility() { - return visibility; - } + public Visibility getVisibility() { + return visibility; + } - public void setVisibility(Visibility visibility) { - this.visibility = visibility; - } + public void setVisibility(Visibility visibility) { + this.visibility = visibility; + } - @Override - public abstract String toString(); + @Override + public abstract String toString(); - public void printStatus() { - System.out.println(String.format("%s, [size=%s] [visibility=%s]", this, - getSize(), getVisibility())); - System.out.println(); - } + /** + * Print status + */ + public void printStatus() { + System.out.println(String.format("%s, [size=%s] [visibility=%s]", this, getSize(), + getVisibility())); + System.out.println(); + } } diff --git a/command/src/main/java/com/iluwatar/command/Visibility.java b/command/src/main/java/com/iluwatar/command/Visibility.java index 3316ac9e3..4ab079d18 100644 --- a/command/src/main/java/com/iluwatar/command/Visibility.java +++ b/command/src/main/java/com/iluwatar/command/Visibility.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.command; /** @@ -7,16 +29,16 @@ package com.iluwatar.command; */ public enum Visibility { - VISIBLE("visible"), INVISIBLE("invisible"), UNDEFINED(""); + VISIBLE("visible"), INVISIBLE("invisible"), UNDEFINED(""); - private String title; + private String title; - Visibility(String title) { - this.title = title; - } + Visibility(String title) { + this.title = title; + } - @Override - public String toString() { - return title; - } + @Override + public String toString() { + return title; + } } diff --git a/command/src/main/java/com/iluwatar/command/Wizard.java b/command/src/main/java/com/iluwatar/command/Wizard.java index 995b4441a..bd0ef85d4 100644 --- a/command/src/main/java/com/iluwatar/command/Wizard.java +++ b/command/src/main/java/com/iluwatar/command/Wizard.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.command; import java.util.Deque; @@ -10,38 +32,46 @@ import java.util.LinkedList; */ public class Wizard { - private Deque undoStack = new LinkedList<>(); - private Deque redoStack = new LinkedList<>(); + private Deque undoStack = new LinkedList<>(); + private Deque redoStack = new LinkedList<>(); - public Wizard() { - } + public Wizard() {} - public void castSpell(Command command, Target target) { - System.out.println(this + " casts " + command + " at " + target); - command.execute(target); - undoStack.offerLast(command); - } + /** + * Cast spell + */ + public void castSpell(Command command, Target target) { + System.out.println(this + " casts " + command + " at " + target); + command.execute(target); + undoStack.offerLast(command); + } - public void undoLastSpell() { - if (!undoStack.isEmpty()) { - Command previousSpell = undoStack.pollLast(); - redoStack.offerLast(previousSpell); - System.out.println(this + " undoes " + previousSpell); - previousSpell.undo(); - } - } + /** + * Undo last spell + */ + public void undoLastSpell() { + if (!undoStack.isEmpty()) { + Command previousSpell = undoStack.pollLast(); + redoStack.offerLast(previousSpell); + System.out.println(this + " undoes " + previousSpell); + previousSpell.undo(); + } + } - public void redoLastSpell() { - if (!redoStack.isEmpty()) { - Command previousSpell = redoStack.pollLast(); - undoStack.offerLast(previousSpell); - System.out.println(this + " redoes " + previousSpell); - previousSpell.redo(); - } - } + /** + * Redo last spell + */ + public void redoLastSpell() { + if (!redoStack.isEmpty()) { + Command previousSpell = redoStack.pollLast(); + undoStack.offerLast(previousSpell); + System.out.println(this + " redoes " + previousSpell); + previousSpell.redo(); + } + } - @Override - public String toString() { - return "Wizard"; - } + @Override + public String toString() { + return "Wizard"; + } } diff --git a/command/src/test/java/com/iluwatar/command/AppTest.java b/command/src/test/java/com/iluwatar/command/AppTest.java index 2fd5c16b4..560594272 100644 --- a/command/src/test/java/com/iluwatar/command/AppTest.java +++ b/command/src/test/java/com/iluwatar/command/AppTest.java @@ -1,19 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 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.command; import org.junit.Test; -import com.iluwatar.command.App; +import java.io.IOException; /** - * - * Application test - * + * Tests that Command example runs without errors. */ public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } } diff --git a/command/src/test/java/com/iluwatar/command/CommandTest.java b/command/src/test/java/com/iluwatar/command/CommandTest.java new file mode 100644 index 000000000..8201d50c6 --- /dev/null +++ b/command/src/test/java/com/iluwatar/command/CommandTest.java @@ -0,0 +1,93 @@ +/** + * The MIT License + * Copyright (c) 2014 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.command; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +/** + * The Command pattern is a behavioral design pattern in which an object is used to encapsulate all + * information needed to perform an action or trigger an event at a later time. This information + * includes the method name, the object that owns the method and values for the method parameters. + * + *

Four terms always associated with the command pattern are command, receiver, invoker and + * client. A command object (spell) knows about the receiver (target) and invokes a method of + * the receiver.Values for parameters of the receiver method are stored in the command. The receiver + * then does the work. An invoker object (wizard) knows how to execute a command, and optionally + * does bookkeeping about the command execution. The invoker does not know anything about a + * concrete command, it knows only about command interface. Both an invoker object and several + * command objects are held by a client object (app). The client decides which commands to execute + * at which points. To execute a command, it passes the command object to the invoker object. + */ +public class CommandTest { + + private static final String GOBLIN = "Goblin"; + + /** + * This test verifies that when the wizard casts spells on the goblin. The wizard keeps track of + * the previous spells cast, so it is easy to undo them. In addition, it also verifies that the + * wizard keeps track of the spells undone, so they can be redone. + */ + @Test + public void testCommand() { + + Wizard wizard = new Wizard(); + Goblin goblin = new Goblin(); + + wizard.castSpell(new ShrinkSpell(), goblin); + verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.VISIBLE); + + wizard.castSpell(new InvisibilitySpell(), goblin); + verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.INVISIBLE); + + wizard.undoLastSpell(); + verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.VISIBLE); + + wizard.undoLastSpell(); + verifyGoblin(goblin, GOBLIN, Size.NORMAL, Visibility.VISIBLE); + + wizard.redoLastSpell(); + verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.VISIBLE); + + wizard.redoLastSpell(); + verifyGoblin(goblin, GOBLIN, Size.SMALL, Visibility.INVISIBLE); + } + + /** + * This method asserts that the passed goblin object has the name as expectedName, size as + * expectedSize and visibility as expectedVisibility. + * + * @param goblin a goblin object whose state is to be verified against other parameters + * @param expectedName expectedName of the goblin + * @param expectedSize expected size of the goblin + * @param expectedVisibilty exepcted visibility of the goblin + */ + private void verifyGoblin(Goblin goblin, String expectedName, Size expectedSize, + Visibility expectedVisibilty) { + assertEquals("Goblin's name must be same as expectedName", expectedName, goblin.toString()); + assertEquals("Goblin's size must be same as expectedSize", expectedSize, goblin.getSize()); + assertEquals("Goblin's visibility must be same as expectedVisibility", expectedVisibilty, + goblin.getVisibility()); + } +} diff --git a/composite/index.md b/composite/README.md similarity index 87% rename from composite/index.md rename to composite/README.md index 4a31a1b33..8b980292d 100644 --- a/composite/index.md +++ b/composite/README.md @@ -7,24 +7,27 @@ categories: Structural tags: - Java - Gang Of Four + - Difficulty-Intermediate --- -**Intent:** Compose objects into tree structures to represent part-whole +## Intent +Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly. ![alt text](./etc/composite_1.png "Composite") -**Applicability:** Use the Composite pattern when +## Applicability +Use the Composite pattern when * you want to represent part-whole hierarchies of objects * you want clients to be able to ignore the difference between compositions of objects and individual objects. Clients will treat all objects in the composite structure uniformly -**Real world examples:** +## Real world examples * [java.awt.Container](http://docs.oracle.com/javase/8/docs/api/java/awt/Container.html) and [java.awt.Component](http://docs.oracle.com/javase/8/docs/api/java/awt/Component.html) * [Apache Wicket](https://github.com/apache/wicket) component tree, see [Component](https://github.com/apache/wicket/blob/91e154702ab1ff3481ef6cbb04c6044814b7e130/wicket-core/src/main/java/org/apache/wicket/Component.java) and [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/composite/pom.xml b/composite/pom.xml index 2b8298647..ace29d8d1 100644 --- a/composite/pom.xml +++ b/composite/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT composite diff --git a/composite/src/main/java/com/iluwatar/composite/App.java b/composite/src/main/java/com/iluwatar/composite/App.java index 7bd0e4d01..cfe37876f 100644 --- a/composite/src/main/java/com/iluwatar/composite/App.java +++ b/composite/src/main/java/com/iluwatar/composite/App.java @@ -1,33 +1,56 @@ +/** + * The MIT License + * Copyright (c) 2014 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.composite; /** - * The Composite pattern is a partitioning design pattern. The Composite pattern - * describes that a group of objects is to be treated in the same way as a single - * instance of an object. The intent of a composite is to "compose" objects into - * tree structures to represent part-whole hierarchies. Implementing the Composite - * pattern lets clients treat individual objects and compositions uniformly. + * The Composite pattern is a partitioning design pattern. The Composite pattern describes that a + * group of objects is to be treated in the same way as a single instance of an object. The intent + * of a composite is to "compose" objects into tree structures to represent part-whole hierarchies. + * Implementing the Composite pattern lets clients treat individual objects and compositions + * uniformly. *

- * In this example we have sentences composed of words composed of letters. All of - * the objects can be treated through the same interface ({@link LetterComposite}). + * In this example we have sentences composed of words composed of letters. All of the objects can + * be treated through the same interface ({@link LetterComposite}). * */ public class App { - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { - System.out.println("Message from the orcs: "); + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + System.out.println("Message from the orcs: "); - LetterComposite orcMessage = new Messenger().messageFromOrcs(); - orcMessage.print(); + LetterComposite orcMessage = new Messenger().messageFromOrcs(); + orcMessage.print(); - System.out.println("\n"); + System.out.println("\n"); - System.out.println("Message from the elves: "); + System.out.println("Message from the elves: "); - LetterComposite elfMessage = new Messenger().messageFromElves(); - elfMessage.print(); - } + LetterComposite elfMessage = new Messenger().messageFromElves(); + elfMessage.print(); + } } diff --git a/composite/src/main/java/com/iluwatar/composite/Letter.java b/composite/src/main/java/com/iluwatar/composite/Letter.java index 8304ea801..d6a4005d2 100644 --- a/composite/src/main/java/com/iluwatar/composite/Letter.java +++ b/composite/src/main/java/com/iluwatar/composite/Letter.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.composite; /** @@ -7,20 +29,19 @@ package com.iluwatar.composite; */ public class Letter extends LetterComposite { - private char c; + private char c; - public Letter(char c) { - this.c = c; - } + public Letter(char c) { + this.c = c; + } - @Override - protected void printThisBefore() { - System.out.print(c); - } - - @Override - protected void printThisAfter() { - // nop - } + @Override + protected void printThisBefore() { + System.out.print(c); + } + @Override + protected void printThisAfter() { + // nop + } } diff --git a/composite/src/main/java/com/iluwatar/composite/LetterComposite.java b/composite/src/main/java/com/iluwatar/composite/LetterComposite.java index e58d51b25..940562969 100644 --- a/composite/src/main/java/com/iluwatar/composite/LetterComposite.java +++ b/composite/src/main/java/com/iluwatar/composite/LetterComposite.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.composite; import java.util.ArrayList; @@ -10,25 +32,28 @@ import java.util.List; */ public abstract class LetterComposite { - private List children = new ArrayList(); + private List children = new ArrayList<>(); - public void add(LetterComposite letter) { - children.add(letter); - } + public void add(LetterComposite letter) { + children.add(letter); + } - public int count() { - return children.size(); - } + public int count() { + return children.size(); + } - protected abstract void printThisBefore(); + protected abstract void printThisBefore(); - protected abstract void printThisAfter(); + protected abstract void printThisAfter(); - public void print() { - printThisBefore(); - for (LetterComposite letter : children) { - letter.print(); - } - printThisAfter(); - } + /** + * Print + */ + public void print() { + printThisBefore(); + for (LetterComposite letter : children) { + letter.print(); + } + printThisAfter(); + } } diff --git a/composite/src/main/java/com/iluwatar/composite/Messenger.java b/composite/src/main/java/com/iluwatar/composite/Messenger.java index aa0560d4d..3b758d680 100644 --- a/composite/src/main/java/com/iluwatar/composite/Messenger.java +++ b/composite/src/main/java/com/iluwatar/composite/Messenger.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.composite; import java.util.ArrayList; @@ -11,48 +33,47 @@ import java.util.List; */ public class Messenger { - LetterComposite messageFromOrcs() { + LetterComposite messageFromOrcs() { - List words = new ArrayList(); + List words = new ArrayList<>(); - words.add(new Word(Arrays.asList(new Letter('W'), new Letter('h'), - new Letter('e'), new Letter('r'), new Letter('e')))); - words.add(new Word(Arrays.asList(new Letter('t'), new Letter('h'), - new Letter('e'), new Letter('r'), new Letter('e')))); - words.add(new Word(Arrays.asList(new Letter('i'), new Letter('s')))); - words.add(new Word(Arrays.asList(new Letter('a')))); - words.add(new Word(Arrays.asList(new Letter('w'), new Letter('h'), - new Letter('i'), new Letter('p')))); - words.add(new Word(Arrays.asList(new Letter('t'), new Letter('h'), - new Letter('e'), new Letter('r'), new Letter('e')))); - words.add(new Word(Arrays.asList(new Letter('i'), new Letter('s')))); - words.add(new Word(Arrays.asList(new Letter('a')))); - words.add(new Word(Arrays.asList(new Letter('w'), new Letter('a'), - new Letter('y')))); + words.add(new Word(Arrays.asList(new Letter('W'), new Letter('h'), new Letter('e'), new Letter( + 'r'), new Letter('e')))); + words.add(new Word(Arrays.asList(new Letter('t'), new Letter('h'), new Letter('e'), new Letter( + 'r'), new Letter('e')))); + words.add(new Word(Arrays.asList(new Letter('i'), new Letter('s')))); + words.add(new Word(Arrays.asList(new Letter('a')))); + words.add(new Word(Arrays.asList(new Letter('w'), new Letter('h'), new Letter('i'), new Letter( + 'p')))); + words.add(new Word(Arrays.asList(new Letter('t'), new Letter('h'), new Letter('e'), new Letter( + 'r'), new Letter('e')))); + words.add(new Word(Arrays.asList(new Letter('i'), new Letter('s')))); + words.add(new Word(Arrays.asList(new Letter('a')))); + words.add(new Word(Arrays.asList(new Letter('w'), new Letter('a'), new Letter('y')))); - return new Sentence(words); + return new Sentence(words); - } + } - LetterComposite messageFromElves() { + LetterComposite messageFromElves() { - List words = new ArrayList(); + List words = new ArrayList<>(); - words.add(new Word(Arrays.asList(new Letter('M'), new Letter('u'), - new Letter('c'), new Letter('h')))); - words.add(new Word(Arrays.asList(new Letter('w'), new Letter('i'), - new Letter('n'), new Letter('d')))); - words.add(new Word(Arrays.asList(new Letter('p'), new Letter('o'), - new Letter('u'), new Letter('r'), new Letter('s')))); - words.add(new Word(Arrays.asList(new Letter('f'), new Letter('r'), - new Letter('o'), new Letter('m')))); - words.add(new Word(Arrays.asList(new Letter('y'), new Letter('o'), - new Letter('u'), new Letter('r')))); - words.add(new Word(Arrays.asList(new Letter('m'), new Letter('o'), - new Letter('u'), new Letter('t'), new Letter('h')))); + words.add(new Word(Arrays.asList(new Letter('M'), new Letter('u'), new Letter('c'), new Letter( + 'h')))); + words.add(new Word(Arrays.asList(new Letter('w'), new Letter('i'), new Letter('n'), new Letter( + 'd')))); + words.add(new Word(Arrays.asList(new Letter('p'), new Letter('o'), new Letter('u'), new Letter( + 'r'), new Letter('s')))); + words.add(new Word(Arrays.asList(new Letter('f'), new Letter('r'), new Letter('o'), new Letter( + 'm')))); + words.add(new Word(Arrays.asList(new Letter('y'), new Letter('o'), new Letter('u'), new Letter( + 'r')))); + words.add(new Word(Arrays.asList(new Letter('m'), new Letter('o'), new Letter('u'), new Letter( + 't'), new Letter('h')))); - return new Sentence(words); + return new Sentence(words); - } + } } diff --git a/composite/src/main/java/com/iluwatar/composite/Sentence.java b/composite/src/main/java/com/iluwatar/composite/Sentence.java index 2fc13701b..eea1d4b1d 100644 --- a/composite/src/main/java/com/iluwatar/composite/Sentence.java +++ b/composite/src/main/java/com/iluwatar/composite/Sentence.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.composite; import java.util.List; @@ -9,20 +31,22 @@ import java.util.List; */ public class Sentence extends LetterComposite { - public Sentence(List words) { - for (Word w : words) { - this.add(w); - } - } + /** + * Constructor + */ + public Sentence(List words) { + for (Word w : words) { + this.add(w); + } + } - @Override - protected void printThisBefore() { - // nop - } - - @Override - protected void printThisAfter() { - System.out.print("."); - } + @Override + protected void printThisBefore() { + // nop + } + @Override + protected void printThisAfter() { + System.out.print("."); + } } diff --git a/composite/src/main/java/com/iluwatar/composite/Word.java b/composite/src/main/java/com/iluwatar/composite/Word.java index e715ed28a..819f166dc 100644 --- a/composite/src/main/java/com/iluwatar/composite/Word.java +++ b/composite/src/main/java/com/iluwatar/composite/Word.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.composite; import java.util.List; @@ -9,20 +31,22 @@ import java.util.List; */ public class Word extends LetterComposite { - public Word(List letters) { - for (Letter l : letters) { - this.add(l); - } - } + /** + * Constructor + */ + public Word(List letters) { + for (Letter l : letters) { + this.add(l); + } + } - @Override - protected void printThisBefore() { - System.out.print(" "); - } - - @Override - protected void printThisAfter() { - // nop - } + @Override + protected void printThisBefore() { + System.out.print(" "); + } + @Override + protected void printThisAfter() { + // nop + } } diff --git a/composite/src/test/java/com/iluwatar/composite/AppTest.java b/composite/src/test/java/com/iluwatar/composite/AppTest.java index 872ccea78..336438a99 100644 --- a/composite/src/test/java/com/iluwatar/composite/AppTest.java +++ b/composite/src/test/java/com/iluwatar/composite/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.composite; import org.junit.Test; -import com.iluwatar.composite.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.composite.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/composite/src/test/java/com/iluwatar/composite/MessengerTest.java b/composite/src/test/java/com/iluwatar/composite/MessengerTest.java new file mode 100644 index 000000000..5f75ea017 --- /dev/null +++ b/composite/src/test/java/com/iluwatar/composite/MessengerTest.java @@ -0,0 +1,112 @@ +/** + * The MIT License + * Copyright (c) 2014 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.composite; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/11/15 - 8:12 PM + * + * @author Jeroen Meulemeester + */ +public class MessengerTest { + + /** + * The buffer used to capture every write to {@link System#out} + */ + private ByteArrayOutputStream stdOutBuffer = new ByteArrayOutputStream(); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream realStdOut = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + this.stdOutBuffer = new ByteArrayOutputStream(); + System.setOut(new PrintStream(stdOutBuffer)); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(realStdOut); + } + + /** + * Test the message from the orcs + */ + @Test + public void testMessageFromOrcs() { + final Messenger messenger = new Messenger(); + testMessage( + messenger.messageFromOrcs(), + "Where there is a whip there is a way." + ); + } + + /** + * Test the message from the elves + */ + @Test + public void testMessageFromElves() { + final Messenger messenger = new Messenger(); + testMessage( + messenger.messageFromElves(), + "Much wind pours from your mouth." + ); + } + + /** + * Test if the given composed message matches the expected message + * + * @param composedMessage The composed message, received from the messenger + * @param message The expected message + */ + private void testMessage(final LetterComposite composedMessage, final String message) { + // Test is the composed message has the correct number of words + final String[] words = message.split(" "); + assertNotNull(composedMessage); + assertEquals(words.length, composedMessage.count()); + + // Print the message to the mocked stdOut ... + composedMessage.print(); + + // ... and verify if the message matches with the expected one + assertEquals(message, new String(this.stdOutBuffer.toByteArray()).trim()); + } + +} diff --git a/dao/index.md b/dao/README.md similarity index 69% rename from dao/index.md rename to dao/README.md index cf9f43a68..785a1c362 100644 --- a/dao/index.md +++ b/dao/README.md @@ -3,22 +3,24 @@ layout: pattern title: Data Access Object folder: dao permalink: /patterns/dao/ -categories: Architectural +categories: Persistence Tier tags: - Java - Difficulty-Beginner --- -**Intent:** Object provides an abstract interface to some type of database or +## Intent +Object provides an abstract interface to some type of database or other persistence mechanism. ![alt text](./etc/dao.png "Data Access Object") -**Applicability:** Use the Data Access Object in any of the following situations +## Applicability +Use the Data Access Object in any of the following situations * when you want to consolidate how the data layer is accessed * when you want to avoid writing multiple data retrieval/persistence layers -**Credits:** +## Credits * [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) diff --git a/dao/etc/dao.png b/dao/etc/dao.png index 9fe34b976..452e72ba1 100644 Binary files a/dao/etc/dao.png and b/dao/etc/dao.png differ diff --git a/dao/etc/dao.ucls b/dao/etc/dao.ucls index bf11f18b3..0706837fc 100644 --- a/dao/etc/dao.ucls +++ b/dao/etc/dao.ucls @@ -1,45 +1,71 @@ - - + + - - + + - - - - - - - - - + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + diff --git a/dao/pom.xml b/dao/pom.xml index 69191b94d..f64eff8bc 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -1,24 +1,105 @@ + - 4.0.0 - - com.iluwatar - java-design-patterns - 1.7.0 - - dao - - - - junit - junit - test - - - log4j - log4j - - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" + xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + dao + + + + junit + junit + test + + + log4j + log4j + + + com.h2database + h2 + + + de.bechte.junit + junit-hierarchicalcontextrunner + + + org.mockito + mockito-core + + + + + + + + + + src/main/resources + + + src/main/resources + + log4j.xml + + .. + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.6 + + + log4j.xml + + + + true + + + + + + + diff --git a/dao/src/main/java/com/iluwatar/dao/App.java b/dao/src/main/java/com/iluwatar/dao/App.java index 95cf93e5b..8a0c06739 100644 --- a/dao/src/main/java/com/iluwatar/dao/App.java +++ b/dao/src/main/java/com/iluwatar/dao/App.java @@ -1,59 +1,135 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dao; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; import java.util.ArrayList; import java.util.List; +import java.util.stream.Stream; + +import javax.sql.DataSource; import org.apache.log4j.Logger; +import org.h2.jdbcx.JdbcDataSource; /** + * Data Access Object (DAO) is an object that provides an abstract interface to some type of + * database or other persistence mechanism. By mapping application calls to the persistence layer, + * DAO provide some specific data operations without exposing details of the database. This + * isolation supports the Single responsibility principle. It separates what data accesses the + * application needs, in terms of domain-specific objects and data types (the public interface of + * the DAO), from how these needs can be satisfied with a specific DBMS. + * + *

With the DAO pattern, we can use various method calls to retrieve/add/delete/update data + * without directly interacting with the data source. The below example demonstrates basic CRUD + * operations: select, add, update, and delete. * - * Data Access Object (DAO) is an object that provides an abstract interface to some type of database or other - * persistence mechanism. By mapping application calls to the persistence layer, DAO provide some specific data - * operations without exposing details of the database. This isolation supports the Single responsibility principle. - * It separates what data accesses the application needs, in terms of domain-specific objects and data types - * (the public interface of the DAO), from how these needs can be satisfied with a specific DBMS. - *

- * With the DAO pattern, we can use various method calls to retrieve/add/delete/update data without directly - * interacting with the data. The below example demonstrates basic CRUD operations: select, add, update, and delete. * */ public class App { + private static final String DB_URL = "jdbc:h2:~/dao"; + private static Logger log = Logger.getLogger(App.class); + + /** + * Program entry point. + * + * @param args command line args. + * @throws Exception if any error occurs. + */ + public static void main(final String[] args) throws Exception { + final CustomerDao inMemoryDao = new InMemoryCustomerDao(); + performOperationsUsing(inMemoryDao); + + final DataSource dataSource = createDataSource(); + createSchema(dataSource); + final CustomerDao dbDao = new DbCustomerDao(dataSource); + performOperationsUsing(dbDao); + deleteSchema(dataSource); + } - private static Logger LOGGER = Logger.getLogger(App.class); + private static void deleteSchema(DataSource dataSource) throws SQLException { + try (Connection connection = dataSource.getConnection(); + Statement statement = connection.createStatement()) { + statement.execute(CustomerSchemaSql.DELETE_SCHEMA_SQL); + } + } - /** - * Program entry point. - * - * @param args command line args. - */ - public static void main(final String[] args) { - final CustomerDaoImpl customerDao = new CustomerDaoImpl(generateSampleCustomers()); - LOGGER.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers()); - LOGGER.info("customerDao.getCusterById(2): " + customerDao.getCustomerById(2)); - final Customer customer = new Customer(4, "Dan", "Danson"); - customerDao.addCustomer(customer); - LOGGER.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers()); - customer.setFirstName("Daniel"); - customer.setLastName("Danielson"); - customerDao.updateCustomer(customer); - LOGGER.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers()); - customerDao.deleteCustomer(customer); - LOGGER.info("customerDao.getAllCustomers(): " + customerDao.getAllCustomers()); - } + private static void createSchema(DataSource dataSource) throws SQLException { + try (Connection connection = dataSource.getConnection(); + Statement statement = connection.createStatement()) { + statement.execute(CustomerSchemaSql.CREATE_SCHEMA_SQL); + } + } - /** - * Generate customers. - * - * @return list of customers. - */ - public static List generateSampleCustomers() { - final Customer customer1 = new Customer(1, "Adam", "Adamson"); - final Customer customer2 = new Customer(2, "Bob", "Bobson"); - final Customer customer3 = new Customer(3, "Carl", "Carlson"); - final List customers = new ArrayList(); - customers.add(customer1); - customers.add(customer2); - customers.add(customer3); - return customers; - } + private static DataSource createDataSource() { + JdbcDataSource dataSource = new JdbcDataSource(); + dataSource.setURL(DB_URL); + return dataSource; + } + + private static void performOperationsUsing(final CustomerDao customerDao) throws Exception { + addCustomers(customerDao); + log.info("customerDao.getAllCustomers(): "); + try (Stream customerStream = customerDao.getAll()) { + customerStream.forEach((customer) -> log.info(customer)); + } + log.info("customerDao.getCustomerById(2): " + customerDao.getById(2)); + final Customer customer = new Customer(4, "Dan", "Danson"); + customerDao.add(customer); + log.info("customerDao.getAllCustomers(): " + customerDao.getAll()); + customer.setFirstName("Daniel"); + customer.setLastName("Danielson"); + customerDao.update(customer); + log.info("customerDao.getAllCustomers(): "); + try (Stream customerStream = customerDao.getAll()) { + customerStream.forEach((cust) -> log.info(cust)); + } + customerDao.delete(customer); + log.info("customerDao.getAllCustomers(): " + customerDao.getAll()); + } + + private static void addCustomers(CustomerDao customerDao) throws Exception { + for (Customer customer : generateSampleCustomers()) { + customerDao.add(customer); + } + } + + /** + * Generate customers. + * + * @return list of customers. + */ + public static List generateSampleCustomers() { + final Customer customer1 = new Customer(1, "Adam", "Adamson"); + final Customer customer2 = new Customer(2, "Bob", "Bobson"); + final Customer customer3 = new Customer(3, "Carl", "Carlson"); + final List customers = new ArrayList<>(); + customers.add(customer1); + customers.add(customer2); + customers.add(customer3); + return customers; + } } diff --git a/dao/src/main/java/com/iluwatar/dao/Customer.java b/dao/src/main/java/com/iluwatar/dao/Customer.java index e6d7f7763..d6b512dd6 100644 --- a/dao/src/main/java/com/iluwatar/dao/Customer.java +++ b/dao/src/main/java/com/iluwatar/dao/Customer.java @@ -1,8 +1,30 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dao; /** - * - * Customer + * A customer POJO that represents the data that will be read from the data source. * */ public class Customer { @@ -11,6 +33,9 @@ public class Customer { private String firstName; private String lastName; + /** + * Creates an instance of customer. + */ public Customer(final int id, final String firstName, final String lastName) { this.id = id; this.firstName = firstName; @@ -48,21 +73,21 @@ public class Customer { } @Override - public boolean equals(final Object o) { + public boolean equals(final Object that) { boolean isEqual = false; - if (this == o) { + if (this == that) { isEqual = true; - } else if (o != null && (getClass() == o.getClass())) { - final Customer customer = (Customer) o; - if (getId() == customer.getId()) + } else if (that != null && getClass() == that.getClass()) { + final Customer customer = (Customer) that; + if (getId() == customer.getId()) { isEqual = true; + } } return isEqual; } @Override public int hashCode() { - int result = getId(); - return result; + return getId(); } } diff --git a/dao/src/main/java/com/iluwatar/dao/CustomerDao.java b/dao/src/main/java/com/iluwatar/dao/CustomerDao.java index 79d23ba20..059a9e9f7 100644 --- a/dao/src/main/java/com/iluwatar/dao/CustomerDao.java +++ b/dao/src/main/java/com/iluwatar/dao/CustomerDao.java @@ -1,21 +1,81 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dao; -import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; /** + * In an application the Data Access Object (DAO) is a part of Data access layer. It is an object + * that provides an interface to some type of persistence mechanism. By mapping application calls + * to the persistence layer, DAO provides some specific data operations without exposing details + * of the database. This isolation supports the Single responsibility principle. It separates what + * data accesses the application needs, in terms of domain-specific objects and data types + * (the public interface of the DAO), from how these needs can be satisfied with a specific DBMS, + * database schema, etc. * - * CustomerDao - * + *

Any change in the way data is stored and retrieved will not change the client code as the + * client will be using interface and need not worry about exact source. + * + * @see InMemoryCustomerDao + * @see DbCustomerDao */ public interface CustomerDao { - List getAllCustomers(); + /** + * @return all the customers as a stream. The stream may be lazily or eagerly evaluated based + * on the implementation. The stream must be closed after use. + * @throws Exception if any error occurs. + */ + Stream getAll() throws Exception; + + /** + * @param id unique identifier of the customer. + * @return an optional with customer if a customer with unique identifier id + * exists, empty optional otherwise. + * @throws Exception if any error occurs. + */ + Optional getById(int id) throws Exception; - Customer getCustomerById(int id); + /** + * @param customer the customer to be added. + * @return true if customer is successfully added, false if customer already exists. + * @throws Exception if any error occurs. + */ + boolean add(Customer customer) throws Exception; - void addCustomer(Customer customer); + /** + * @param customer the customer to be updated. + * @return true if customer exists and is successfully updated, false otherwise. + * @throws Exception if any error occurs. + */ + boolean update(Customer customer) throws Exception; - void updateCustomer(Customer customer); - - void deleteCustomer(Customer customer); + /** + * @param customer the customer to be deleted. + * @return true if customer exists and is successfully deleted, false otherwise. + * @throws Exception if any error occurs. + */ + boolean delete(Customer customer) throws Exception; } diff --git a/dao/src/main/java/com/iluwatar/dao/CustomerDaoImpl.java b/dao/src/main/java/com/iluwatar/dao/CustomerDaoImpl.java deleted file mode 100644 index e590fb1a6..000000000 --- a/dao/src/main/java/com/iluwatar/dao/CustomerDaoImpl.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.iluwatar.dao; - -import java.util.List; - -/** - * - * The data access object (DAO) is an object that provides an abstract interface to some type of - * database or other persistence mechanism. By mapping application calls to the persistence layer, - * DAO provide some specific data operations without exposing details of the database. This - * isolation supports the Single responsibility principle. It separates what data accesses the - * application needs, in terms of domain-specific objects and data types (the public interface of - * the DAO), from how these needs can be satisfied with a specific DBMS, database schema, etc. - * - */ -public class CustomerDaoImpl implements CustomerDao { - - // Represents the DB structure for our example so we don't have to managed it ourselves - // Note: Normally this would be in the form of an actual database and not part of the Dao Impl. - private List customers; - - public CustomerDaoImpl(final List customers) { - this.customers = customers; - } - - @Override - public List getAllCustomers() { - return customers; - } - - @Override - public Customer getCustomerById(final int id) { - Customer customer = null; - for (final Customer cus : getAllCustomers()) { - if (cus.getId() == id) { - customer = cus; - break; - } - } - return customer; - } - - @Override - public void addCustomer(final Customer customer) { - if (getCustomerById(customer.getId()) == null) { - customers.add(customer); - } - } - - - @Override - public void updateCustomer(final Customer customer) { - if (getAllCustomers().contains(customer)) { - final int index = getAllCustomers().indexOf(customer); - getAllCustomers().set(index, customer); - } - } - - @Override - public void deleteCustomer(final Customer customer) { - getAllCustomers().remove(customer); - } -} diff --git a/dao/src/main/java/com/iluwatar/dao/CustomerSchemaSql.java b/dao/src/main/java/com/iluwatar/dao/CustomerSchemaSql.java new file mode 100644 index 000000000..860826abf --- /dev/null +++ b/dao/src/main/java/com/iluwatar/dao/CustomerSchemaSql.java @@ -0,0 +1,31 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dao; + +public interface CustomerSchemaSql { + + String CREATE_SCHEMA_SQL = "CREATE TABLE CUSTOMERS (ID NUMBER, FNAME VARCHAR(100), " + + "LNAME VARCHAR(100))"; + + String DELETE_SCHEMA_SQL = "DROP TABLE CUSTOMERS"; +} diff --git a/dao/src/main/java/com/iluwatar/dao/DbCustomerDao.java b/dao/src/main/java/com/iluwatar/dao/DbCustomerDao.java new file mode 100644 index 000000000..622b5b1f0 --- /dev/null +++ b/dao/src/main/java/com/iluwatar/dao/DbCustomerDao.java @@ -0,0 +1,183 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dao; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Optional; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.Consumer; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import javax.sql.DataSource; + +/** + * An implementation of {@link CustomerDao} that persists customers in RDBMS. + * + */ +public class DbCustomerDao implements CustomerDao { + + private final DataSource dataSource; + + /** + * Creates an instance of {@link DbCustomerDao} which uses provided dataSource + * to store and retrieve customer information. + * + * @param dataSource a non-null dataSource. + */ + public DbCustomerDao(DataSource dataSource) { + this.dataSource = dataSource; + } + + /** + * @return a lazily populated stream of customers. Note the stream returned must be closed to + * free all the acquired resources. The stream keeps an open connection to the database till + * it is complete or is closed manually. + */ + @Override + public Stream getAll() throws Exception { + + Connection connection; + try { + connection = getConnection(); + PreparedStatement statement = connection.prepareStatement("SELECT * FROM CUSTOMERS"); + ResultSet resultSet = statement.executeQuery(); + return StreamSupport.stream(new Spliterators.AbstractSpliterator(Long.MAX_VALUE, + Spliterator.ORDERED) { + + @Override + public boolean tryAdvance(Consumer action) { + try { + if (!resultSet.next()) { + return false; + } + action.accept(createCustomer(resultSet)); + return true; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + }, false).onClose(() -> mutedClose(connection)); + } catch (SQLException e) { + throw new Exception(e.getMessage(), e); + } + } + + private Connection getConnection() throws SQLException { + return dataSource.getConnection(); + } + + private void mutedClose(Connection connection) { + try { + connection.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private Customer createCustomer(ResultSet resultSet) throws SQLException { + return new Customer(resultSet.getInt("ID"), + resultSet.getString("FNAME"), + resultSet.getString("LNAME")); + } + + /** + * {@inheritDoc} + */ + @Override + public Optional getById(int id) throws Exception { + try (Connection connection = getConnection(); + PreparedStatement statement = + connection.prepareStatement("SELECT * FROM CUSTOMERS WHERE ID = ?")) { + + statement.setInt(1, id); + ResultSet resultSet = statement.executeQuery(); + if (resultSet.next()) { + return Optional.of(createCustomer(resultSet)); + } else { + return Optional.empty(); + } + } catch (SQLException ex) { + throw new Exception(ex.getMessage(), ex); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean add(Customer customer) throws Exception { + if (getById(customer.getId()).isPresent()) { + return false; + } + + try (Connection connection = getConnection(); + PreparedStatement statement = + connection.prepareStatement("INSERT INTO CUSTOMERS VALUES (?,?,?)")) { + statement.setInt(1, customer.getId()); + statement.setString(2, customer.getFirstName()); + statement.setString(3, customer.getLastName()); + statement.execute(); + return true; + } catch (SQLException ex) { + throw new Exception(ex.getMessage(), ex); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean update(Customer customer) throws Exception { + try (Connection connection = getConnection(); + PreparedStatement statement = + connection.prepareStatement("UPDATE CUSTOMERS SET FNAME = ?, LNAME = ? WHERE ID = ?")) { + statement.setString(1, customer.getFirstName()); + statement.setString(2, customer.getLastName()); + statement.setInt(3, customer.getId()); + return statement.executeUpdate() > 0; + } catch (SQLException ex) { + throw new Exception(ex.getMessage(), ex); + } + } + + /** + * {@inheritDoc} + */ + @Override + public boolean delete(Customer customer) throws Exception { + try (Connection connection = getConnection(); + PreparedStatement statement = + connection.prepareStatement("DELETE FROM CUSTOMERS WHERE ID = ?")) { + statement.setInt(1, customer.getId()); + return statement.executeUpdate() > 0; + } catch (SQLException ex) { + throw new Exception(ex.getMessage(), ex); + } + } +} diff --git a/dao/src/main/java/com/iluwatar/dao/InMemoryCustomerDao.java b/dao/src/main/java/com/iluwatar/dao/InMemoryCustomerDao.java new file mode 100644 index 000000000..15c63d1de --- /dev/null +++ b/dao/src/main/java/com/iluwatar/dao/InMemoryCustomerDao.java @@ -0,0 +1,73 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dao; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Stream; + +/** + * An in memory implementation of {@link CustomerDao}, which stores the customers in JVM memory + * and data is lost when the application exits. + *
+ * This implementation is useful as temporary database or for testing. + */ +public class InMemoryCustomerDao implements CustomerDao { + + private Map idToCustomer = new HashMap<>(); + + /** + * An eagerly evaluated stream of customers stored in memory. + */ + @Override + public Stream getAll() { + return idToCustomer.values().stream(); + } + + @Override + public Optional getById(final int id) { + return Optional.ofNullable(idToCustomer.get(id)); + } + + @Override + public boolean add(final Customer customer) { + if (getById(customer.getId()).isPresent()) { + return false; + } + + idToCustomer.put(customer.getId(), customer); + return true; + } + + @Override + public boolean update(final Customer customer) { + return idToCustomer.replace(customer.getId(), customer) != null; + } + + @Override + public boolean delete(final Customer customer) { + return idToCustomer.remove(customer.getId()) != null; + } +} diff --git a/dao/src/main/resources/log4j.xml b/dao/src/main/resources/log4j.xml new file mode 100644 index 000000000..b591c17e1 --- /dev/null +++ b/dao/src/main/resources/log4j.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dao/src/test/java/com/iluwatar/dao/AppTest.java b/dao/src/test/java/com/iluwatar/dao/AppTest.java new file mode 100644 index 000000000..169fc046e --- /dev/null +++ b/dao/src/test/java/com/iluwatar/dao/AppTest.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dao; + +import org.junit.Test; + +/** + * Tests that DAO example runs without errors. + */ +public class AppTest { + @Test + public void test() throws Exception { + String[] args = {}; + App.main(args); + } +} diff --git a/dao/src/test/java/com/iluwatar/dao/CustomerDaoImplTest.java b/dao/src/test/java/com/iluwatar/dao/CustomerDaoImplTest.java deleted file mode 100644 index 230314d80..000000000 --- a/dao/src/test/java/com/iluwatar/dao/CustomerDaoImplTest.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.iluwatar.dao; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.Before; -import org.junit.Test; - -public class CustomerDaoImplTest { - - private CustomerDaoImpl impl; - private List customers; - private static final Customer CUSTOMER = new Customer(1, "Freddy", "Kruger"); - - @Before - public void setUp() { - customers = new ArrayList(); - customers.add(CUSTOMER); - impl = new CustomerDaoImpl(customers); - } - - @Test - public void deleteExistingCustomer() { - assertEquals(1, impl.getAllCustomers().size()); - impl.deleteCustomer(CUSTOMER); - assertTrue(impl.getAllCustomers().isEmpty()); - } - - @Test - public void deleteNonExistingCustomer() { - final Customer nonExistingCustomer = new Customer(2, "Robert", "Englund"); - impl.deleteCustomer(nonExistingCustomer); - assertEquals(1, impl.getAllCustomers().size()); - } - - @Test - public void updateExistingCustomer() { - final String newFirstname = "Bernard"; - final String newLastname = "Montgomery"; - final Customer customer = new Customer(CUSTOMER.getId(), newFirstname, newLastname); - impl.updateCustomer(customer); - final Customer cust = impl.getCustomerById(CUSTOMER.getId()); - assertEquals(newFirstname, cust.getFirstName()); - assertEquals(newLastname, cust.getLastName()); - } - - @Test - public void updateNonExistingCustomer() { - final int nonExistingId = getNonExistingCustomerId(); - final String newFirstname = "Douglas"; - final String newLastname = "MacArthur"; - final Customer customer = new Customer(nonExistingId, newFirstname, newLastname); - impl.updateCustomer(customer); - assertNull(impl.getCustomerById(nonExistingId)); - final Customer existingCustomer = impl.getCustomerById(CUSTOMER.getId()); - assertEquals(CUSTOMER.getFirstName(), existingCustomer.getFirstName()); - assertEquals(CUSTOMER.getLastName(), existingCustomer.getLastName()); - } - - @Test - public void addCustomer() { - final Customer newCustomer = new Customer(3, "George", "Patton"); - impl.addCustomer(newCustomer); - assertEquals(2, impl.getAllCustomers().size()); - } - - @Test - public void addAlreadyAddedCustomer() { - final Customer newCustomer = new Customer(3, "George", "Patton"); - impl.addCustomer(newCustomer); - assertEquals(2, impl.getAllCustomers().size()); - impl.addCustomer(newCustomer); - assertEquals(2, impl.getAllCustomers().size()); - } - - @Test - public void getExistinCustomerById() { - assertEquals(CUSTOMER, impl.getCustomerById(CUSTOMER.getId())); - } - - @Test - public void getNonExistinCustomerById() { - final int nonExistingId = getNonExistingCustomerId(); - assertNull(impl.getCustomerById(nonExistingId)); - } - - /** - * An arbitrary number which does not correspond to an active Customer id. - * - * @return an int of a customer id which doesn't exist - */ - private int getNonExistingCustomerId() { - return 999; - } -} diff --git a/dao/src/test/java/com/iluwatar/dao/CustomerTest.java b/dao/src/test/java/com/iluwatar/dao/CustomerTest.java index 8511a577b..6a02fd6be 100644 --- a/dao/src/test/java/com/iluwatar/dao/CustomerTest.java +++ b/dao/src/test/java/com/iluwatar/dao/CustomerTest.java @@ -1,3 +1,26 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dao; import static org.junit.Assert.assertEquals; @@ -63,12 +86,12 @@ public class CustomerTest { @Test public void testToString() { final StringBuffer buffer = new StringBuffer(); - buffer.append("Customer{id="); - buffer.append("" + customer.getId()); - buffer.append(", firstName='"); - buffer.append(customer.getFirstName()); - buffer.append("\', lastName='"); - buffer.append(customer.getLastName() + "\'}"); + buffer.append("Customer{id=") + .append("" + customer.getId()) + .append(", firstName='") + .append(customer.getFirstName()) + .append("\', lastName='") + .append(customer.getLastName() + "\'}"); assertEquals(buffer.toString(), customer.toString()); } } diff --git a/dao/src/test/java/com/iluwatar/dao/DbCustomerDaoTest.java b/dao/src/test/java/com/iluwatar/dao/DbCustomerDaoTest.java new file mode 100644 index 000000000..ac69699df --- /dev/null +++ b/dao/src/test/java/com/iluwatar/dao/DbCustomerDaoTest.java @@ -0,0 +1,269 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dao; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.stream.Stream; + +import javax.sql.DataSource; + +import org.h2.jdbcx.JdbcDataSource; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.mockito.Mockito; + +import de.bechte.junit.runners.context.HierarchicalContextRunner; + +/** + * Tests {@link DbCustomerDao}. + */ +@RunWith(HierarchicalContextRunner.class) +public class DbCustomerDaoTest { + + private static final String DB_URL = "jdbc:h2:~/dao"; + private DbCustomerDao dao; + private Customer existingCustomer = new Customer(1, "Freddy", "Krueger"); + + /** + * Creates customers schema. + * @throws SQLException if there is any error while creating schema. + */ + @Before + public void createSchema() throws SQLException { + try (Connection connection = DriverManager.getConnection(DB_URL); + Statement statement = connection.createStatement()) { + statement.execute(CustomerSchemaSql.CREATE_SCHEMA_SQL); + } + } + + /** + * Represents the scenario where DB connectivity is present. + */ + public class ConnectionSuccess { + + /** + * Setup for connection success scenario. + * @throws Exception if any error occurs. + */ + @Before + public void setUp() throws Exception { + JdbcDataSource dataSource = new JdbcDataSource(); + dataSource.setURL(DB_URL); + dao = new DbCustomerDao(dataSource); + boolean result = dao.add(existingCustomer); + assertTrue(result); + } + + /** + * Represents the scenario when DAO operations are being performed on a non existing customer. + */ + public class NonExistingCustomer { + + @Test + public void addingShouldResultInSuccess() throws Exception { + try (Stream allCustomers = dao.getAll()) { + assumeTrue(allCustomers.count() == 1); + } + + final Customer nonExistingCustomer = new Customer(2, "Robert", "Englund"); + boolean result = dao.add(nonExistingCustomer); + assertTrue(result); + + assertCustomerCountIs(2); + assertEquals(nonExistingCustomer, dao.getById(nonExistingCustomer.getId()).get()); + } + + @Test + public void deletionShouldBeFailureAndNotAffectExistingCustomers() throws Exception { + final Customer nonExistingCustomer = new Customer(2, "Robert", "Englund"); + boolean result = dao.delete(nonExistingCustomer); + + assertFalse(result); + assertCustomerCountIs(1); + } + + @Test + public void updationShouldBeFailureAndNotAffectExistingCustomers() throws Exception { + final int nonExistingId = getNonExistingCustomerId(); + final String newFirstname = "Douglas"; + final String newLastname = "MacArthur"; + final Customer customer = new Customer(nonExistingId, newFirstname, newLastname); + boolean result = dao.update(customer); + + assertFalse(result); + assertFalse(dao.getById(nonExistingId).isPresent()); + } + + @Test + public void retrieveShouldReturnNoCustomer() throws Exception { + assertFalse(dao.getById(getNonExistingCustomerId()).isPresent()); + } + } + + /** + * Represents a scenario where DAO operations are being performed on an already existing + * customer. + * + */ + public class ExistingCustomer { + + @Test + public void addingShouldResultInFailureAndNotAffectExistingCustomers() throws Exception { + Customer existingCustomer = new Customer(1, "Freddy", "Krueger"); + + boolean result = dao.add(existingCustomer); + + assertFalse(result); + assertCustomerCountIs(1); + assertEquals(existingCustomer, dao.getById(existingCustomer.getId()).get()); + } + + @Test + public void deletionShouldBeSuccessAndCustomerShouldBeNonAccessible() throws Exception { + boolean result = dao.delete(existingCustomer); + + assertTrue(result); + assertCustomerCountIs(0); + assertFalse(dao.getById(existingCustomer.getId()).isPresent()); + } + + @Test + public void updationShouldBeSuccessAndAccessingTheSameCustomerShouldReturnUpdatedInformation() throws Exception { + final String newFirstname = "Bernard"; + final String newLastname = "Montgomery"; + final Customer customer = new Customer(existingCustomer.getId(), newFirstname, newLastname); + boolean result = dao.update(customer); + + assertTrue(result); + + final Customer cust = dao.getById(existingCustomer.getId()).get(); + assertEquals(newFirstname, cust.getFirstName()); + assertEquals(newLastname, cust.getLastName()); + } + } + } + + /** + * Represents a scenario where DB connectivity is not present due to network issue, or + * DB service unavailable. + * + */ + public class ConnectivityIssue { + + private static final String EXCEPTION_CAUSE = "Connection not available"; + @Rule public ExpectedException exception = ExpectedException.none(); + + /** + * setup a connection failure scenario. + * @throws SQLException if any error occurs. + */ + @Before + public void setUp() throws SQLException { + dao = new DbCustomerDao(mockedDatasource()); + exception.expect(Exception.class); + exception.expectMessage(EXCEPTION_CAUSE); + } + + private DataSource mockedDatasource() throws SQLException { + DataSource mockedDataSource = mock(DataSource.class); + Connection mockedConnection = mock(Connection.class); + SQLException exception = new SQLException(EXCEPTION_CAUSE); + doThrow(exception).when(mockedConnection).prepareStatement(Mockito.anyString()); + doReturn(mockedConnection).when(mockedDataSource).getConnection(); + return mockedDataSource; + } + + @Test + public void addingACustomerFailsWithExceptionAsFeedbackToClient() throws Exception { + dao.add(new Customer(2, "Bernard", "Montgomery")); + } + + @Test + public void deletingACustomerFailsWithExceptionAsFeedbackToTheClient() throws Exception { + dao.delete(existingCustomer); + } + + @Test + public void updatingACustomerFailsWithFeedbackToTheClient() throws Exception { + final String newFirstname = "Bernard"; + final String newLastname = "Montgomery"; + + dao.update(new Customer(existingCustomer.getId(), newFirstname, newLastname)); + } + + @Test + public void retrievingACustomerByIdFailsWithExceptionAsFeedbackToClient() throws Exception { + dao.getById(existingCustomer.getId()); + } + + @Test + public void retrievingAllCustomersFailsWithExceptionAsFeedbackToClient() throws Exception { + dao.getAll(); + } + + } + + /** + * Delete customer schema for fresh setup per test. + * @throws SQLException if any error occurs. + */ + @After + public void deleteSchema() throws SQLException { + try (Connection connection = DriverManager.getConnection(DB_URL); + Statement statement = connection.createStatement()) { + statement.execute(CustomerSchemaSql.DELETE_SCHEMA_SQL); + } + } + + private void assertCustomerCountIs(int count) throws Exception { + try (Stream allCustomers = dao.getAll()) { + assertTrue(allCustomers.count() == count); + } + } + + + /** + * An arbitrary number which does not correspond to an active Customer id. + * + * @return an int of a customer id which doesn't exist + */ + private int getNonExistingCustomerId() { + return 999; + } +} diff --git a/dao/src/test/java/com/iluwatar/dao/InMemoryCustomerDaoTest.java b/dao/src/test/java/com/iluwatar/dao/InMemoryCustomerDaoTest.java new file mode 100644 index 000000000..65a087b9b --- /dev/null +++ b/dao/src/test/java/com/iluwatar/dao/InMemoryCustomerDaoTest.java @@ -0,0 +1,164 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dao; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; + +import java.util.Optional; +import java.util.stream.Stream; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import de.bechte.junit.runners.context.HierarchicalContextRunner; + +/** + * Tests {@link InMemoryCustomerDao}. + */ +@RunWith(HierarchicalContextRunner.class) +public class InMemoryCustomerDaoTest { + + private InMemoryCustomerDao dao; + private static final Customer CUSTOMER = new Customer(1, "Freddy", "Krueger"); + + @Before + public void setUp() { + dao = new InMemoryCustomerDao(); + assertTrue(dao.add(CUSTOMER)); + } + + /** + * Represents the scenario when the DAO operations are being performed on a non existent + * customer. + */ + public class NonExistingCustomer { + + @Test + public void addingShouldResultInSuccess() throws Exception { + try (Stream allCustomers = dao.getAll()) { + assumeTrue(allCustomers.count() == 1); + } + + final Customer nonExistingCustomer = new Customer(2, "Robert", "Englund"); + boolean result = dao.add(nonExistingCustomer); + assertTrue(result); + + assertCustomerCountIs(2); + assertEquals(nonExistingCustomer, dao.getById(nonExistingCustomer.getId()).get()); + } + + @Test + public void deletionShouldBeFailureAndNotAffectExistingCustomers() throws Exception { + final Customer nonExistingCustomer = new Customer(2, "Robert", "Englund"); + boolean result = dao.delete(nonExistingCustomer); + + assertFalse(result); + assertCustomerCountIs(1); + } + + @Test + public void updationShouldBeFailureAndNotAffectExistingCustomers() throws Exception { + final int nonExistingId = getNonExistingCustomerId(); + final String newFirstname = "Douglas"; + final String newLastname = "MacArthur"; + final Customer customer = new Customer(nonExistingId, newFirstname, newLastname); + boolean result = dao.update(customer); + + assertFalse(result); + assertFalse(dao.getById(nonExistingId).isPresent()); + } + + @Test + public void retrieveShouldReturnNoCustomer() throws Exception { + assertFalse(dao.getById(getNonExistingCustomerId()).isPresent()); + } + } + + /** + * Represents the scenario when the DAO operations are being performed on an already existing + * customer. + */ + public class ExistingCustomer { + + @Test + public void addingShouldResultInFailureAndNotAffectExistingCustomers() throws Exception { + boolean result = dao.add(CUSTOMER); + + assertFalse(result); + assertCustomerCountIs(1); + assertEquals(CUSTOMER, dao.getById(CUSTOMER.getId()).get()); + } + + @Test + public void deletionShouldBeSuccessAndCustomerShouldBeNonAccessible() throws Exception { + boolean result = dao.delete(CUSTOMER); + + assertTrue(result); + assertCustomerCountIs(0); + assertFalse(dao.getById(CUSTOMER.getId()).isPresent()); + } + + @Test + public void updationShouldBeSuccessAndAccessingTheSameCustomerShouldReturnUpdatedInformation() throws Exception { + final String newFirstname = "Bernard"; + final String newLastname = "Montgomery"; + final Customer customer = new Customer(CUSTOMER.getId(), newFirstname, newLastname); + boolean result = dao.update(customer); + + assertTrue(result); + + final Customer cust = dao.getById(CUSTOMER.getId()).get(); + assertEquals(newFirstname, cust.getFirstName()); + assertEquals(newLastname, cust.getLastName()); + } + + @Test + public void retriveShouldReturnTheCustomer() { + Optional optionalCustomer = dao.getById(CUSTOMER.getId()); + + assertTrue(optionalCustomer.isPresent()); + assertEquals(CUSTOMER, optionalCustomer.get()); + } + } + + /** + * An arbitrary number which does not correspond to an active Customer id. + * + * @return an int of a customer id which doesn't exist + */ + private int getNonExistingCustomerId() { + return 999; + } + + private void assertCustomerCountIs(int count) throws Exception { + try (Stream allCustomers = dao.getAll()) { + assertTrue(allCustomers.count() == count); + } + } +} diff --git a/dao/src/test/resources/log4j.xml b/dao/src/test/resources/log4j.xml deleted file mode 100644 index 136817f50..000000000 --- a/dao/src/test/resources/log4j.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/data-mapper/etc/data-mapper.png b/data-mapper/etc/data-mapper.png new file mode 100644 index 000000000..bcda8054a Binary files /dev/null and b/data-mapper/etc/data-mapper.png differ diff --git a/data-mapper/etc/data-mapper.ucls b/data-mapper/etc/data-mapper.ucls new file mode 100644 index 000000000..2467983ce --- /dev/null +++ b/data-mapper/etc/data-mapper.ucls @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data-mapper/index.md b/data-mapper/index.md new file mode 100644 index 000000000..075e8eece --- /dev/null +++ b/data-mapper/index.md @@ -0,0 +1,25 @@ +--- +layout: pattern +title: Data Mapper +folder: data-mapper +permalink: /patterns/data-mapper/ +categories: Persistence Tier +tags: + - Java + - Difficulty-Beginner +--- + +## Intent +A layer of mappers that moves data between objects and a database while keeping them independent of each other and the mapper itself + +![alt text](./etc/data-mapper.png "Data Mapper") + +## Applicability +Use the Data Mapper in any of the following situations + +* when you want to decouple data objects from DB access layer +* when you want to write multiple data retrieval/persistence implementations + +## Credits + +* [Data Mapper](http://richard.jp.leguen.ca/tutoring/soen343-f2010/tutorials/implementing-data-mapper/) diff --git a/data-mapper/pom.xml b/data-mapper/pom.xml new file mode 100644 index 000000000..7d1b83469 --- /dev/null +++ b/data-mapper/pom.xml @@ -0,0 +1,45 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + data-mapper + + + junit + junit + test + + + log4j + log4j + + + diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/App.java b/data-mapper/src/main/java/com/iluwatar/datamapper/App.java new file mode 100644 index 000000000..5fcd0d9ea --- /dev/null +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/App.java @@ -0,0 +1,77 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * 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.datamapper; + +import java.util.Optional; + +import org.apache.log4j.Logger; + +/** + * The Data Mapper (DM) is a layer of software that separates the in-memory objects from the + * database. Its responsibility is to transfer data between the two and also to isolate them from + * each other. With Data Mapper the in-memory objects needn't know even that there's a database + * present; they need no SQL interface code, and certainly no knowledge of the database schema. (The + * database schema is always ignorant of the objects that use it.) Since it's a form of Mapper , + * Data Mapper itself is even unknown to the domain layer. + *

+ * The below example demonstrates basic CRUD operations: Create, Read, Update, and Delete. + * + */ +public final class App { + + private static Logger log = Logger.getLogger(App.class); + + /** + * Program entry point. + * + * @param args command line args. + */ + public static void main(final String... args) { + + /* Create new data mapper for type 'first' */ + final StudentDataMapper mapper = new StudentDataMapperImpl(); + + /* Create new student */ + Student student = new Student(1, "Adam", 'A'); + + /* Add student in respectibe store */ + mapper.insert(student); + + log.debug("App.main(), student : " + student + ", is inserted"); + + /* Find this student */ + final Optional studentToBeFound = mapper.find(student.getStudentId()); + + log.debug("App.main(), student : " + studentToBeFound + ", is searched"); + + /* Update existing student object */ + student = new Student(student.getStudentId(), "AdamUpdated", 'A'); + + /* Update student in respectibe db */ + mapper.update(student); + + log.debug("App.main(), student : " + student + ", is updated"); + log.debug("App.main(), student : " + student + ", is going to be deleted"); + + /* Delete student in db */ + mapper.delete(student); + } + + private App() {} +} diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/DataMapperException.java b/data-mapper/src/main/java/com/iluwatar/datamapper/DataMapperException.java new file mode 100644 index 000000000..a6995b06d --- /dev/null +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/DataMapperException.java @@ -0,0 +1,42 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * 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.datamapper; + +/** + * Using Runtime Exception for avoiding dependancy on implementation exceptions. This helps in + * decoupling. + * + * @author amit.dixit + * + */ +public final class DataMapperException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** + * Constructs a new runtime exception with the specified detail message. The cause is not + * initialized, and may subsequently be initialized by a call to {@link #initCause}. + * + * @param message the detail message. The detail message is saved for later retrieval by the + * {@link #getMessage()} method. + */ + public DataMapperException(final String message) { + super(message); + } +} diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/Student.java b/data-mapper/src/main/java/com/iluwatar/datamapper/Student.java new file mode 100644 index 000000000..0164533c8 --- /dev/null +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/Student.java @@ -0,0 +1,139 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * 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.datamapper; + + +import java.io.Serializable; + +public final class Student implements Serializable { + + private static final long serialVersionUID = 1L; + + private int studentId; + private String name; + private char grade; + + + /** + * Use this constructor to create a Student with all details + * + * @param studentId as unique student id + * @param name as student name + * @param grade as respective grade of student + */ + public Student(final int studentId, final String name, final char grade) { + super(); + + this.studentId = studentId; + this.name = name; + this.grade = grade; + } + + /** + * + * @return the student id + */ + public int getStudentId() { + return studentId; + } + + /** + * + * @param studentId as unique student id + */ + public void setStudentId(final int studentId) { + this.studentId = studentId; + } + + /** + * + * @return name of student + */ + public String getName() { + return name; + } + + /** + * + * @param name as 'name' of student + */ + public void setName(final String name) { + this.name = name; + } + + /** + * + * @return grade of student + */ + public char getGrade() { + return grade; + } + + /** + * + * @param grade as 'grade of student' + */ + public void setGrade(final char grade) { + this.grade = grade; + } + + /** + * + */ + @Override + public boolean equals(final Object inputObject) { + + boolean isEqual = false; + + /* Check if both objects are same */ + if (this == inputObject) { + + isEqual = true; + } else if (inputObject != null && getClass() == inputObject.getClass()) { + + final Student inputStudent = (Student) inputObject; + + /* If student id matched */ + if (this.getStudentId() == inputStudent.getStudentId()) { + + isEqual = true; + } + } + + return isEqual; + } + + /** + * + */ + @Override + public int hashCode() { + + /* Student id is assumed to be unique */ + return this.getStudentId(); + } + + /** + * + */ + @Override + public String toString() { + return "Student [studentId=" + studentId + ", name=" + name + ", grade=" + grade + "]"; + } +} diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapper.java b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapper.java new file mode 100644 index 000000000..40f0c5c72 --- /dev/null +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapper.java @@ -0,0 +1,32 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * 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.datamapper; + +import java.util.Optional; + +public interface StudentDataMapper { + + Optional find(int studentId); + + void insert(Student student) throws DataMapperException; + + void update(Student student) throws DataMapperException; + + void delete(Student student) throws DataMapperException; +} diff --git a/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapperImpl.java b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapperImpl.java new file mode 100644 index 000000000..7ecd9e7f8 --- /dev/null +++ b/data-mapper/src/main/java/com/iluwatar/datamapper/StudentDataMapperImpl.java @@ -0,0 +1,102 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * 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.datamapper; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public final class StudentDataMapperImpl implements StudentDataMapper { + + /* Note: Normally this would be in the form of an actual database */ + private List students = new ArrayList<>(); + + @Override + public Optional find(int studentId) { + + /* Compare with existing students */ + for (final Student student : this.getStudents()) { + + /* Check if student is found */ + if (student.getStudentId() == studentId) { + + return Optional.of(student); + } + } + + /* Return empty value */ + return Optional.empty(); + } + + @Override + public void update(Student studentToBeUpdated) throws DataMapperException { + + + /* Check with existing students */ + if (this.getStudents().contains(studentToBeUpdated)) { + + /* Get the index of student in list */ + final int index = this.getStudents().indexOf(studentToBeUpdated); + + /* Update the student in list */ + this.getStudents().set(index, studentToBeUpdated); + + } else { + + /* Throw user error after wrapping in a runtime exception */ + throw new DataMapperException("Student [" + studentToBeUpdated.getName() + "] is not found"); + } + } + + @Override + public void insert(Student studentToBeInserted) throws DataMapperException { + + /* Check with existing students */ + if (!this.getStudents().contains(studentToBeInserted)) { + + /* Add student in list */ + this.getStudents().add(studentToBeInserted); + + } else { + + /* Throw user error after wrapping in a runtime exception */ + throw new DataMapperException("Student already [" + studentToBeInserted.getName() + "] exists"); + } + } + + @Override + public void delete(Student studentToBeDeleted) throws DataMapperException { + + /* Check with existing students */ + if (this.getStudents().contains(studentToBeDeleted)) { + + /* Delete the student from list */ + this.getStudents().remove(studentToBeDeleted); + + } else { + + /* Throw user error after wrapping in a runtime exception */ + throw new DataMapperException("Student [" + studentToBeDeleted.getName() + "] is not found"); + } + } + + public List getStudents() { + return this.students; + } +} diff --git a/data-mapper/src/main/resources/log4j.xml b/data-mapper/src/main/resources/log4j.xml new file mode 100644 index 000000000..b591c17e1 --- /dev/null +++ b/data-mapper/src/main/resources/log4j.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data-mapper/src/test/java/com/iluwatar/datamapper/AppTest.java b/data-mapper/src/test/java/com/iluwatar/datamapper/AppTest.java new file mode 100644 index 000000000..f2858100e --- /dev/null +++ b/data-mapper/src/test/java/com/iluwatar/datamapper/AppTest.java @@ -0,0 +1,34 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * 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.datamapper; + +import com.iluwatar.datamapper.App; +import org.junit.Test; + +/** + * Tests that Data-Mapper example runs without errors. + */ +public final class AppTest { + + @Test + public void test() { + final String[] args = {}; + App.main(args); + } +} diff --git a/data-mapper/src/test/java/com/iluwatar/datamapper/DataMapperTest.java b/data-mapper/src/test/java/com/iluwatar/datamapper/DataMapperTest.java new file mode 100644 index 000000000..17f4d3922 --- /dev/null +++ b/data-mapper/src/test/java/com/iluwatar/datamapper/DataMapperTest.java @@ -0,0 +1,73 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * 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.datamapper; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.iluwatar.datamapper.Student; +import com.iluwatar.datamapper.StudentDataMapper; +import com.iluwatar.datamapper.StudentDataMapperImpl; + +/** + * The Data Mapper (DM) is a layer of software that separates the in-memory objects from the + * database. Its responsibility is to transfer data between the two and also to isolate them from + * each other. With Data Mapper the in-memory objects needn't know even that there's a database + * present; they need no SQL interface code, and certainly no knowledge of the database schema. (The + * database schema is always ignorant of the objects that use it.) Since it's a form of Mapper , + * Data Mapper itself is even unknown to the domain layer. + *

+ */ +public class DataMapperTest { + + /** + * This test verify that first data mapper is able to perform all CRUD operations on Student + */ + @Test + public void testFirstDataMapper() { + + /* Create new data mapper of first type */ + final StudentDataMapper mapper = new StudentDataMapperImpl(); + + /* Create new student */ + Student student = new Student(1, "Adam", 'A'); + + /* Add student in respectibe db */ + mapper.insert(student); + + /* Check if student is added in db */ + assertEquals(student.getStudentId(), mapper.find(student.getStudentId()).get().getStudentId()); + + /* Update existing student object */ + student = new Student(student.getStudentId(), "AdamUpdated", 'A'); + + /* Update student in respectibe db */ + mapper.update(student); + + /* Check if student is updated in db */ + assertEquals(mapper.find(student.getStudentId()).get().getName(), "AdamUpdated"); + + /* Delete student in db */ + mapper.delete(student); + + /* Result should be false */ + assertEquals(false, mapper.find(student.getStudentId()).isPresent()); + } +} diff --git a/data-mapper/src/test/java/com/iluwatar/datamapper/StudentTest.java b/data-mapper/src/test/java/com/iluwatar/datamapper/StudentTest.java new file mode 100644 index 000000000..a3c0e46c1 --- /dev/null +++ b/data-mapper/src/test/java/com/iluwatar/datamapper/StudentTest.java @@ -0,0 +1,51 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * 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.datamapper; + +import org.junit.Test; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertFalse; + +public final class StudentTest { + + @Test + /** + * This API tests the equality behaviour of Student object + * Object Equality should work as per logic defined in equals method + * + * @throws Exception if any execution error during test + */ + public void testEquality() throws Exception { + + /* Create some students */ + final Student firstStudent = new Student(1, "Adam", 'A'); + final Student secondStudent = new Student(2, "Donald", 'B'); + final Student secondSameStudent = new Student(2, "Donald", 'B'); + final Student firstSameStudent = firstStudent; + + /* Check equals functionality: should return 'true' */ + assertTrue(firstStudent.equals(firstSameStudent)); + + /* Check equals functionality: should return 'false' */ + assertFalse(firstStudent.equals(secondStudent)); + + /* Check equals functionality: should return 'true' */ + assertTrue(secondStudent.equals(secondSameStudent)); + } +} diff --git a/decorator/index.md b/decorator/README.md similarity index 61% rename from decorator/index.md rename to decorator/README.md index 55849fce6..63795114c 100644 --- a/decorator/index.md +++ b/decorator/README.md @@ -7,20 +7,28 @@ categories: Structural tags: - Java - Gang Of Four + - Difficulty-Beginner --- -**Intent:** Attach additional responsibilities to an object dynamically. +## Also known as +Wrapper + +## Intent +Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality. -![alt text](./etc/decorator_1.png "Decorator") +![alt text](./etc/decorator.png "Decorator") -**Applicability:** Use Decorator +## Applicability +Use Decorator * to add responsibilities to individual objects dynamically and transparently, that is, without affecting other objects * for responsibilities that can be withdrawn * when extension by subclassing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination. Or a class definition may be hidden or otherwise unavailable for subclassing -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) +* [Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions](http://www.amazon.com/Functional-Programming-Java-Harnessing-Expressions/dp/1937785467/ref=sr_1_1) +* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) diff --git a/decorator/etc/decorator.png b/decorator/etc/decorator.png index 1e4bfdac2..47a87b20b 100644 Binary files a/decorator/etc/decorator.png and b/decorator/etc/decorator.png differ diff --git a/decorator/etc/decorator.ucls b/decorator/etc/decorator.ucls index 7adb8c3a6..a5353d4ec 100644 --- a/decorator/etc/decorator.ucls +++ b/decorator/etc/decorator.ucls @@ -1,18 +1,18 @@ - + - + - - + + @@ -21,29 +21,46 @@ - + - + + + + + + + + + + + + - - - - + + + + - - + + + + + + diff --git a/decorator/etc/decorator_1.png b/decorator/etc/decorator_1.png deleted file mode 100644 index 5a7afe2d1..000000000 Binary files a/decorator/etc/decorator_1.png and /dev/null differ diff --git a/decorator/pom.xml b/decorator/pom.xml index 92142f184..7ba2a4ee8 100644 --- a/decorator/pom.xml +++ b/decorator/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT decorator @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/decorator/src/main/java/com/iluwatar/decorator/App.java b/decorator/src/main/java/com/iluwatar/decorator/App.java index bd697d4a4..bdc574fbc 100644 --- a/decorator/src/main/java/com/iluwatar/decorator/App.java +++ b/decorator/src/main/java/com/iluwatar/decorator/App.java @@ -1,38 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 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.decorator; /** * - * The Decorator pattern is a more flexible alternative to subclassing. The Decorator - * class implements the same interface as the target and uses composition to - * "decorate" calls to the target. Using the Decorator pattern it is possible to - * change the behavior of the class during runtime. + * The Decorator pattern is a more flexible alternative to subclassing. The Decorator class + * implements the same interface as the target and uses composition to "decorate" calls to the + * target. Using the Decorator pattern it is possible to change the behavior of the class during + * runtime. *

- * In this example we show how the simple {@link Troll} first attacks and then - * flees the battle. Then we decorate the {@link Troll} with a {@link SmartTroll} - * and perform the attack again. You can see how the behavior changes after the - * decoration. + * In this example we show how the simple {@link Troll} first attacks and then flees the battle. + * Then we decorate the {@link Troll} with a {@link SmartHostile} and perform the attack again. You + * can see how the behavior changes after the decoration. * */ public class App { - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { - // simple troll - System.out.println("A simple looking troll approaches."); - Hostile troll = new Troll(); - troll.attack(); - troll.fleeBattle(); - System.out.printf("Simple troll power %d.\n", troll.getAttackPower()); + // simple troll + System.out.println("A simple looking troll approaches."); + Hostile troll = new Troll(); + troll.attack(); + troll.fleeBattle(); + System.out.printf("Simple troll power %d.\n", troll.getAttackPower()); - // change the behavior of the simple troll by adding a decorator - System.out.println("\nA smart looking troll surprises you."); - Hostile smart = new SmartTroll(troll); - smart.attack(); - smart.fleeBattle(); - System.out.printf("Smart troll power %d.\n", smart.getAttackPower()); - } + // change the behavior of the simple troll by adding a decorator + System.out.println("\nA smart looking troll surprises you."); + Hostile smart = new SmartHostile(troll); + smart.attack(); + smart.fleeBattle(); + System.out.printf("Smart troll power %d.\n", smart.getAttackPower()); + } } diff --git a/decorator/src/main/java/com/iluwatar/decorator/Hostile.java b/decorator/src/main/java/com/iluwatar/decorator/Hostile.java index 709072501..d3414c9dd 100644 --- a/decorator/src/main/java/com/iluwatar/decorator/Hostile.java +++ b/decorator/src/main/java/com/iluwatar/decorator/Hostile.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.decorator; /** @@ -7,10 +29,10 @@ package com.iluwatar.decorator; */ public interface Hostile { - void attack(); + void attack(); - int getAttackPower(); + int getAttackPower(); - void fleeBattle(); + void fleeBattle(); } diff --git a/decorator/src/main/java/com/iluwatar/decorator/SmartHostile.java b/decorator/src/main/java/com/iluwatar/decorator/SmartHostile.java new file mode 100644 index 000000000..3b4b86276 --- /dev/null +++ b/decorator/src/main/java/com/iluwatar/decorator/SmartHostile.java @@ -0,0 +1,56 @@ +/** + * The MIT License + * Copyright (c) 2014 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.decorator; + +/** + * SmartHostile is a decorator for {@link Hostile} objects. The calls to the {@link Hostile} interface + * are intercepted and decorated. Finally the calls are delegated to the decorated {@link Hostile} + * object. + * + */ +public class SmartHostile implements Hostile { + + private Hostile decorated; + + public SmartHostile(Hostile decorated) { + this.decorated = decorated; + } + + @Override + public void attack() { + System.out.println("It throws a rock at you!"); + decorated.attack(); + } + + @Override + public int getAttackPower() { + // decorated hostile's power + 20 because it is smart + return decorated.getAttackPower() + 20; + } + + @Override + public void fleeBattle() { + System.out.println("It calls for help!"); + decorated.fleeBattle(); + } +} diff --git a/decorator/src/main/java/com/iluwatar/decorator/SmartTroll.java b/decorator/src/main/java/com/iluwatar/decorator/SmartTroll.java deleted file mode 100644 index 909f94c95..000000000 --- a/decorator/src/main/java/com/iluwatar/decorator/SmartTroll.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.iluwatar.decorator; - -/** - * SmartTroll is a decorator for {@link Hostile} objects. - * The calls to the {@link Hostile} interface are intercepted - * and decorated. Finally the calls are delegated - * to the decorated {@link Hostile} object. - * - */ -public class SmartTroll implements Hostile { - - private Hostile decorated; - - public SmartTroll(Hostile decorated) { - this.decorated = decorated; - } - - @Override - public void attack() { - System.out.println("The troll throws a rock at you!"); - decorated.attack(); - } - - @Override - public int getAttackPower() { - // decorated troll power + 20 because it is smart - return decorated.getAttackPower() + 20; - } - - @Override - public void fleeBattle() { - System.out.println("The troll calls for help!"); - decorated.fleeBattle(); - } - -} diff --git a/decorator/src/main/java/com/iluwatar/decorator/Troll.java b/decorator/src/main/java/com/iluwatar/decorator/Troll.java index 85d873dbe..628adda4b 100644 --- a/decorator/src/main/java/com/iluwatar/decorator/Troll.java +++ b/decorator/src/main/java/com/iluwatar/decorator/Troll.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.decorator; /** @@ -7,17 +29,18 @@ package com.iluwatar.decorator; */ public class Troll implements Hostile { - public void attack() { - System.out.println("The troll swings at you with a club!"); - } + @Override + public void attack() { + System.out.println("The troll swings at you with a club!"); + } - @Override - public int getAttackPower() { - return 10; - } - - public void fleeBattle() { - System.out.println("The troll shrieks in horror and runs away!"); - } + @Override + public int getAttackPower() { + return 10; + } + @Override + public void fleeBattle() { + System.out.println("The troll shrieks in horror and runs away!"); + } } diff --git a/decorator/src/test/java/com/iluwatar/decorator/AppTest.java b/decorator/src/test/java/com/iluwatar/decorator/AppTest.java index b74bd3a06..747144c6d 100644 --- a/decorator/src/test/java/com/iluwatar/decorator/AppTest.java +++ b/decorator/src/test/java/com/iluwatar/decorator/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.decorator; import org.junit.Test; -import com.iluwatar.decorator.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.decorator.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/decorator/src/test/java/com/iluwatar/decorator/SmartHostileTest.java b/decorator/src/test/java/com/iluwatar/decorator/SmartHostileTest.java new file mode 100644 index 000000000..6432d4e90 --- /dev/null +++ b/decorator/src/test/java/com/iluwatar/decorator/SmartHostileTest.java @@ -0,0 +1,58 @@ +/** + * The MIT License + * Copyright (c) 2014 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.decorator; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.*; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +/** + * Date: 12/7/15 - 7:47 PM + * + * @author Jeroen Meulemeester + */ +public class SmartHostileTest { + + @Test + public void testSmartHostile() throws Exception { + // Create a normal troll first, but make sure we can spy on it later on. + final Hostile simpleTroll = spy(new Troll()); + + // Now we want to decorate the troll to make it smarter ... + final Hostile smartTroll = new SmartHostile(simpleTroll); + assertEquals(30, smartTroll.getAttackPower()); + verify(simpleTroll, times(1)).getAttackPower(); + + // Check if the smart troll actions are delegated to the decorated troll + smartTroll.attack(); + verify(simpleTroll, times(1)).attack(); + + smartTroll.fleeBattle(); + verify(simpleTroll, times(1)).fleeBattle(); + verifyNoMoreInteractions(simpleTroll); + + } + +} diff --git a/decorator/src/test/java/com/iluwatar/decorator/TrollTest.java b/decorator/src/test/java/com/iluwatar/decorator/TrollTest.java new file mode 100644 index 000000000..84b0f6d20 --- /dev/null +++ b/decorator/src/test/java/com/iluwatar/decorator/TrollTest.java @@ -0,0 +1,84 @@ +/** + * The MIT License + * Copyright (c) 2014 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.decorator; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.PrintStream; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +/** + * Date: 12/7/15 - 7:26 PM + * + * @author Jeroen Meulemeester + */ +public class TrollTest { + + /** + * The mocked standard out stream, required since the actions don't have any influence on other + * objects, except for writing to the std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + @Test + public void testTrollActions() throws Exception { + final Troll troll = new Troll(); + assertEquals(10, troll.getAttackPower()); + + troll.attack(); + verify(this.stdOutMock, times(1)).println(eq("The troll swings at you with a club!")); + + troll.fleeBattle(); + verify(this.stdOutMock, times(1)).println(eq("The troll shrieks in horror and runs away!")); + + verifyNoMoreInteractions(this.stdOutMock); + } + +} \ No newline at end of file diff --git a/delegation/README.md b/delegation/README.md new file mode 100644 index 000000000..e5c0c6376 --- /dev/null +++ b/delegation/README.md @@ -0,0 +1,30 @@ +--- +layout: pattern +title: Delegation +folder: delegation +permalink: /patterns/delegation/ +categories: Behavioral +tags: + - Java + - Difficulty-Beginner +--- + +## Also known as +Proxy Pattern + +## Intent +It is a technique where an object expresses certain behavior to the outside but in +reality delegates responsibility for implementing that behaviour to an associated object. + +![alt text](./etc/delegation.png "Delegate") + +## Applicability +Use the Delegate pattern in order to achieve the following + +* Reduce the coupling of methods to their class +* Components that behave identically, but realize that this situation can change in the future. + +## Credits + +* [Delegate Pattern: Wikipedia ](https://en.wikipedia.org/wiki/Delegation_pattern) +* [Proxy Pattern: Wikipedia ](https://en.wikipedia.org/wiki/Proxy_pattern) diff --git a/delegation/etc/delegation.png b/delegation/etc/delegation.png new file mode 100644 index 000000000..375ef4d6b Binary files /dev/null and b/delegation/etc/delegation.png differ diff --git a/delegation/etc/delegation.ucls b/delegation/etc/delegation.ucls new file mode 100644 index 000000000..e3ce08873 --- /dev/null +++ b/delegation/etc/delegation.ucls @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/delegation/pom.xml b/delegation/pom.xml new file mode 100644 index 000000000..2fca225b8 --- /dev/null +++ b/delegation/pom.xml @@ -0,0 +1,51 @@ + + + + + + java-design-patterns + com.iluwatar + 1.13.0-SNAPSHOT + + 4.0.0 + + delegation + + + + junit + junit + test + + + com.github.stefanbirkner + system-rules + test + + + \ No newline at end of file diff --git a/delegation/src/main/java/com/iluwatar/delegation/simple/App.java b/delegation/src/main/java/com/iluwatar/delegation/simple/App.java new file mode 100644 index 000000000..1940e7ae2 --- /dev/null +++ b/delegation/src/main/java/com/iluwatar/delegation/simple/App.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 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.delegation.simple; + +import com.iluwatar.delegation.simple.printers.CanonPrinter; +import com.iluwatar.delegation.simple.printers.EpsonPrinter; +import com.iluwatar.delegation.simple.printers.HpPrinter; + +/** + * The delegate pattern provides a mechanism to abstract away the implementation and control of the desired action. + * The class being called in this case {@link PrinterController} is not responsible for the actual desired action, + * but is actually delegated to a helper class either {@link CanonPrinter}, {@link EpsonPrinter} or {@link HpPrinter}. + * The consumer does not have or require knowledge of the actual class carrying out the action, only the + * container on which they are calling. + * + * In this example the delegates are {@link EpsonPrinter}, {@link HpPrinter} and {@link CanonPrinter} they all implement + * {@link Printer}. The {@link PrinterController} class also implements {@link Printer}. However neither provide the + * functionality of {@link Printer} by printing to the screen, they actually call upon the instance of {@link Printer} + * that they were instantiated with. Therefore delegating the behaviour to another class. + */ +public class App { + + public static final String MESSAGE_TO_PRINT = "hello world"; + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + PrinterController hpPrinterController = new PrinterController(new HpPrinter()); + PrinterController canonPrinterController = new PrinterController(new CanonPrinter()); + PrinterController epsonPrinterController = new PrinterController(new EpsonPrinter()); + + hpPrinterController.print(MESSAGE_TO_PRINT); + canonPrinterController.print(MESSAGE_TO_PRINT); + epsonPrinterController.print(MESSAGE_TO_PRINT); + } + +} diff --git a/delegation/src/main/java/com/iluwatar/delegation/simple/Printer.java b/delegation/src/main/java/com/iluwatar/delegation/simple/Printer.java new file mode 100644 index 000000000..a25f0fba3 --- /dev/null +++ b/delegation/src/main/java/com/iluwatar/delegation/simple/Printer.java @@ -0,0 +1,45 @@ +/** + * The MIT License + * Copyright (c) 2014 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.delegation.simple; + +import com.iluwatar.delegation.simple.printers.CanonPrinter; +import com.iluwatar.delegation.simple.printers.EpsonPrinter; +import com.iluwatar.delegation.simple.printers.HpPrinter; + +/** + * Interface that both the Controller and the Delegate will implement. + * + * @see CanonPrinter + * @see EpsonPrinter + * @see HpPrinter + */ +public interface Printer { + + /** + * Method that takes a String to print to the screen. This will be implemented on both the + * controller and the delegate allowing the controller to call the same method on the delegate class. + * + * @param message to be printed to the screen + */ + void print(final String message); +} diff --git a/delegation/src/main/java/com/iluwatar/delegation/simple/PrinterController.java b/delegation/src/main/java/com/iluwatar/delegation/simple/PrinterController.java new file mode 100644 index 000000000..8a4f20dd2 --- /dev/null +++ b/delegation/src/main/java/com/iluwatar/delegation/simple/PrinterController.java @@ -0,0 +1,45 @@ +/** + * The MIT License + * Copyright (c) 2014 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.delegation.simple; + +public class PrinterController implements Printer { + + private final Printer printer; + + public PrinterController(Printer printer) { + this.printer = printer; + } + + /** + * This method is implemented from {@link Printer} however instead on providing an + * implementation, it instead calls upon the class passed through the constructor. This is the delegate, + * hence the pattern. Therefore meaning that the caller does not care of the implementing class only the owning + * controller. + * + * @param message to be printed to the screen + */ + @Override + public void print(String message) { + printer.print(message); + } +} diff --git a/delegation/src/main/java/com/iluwatar/delegation/simple/printers/CanonPrinter.java b/delegation/src/main/java/com/iluwatar/delegation/simple/printers/CanonPrinter.java new file mode 100644 index 000000000..5e3966d85 --- /dev/null +++ b/delegation/src/main/java/com/iluwatar/delegation/simple/printers/CanonPrinter.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 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.delegation.simple.printers; + +import com.iluwatar.delegation.simple.Printer; + +/** + * Specialised Implementation of {@link Printer} for a Canon Printer, in + * this case the message to be printed is appended to "Canon Printer : " + * + * @see Printer + */ +public class CanonPrinter implements Printer { + + /** + * {@inheritDoc} + */ + @Override + public void print(String message) { + System.out.print("Canon Printer : " + message); + } + +} diff --git a/delegation/src/main/java/com/iluwatar/delegation/simple/printers/EpsonPrinter.java b/delegation/src/main/java/com/iluwatar/delegation/simple/printers/EpsonPrinter.java new file mode 100644 index 000000000..d37fbf613 --- /dev/null +++ b/delegation/src/main/java/com/iluwatar/delegation/simple/printers/EpsonPrinter.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 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.delegation.simple.printers; + +import com.iluwatar.delegation.simple.Printer; + +/** + * Specialised Implementation of {@link Printer} for a Epson Printer, in + * this case the message to be printed is appended to "Epson Printer : " + * + * @see Printer + */ +public class EpsonPrinter implements Printer { + + /** + * {@inheritDoc} + */ + @Override + public void print(String message) { + System.out.print("Epson Printer : " + message); + } + +} diff --git a/delegation/src/main/java/com/iluwatar/delegation/simple/printers/HpPrinter.java b/delegation/src/main/java/com/iluwatar/delegation/simple/printers/HpPrinter.java new file mode 100644 index 000000000..f81debf62 --- /dev/null +++ b/delegation/src/main/java/com/iluwatar/delegation/simple/printers/HpPrinter.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 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.delegation.simple.printers; + +import com.iluwatar.delegation.simple.Printer; + +/** + * Specialised Implementation of {@link Printer} for a HP Printer, in + * this case the message to be printed is appended to "HP Printer : " + * + * @see Printer + */ +public class HpPrinter implements Printer { + + /** + * {@inheritDoc} + */ + @Override + public void print(String message) { + System.out.print("HP Printer : " + message); + } + +} diff --git a/delegation/src/test/java/com/iluwatar/delegation/simple/AppTest.java b/delegation/src/test/java/com/iluwatar/delegation/simple/AppTest.java new file mode 100644 index 000000000..63e469a59 --- /dev/null +++ b/delegation/src/test/java/com/iluwatar/delegation/simple/AppTest.java @@ -0,0 +1,35 @@ +/** + * The MIT License + * Copyright (c) 2014 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.delegation.simple; + +import org.junit.Test; + +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } + +} diff --git a/delegation/src/test/java/com/iluwatar/delegation/simple/DelegateTest.java b/delegation/src/test/java/com/iluwatar/delegation/simple/DelegateTest.java new file mode 100644 index 000000000..6a19c9a1a --- /dev/null +++ b/delegation/src/test/java/com/iluwatar/delegation/simple/DelegateTest.java @@ -0,0 +1,65 @@ +/** + * The MIT License + * Copyright (c) 2014 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.delegation.simple; + +import com.iluwatar.delegation.simple.printers.CanonPrinter; +import com.iluwatar.delegation.simple.printers.EpsonPrinter; +import com.iluwatar.delegation.simple.printers.HpPrinter; +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.SystemOutRule; + +import static org.junit.Assert.assertEquals; + +public class DelegateTest { + + private static final String MESSAGE = "Test Message Printed"; + + @Rule + public final SystemOutRule systemOutRule = new SystemOutRule().enableLog(); + + @Test + public void testCanonPrinter() throws Exception { + PrinterController printerController = new PrinterController(new CanonPrinter()); + printerController.print(MESSAGE); + + assertEquals("Canon Printer : Test Message Printed", systemOutRule.getLog()); + } + + @Test + public void testHpPrinter() throws Exception { + PrinterController printerController = new PrinterController(new HpPrinter()); + printerController.print(MESSAGE); + + assertEquals("HP Printer : Test Message Printed", systemOutRule.getLog()); + } + + @Test + public void testEpsonPrinter() throws Exception { + PrinterController printerController = new PrinterController(new EpsonPrinter()); + printerController.print(MESSAGE); + + assertEquals("Epson Printer : Test Message Printed", systemOutRule.getLog()); + } + +} diff --git a/dependency-injection/index.md b/dependency-injection/README.md similarity index 80% rename from dependency-injection/index.md rename to dependency-injection/README.md index f6ead97a7..735f589b1 100644 --- a/dependency-injection/index.md +++ b/dependency-injection/README.md @@ -4,10 +4,13 @@ title: Dependency Injection folder: dependency-injection permalink: /patterns/dependency-injection/ categories: Behavioral -tags: Java +tags: + - Java + - Difficulty-Beginner --- -**Intent:** Dependency Injection is a software design pattern in which one or +## Intent +Dependency Injection is a software design pattern in which one or more dependencies (or services) are injected, or passed by reference, into a dependent object (or client) and are made part of the client's state. The pattern separates the creation of a client's dependencies from its own @@ -16,7 +19,8 @@ inversion of control and single responsibility principles. ![alt text](./etc/dependency-injection.png "Dependency Injection") -**Applicability:** Use the Dependency Injection pattern when +## Applicability +Use the Dependency Injection pattern when * when you need to remove knowledge of concrete implementation from object * to enable unit testing of classes in isolation using mock objects or stubs diff --git a/dependency-injection/pom.xml b/dependency-injection/pom.xml index 9a28933f8..3472da240 100644 --- a/dependency-injection/pom.xml +++ b/dependency-injection/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT dependency-injection @@ -13,6 +37,11 @@ junit junit test + + + org.mockito + mockito-core + test com.google.inject diff --git a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/AdvancedWizard.java b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/AdvancedWizard.java index 8202cc58e..4eeaccdea 100644 --- a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/AdvancedWizard.java +++ b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/AdvancedWizard.java @@ -1,22 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dependency.injection; /** * - * AdvancedWizard implements inversion of control. - * It depends on abstraction that can be injected through - * its constructor. + * AdvancedWizard implements inversion of control. It depends on abstraction that can be injected + * through its constructor. * */ public class AdvancedWizard implements Wizard { - - private Tobacco tobacco; - public AdvancedWizard(Tobacco tobacco) { - this.tobacco = tobacco; - } + private Tobacco tobacco; - @Override - public void smoke() { - tobacco.smoke(this); - } + public AdvancedWizard(Tobacco tobacco) { + this.tobacco = tobacco; + } + + @Override + public void smoke() { + tobacco.smoke(this); + } } diff --git a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/App.java b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/App.java index a882863b7..faf2a6a43 100644 --- a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/App.java +++ b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dependency.injection; import com.google.inject.Guice; @@ -5,40 +27,41 @@ import com.google.inject.Injector; /** * - * Dependency Injection pattern deals with how objects handle their dependencies. The pattern + * Dependency Injection pattern deals with how objects handle their dependencies. The pattern * implements so called inversion of control principle. Inversion of control has two specific rules: - * - High-level modules should not depend on low-level modules. Both should depend on abstractions. - * - Abstractions should not depend on details. Details should depend on abstractions. - *

- * In this example we show you three different wizards. The first one ({@link SimpleWizard}) is a naive - * implementation violating the inversion of control principle. It depends directly on a concrete - * implementation which cannot be changed. - *

- * The second wizard ({@link AdvancedWizard}) is more flexible. It does not depend on any concrete implementation - * but abstraction. It utilizes Dependency Injection pattern allowing its {@link Tobacco} dependency to be - * injected through its constructor. This way, handling the dependency is no longer the wizard's - * responsibility. It is resolved outside the wizard class. - *

- * The third example takes the pattern a step further. It uses Guice framework for Dependency Injection. - * {@link TobaccoModule} binds a concrete implementation to abstraction. Injector is then used to create - * {@link GuiceWizard} object with correct dependencies. + * - High-level modules should not depend on low-level modules. Both should depend on abstractions. + * - Abstractions should not depend on details. Details should depend on abstractions. + *

+ * In this example we show you three different wizards. The first one ({@link SimpleWizard}) is a + * naive implementation violating the inversion of control principle. It depends directly on a + * concrete implementation which cannot be changed. + *

+ * The second wizard ({@link AdvancedWizard}) is more flexible. It does not depend on any concrete + * implementation but abstraction. It utilizes Dependency Injection pattern allowing its + * {@link Tobacco} dependency to be injected through its constructor. This way, handling the + * dependency is no longer the wizard's responsibility. It is resolved outside the wizard class. + *

+ * The third example takes the pattern a step further. It uses Guice framework for Dependency + * Injection. {@link TobaccoModule} binds a concrete implementation to abstraction. Injector is then + * used to create {@link GuiceWizard} object with correct dependencies. * */ public class App { - - /** - * Program entry point - * @param args command line args - */ - public static void main( String[] args ) { - SimpleWizard simpleWizard = new SimpleWizard(); - simpleWizard.smoke(); - - AdvancedWizard advancedWizard = new AdvancedWizard(new SecondBreakfastTobacco()); - advancedWizard.smoke(); - - Injector injector = Guice.createInjector(new TobaccoModule()); - GuiceWizard guiceWizard = injector.getInstance(GuiceWizard.class); - guiceWizard.smoke(); - } + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + SimpleWizard simpleWizard = new SimpleWizard(); + simpleWizard.smoke(); + + AdvancedWizard advancedWizard = new AdvancedWizard(new SecondBreakfastTobacco()); + advancedWizard.smoke(); + + Injector injector = Guice.createInjector(new TobaccoModule()); + GuiceWizard guiceWizard = injector.getInstance(GuiceWizard.class); + guiceWizard.smoke(); + } } diff --git a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/GuiceWizard.java b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/GuiceWizard.java index 9393377f1..6e3baee5a 100644 --- a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/GuiceWizard.java +++ b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/GuiceWizard.java @@ -1,25 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dependency.injection; import javax.inject.Inject; /** * - * GuiceWizard implements inversion of control. - * Its dependencies are injected through its constructor - * by Guice framework. + * GuiceWizard implements inversion of control. Its dependencies are injected through its + * constructor by Guice framework. * */ public class GuiceWizard implements Wizard { - - private Tobacco tobacco; - - @Inject - public GuiceWizard(Tobacco tobacco) { - this.tobacco = tobacco; - } - @Override - public void smoke() { - tobacco.smoke(this); - } + private Tobacco tobacco; + + @Inject + public GuiceWizard(Tobacco tobacco) { + this.tobacco = tobacco; + } + + @Override + public void smoke() { + tobacco.smoke(this); + } } diff --git a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/OldTobyTobacco.java b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/OldTobyTobacco.java index 9197066ee..523be1d37 100644 --- a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/OldTobyTobacco.java +++ b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/OldTobyTobacco.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dependency.injection; /** diff --git a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/RivendellTobacco.java b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/RivendellTobacco.java index 9eb137a05..18691a161 100644 --- a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/RivendellTobacco.java +++ b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/RivendellTobacco.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dependency.injection; /** diff --git a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/SecondBreakfastTobacco.java b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/SecondBreakfastTobacco.java index 269f531d3..57565daf0 100644 --- a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/SecondBreakfastTobacco.java +++ b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/SecondBreakfastTobacco.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dependency.injection; /** diff --git a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/SimpleWizard.java b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/SimpleWizard.java index 5bc2c8377..6928fe7fe 100644 --- a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/SimpleWizard.java +++ b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/SimpleWizard.java @@ -1,16 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dependency.injection; /** * - * Naive Wizard implementation violating the inversion of control principle. - * It should depend on abstraction instead. + * Naive Wizard implementation violating the inversion of control principle. It should depend on + * abstraction instead. * */ public class SimpleWizard implements Wizard { - - private OldTobyTobacco tobacco = new OldTobyTobacco(); - - public void smoke() { - tobacco.smoke(this); - } + + private OldTobyTobacco tobacco = new OldTobyTobacco(); + + public void smoke() { + tobacco.smoke(this); + } } diff --git a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/Tobacco.java b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/Tobacco.java index 7ee97404d..74a564ab5 100644 --- a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/Tobacco.java +++ b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/Tobacco.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dependency.injection; /** @@ -6,8 +28,9 @@ package com.iluwatar.dependency.injection; * */ public abstract class Tobacco { - - public void smoke(Wizard wizard) { - System.out.println(String.format("%s smoking %s", wizard.getClass().getSimpleName(), this.getClass().getSimpleName())); - } + + public void smoke(Wizard wizard) { + System.out.println(String.format("%s smoking %s", wizard.getClass().getSimpleName(), this + .getClass().getSimpleName())); + } } diff --git a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/TobaccoModule.java b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/TobaccoModule.java index d2dd1072e..b8d4df676 100644 --- a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/TobaccoModule.java +++ b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/TobaccoModule.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dependency.injection; import com.google.inject.AbstractModule; @@ -9,8 +31,8 @@ import com.google.inject.AbstractModule; */ public class TobaccoModule extends AbstractModule { - @Override - protected void configure() { - bind(Tobacco.class).to(RivendellTobacco.class); - } + @Override + protected void configure() { + bind(Tobacco.class).to(RivendellTobacco.class); + } } diff --git a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/Wizard.java b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/Wizard.java index 8ac9f9fcb..a5d4d68e0 100644 --- a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/Wizard.java +++ b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/Wizard.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dependency.injection; /** @@ -6,7 +28,7 @@ package com.iluwatar.dependency.injection; * */ public interface Wizard { - - void smoke(); + + void smoke(); } diff --git a/dependency-injection/src/test/java/com/iluwatar/dependency/injection/AdvancedWizardTest.java b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/AdvancedWizardTest.java new file mode 100644 index 000000000..d1f5e574c --- /dev/null +++ b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/AdvancedWizardTest.java @@ -0,0 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dependency.injection; + +import org.junit.Test; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/10/15 - 8:40 PM + * + * @author Jeroen Meulemeester + */ +public class AdvancedWizardTest extends StdOutTest { + + /** + * Test if the {@link AdvancedWizard} smokes whatever instance of {@link Tobacco} is passed to him + * through the constructor parameter + */ + @Test + public void testSmokeEveryThing() throws Exception { + + final Tobacco[] tobaccos = { + new OldTobyTobacco(), new RivendellTobacco(), new SecondBreakfastTobacco() + }; + + for (final Tobacco tobacco : tobaccos) { + final AdvancedWizard advancedWizard = new AdvancedWizard(tobacco); + advancedWizard.smoke(); + + // Verify if the wizard is smoking the correct tobacco ... + verify(getStdOutMock(), times(1)).println("AdvancedWizard smoking " + tobacco.getClass().getSimpleName()); + + // ... and nothing else is happening. + verifyNoMoreInteractions(getStdOutMock()); + } + + } + +} \ No newline at end of file diff --git a/dependency-injection/src/test/java/com/iluwatar/dependency/injection/AppTest.java b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/AppTest.java index 5315fe1db..2003933ac 100644 --- a/dependency-injection/src/test/java/com/iluwatar/dependency/injection/AppTest.java +++ b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dependency.injection; import org.junit.Test; -import com.iluwatar.dependency.injection.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.dependency.injection.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/dependency-injection/src/test/java/com/iluwatar/dependency/injection/GuiceWizardTest.java b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/GuiceWizardTest.java new file mode 100644 index 000000000..1d3d679df --- /dev/null +++ b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/GuiceWizardTest.java @@ -0,0 +1,100 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dependency.injection; + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; + +import org.junit.Test; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/10/15 - 8:57 PM + * + * @author Jeroen Meulemeester + */ +public class GuiceWizardTest extends StdOutTest { + + /** + * Test if the {@link GuiceWizard} smokes whatever instance of {@link Tobacco} is passed to him + * through the constructor parameter + */ + @Test + public void testSmokeEveryThingThroughConstructor() throws Exception { + + final Tobacco[] tobaccos = { + new OldTobyTobacco(), new RivendellTobacco(), new SecondBreakfastTobacco() + }; + + for (final Tobacco tobacco : tobaccos) { + final GuiceWizard guiceWizard = new GuiceWizard(tobacco); + guiceWizard.smoke(); + + // Verify if the wizard is smoking the correct tobacco ... + verify(getStdOutMock(), times(1)).println("GuiceWizard smoking " + tobacco.getClass().getSimpleName()); + + // ... and nothing else is happening. + verifyNoMoreInteractions(getStdOutMock()); + } + + } + + /** + * Test if the {@link GuiceWizard} smokes whatever instance of {@link Tobacco} is passed to him + * through the Guice google inject framework + */ + @Test + public void testSmokeEveryThingThroughInjectionFramework() throws Exception { + + @SuppressWarnings("unchecked") + final Class[] tobaccos = new Class[]{ + OldTobyTobacco.class, RivendellTobacco.class, SecondBreakfastTobacco.class + }; + + for (final Class tobaccoClass : tobaccos) { + // Configure the tobacco in the injection framework ... + final Injector injector = Guice.createInjector(new AbstractModule() { + @Override + protected void configure() { + bind(Tobacco.class).to(tobaccoClass); + } + }); + + // ... and create a new wizard with it + final GuiceWizard guiceWizard = injector.getInstance(GuiceWizard.class); + guiceWizard.smoke(); + + // Verify if the wizard is smoking the correct tobacco ... + verify(getStdOutMock(), times(1)).println("GuiceWizard smoking " + tobaccoClass.getSimpleName()); + + // ... and nothing else is happening. + verifyNoMoreInteractions(getStdOutMock()); + } + + } + +} \ No newline at end of file diff --git a/dependency-injection/src/test/java/com/iluwatar/dependency/injection/SimpleWizardTest.java b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/SimpleWizardTest.java new file mode 100644 index 000000000..e5a856e8d --- /dev/null +++ b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/SimpleWizardTest.java @@ -0,0 +1,48 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dependency.injection; + +import org.junit.Test; + +import static org.mockito.Mockito.*; + +/** + * Date: 12/10/15 - 8:26 PM + * + * @author Jeroen Meulemeester + */ +public class SimpleWizardTest extends StdOutTest { + + /** + * Test if the {@link SimpleWizard} does the only thing it can do: Smoke it's {@link + * OldTobyTobacco} + */ + @Test + public void testSmoke() { + final SimpleWizard simpleWizard = new SimpleWizard(); + simpleWizard.smoke(); + verify(getStdOutMock(), times(1)).println("SimpleWizard smoking OldTobyTobacco"); + verifyNoMoreInteractions(getStdOutMock()); + } + +} \ No newline at end of file diff --git a/dependency-injection/src/test/java/com/iluwatar/dependency/injection/StdOutTest.java b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/StdOutTest.java new file mode 100644 index 000000000..57272c511 --- /dev/null +++ b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/StdOutTest.java @@ -0,0 +1,75 @@ +/** + * The MIT License + * Copyright (c) 2014 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.dependency.injection; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since the actions of the wizard don't + * have any influence on any other accessible objects, except for writing to std-out using {@link + * System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } +} diff --git a/double-checked-locking/index.md b/double-checked-locking/README.md similarity index 82% rename from double-checked-locking/index.md rename to double-checked-locking/README.md index b1b0108ec..da1fdd1a2 100644 --- a/double-checked-locking/index.md +++ b/double-checked-locking/README.md @@ -4,17 +4,22 @@ title: Double Checked Locking folder: double-checked-locking permalink: /patterns/double-checked-locking/ categories: Concurrency -tags: Java +tags: + - Java + - Difficulty-Beginner + - Idiom --- -**Intent:** Reduce the overhead of acquiring a lock by first testing the +## Intent +Reduce the overhead of acquiring a lock by first testing the locking criterion (the "lock hint") without actually acquiring the lock. Only if the locking criterion check indicates that locking is required does the actual locking logic proceed. ![alt text](./etc/double_checked_locking_1.png "Double Checked Locking") -**Applicability:** Use the Double Checked Locking pattern when +## Applicability +Use the Double Checked Locking pattern when * there is a concurrent access in object creation, e.g. singleton, where you want to create single instance of the same class and checking if it's null or not maybe not be enough when there are two or more threads that checks if instance is null or not. * there is a concurrent access on a method where method's behaviour changes according to the some constraints and these constraint change within this method. diff --git a/double-checked-locking/pom.xml b/double-checked-locking/pom.xml index 6f1fe93eb..239bcf870 100644 --- a/double-checked-locking/pom.xml +++ b/double-checked-locking/pom.xml @@ -1,9 +1,33 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT double-checked-locking @@ -12,5 +36,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/App.java b/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/App.java index b8d6a41a0..98309e181 100644 --- a/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/App.java +++ b/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.doublechecked.locking; import java.util.concurrent.ExecutorService; @@ -6,38 +28,37 @@ import java.util.concurrent.TimeUnit; /** * - * Double Checked Locking is a concurrency design pattern used to reduce the overhead - * of acquiring a lock by first testing the locking criterion (the "lock hint") without - * actually acquiring the lock. Only if the locking criterion check indicates that - * locking is required does the actual locking logic proceed. + * Double Checked Locking is a concurrency design pattern used to reduce the overhead of acquiring a + * lock by first testing the locking criterion (the "lock hint") without actually acquiring the + * lock. Only if the locking criterion check indicates that locking is required does the actual + * locking logic proceed. *

- * In {@link Inventory} we store the items with a given size. However, we do not store - * more items than the inventory size. To address concurrent access problems we - * use double checked locking to add item to inventory. In this method, the - * thread which gets the lock first adds the item. + * In {@link Inventory} we store the items with a given size. However, we do not store more items + * than the inventory size. To address concurrent access problems we use double checked locking to + * add item to inventory. In this method, the thread which gets the lock first adds the item. * */ public class App { - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { - final Inventory inventory = new Inventory(1000); - ExecutorService executorService = Executors.newFixedThreadPool(3); - for (int i = 0; i < 3; i++) { - executorService.execute(() -> { - while (inventory.addItem(new Item())) - ; - }); - } + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + final Inventory inventory = new Inventory(1000); + ExecutorService executorService = Executors.newFixedThreadPool(3); + for (int i = 0; i < 3; i++) { + executorService.execute(() -> { + while (inventory.addItem(new Item())) {}; + }); + } - executorService.shutdown(); - try { - executorService.awaitTermination(5, TimeUnit.SECONDS); - } catch (InterruptedException e) { - System.out.println("Error waiting for ExecutorService shutdown"); - } - } + executorService.shutdown(); + try { + executorService.awaitTermination(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + System.out.println("Error waiting for ExecutorService shutdown"); + } + } } diff --git a/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/Inventory.java b/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/Inventory.java index a58926d9d..176203a44 100644 --- a/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/Inventory.java +++ b/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/Inventory.java @@ -1,6 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.doublechecked.locking; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -12,32 +35,46 @@ import java.util.concurrent.locks.ReentrantLock; */ public class Inventory { - private final int inventorySize; - private final List items; - private final Lock lock; + private final int inventorySize; + private final List items; + private final Lock lock; - public Inventory(int inventorySize) { - this.inventorySize = inventorySize; - this.items = new ArrayList<>(inventorySize); - this.lock = new ReentrantLock(); - } + /** + * Constructor + */ + public Inventory(int inventorySize) { + this.inventorySize = inventorySize; + this.items = new ArrayList<>(inventorySize); + this.lock = new ReentrantLock(); + } - public boolean addItem(Item item) { - if (items.size() < inventorySize) { - lock.lock(); - try { - if (items.size() < inventorySize) { - items.add(item); - System.out.println(Thread.currentThread() - + ": items.size()=" + items.size() - + ", inventorySize=" + inventorySize); - return true; - } - } finally { - lock.unlock(); - } - } - return false; - } + /** + * Add item + */ + public boolean addItem(Item item) { + if (items.size() < inventorySize) { + lock.lock(); + try { + if (items.size() < inventorySize) { + items.add(item); + System.out.println(Thread.currentThread() + ": items.size()=" + items.size() + + ", inventorySize=" + inventorySize); + return true; + } + } finally { + lock.unlock(); + } + } + return false; + } + + /** + * Get all the items in the inventory + * + * @return All the items of the inventory, as an unmodifiable list + */ + public final List getItems() { + return Collections.unmodifiableList(items); + } } diff --git a/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/Item.java b/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/Item.java index 8fc7f3888..c805022ab 100644 --- a/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/Item.java +++ b/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/Item.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.doublechecked.locking; /** @@ -6,7 +28,4 @@ package com.iluwatar.doublechecked.locking; * */ public class Item { - - private String name; - private int level; } diff --git a/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/AppTest.java b/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/AppTest.java index 14fd47488..748c66c6a 100644 --- a/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/AppTest.java +++ b/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.doublechecked.locking; import org.junit.Test; -import com.iluwatar.doublechecked.locking.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.doublechecked.locking.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/InventoryTest.java b/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/InventoryTest.java new file mode 100644 index 000000000..485c9573e --- /dev/null +++ b/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/InventoryTest.java @@ -0,0 +1,132 @@ +/** + * The MIT License + * Copyright (c) 2014 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.doublechecked.locking; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import java.io.PrintStream; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import static junit.framework.Assert.assertTrue; +import static junit.framework.TestCase.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/10/15 - 9:34 PM + * + * @author Jeroen Meulemeester + */ +public class InventoryTest { + + /** + * The mocked standard out {@link PrintStream}, used to verify a steady increasing size of the + * {@link Inventory} while adding items from multiple threads concurrently + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * The number of threads used to stress test the locking of the {@link Inventory#addItem(Item)} + * method + */ + private static final int THREAD_COUNT = 8; + + /** + * The maximum number of {@link Item}s allowed in the {@link Inventory} + */ + private static final int INVENTORY_SIZE = 1000; + + /** + * Concurrently add multiple items to the inventory, and check if the items were added in order by + * checking the stdOut for continuous growth of the inventory. When 'items.size()=xx' shows up out + * of order, it means that the locking is not ok, increasing the risk of going over the inventory + * item limit. + */ + @Test(timeout = 10000) + public void testAddItem() throws Exception { + // Create a new inventory with a limit of 1000 items and put some load on the add method + final Inventory inventory = new Inventory(INVENTORY_SIZE); + final ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT); + for (int i = 0; i < THREAD_COUNT; i++) { + executorService.execute(() -> { + while (inventory.addItem(new Item())) {}; + }); + } + + // Wait until all threads have finished + executorService.shutdown(); + executorService.awaitTermination(5, TimeUnit.SECONDS); + + // Check the number of items in the inventory. It should not have exceeded the allowed maximum + final List items = inventory.getItems(); + assertNotNull(items); + assertEquals(INVENTORY_SIZE, items.size()); + + // Capture all stdOut messages ... + final ArgumentCaptor stdOutCaptor = ArgumentCaptor.forClass(String.class); + verify(this.stdOutMock, times(INVENTORY_SIZE)).println(stdOutCaptor.capture()); + + // ... verify if we got all 1000 + final List values = stdOutCaptor.getAllValues(); + assertEquals(INVENTORY_SIZE, values.size()); + + // ... and check if the inventory size is increasing continuously + for (int i = 0; i < values.size(); i++) { + assertNotNull(values.get(i)); + assertTrue(values.get(i).contains("items.size()=" + (i + 1))); + } + + verifyNoMoreInteractions(this.stdOutMock); + } + +} \ No newline at end of file diff --git a/double-dispatch/index.md b/double-dispatch/README.md similarity index 69% rename from double-dispatch/index.md rename to double-dispatch/README.md index 6847b7a41..ae87208a2 100644 --- a/double-dispatch/index.md +++ b/double-dispatch/README.md @@ -4,18 +4,23 @@ title: Double Dispatch folder: double-dispatch permalink: /patterns/double-dispatch/ categories: Other -tags: Java +tags: + - Java + - Difficulty-Intermediate + - Idiom --- -**Intent:** Double Dispatch pattern is a way to create maintainable dynamic +## Intent +Double Dispatch pattern is a way to create maintainable dynamic behavior based on receiver and parameter types. ![alt text](./etc/double-dispatch.png "Double Dispatch") -**Applicability:** Use the Double Dispatch pattern when +## Applicability +Use the Double Dispatch pattern when * the dynamic behavior is not defined only based on receiving object's type but also on the receiving method's parameter type. -**Real world examples:** +## Real world examples * [ObjectOutputStream](https://docs.oracle.com/javase/8/docs/api/java/io/ObjectOutputStream.html) diff --git a/double-dispatch/pom.xml b/double-dispatch/pom.xml index 057d75e8e..4f31b2e7e 100644 --- a/double-dispatch/pom.xml +++ b/double-dispatch/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT double-dispatch @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/App.java b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/App.java index f32c93cb1..40a0485a5 100644 --- a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/App.java +++ b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.doubledispatch; import java.util.ArrayList; @@ -5,46 +27,50 @@ import java.util.List; /** * - * When a message with a parameter is sent to an object, the resultant behaviour is defined by the - * implementation of that method in the receiver. Sometimes the behaviour must also be determined - * by the type of the parameter. + * When a message with a parameter is sent to an object, the resultant behaviour is defined by the implementation of + * that method in the receiver. Sometimes the behaviour must also be determined by the type of the parameter. *

- * One way to implement this would be to create multiple instanceof-checks for the methods parameter. - * However, this creates a maintenance issue. When new types are added we would also need to change - * the method's implementation and add a new instanceof-check. This violates the single responsibility - * principle - a class should have only one reason to change. + * One way to implement this would be to create multiple instanceof-checks for the methods parameter. However, this + * creates a maintenance issue. When new types are added we would also need to change the method's implementation and + * add a new instanceof-check. This violates the single responsibility principle - a class should have only one reason + * to change. *

- * Instead of the instanceof-checks a better way is to make another virtual call on the parameter - * object. This way new functionality can be easily added without the need to modify existing - * implementation (open-closed principle). + * Instead of the instanceof-checks a better way is to make another virtual call on the parameter object. This way new + * functionality can be easily added without the need to modify existing implementation (open-closed principle). *

- * In this example we have hierarchy of objects ({@link GameObject}) that can collide to each other. Each - * object has its own coordinates which are checked against the other objects' coordinates. If - * there is an overlap, then the objects collide utilizing the Double Dispatch pattern. + * In this example we have hierarchy of objects ({@link GameObject}) that can collide to each other. Each object has its + * own coordinates which are checked against the other objects' coordinates. If there is an overlap, then the objects + * collide utilizing the Double Dispatch pattern. * */ public class App { - - /** - * Program entry point - * @param args command line args - */ - public static void main( String[] args ) { - // initialize game objects and print their status - List objects = new ArrayList<>(); - objects.add(new FlamingAsteroid(0, 0, 5, 5)); - objects.add(new SpaceStationMir(1, 1, 2, 2)); - objects.add(new Meteoroid(10, 10, 15, 15)); - objects.add(new SpaceStationIss(12, 12, 14, 14)); - objects.stream().forEach(o -> System.out.println(o)); - System.out.println(""); - - // collision check - objects.stream().forEach(o1 -> objects.stream().forEach(o2 -> { if (o1 != o2 && o1.intersectsWith(o2)) o1.collision(o2); } )); - System.out.println(""); - - // output eventual object statuses - objects.stream().forEach(o -> System.out.println(o)); - System.out.println(""); - } + + /** + * Program entry point + * + * @param args + * command line args + */ + public static void main(String[] args) { + // initialize game objects and print their status + List objects = new ArrayList<>(); + objects.add(new FlamingAsteroid(0, 0, 5, 5)); + objects.add(new SpaceStationMir(1, 1, 2, 2)); + objects.add(new Meteoroid(10, 10, 15, 15)); + objects.add(new SpaceStationIss(12, 12, 14, 14)); + objects.stream().forEach(o -> System.out.println(o)); + System.out.println(""); + + // collision check + objects.stream().forEach(o1 -> objects.stream().forEach(o2 -> { + if (o1 != o2 && o1.intersectsWith(o2)) { + o1.collision(o2); + } + })); + System.out.println(""); + + // output eventual object statuses + objects.stream().forEach(o -> System.out.println(o)); + System.out.println(""); + } } diff --git a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/FlamingAsteroid.java b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/FlamingAsteroid.java index 73b633e5b..6cff89f58 100644 --- a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/FlamingAsteroid.java +++ b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/FlamingAsteroid.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.doubledispatch; /** @@ -7,13 +29,13 @@ package com.iluwatar.doubledispatch; */ public class FlamingAsteroid extends Meteoroid { - public FlamingAsteroid(int left, int top, int right, int bottom) { - super(left, top, right, bottom); - setOnFire(true); - } + public FlamingAsteroid(int left, int top, int right, int bottom) { + super(left, top, right, bottom); + setOnFire(true); + } - @Override - public void collision(GameObject gameObject) { - gameObject.collisionResolve(this); - } + @Override + public void collision(GameObject gameObject) { + gameObject.collisionResolve(this); + } } diff --git a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/GameObject.java b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/GameObject.java index afe05e9b2..fea0cdfd1 100644 --- a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/GameObject.java +++ b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/GameObject.java @@ -1,49 +1,70 @@ +/** + * The MIT License + * Copyright (c) 2014 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.doubledispatch; /** * - * Game objects have coordinates and some - * other status information. + * Game objects have coordinates and some other status information. * */ public abstract class GameObject extends Rectangle { - - private boolean damaged; - private boolean onFire; - - public GameObject(int left, int top, int right, int bottom) { - super(left, top, right, bottom); - } - - @Override - public String toString() { - return String.format("%s at %s damaged=%b onFire=%b", this.getClass().getSimpleName(), - super.toString(), isDamaged(), isOnFire()); - } - - public boolean isOnFire() { - return onFire; - } - - public void setOnFire(boolean onFire) { - this.onFire = onFire; - } - - public boolean isDamaged() { - return damaged; - } - - public void setDamaged(boolean damaged) { - this.damaged = damaged; - } - - public abstract void collision(GameObject gameObject); - - public abstract void collisionResolve(FlamingAsteroid asteroid); - public abstract void collisionResolve(Meteoroid meteoroid); + private boolean damaged; + private boolean onFire; - public abstract void collisionResolve(SpaceStationMir mir); + public GameObject(int left, int top, int right, int bottom) { + super(left, top, right, bottom); + } - public abstract void collisionResolve(SpaceStationIss iss); + @Override + public String toString() { + return String.format("%s at %s damaged=%b onFire=%b", this.getClass().getSimpleName(), + super.toString(), isDamaged(), isOnFire()); + } + + public boolean isOnFire() { + return onFire; + } + + public void setOnFire(boolean onFire) { + this.onFire = onFire; + } + + public boolean isDamaged() { + return damaged; + } + + public void setDamaged(boolean damaged) { + this.damaged = damaged; + } + + public abstract void collision(GameObject gameObject); + + public abstract void collisionResolve(FlamingAsteroid asteroid); + + public abstract void collisionResolve(Meteoroid meteoroid); + + public abstract void collisionResolve(SpaceStationMir mir); + + public abstract void collisionResolve(SpaceStationIss iss); } diff --git a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/Meteoroid.java b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/Meteoroid.java index fb59c49b4..cc68a85ec 100644 --- a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/Meteoroid.java +++ b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/Meteoroid.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.doubledispatch; /** @@ -7,32 +29,36 @@ package com.iluwatar.doubledispatch; */ public class Meteoroid extends GameObject { - public Meteoroid(int left, int top, int right, int bottom) { - super(left, top, right, bottom); - } + public Meteoroid(int left, int top, int right, int bottom) { + super(left, top, right, bottom); + } - @Override - public void collision(GameObject gameObject) { - gameObject.collisionResolve(this); - } + @Override + public void collision(GameObject gameObject) { + gameObject.collisionResolve(this); + } - @Override - public void collisionResolve(FlamingAsteroid asteroid) { - System.out.println(String.format("%s hits %s.", asteroid.getClass().getSimpleName(), this.getClass().getSimpleName())); - } + @Override + public void collisionResolve(FlamingAsteroid asteroid) { + System.out.println(String.format("%s hits %s.", asteroid.getClass().getSimpleName(), this + .getClass().getSimpleName())); + } - @Override - public void collisionResolve(Meteoroid meteoroid) { - System.out.println(String.format("%s hits %s.", meteoroid.getClass().getSimpleName(), this.getClass().getSimpleName())); - } + @Override + public void collisionResolve(Meteoroid meteoroid) { + System.out.println(String.format("%s hits %s.", meteoroid.getClass().getSimpleName(), this + .getClass().getSimpleName())); + } - @Override - public void collisionResolve(SpaceStationMir mir) { - System.out.println(String.format("%s hits %s.", mir.getClass().getSimpleName(), this.getClass().getSimpleName())); - } + @Override + public void collisionResolve(SpaceStationMir mir) { + System.out.println(String.format("%s hits %s.", mir.getClass().getSimpleName(), this.getClass() + .getSimpleName())); + } - @Override - public void collisionResolve(SpaceStationIss iss) { - System.out.println(String.format("%s hits %s.", iss.getClass().getSimpleName(), this.getClass().getSimpleName())); - } + @Override + public void collisionResolve(SpaceStationIss iss) { + System.out.println(String.format("%s hits %s.", iss.getClass().getSimpleName(), this.getClass() + .getSimpleName())); + } } diff --git a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/Rectangle.java b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/Rectangle.java index 12aca0056..496bb8769 100644 --- a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/Rectangle.java +++ b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/Rectangle.java @@ -1,44 +1,72 @@ +/** + * The MIT License + * Copyright (c) 2014 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.doubledispatch; /** * - * Rectangle has coordinates and can be checked for overlap against - * other Rectangles. + * Rectangle has coordinates and can be checked for overlap against other Rectangles. * */ public class Rectangle { - private int left; - private int top; - private int right; - private int bottom; + private int left; + private int top; + private int right; + private int bottom; - public Rectangle(int left, int top, int right, int bottom) { - this.left = left; - this.top = top; - this.right = right; - this.bottom = bottom; - } - - public int getLeft() { - return left; - } - public int getTop() { - return top; - } - public int getRight() { - return right; - } - public int getBottom() { - return bottom; - } - - boolean intersectsWith(Rectangle r) { - return !(r.getLeft() > getRight() || r.getRight() < getLeft() || r.getTop() > getBottom() || r.getBottom() < getTop()); - } - - @Override - public String toString() { - return String.format("[%d,%d,%d,%d]", getLeft(), getTop(), getRight(), getBottom()); - } + /** + * Constructor + */ + public Rectangle(int left, int top, int right, int bottom) { + this.left = left; + this.top = top; + this.right = right; + this.bottom = bottom; + } + + public int getLeft() { + return left; + } + + public int getTop() { + return top; + } + + public int getRight() { + return right; + } + + public int getBottom() { + return bottom; + } + + boolean intersectsWith(Rectangle r) { + return !(r.getLeft() > getRight() || r.getRight() < getLeft() || r.getTop() > getBottom() || r + .getBottom() < getTop()); + } + + @Override + public String toString() { + return String.format("[%d,%d,%d,%d]", getLeft(), getTop(), getRight(), getBottom()); + } } diff --git a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/SpaceStationIss.java b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/SpaceStationIss.java index 50eae3df8..1150fc60b 100644 --- a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/SpaceStationIss.java +++ b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/SpaceStationIss.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.doubledispatch; /** @@ -7,12 +29,12 @@ package com.iluwatar.doubledispatch; */ public class SpaceStationIss extends SpaceStationMir { - public SpaceStationIss(int left, int top, int right, int bottom) { - super(left, top, right, bottom); - } + public SpaceStationIss(int left, int top, int right, int bottom) { + super(left, top, right, bottom); + } - @Override - public void collision(GameObject gameObject) { - gameObject.collisionResolve(this); - } + @Override + public void collision(GameObject gameObject) { + gameObject.collisionResolve(this); + } } diff --git a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/SpaceStationMir.java b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/SpaceStationMir.java index 059b41271..e7a55d0ee 100644 --- a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/SpaceStationMir.java +++ b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/SpaceStationMir.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.doubledispatch; /** @@ -7,45 +29,42 @@ package com.iluwatar.doubledispatch; */ public class SpaceStationMir extends GameObject { - public SpaceStationMir(int left, int top, int right, int bottom) { - super(left, top, right, bottom); - } + public SpaceStationMir(int left, int top, int right, int bottom) { + super(left, top, right, bottom); + } - @Override - public void collision(GameObject gameObject) { - gameObject.collisionResolve(this); - } + @Override + public void collision(GameObject gameObject) { + gameObject.collisionResolve(this); + } - @Override - public void collisionResolve(FlamingAsteroid asteroid) { - System.out.println(String.format("%s hits %s. %s is damaged! %s is set on fire!", - asteroid.getClass().getSimpleName(), this.getClass().getSimpleName(), - this.getClass().getSimpleName(), this.getClass().getSimpleName())); - setDamaged(true); - setOnFire(true); - } + @Override + public void collisionResolve(FlamingAsteroid asteroid) { + System.out.println(String.format("%s hits %s. %s is damaged! %s is set on fire!", asteroid + .getClass().getSimpleName(), this.getClass().getSimpleName(), this.getClass() + .getSimpleName(), this.getClass().getSimpleName())); + setDamaged(true); + setOnFire(true); + } - @Override - public void collisionResolve(Meteoroid meteoroid) { - System.out.println(String.format("%s hits %s. %s is damaged!", - meteoroid.getClass().getSimpleName(), this.getClass().getSimpleName(), - this.getClass().getSimpleName())); - setDamaged(true); - } + @Override + public void collisionResolve(Meteoroid meteoroid) { + System.out.println(String.format("%s hits %s. %s is damaged!", meteoroid.getClass() + .getSimpleName(), this.getClass().getSimpleName(), this.getClass().getSimpleName())); + setDamaged(true); + } - @Override - public void collisionResolve(SpaceStationMir mir) { - System.out.println(String.format("%s hits %s. %s is damaged!", - mir.getClass().getSimpleName(), this.getClass().getSimpleName(), - this.getClass().getSimpleName())); - setDamaged(true); - } + @Override + public void collisionResolve(SpaceStationMir mir) { + System.out.println(String.format("%s hits %s. %s is damaged!", mir.getClass().getSimpleName(), + this.getClass().getSimpleName(), this.getClass().getSimpleName())); + setDamaged(true); + } - @Override - public void collisionResolve(SpaceStationIss iss) { - System.out.println(String.format("%s hits %s. %s is damaged!", - iss.getClass().getSimpleName(), this.getClass().getSimpleName(), - this.getClass().getSimpleName())); - setDamaged(true); - } + @Override + public void collisionResolve(SpaceStationIss iss) { + System.out.println(String.format("%s hits %s. %s is damaged!", iss.getClass().getSimpleName(), + this.getClass().getSimpleName(), this.getClass().getSimpleName())); + setDamaged(true); + } } diff --git a/double-dispatch/src/test/java/com/iluwatar/doubledispatch/AppTest.java b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/AppTest.java index be93ee559..c1a6aa690 100644 --- a/double-dispatch/src/test/java/com/iluwatar/doubledispatch/AppTest.java +++ b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/AppTest.java @@ -1,19 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 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.doubledispatch; import org.junit.Test; -import com.iluwatar.doubledispatch.App; - /** * * Application test * */ public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } + + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/double-dispatch/src/test/java/com/iluwatar/doubledispatch/CollisionTest.java b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/CollisionTest.java new file mode 100644 index 000000000..dbc8fc55e --- /dev/null +++ b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/CollisionTest.java @@ -0,0 +1,158 @@ +/** + * The MIT License + * Copyright (c) 2014 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.doubledispatch; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; +import java.util.Objects; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class CollisionTest { + + /** + * The mocked standard out {@link PrintStream}, required if some of the actions on the tested + * object don't have a direct influence on any other accessible objects, except for writing to + * std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + + /** + * Get the tested object + * + * @return The tested object, should never return 'null' + */ + abstract O getTestedObject(); + + /** + * Collide the tested item with the other given item and verify if the damage and fire state is as + * expected + * + * @param other The other object we have to collide with + * @param otherDamaged Indicates if the other object should be damaged after the collision + * @param otherOnFire Indicates if the other object should be burning after the collision + * @param thisDamaged Indicates if the test object should be damaged after the collision + * @param thisOnFire Indicates if the other object should be burning after the collision + * @param description The expected description of the collision + */ + void testCollision(final GameObject other, final boolean otherDamaged, final boolean otherOnFire, + final boolean thisDamaged, final boolean thisOnFire, final String description) { + + Objects.requireNonNull(other); + Objects.requireNonNull(getTestedObject()); + + final O tested = getTestedObject(); + + tested.collision(other); + + verify(getStdOutMock(), times(1)).println(description); + verifyNoMoreInteractions(getStdOutMock()); + + testOnFire(other, tested, otherOnFire); + testDamaged(other, tested, otherDamaged); + + testOnFire(tested, other, thisOnFire); + testDamaged(tested, other, thisDamaged); + + } + + /** + * Test if the fire state of the target matches the expected state after colliding with the given + * object + * + * @param target The target object + * @param other The other object + * @param expectTargetOnFire The expected state of fire on the target object + */ + private void testOnFire(final GameObject target, final GameObject other, final boolean expectTargetOnFire) { + final String targetName = target.getClass().getSimpleName(); + final String otherName = other.getClass().getSimpleName(); + + final String errorMessage = expectTargetOnFire + ? "Expected [" + targetName + "] to be on fire after colliding with [" + otherName + "] but it was not!" + : "Expected [" + targetName + "] not to be on fire after colliding with [" + otherName + "] but it was!"; + + assertEquals(errorMessage, expectTargetOnFire, target.isOnFire()); + } + + /** + * Test if the damage state of the target matches the expected state after colliding with the + * given object + * + * @param target The target object + * @param other The other object + * @param expectedDamage The expected state of damage on the target object + */ + private void testDamaged(final GameObject target, final GameObject other, final boolean expectedDamage) { + final String targetName = target.getClass().getSimpleName(); + final String otherName = other.getClass().getSimpleName(); + + final String errorMessage = expectedDamage + ? "Expected [" + targetName + "] to be damaged after colliding with [" + otherName + "] but it was not!" + : "Expected [" + targetName + "] not to be damaged after colliding with [" + otherName + "] but it was!"; + + assertEquals(errorMessage, expectedDamage, target.isDamaged()); + } + +} diff --git a/double-dispatch/src/test/java/com/iluwatar/doubledispatch/FlamingAsteroidTest.java b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/FlamingAsteroidTest.java new file mode 100644 index 000000000..31a16f093 --- /dev/null +++ b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/FlamingAsteroidTest.java @@ -0,0 +1,110 @@ +/** + * The MIT License + * Copyright (c) 2014 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.doubledispatch; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Date: 12/10/15 - 11:31 PM + * + * @author Jeroen Meulemeester + */ +public class FlamingAsteroidTest extends CollisionTest { + + @Override + final FlamingAsteroid getTestedObject() { + return new FlamingAsteroid(1, 2, 3, 4); + } + + /** + * Test the constructor parameters + */ + @Test + public void testConstructor() { + final FlamingAsteroid asteroid = new FlamingAsteroid(1, 2, 3, 4); + assertEquals(1, asteroid.getLeft()); + assertEquals(2, asteroid.getTop()); + assertEquals(3, asteroid.getRight()); + assertEquals(4, asteroid.getBottom()); + assertTrue(asteroid.isOnFire()); + assertFalse(asteroid.isDamaged()); + assertEquals("FlamingAsteroid at [1,2,3,4] damaged=false onFire=true", asteroid.toString()); + } + + /** + * Test what happens we collide with an asteroid + */ + @Test + public void testCollideFlamingAsteroid() { + testCollision( + new FlamingAsteroid(1, 2, 3, 4), + false, true, + false, true, + "FlamingAsteroid hits FlamingAsteroid." + ); + } + + /** + * Test what happens we collide with an meteoroid + */ + @Test + public void testCollideMeteoroid() { + testCollision( + new Meteoroid(1, 1, 3, 4), + false, false, + false, true, + "FlamingAsteroid hits Meteoroid." + ); + } + + /** + * Test what happens we collide with ISS + */ + @Test + public void testCollideSpaceStationIss() { + testCollision( + new SpaceStationIss(1, 1, 3, 4), + true, true, + false, true, + "FlamingAsteroid hits SpaceStationIss. SpaceStationIss is damaged! SpaceStationIss is set on fire!" + ); + } + + /** + * Test what happens we collide with MIR + */ + @Test + public void testCollideSpaceStationMir() { + testCollision( + new SpaceStationMir(1, 1, 3, 4), + true, true, + false, true, + "FlamingAsteroid hits SpaceStationMir. SpaceStationMir is damaged! SpaceStationMir is set on fire!" + ); + } + +} \ No newline at end of file diff --git a/double-dispatch/src/test/java/com/iluwatar/doubledispatch/MeteoroidTest.java b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/MeteoroidTest.java new file mode 100644 index 000000000..147fe430a --- /dev/null +++ b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/MeteoroidTest.java @@ -0,0 +1,109 @@ +/** + * The MIT License + * Copyright (c) 2014 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.doubledispatch; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +/** + * Date: 12/10/15 - 11:31 PM + * + * @author Jeroen Meulemeester + */ +public class MeteoroidTest extends CollisionTest { + + @Override + final Meteoroid getTestedObject() { + return new Meteoroid(1, 2, 3, 4); + } + + /** + * Test the constructor parameters + */ + @Test + public void testConstructor() { + final Meteoroid meteoroid = new Meteoroid(1, 2, 3, 4); + assertEquals(1, meteoroid.getLeft()); + assertEquals(2, meteoroid.getTop()); + assertEquals(3, meteoroid.getRight()); + assertEquals(4, meteoroid.getBottom()); + assertFalse(meteoroid.isOnFire()); + assertFalse(meteoroid.isDamaged()); + assertEquals("Meteoroid at [1,2,3,4] damaged=false onFire=false", meteoroid.toString()); + } + + /** + * Test what happens we collide with an asteroid + */ + @Test + public void testCollideFlamingAsteroid() { + testCollision( + new FlamingAsteroid(1, 1, 3, 4), + false, true, + false, false, + "Meteoroid hits FlamingAsteroid." + ); + } + + /** + * Test what happens we collide with an meteoroid + */ + @Test + public void testCollideMeteoroid() { + testCollision( + new Meteoroid(1, 1, 3, 4), + false, false, + false, false, + "Meteoroid hits Meteoroid." + ); + } + + /** + * Test what happens we collide with ISS + */ + @Test + public void testCollideSpaceStationIss() { + testCollision( + new SpaceStationIss(1, 1, 3, 4), + true, false, + false, false, + "Meteoroid hits SpaceStationIss. SpaceStationIss is damaged!" + ); + } + + /** + * Test what happens we collide with MIR + */ + @Test + public void testCollideSpaceStationMir() { + testCollision( + new SpaceStationMir(1, 1, 3, 4), + true, false, + false, false, + "Meteoroid hits SpaceStationMir. SpaceStationMir is damaged!" + ); + } + +} \ No newline at end of file diff --git a/double-dispatch/src/test/java/com/iluwatar/doubledispatch/RectangleTest.java b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/RectangleTest.java index bb8aa954e..b7c0dbd2e 100644 --- a/double-dispatch/src/test/java/com/iluwatar/doubledispatch/RectangleTest.java +++ b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/RectangleTest.java @@ -1,22 +1,69 @@ +/** + * The MIT License + * Copyright (c) 2014 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.doubledispatch; -import org.junit.Assert; import org.junit.Test; -import com.iluwatar.doubledispatch.Rectangle; +import static junit.framework.TestCase.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; /** - * * Unit test for Rectangle - * */ public class RectangleTest { - @Test - public void test() { - Assert.assertTrue(new Rectangle(0,0,1,1).intersectsWith(new Rectangle(0,0,1,1))); - Assert.assertTrue(new Rectangle(0,0,1,1).intersectsWith(new Rectangle(-1,-5,7,8))); - Assert.assertFalse(new Rectangle(0,0,1,1).intersectsWith(new Rectangle(2,2,3,3))); - Assert.assertFalse(new Rectangle(0,0,1,1).intersectsWith(new Rectangle(-2,-2,-1,-1))); - } + /** + * Test if the values passed through the constructor matches the values fetched from the getters + */ + @Test + public void testConstructor() { + final Rectangle rectangle = new Rectangle(1, 2, 3, 4); + assertEquals(1, rectangle.getLeft()); + assertEquals(2, rectangle.getTop()); + assertEquals(3, rectangle.getRight()); + assertEquals(4, rectangle.getBottom()); + } + + /** + * Test if the values passed through the constructor matches the values in the {@link + * #toString()} + */ + @Test + public void testToString() throws Exception { + final Rectangle rectangle = new Rectangle(1, 2, 3, 4); + assertEquals("[1,2,3,4]", rectangle.toString()); + } + + /** + * Test if the {@link Rectangle} class can detect if it intersects with another rectangle. + */ + @Test + public void testIntersection() { + assertTrue(new Rectangle(0, 0, 1, 1).intersectsWith(new Rectangle(0, 0, 1, 1))); + assertTrue(new Rectangle(0, 0, 1, 1).intersectsWith(new Rectangle(-1, -5, 7, 8))); + assertFalse(new Rectangle(0, 0, 1, 1).intersectsWith(new Rectangle(2, 2, 3, 3))); + assertFalse(new Rectangle(0, 0, 1, 1).intersectsWith(new Rectangle(-2, -2, -1, -1))); + } + } diff --git a/double-dispatch/src/test/java/com/iluwatar/doubledispatch/SpaceStationIssTest.java b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/SpaceStationIssTest.java new file mode 100644 index 000000000..d06f84b22 --- /dev/null +++ b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/SpaceStationIssTest.java @@ -0,0 +1,109 @@ +/** + * The MIT License + * Copyright (c) 2014 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.doubledispatch; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +/** + * Date: 12/10/15 - 11:31 PM + * + * @author Jeroen Meulemeester + */ +public class SpaceStationIssTest extends CollisionTest { + + @Override + final SpaceStationIss getTestedObject() { + return new SpaceStationIss(1, 2, 3, 4); + } + + /** + * Test the constructor parameters + */ + @Test + public void testConstructor() { + final SpaceStationIss iss = new SpaceStationIss(1, 2, 3, 4); + assertEquals(1, iss.getLeft()); + assertEquals(2, iss.getTop()); + assertEquals(3, iss.getRight()); + assertEquals(4, iss.getBottom()); + assertFalse(iss.isOnFire()); + assertFalse(iss.isDamaged()); + assertEquals("SpaceStationIss at [1,2,3,4] damaged=false onFire=false", iss.toString()); + } + + /** + * Test what happens we collide with an asteroid + */ + @Test + public void testCollideFlamingAsteroid() { + testCollision( + new FlamingAsteroid(1, 1, 3, 4), + false, true, + false, false, + "SpaceStationIss hits FlamingAsteroid." + ); + } + + /** + * Test what happens we collide with an meteoroid + */ + @Test + public void testCollideMeteoroid() { + testCollision( + new Meteoroid(1, 1, 3, 4), + false, false, + false, false, + "SpaceStationIss hits Meteoroid." + ); + } + + /** + * Test what happens we collide with ISS + */ + @Test + public void testCollideSpaceStationIss() { + testCollision( + new SpaceStationIss(1, 1, 3, 4), + true, false, + false, false, + "SpaceStationIss hits SpaceStationIss. SpaceStationIss is damaged!" + ); + } + + /** + * Test what happens we collide with MIR + */ + @Test + public void testCollideSpaceStationMir() { + testCollision( + new SpaceStationMir(1, 1, 3, 4), + true, false, + false, false, + "SpaceStationIss hits SpaceStationMir. SpaceStationMir is damaged!" + ); + } + +} \ No newline at end of file diff --git a/double-dispatch/src/test/java/com/iluwatar/doubledispatch/SpaceStationMirTest.java b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/SpaceStationMirTest.java new file mode 100644 index 000000000..c107aed8b --- /dev/null +++ b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/SpaceStationMirTest.java @@ -0,0 +1,109 @@ +/** + * The MIT License + * Copyright (c) 2014 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.doubledispatch; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +/** + * Date: 12/10/15 - 11:31 PM + * + * @author Jeroen Meulemeester + */ +public class SpaceStationMirTest extends CollisionTest { + + @Override + final SpaceStationMir getTestedObject() { + return new SpaceStationMir(1, 2, 3, 4); + } + + /** + * Test the constructor parameters + */ + @Test + public void testConstructor() { + final SpaceStationMir mir = new SpaceStationMir(1, 2, 3, 4); + assertEquals(1, mir.getLeft()); + assertEquals(2, mir.getTop()); + assertEquals(3, mir.getRight()); + assertEquals(4, mir.getBottom()); + assertFalse(mir.isOnFire()); + assertFalse(mir.isDamaged()); + assertEquals("SpaceStationMir at [1,2,3,4] damaged=false onFire=false", mir.toString()); + } + + /** + * Test what happens we collide with an asteroid + */ + @Test + public void testCollideFlamingAsteroid() { + testCollision( + new FlamingAsteroid(1, 1, 3, 4), + false, true, + false, false, + "SpaceStationMir hits FlamingAsteroid." + ); + } + + /** + * Test what happens we collide with an meteoroid + */ + @Test + public void testCollideMeteoroid() { + testCollision( + new Meteoroid(1, 1, 3, 4), + false, false, + false, false, + "SpaceStationMir hits Meteoroid." + ); + } + + /** + * Test what happens we collide with ISS + */ + @Test + public void testCollideSpaceStationIss() { + testCollision( + new SpaceStationIss(1, 1, 3, 4), + true, false, + false, false, + "SpaceStationMir hits SpaceStationIss. SpaceStationIss is damaged!" + ); + } + + /** + * Test what happens we collide with MIR + */ + @Test + public void testCollideSpaceStationMir() { + testCollision( + new SpaceStationMir(1, 1, 3, 4), + true, false, + false, false, + "SpaceStationMir hits SpaceStationMir. SpaceStationMir is damaged!" + ); + } + +} \ No newline at end of file diff --git a/event-aggregator/index.md b/event-aggregator/README.md similarity index 84% rename from event-aggregator/index.md rename to event-aggregator/README.md index 1c89c7188..ac07869e7 100644 --- a/event-aggregator/index.md +++ b/event-aggregator/README.md @@ -4,10 +4,14 @@ title: Event Aggregator folder: event-aggregator permalink: /patterns/event-aggregator/ categories: Structural -tags: Java +tags: + - Java + - Difficulty-Beginner + - Reactive --- -**Intent:** A system with lots of objects can lead to complexities when a +## Intent +A system with lots of objects can lead to complexities when a client wants to subscribe to events. The client has to find and register for each object individually, if each object has multiple events then each event requires a separate subscription. An Event Aggregator acts as a single source @@ -16,7 +20,8 @@ allowing clients to register with just the aggregator. ![alt text](./etc/classes.png "Event Aggregator") -**Applicability:** Use the Event Aggregator pattern when +## Applicability +Use the Event Aggregator pattern when * Event Aggregator is a good choice when you have lots of objects that are potential event sources. Rather than have the observer deal with registering @@ -24,6 +29,6 @@ allowing clients to register with just the aggregator. Aggregator. As well as simplifying registration, a Event Aggregator also simplifies the memory management issues in using observers. -**Credits:** +## Credits * [Martin Fowler - Event Aggregator](http://martinfowler.com/eaaDev/EventAggregator.html) diff --git a/event-aggregator/pom.xml b/event-aggregator/pom.xml index 4e7f1b55b..b7de6e01b 100644 --- a/event-aggregator/pom.xml +++ b/event-aggregator/pom.xml @@ -1,10 +1,34 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT event-aggregator @@ -13,5 +37,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/App.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/App.java index b69d8ceaa..879355b65 100644 --- a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/App.java +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.event.aggregator; import java.util.ArrayList; @@ -5,39 +27,40 @@ import java.util.List; /** * - * A system with lots of objects can lead to complexities when a client wants to subscribe - * to events. The client has to find and register for each object individually, if each - * object has multiple events then each event requires a separate subscription. + * A system with lots of objects can lead to complexities when a client wants to subscribe to + * events. The client has to find and register for each object individually, if each object has + * multiple events then each event requires a separate subscription. *

- * An Event Aggregator acts as a single source of events for many objects. It registers - * for all the events of the many objects allowing clients to register with just the aggregator. + * An Event Aggregator acts as a single source of events for many objects. It registers for all the + * events of the many objects allowing clients to register with just the aggregator. *

* In the example {@link LordBaelish}, {@link LordVarys} and {@link Scout} deliver events to - * {@link KingsHand}. {@link KingsHand}, the event aggregator, then delivers the events - * to {@link KingJoffrey}. + * {@link KingsHand}. {@link KingsHand}, the event aggregator, then delivers the events to + * {@link KingJoffrey}. * */ public class App { - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { - - KingJoffrey kingJoffrey = new KingJoffrey(); - KingsHand kingsHand = new KingsHand(kingJoffrey); + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { - List emitters = new ArrayList<>(); - emitters.add(kingsHand); - emitters.add(new LordBaelish(kingsHand)); - emitters.add(new LordVarys(kingsHand)); - emitters.add(new Scout(kingsHand)); - - for (Weekday day: Weekday.values()) { - for (EventEmitter emitter: emitters) { - emitter.timePasses(day); - } - } - } + KingJoffrey kingJoffrey = new KingJoffrey(); + KingsHand kingsHand = new KingsHand(kingJoffrey); + + List emitters = new ArrayList<>(); + emitters.add(kingsHand); + emitters.add(new LordBaelish(kingsHand)); + emitters.add(new LordVarys(kingsHand)); + emitters.add(new Scout(kingsHand)); + + for (Weekday day : Weekday.values()) { + for (EventEmitter emitter : emitters) { + emitter.timePasses(day); + } + } + } } diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Event.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Event.java index 79d496b80..7397530ef 100644 --- a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Event.java +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Event.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.event.aggregator; /** @@ -7,15 +29,16 @@ package com.iluwatar.event.aggregator; */ public enum Event { - STARK_SIGHTED("Stark sighted"), WARSHIPS_APPROACHING("Warships approaching"), TRAITOR_DETECTED("Traitor detected"); - - private String description; - - Event(String description) { - this.description = description; - } - - public String toString() { - return description; - } + STARK_SIGHTED("Stark sighted"), WARSHIPS_APPROACHING("Warships approaching"), TRAITOR_DETECTED( + "Traitor detected"); + + private String description; + + Event(String description) { + this.description = description; + } + + public String toString() { + return description; + } } diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/EventEmitter.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/EventEmitter.java index 1832afeaa..b34235df5 100644 --- a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/EventEmitter.java +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/EventEmitter.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.event.aggregator; import java.util.LinkedList; @@ -10,26 +32,26 @@ import java.util.List; */ public abstract class EventEmitter { - private List observers; + private List observers; - public EventEmitter() { - observers = new LinkedList<>(); - } + public EventEmitter() { + observers = new LinkedList<>(); + } - public EventEmitter(EventObserver obs) { - this(); - registerObserver(obs); - } - - public void registerObserver(EventObserver obs) { - observers.add(obs); - } - - protected void notifyObservers(Event e) { - for (EventObserver obs: observers) { - obs.onEvent(e); - } - } - - public abstract void timePasses(Weekday day); + public EventEmitter(EventObserver obs) { + this(); + registerObserver(obs); + } + + public final void registerObserver(EventObserver obs) { + observers.add(obs); + } + + protected void notifyObservers(Event e) { + for (EventObserver obs : observers) { + obs.onEvent(e); + } + } + + public abstract void timePasses(Weekday day); } diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/EventObserver.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/EventObserver.java index a9785627f..020f23284 100644 --- a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/EventObserver.java +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/EventObserver.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.event.aggregator; /** @@ -6,7 +28,7 @@ package com.iluwatar.event.aggregator; * */ public interface EventObserver { - - void onEvent(Event e); + + void onEvent(Event e); } diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/KingJoffrey.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/KingJoffrey.java index da45f2f1e..fdda59693 100644 --- a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/KingJoffrey.java +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/KingJoffrey.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.event.aggregator; /** @@ -7,8 +29,8 @@ package com.iluwatar.event.aggregator; */ public class KingJoffrey implements EventObserver { - @Override - public void onEvent(Event e) { - System.out.println("Received event from the King's Hand: " + e.toString()); - } + @Override + public void onEvent(Event e) { + System.out.println("Received event from the King's Hand: " + e.toString()); + } } diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/KingsHand.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/KingsHand.java index a3e01334b..32c8d98d6 100644 --- a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/KingsHand.java +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/KingsHand.java @@ -1,28 +1,49 @@ +/** + * The MIT License + * Copyright (c) 2014 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.event.aggregator; /** * - * KingsHand observes events from multiple sources and delivers them - * to listeners. + * KingsHand observes events from multiple sources and delivers them to listeners. * */ public class KingsHand extends EventEmitter implements EventObserver { - public KingsHand() { - super(); - } + public KingsHand() { + super(); + } - public KingsHand(EventObserver obs) { - super(obs); - } - - @Override - public void onEvent(Event e) { - notifyObservers(e); - } + public KingsHand(EventObserver obs) { + super(obs); + } - @Override - public void timePasses(Weekday day) { - // NOP - } + @Override + public void onEvent(Event e) { + notifyObservers(e); + } + + @Override + public void timePasses(Weekday day) { + // NOP + } } diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/LordBaelish.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/LordBaelish.java index b13ec88fc..2fdfeada9 100644 --- a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/LordBaelish.java +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/LordBaelish.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.event.aggregator; /** @@ -6,19 +28,19 @@ package com.iluwatar.event.aggregator; * */ public class LordBaelish extends EventEmitter { - - public LordBaelish() { - super(); - } - public LordBaelish(EventObserver obs) { - super(obs); - } - - @Override - public void timePasses(Weekday day) { - if (day.equals(Weekday.FRIDAY)) { - notifyObservers(Event.STARK_SIGHTED); - } - } + public LordBaelish() { + super(); + } + + public LordBaelish(EventObserver obs) { + super(obs); + } + + @Override + public void timePasses(Weekday day) { + if (day.equals(Weekday.FRIDAY)) { + notifyObservers(Event.STARK_SIGHTED); + } + } } diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/LordVarys.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/LordVarys.java index d1fec048a..b22708d63 100644 --- a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/LordVarys.java +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/LordVarys.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.event.aggregator; /** @@ -6,19 +28,19 @@ package com.iluwatar.event.aggregator; * */ public class LordVarys extends EventEmitter { - - public LordVarys() { - super(); - } - public LordVarys(EventObserver obs) { - super(obs); - } - - @Override - public void timePasses(Weekday day) { - if (day.equals(Weekday.SATURDAY)) { - notifyObservers(Event.TRAITOR_DETECTED); - } - } + public LordVarys() { + super(); + } + + public LordVarys(EventObserver obs) { + super(obs); + } + + @Override + public void timePasses(Weekday day) { + if (day.equals(Weekday.SATURDAY)) { + notifyObservers(Event.TRAITOR_DETECTED); + } + } } diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Scout.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Scout.java index 8ff4e04ab..3b0945367 100644 --- a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Scout.java +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Scout.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.event.aggregator; /** @@ -6,19 +28,19 @@ package com.iluwatar.event.aggregator; * */ public class Scout extends EventEmitter { - - public Scout() { - super(); - } - public Scout(EventObserver obs) { - super(obs); - } - - @Override - public void timePasses(Weekday day) { - if (day.equals(Weekday.TUESDAY)) { - notifyObservers(Event.WARSHIPS_APPROACHING); - } - } + public Scout() { + super(); + } + + public Scout(EventObserver obs) { + super(obs); + } + + @Override + public void timePasses(Weekday day) { + if (day.equals(Weekday.TUESDAY)) { + notifyObservers(Event.WARSHIPS_APPROACHING); + } + } } diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Weekday.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Weekday.java index bafc4f36a..d6f10ce22 100644 --- a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Weekday.java +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/Weekday.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.event.aggregator; /** @@ -6,16 +28,17 @@ package com.iluwatar.event.aggregator; * */ public enum Weekday { - - MONDAY("Monday"), TUESDAY("Tuesday"), WEDNESDAY("Wednesday"), THURSDAY("Thursday"), FRIDAY("Friday"), SATURDAY("Saturday"), SUNDAY("Sunday"); - private String description; - - Weekday(String description) { - this.description = description; - } - - public String toString() { - return description; - } + MONDAY("Monday"), TUESDAY("Tuesday"), WEDNESDAY("Wednesday"), THURSDAY("Thursday"), FRIDAY( + "Friday"), SATURDAY("Saturday"), SUNDAY("Sunday"); + + private String description; + + Weekday(String description) { + this.description = description; + } + + public String toString() { + return description; + } } diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/AppTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/AppTest.java index f8f765880..2330e1f1e 100644 --- a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/AppTest.java +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/AppTest.java @@ -1,7 +1,28 @@ +/** + * The MIT License + * Copyright (c) 2014 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.event.aggregator; -import org.junit.Test; -import com.iluwatar.event.aggregator.App; +import org.junit.Test; /** * @@ -10,9 +31,9 @@ import com.iluwatar.event.aggregator.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/EventEmitterTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/EventEmitterTest.java new file mode 100644 index 000000000..63fc31a1f --- /dev/null +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/EventEmitterTest.java @@ -0,0 +1,155 @@ +/** + * The MIT License + * Copyright (c) 2014 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.event.aggregator; + +import org.junit.Test; + +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Supplier; + +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/12/15 - 10:58 PM + * + * @author Jeroen Meulemeester + */ +public abstract class EventEmitterTest { + + /** + * Factory used to create a new instance of the test object with a default observer + */ + private final Function factoryWithDefaultObserver; + + /** + * Factory used to create a new instance of the test object without passing a default observer + */ + private final Supplier factoryWithoutDefaultObserver; + + /** + * The day of the week an event is expected + */ + private final Weekday specialDay; + + /** + * The expected event, emitted on the special day + */ + private final Event event; + + /** + * Create a new event emitter test, using the given test object factories, special day and event + */ + EventEmitterTest(final Weekday specialDay, final Event event, + final Function factoryWithDefaultObserver, + final Supplier factoryWithoutDefaultObserver) { + + this.specialDay = specialDay; + this.event = event; + this.factoryWithDefaultObserver = Objects.requireNonNull(factoryWithDefaultObserver); + this.factoryWithoutDefaultObserver = Objects.requireNonNull(factoryWithoutDefaultObserver); + } + + /** + * Go over every day of the month, and check if the event is emitted on the given day. This test + * is executed twice, once without a default emitter and once with + */ + @Test + public void testAllDays() { + testAllDaysWithoutDefaultObserver(specialDay, event); + testAllDaysWithDefaultObserver(specialDay, event); + } + + /** + * Pass each week of the day, day by day to the event emitter and verify of the given observers + * received the correct event on the special day. + * + * @param specialDay The special day on which an event is emitted + * @param event The expected event emitted by the test object + * @param emitter The event emitter + * @param observers The registered observer mocks + */ + private void testAllDays(final Weekday specialDay, final Event event, final E emitter, + final EventObserver... observers) { + + for (final Weekday weekday : Weekday.values()) { + // Pass each week of the day, day by day to the event emitter + emitter.timePasses(weekday); + + if (weekday == specialDay) { + // On a special day, every observer should have received the event + for (final EventObserver observer : observers) { + verify(observer, times(1)).onEvent(eq(event)); + } + } else { + // On any other normal day, the observers should have received nothing at all + verifyZeroInteractions(observers); + } + } + + // The observers should not have received any additional events after the week + verifyNoMoreInteractions(observers); + } + + /** + * Go over every day of the month, and check if the event is emitted on the given day. Use an + * event emitter without a default observer + * + * @param specialDay The special day on which an event is emitted + * @param event The expected event emitted by the test object + */ + private void testAllDaysWithoutDefaultObserver(final Weekday specialDay, final Event event) { + final EventObserver observer1 = mock(EventObserver.class); + final EventObserver observer2 = mock(EventObserver.class); + + final E emitter = this.factoryWithoutDefaultObserver.get(); + emitter.registerObserver(observer1); + emitter.registerObserver(observer2); + + testAllDays(specialDay, event, emitter, observer1, observer2); + } + + /** + * Go over every day of the month, and check if the event is emitted on the given day. + * + * @param specialDay The special day on which an event is emitted + * @param event The expected event emitted by the test object + */ + private void testAllDaysWithDefaultObserver(final Weekday specialDay, final Event event) { + final EventObserver defaultObserver = mock(EventObserver.class); + final EventObserver observer1 = mock(EventObserver.class); + final EventObserver observer2 = mock(EventObserver.class); + + final E emitter = this.factoryWithDefaultObserver.apply(defaultObserver); + emitter.registerObserver(observer1); + emitter.registerObserver(observer2); + + testAllDays(specialDay, event, emitter, defaultObserver, observer1, observer2); + } + +} diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/EventTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/EventTest.java new file mode 100644 index 000000000..33d1796e9 --- /dev/null +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/EventTest.java @@ -0,0 +1,49 @@ +/** + * The MIT License + * Copyright (c) 2014 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.event.aggregator; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/12/15 - 2:52 PM + * + * @author Jeroen Meulemeester + */ +public class EventTest { + + /** + * Verify if every event has a non-null, non-empty description + */ + @Test + public void testToString() { + for (final Event event : Event.values()) { + final String toString = event.toString(); + assertNotNull(toString); + assertFalse(toString.trim().isEmpty()); + } + } + +} \ No newline at end of file diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingJoffreyTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingJoffreyTest.java new file mode 100644 index 000000000..3e0028ac4 --- /dev/null +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingJoffreyTest.java @@ -0,0 +1,89 @@ +/** + * The MIT License + * Copyright (c) 2014 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.event.aggregator; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/12/15 - 3:04 PM + * + * @author Jeroen Meulemeester + */ +public class KingJoffreyTest { + + /** + * The mocked standard out {@link PrintStream}, required since {@link KingJoffrey} does nothing + * except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Test if {@link KingJoffrey} tells us what event he received + */ + @Test + public void testOnEvent() { + final KingJoffrey kingJoffrey = new KingJoffrey(); + + for (final Event event : Event.values()) { + verifyZeroInteractions(this.stdOutMock); + kingJoffrey.onEvent(event); + + final String expectedMessage = "Received event from the King's Hand: " + event.toString(); + verify(this.stdOutMock, times(1)).println(expectedMessage); + verifyNoMoreInteractions(this.stdOutMock); + } + + } + +} \ No newline at end of file diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingsHandTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingsHandTest.java new file mode 100644 index 000000000..93116a071 --- /dev/null +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingsHandTest.java @@ -0,0 +1,70 @@ +/** + * The MIT License + * Copyright (c) 2014 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.event.aggregator; + +import org.junit.Test; + +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/12/15 - 10:57 AM + * + * @author Jeroen Meulemeester + */ +public class KingsHandTest extends EventEmitterTest { + + /** + * Create a new test instance, using the correct object factory + */ + public KingsHandTest() { + super(null, null, KingsHand::new, KingsHand::new); + } + + /** + * The {@link KingsHand} is both an {@link EventEmitter} as an {@link EventObserver} so verify if every + * event received is passed up to it's superior, in most cases {@link KingJoffrey} but now just a + * mocked observer. + */ + @Test + public void testPassThrough() throws Exception { + final EventObserver observer = mock(EventObserver.class); + final KingsHand kingsHand = new KingsHand(observer); + + // The kings hand should not pass any events before he received one + verifyZeroInteractions(observer); + + // Verify if each event is passed on to the observer, nothing less, nothing more. + for (final Event event : Event.values()) { + kingsHand.onEvent(event); + verify(observer, times(1)).onEvent(eq(event)); + verifyNoMoreInteractions(observer); + } + + } + +} \ No newline at end of file diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/LordBaelishTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/LordBaelishTest.java new file mode 100644 index 000000000..2432e7b40 --- /dev/null +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/LordBaelishTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 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.event.aggregator; + +/** + * Date: 12/12/15 - 10:57 AM + * + * @author Jeroen Meulemeester + */ +public class LordBaelishTest extends EventEmitterTest { + + /** + * Create a new test instance, using the correct object factory + */ + public LordBaelishTest() { + super(Weekday.FRIDAY, Event.STARK_SIGHTED, LordBaelish::new, LordBaelish::new); + } + +} \ No newline at end of file diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/LordVarysTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/LordVarysTest.java new file mode 100644 index 000000000..d65c3f8e6 --- /dev/null +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/LordVarysTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 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.event.aggregator; + +/** + * Date: 12/12/15 - 10:57 AM + * + * @author Jeroen Meulemeester + */ +public class LordVarysTest extends EventEmitterTest { + + /** + * Create a new test instance, using the correct object factory + */ + public LordVarysTest() { + super(Weekday.SATURDAY, Event.TRAITOR_DETECTED, LordVarys::new, LordVarys::new); + } + +} \ No newline at end of file diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/ScoutTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/ScoutTest.java new file mode 100644 index 000000000..701323485 --- /dev/null +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/ScoutTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 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.event.aggregator; + +/** + * Date: 12/12/15 - 10:57 AM + * + * @author Jeroen Meulemeester + */ +public class ScoutTest extends EventEmitterTest { + + /** + * Create a new test instance, using the correct object factory + */ + public ScoutTest() { + super(Weekday.TUESDAY, Event.WARSHIPS_APPROACHING, Scout::new, Scout::new); + } + +} \ No newline at end of file diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/WeekdayTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/WeekdayTest.java new file mode 100644 index 000000000..1e91aab74 --- /dev/null +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/WeekdayTest.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 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.event.aggregator; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/12/15 - 2:12 PM + * + * @author Jeroen Meulemeester + */ +public class WeekdayTest { + + @Test + public void testToString() throws Exception { + for (final Weekday weekday : Weekday.values()) { + final String toString = weekday.toString(); + assertNotNull(toString); + assertEquals(weekday.name(), toString.toUpperCase()); + } + } + +} \ No newline at end of file diff --git a/event-driven-architecture/README.md b/event-driven-architecture/README.md new file mode 100644 index 000000000..843e4c268 --- /dev/null +++ b/event-driven-architecture/README.md @@ -0,0 +1,37 @@ +--- +layout: pattern +title: Event Driven Architecture +folder: event-driven-architecture +permalink: /patterns/event-driven-architecture/ +categories: Architectural +tags: + - Java + - Difficulty-Intermediate + - Reactive +--- + +## Intent +Send and notify state changes of your objects to other applications using an Event-driven Architecture. + +![alt text](./etc/eda.png "Event Driven Architecture") + +## Applicability +Use an Event-driven architecture when + +* you want to create a loosely coupled system +* you want to build a more responsive system +* you want a system that is easier to extend + +## Real world examples + +* SendGrid, an email API, sends events whenever an email is processed, delivered, opened etc... (https://sendgrid.com/docs/API_Reference/Webhooks/event.html) +* Chargify, a billing API, exposes payment activity through various events (https://docs.chargify.com/api-events) +* Amazon's AWS Lambda, lets you execute code in response to events such as changes to Amazon S3 buckets, updates to an Amazon DynamoDB table, or custom events generated by your applications or devices. (https://aws.amazon.com/lambda) +* MySQL runs triggers based on events such as inserts and update events happening on database tables. + +## Credits + +* [Event-driven architecture - Wikipedia](http://www.computerweekly.com/feature/Write-through-write-around-write-back-Cache-explained) +* [Fundamental Components of an Event-Driven Architecture](http://giocc.com/fundamental-components-of-an-event-driven-architecture.html) +* [Real World Applications/Event Driven Applications](https://wiki.haskell.org/Real_World_Applications/Event_Driven_Applications) +* [Event-driven architecture definition](http://searchsoa.techtarget.com/definition/event-driven-architecture) diff --git a/event-driven-architecture/etc/eda.png b/event-driven-architecture/etc/eda.png new file mode 100644 index 000000000..743726451 Binary files /dev/null and b/event-driven-architecture/etc/eda.png differ diff --git a/event-driven-architecture/etc/eda.ucls b/event-driven-architecture/etc/eda.ucls new file mode 100644 index 000000000..776bedc81 --- /dev/null +++ b/event-driven-architecture/etc/eda.ucls @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/event-driven-architecture/pom.xml b/event-driven-architecture/pom.xml new file mode 100644 index 000000000..99ed39891 --- /dev/null +++ b/event-driven-architecture/pom.xml @@ -0,0 +1,52 @@ + + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + + event-driven-architecture + + + + junit + junit + test + + + + org.mockito + mockito-core + test + + + \ No newline at end of file diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/App.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/App.java new file mode 100644 index 000000000..866b3c9e9 --- /dev/null +++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/App.java @@ -0,0 +1,64 @@ +/** + * The MIT License + * Copyright (c) 2014 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.eda; + +import com.iluwatar.eda.event.UserCreatedEvent; +import com.iluwatar.eda.event.UserUpdatedEvent; +import com.iluwatar.eda.framework.Event; +import com.iluwatar.eda.framework.EventDispatcher; +import com.iluwatar.eda.handler.UserCreatedEventHandler; +import com.iluwatar.eda.handler.UserUpdatedEventHandler; +import com.iluwatar.eda.model.User; + +/** + * An event-driven architecture (EDA) is a framework that orchestrates behavior around the + * production, detection and consumption of events as well as the responses they evoke. An event is + * any identifiable occurrence that has significance for system hardware or software.

The + * example below uses an {@link EventDispatcher} to link/register {@link Event} objects to their + * respective handlers once an {@link Event} is dispatched, it's respective handler is invoked and + * the {@link Event} is handled accordingly. + * + */ +public class App { + + /** + * Once the {@link EventDispatcher} is initialised, handlers related to specific events have to be + * made known to the dispatcher by registering them. In this case the {@link UserCreatedEvent} is + * bound to the UserCreatedEventHandler, whilst the {@link UserUpdatedEvent} is bound to the + * {@link UserUpdatedEventHandler}. The dispatcher can now be called to dispatch specific events. + * When a user is saved, the {@link UserCreatedEvent} can be dispatched. + * On the other hand, when a user is updated, {@link UserUpdatedEvent} can be dispatched. + * + */ + public static void main(String[] args) { + + EventDispatcher dispatcher = new EventDispatcher(); + dispatcher.registerHandler(UserCreatedEvent.class, new UserCreatedEventHandler()); + dispatcher.registerHandler(UserUpdatedEvent.class, new UserUpdatedEventHandler()); + + User user = new User("iluwatar"); + dispatcher.dispatch(new UserCreatedEvent(user)); + dispatcher.dispatch(new UserUpdatedEvent(user)); + } + +} diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/event/AbstractEvent.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/event/AbstractEvent.java new file mode 100644 index 000000000..54a916c7b --- /dev/null +++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/event/AbstractEvent.java @@ -0,0 +1,49 @@ +/** + * The MIT License + * Copyright (c) 2014 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.eda.event; + +import com.iluwatar.eda.framework.EventDispatcher; +import com.iluwatar.eda.framework.Event; + +/** + * The {@link AbstractEvent} class serves as a base class for defining custom events happening with your + * system. In this example we have two types of events defined. + *

    + *
  • {@link UserCreatedEvent} - used when a user is created
  • + *
  • {@link UserUpdatedEvent} - used when a user is updated
  • + *
+ * Events can be distinguished using the {@link #getType() getType} method. + */ +public abstract class AbstractEvent implements Event { + + /** + * Returns the event type as a {@link Class} object + * In this example, this method is used by the {@link EventDispatcher} to + * dispatch events depending on their type. + * + * @return the AbstractEvent type as a {@link Class}. + */ + public Class getType() { + return getClass(); + } +} \ No newline at end of file diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/event/UserCreatedEvent.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/event/UserCreatedEvent.java new file mode 100644 index 000000000..717ed1a9d --- /dev/null +++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/event/UserCreatedEvent.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 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.eda.event; + +import com.iluwatar.eda.model.User; + +/** + * The {@link UserCreatedEvent} should should be dispatched whenever a user has been created. + * This class can be extended to contain details about the user has been created. In this example, + * the entire {@link User} object is passed on as data with the event. + */ +public class UserCreatedEvent extends AbstractEvent { + + private User user; + + public UserCreatedEvent(User user) { + this.user = user; + } + + public User getUser() { + return user; + } +} diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/event/UserUpdatedEvent.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/event/UserUpdatedEvent.java new file mode 100644 index 000000000..9646957dc --- /dev/null +++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/event/UserUpdatedEvent.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 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.eda.event; + +import com.iluwatar.eda.model.User; + +/** + * The {@link UserUpdatedEvent} should should be dispatched whenever a user has been updated. + * This class can be extended to contain details about the user has been updated. In this example, + * the entire {@link User} object is passed on as data with the event. + */ +public class UserUpdatedEvent extends AbstractEvent { + + private User user; + + public UserUpdatedEvent(User user) { + this.user = user; + } + + public User getUser() { + return user; + } +} diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/Event.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/Event.java new file mode 100644 index 000000000..c63d2746f --- /dev/null +++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/Event.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 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.eda.framework; + +/** + * A {@link Event} is an object with a specific type that is associated + * to a specific {@link Handler}. + */ +public interface Event { + + /** + * Returns the message type as a {@link Class} object. In this example the message type is + * used to handle events by their type. + * @return the message type as a {@link Class}. + */ + Class getType(); +} diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/EventDispatcher.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/EventDispatcher.java new file mode 100644 index 000000000..9f8e29315 --- /dev/null +++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/EventDispatcher.java @@ -0,0 +1,64 @@ +/** + * The MIT License + * Copyright (c) 2014 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.eda.framework; + +import java.util.HashMap; +import java.util.Map; + +/** + * Handles the routing of {@link Event} messages to associated handlers. + * A {@link HashMap} is used to store the association between events and their respective handlers. + */ +public class EventDispatcher { + + private Map, Handler> handlers; + + public EventDispatcher() { + handlers = new HashMap<>(); + } + + /** + * Links an {@link Event} to a specific {@link Handler}. + * + * @param eventType The {@link Event} to be registered + * @param handler The {@link Handler} that will be handling the {@link Event} + */ + public void registerHandler(Class eventType, + Handler handler) { + handlers.put(eventType, handler); + } + + /** + * Dispatches an {@link Event} depending on it's type. + * + * @param event The {@link Event} to be dispatched + */ + @SuppressWarnings("unchecked") + public void dispatch(E event) { + Handler handler = (Handler) handlers.get(event.getClass()); + if (handler != null) { + handler.onEvent(event); + } + } + +} \ No newline at end of file diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/Handler.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/Handler.java new file mode 100644 index 000000000..44bdab6dc --- /dev/null +++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/framework/Handler.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 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.eda.framework; + +/** + * This interface can be implemented to handle different types of messages. + * Every handler is responsible for a single of type message + */ +public interface Handler { + + /** + * The onEvent method should implement and handle behavior related to the event. + * This can be as simple as calling another service to handle the event on publishing the event on + * a queue to be consumed by other sub systems. + * @param event the {@link Event} object to be handled. + */ + void onEvent(E event); +} \ No newline at end of file diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/handler/UserCreatedEventHandler.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/handler/UserCreatedEventHandler.java new file mode 100644 index 000000000..3ef4e8255 --- /dev/null +++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/handler/UserCreatedEventHandler.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 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.eda.handler; + +import com.iluwatar.eda.event.UserCreatedEvent; +import com.iluwatar.eda.framework.Handler; + +/** + * Handles the {@link UserCreatedEvent} message. + */ +public class UserCreatedEventHandler implements Handler { + + @Override + public void onEvent(UserCreatedEvent event) { + + System.out.println(String.format( + "User '%s' has been Created!", event.getUser().getUsername())); + } + +} diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/handler/UserUpdatedEventHandler.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/handler/UserUpdatedEventHandler.java new file mode 100644 index 000000000..0311d5781 --- /dev/null +++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/handler/UserUpdatedEventHandler.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 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.eda.handler; + +import com.iluwatar.eda.event.UserUpdatedEvent; +import com.iluwatar.eda.framework.Handler; + +/** + * Handles the {@link UserUpdatedEvent} message. + */ +public class UserUpdatedEventHandler implements Handler { + + @Override + public void onEvent(UserUpdatedEvent event) { + + System.out.println(String.format( + "User '%s' has been Updated!", event.getUser().getUsername())); + } +} diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/model/User.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/model/User.java new file mode 100644 index 000000000..82ef960de --- /dev/null +++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/model/User.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 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.eda.model; + +import com.iluwatar.eda.event.UserCreatedEvent; +import com.iluwatar.eda.event.UserUpdatedEvent; + +/** + * This {@link User} class is a basic pojo used to demonstrate user data sent along with + * the {@link UserCreatedEvent} and {@link UserUpdatedEvent} events. + */ +public class User { + + private String username; + + public User(String username) { + this.username = username; + } + + public String getUsername() { + return username; + } +} diff --git a/event-driven-architecture/src/test/java/com/iluwatar/eda/AppTest.java b/event-driven-architecture/src/test/java/com/iluwatar/eda/AppTest.java new file mode 100644 index 000000000..603d0a61b --- /dev/null +++ b/event-driven-architecture/src/test/java/com/iluwatar/eda/AppTest.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 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.eda; + +import org.junit.Test; + +import java.io.IOException; + +/** + * Tests that Event Driven Architecture example runs without errors. + */ +public class AppTest { + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } +} diff --git a/event-driven-architecture/src/test/java/com/iluwatar/eda/event/UserCreatedEventTest.java b/event-driven-architecture/src/test/java/com/iluwatar/eda/event/UserCreatedEventTest.java new file mode 100644 index 000000000..b9074faf2 --- /dev/null +++ b/event-driven-architecture/src/test/java/com/iluwatar/eda/event/UserCreatedEventTest.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 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.eda.event; + +import com.iluwatar.eda.model.User; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * {@link UserCreatedEventTest} tests and verifies {@link AbstractEvent} behaviour. + */ +public class UserCreatedEventTest { + + /** + * This unit test should correctly return the {@link AbstractEvent} class type when calling the + * {@link AbstractEvent#getType() getType} method. + */ + @Test + public void testGetEventType() { + User user = new User("iluwatar"); + UserCreatedEvent userCreatedEvent = new UserCreatedEvent(user); + assertEquals(UserCreatedEvent.class, userCreatedEvent.getType()); + } +} diff --git a/event-driven-architecture/src/test/java/com/iluwatar/eda/framework/EventDispatcherTest.java b/event-driven-architecture/src/test/java/com/iluwatar/eda/framework/EventDispatcherTest.java new file mode 100644 index 000000000..21956afec --- /dev/null +++ b/event-driven-architecture/src/test/java/com/iluwatar/eda/framework/EventDispatcherTest.java @@ -0,0 +1,70 @@ +/** + * The MIT License + * Copyright (c) 2014 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.eda.framework; + +import com.iluwatar.eda.event.UserCreatedEvent; +import com.iluwatar.eda.event.UserUpdatedEvent; +import com.iluwatar.eda.handler.UserCreatedEventHandler; +import com.iluwatar.eda.handler.UserUpdatedEventHandler; +import com.iluwatar.eda.model.User; + +import org.junit.Test; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +/** + * Event Dispatcher unit tests to assert and verify correct event dispatcher behaviour + */ +public class EventDispatcherTest { + + /** + * This unit test should register events and event handlers correctly with the event dispatcher + * and events should be dispatched accordingly. + */ + @Test + public void testEventDriverPattern() { + + EventDispatcher dispatcher = spy(new EventDispatcher()); + UserCreatedEventHandler userCreatedEventHandler = spy(new UserCreatedEventHandler()); + UserUpdatedEventHandler userUpdatedEventHandler = spy(new UserUpdatedEventHandler()); + dispatcher.registerHandler(UserCreatedEvent.class, userCreatedEventHandler); + dispatcher.registerHandler(UserUpdatedEvent.class, userUpdatedEventHandler); + + User user = new User("iluwatar"); + + UserCreatedEvent userCreatedEvent = new UserCreatedEvent(user); + UserUpdatedEvent userUpdatedEvent = new UserUpdatedEvent(user); + + //fire a userCreatedEvent and verify that userCreatedEventHandler has been invoked. + dispatcher.dispatch(userCreatedEvent); + verify(userCreatedEventHandler).onEvent(userCreatedEvent); + verify(dispatcher).dispatch(userCreatedEvent); + + //fire a userCreatedEvent and verify that userUpdatedEventHandler has been invoked. + dispatcher.dispatch(userUpdatedEvent); + verify(userUpdatedEventHandler).onEvent(userUpdatedEvent); + verify(dispatcher).dispatch(userUpdatedEvent); + } + +} diff --git a/exclude-pmd.properties b/exclude-pmd.properties new file mode 100644 index 000000000..aeda4353d --- /dev/null +++ b/exclude-pmd.properties @@ -0,0 +1,26 @@ +# +# The MIT License +# Copyright (c) 2014 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. +# + +com.iluwatar.servicelayer.common.BaseEntity=UnusedPrivateField +com.iluwatar.doublechecked.locking.App=EmptyStatementNotInLoop,EmptyWhileStmt +com.iluwatar.doublechecked.locking.InventoryTest=EmptyStatementNotInLoop,EmptyWhileStmt diff --git a/execute-around/index.md b/execute-around/README.md similarity index 56% rename from execute-around/index.md rename to execute-around/README.md index 56ece4ac4..f669f18ff 100644 --- a/execute-around/index.md +++ b/execute-around/README.md @@ -4,16 +4,24 @@ title: Execute Around folder: execute-around permalink: /patterns/execute-around/ categories: Other -tags: Java +tags: + - Java + - Difficulty-Beginner + - Idiom --- -**Intent:** Execute Around idiom frees the user from certain actions that +## Intent +Execute Around idiom frees the user from certain actions that should always be executed before and after the business method. A good example of this is resource allocation and deallocation leaving the user to specify only what to do with the resource. ![alt text](./etc/execute-around.png "Execute Around") -**Applicability:** Use the Execute Around idiom when +## Applicability +Use the Execute Around idiom when * you use an API that requires methods to be called in pairs such as open/close or allocate/deallocate. + +## Credits +* [Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions](http://www.amazon.com/Functional-Programming-Java-Harnessing-Expressions/dp/1937785467/ref=sr_1_1) diff --git a/execute-around/pom.xml b/execute-around/pom.xml index c6a7785aa..60bb6d0ab 100644 --- a/execute-around/pom.xml +++ b/execute-around/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT execute-around diff --git a/execute-around/src/main/java/com/iluwatar/execute/around/App.java b/execute-around/src/main/java/com/iluwatar/execute/around/App.java index 910a024de..f8ccebdcf 100644 --- a/execute-around/src/main/java/com/iluwatar/execute/around/App.java +++ b/execute-around/src/main/java/com/iluwatar/execute/around/App.java @@ -1,35 +1,55 @@ +/** + * The MIT License + * Copyright (c) 2014 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.execute.around; import java.io.FileWriter; import java.io.IOException; /** - * The Execute Around idiom specifies some code to be executed before and after - * a method. Typically the idiom is used when the API has methods to be executed in - * pairs, such as resource allocation/deallocation or lock acquisition/release. + * The Execute Around idiom specifies some code to be executed before and after a method. Typically + * the idiom is used when the API has methods to be executed in pairs, such as resource + * allocation/deallocation or lock acquisition/release. *

- * In this example, we have {@link SimpleFileWriter} class that opens and closes the file - * for the user. The user specifies only what to do with the file by providing the - * {@link FileWriterAction} implementation. + * In this example, we have {@link SimpleFileWriter} class that opens and closes the file for the + * user. The user specifies only what to do with the file by providing the {@link FileWriterAction} + * implementation. * */ public class App { - - /** - * Program entry point - * @param args command line args - * @throws IOException - */ - public static void main( String[] args ) throws IOException { - new SimpleFileWriter("testfile.txt", new FileWriterAction() { + /** + * Program entry point + */ + public static void main(String[] args) throws IOException { - @Override - public void writeFile(FileWriter writer) throws IOException { - writer.write("Hello"); - writer.append(" "); - writer.append("there!"); - } - }); - } + new SimpleFileWriter("testfile.txt", new FileWriterAction() { + + @Override + public void writeFile(FileWriter writer) throws IOException { + writer.write("Hello"); + writer.append(" "); + writer.append("there!"); + } + }); + } } diff --git a/execute-around/src/main/java/com/iluwatar/execute/around/FileWriterAction.java b/execute-around/src/main/java/com/iluwatar/execute/around/FileWriterAction.java index 904600ec6..159786134 100644 --- a/execute-around/src/main/java/com/iluwatar/execute/around/FileWriterAction.java +++ b/execute-around/src/main/java/com/iluwatar/execute/around/FileWriterAction.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.execute.around; import java.io.FileWriter; @@ -10,6 +32,6 @@ import java.io.IOException; */ public interface FileWriterAction { - void writeFile(FileWriter writer) throws IOException; - + void writeFile(FileWriter writer) throws IOException; + } diff --git a/execute-around/src/main/java/com/iluwatar/execute/around/SimpleFileWriter.java b/execute-around/src/main/java/com/iluwatar/execute/around/SimpleFileWriter.java index a7ee11d15..111bad73e 100644 --- a/execute-around/src/main/java/com/iluwatar/execute/around/SimpleFileWriter.java +++ b/execute-around/src/main/java/com/iluwatar/execute/around/SimpleFileWriter.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.execute.around; import java.io.FileWriter; @@ -5,19 +27,21 @@ import java.io.IOException; /** * - * SimpleFileWriter handles opening and closing file for the user. The user - * only has to specify what to do with the file resource through {@link FileWriterAction} - * parameter. + * SimpleFileWriter handles opening and closing file for the user. The user only has to specify what + * to do with the file resource through {@link FileWriterAction} parameter. * */ public class SimpleFileWriter { - public SimpleFileWriter(String filename, FileWriterAction action) throws IOException { - FileWriter writer = new FileWriter(filename); - try { - action.writeFile(writer); - } finally { - writer.close(); - } - } + /** + * Constructor + */ + public SimpleFileWriter(String filename, FileWriterAction action) throws IOException { + FileWriter writer = new FileWriter(filename); + try { + action.writeFile(writer); + } finally { + writer.close(); + } + } } diff --git a/execute-around/src/test/java/com/iluwatar/execute/around/AppTest.java b/execute-around/src/test/java/com/iluwatar/execute/around/AppTest.java index 1e8a45947..b74f53a25 100644 --- a/execute-around/src/test/java/com/iluwatar/execute/around/AppTest.java +++ b/execute-around/src/test/java/com/iluwatar/execute/around/AppTest.java @@ -1,13 +1,33 @@ +/** + * The MIT License + * Copyright (c) 2014 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.execute.around; -import java.io.File; -import java.io.IOException; - import org.junit.After; import org.junit.Before; import org.junit.Test; -import com.iluwatar.execute.around.App; +import java.io.File; +import java.io.IOException; /** * @@ -15,17 +35,17 @@ import com.iluwatar.execute.around.App; * */ public class AppTest { - - @Test - public void test() throws IOException { - String[] args = {}; - App.main(args); - } - - @Before - @After - public void cleanup() { - File file = new File("testfile.txt"); - file.delete(); - } + + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } + + @Before + @After + public void cleanup() { + File file = new File("testfile.txt"); + file.delete(); + } } diff --git a/execute-around/src/test/java/com/iluwatar/execute/around/SimpleFileWriterTest.java b/execute-around/src/test/java/com/iluwatar/execute/around/SimpleFileWriterTest.java new file mode 100644 index 000000000..abad14935 --- /dev/null +++ b/execute-around/src/test/java/com/iluwatar/execute/around/SimpleFileWriterTest.java @@ -0,0 +1,96 @@ +/** + * The MIT License + * Copyright (c) 2014 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.execute.around; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Date: 12/12/15 - 3:21 PM + * + * @author Jeroen Meulemeester + */ +public class SimpleFileWriterTest { + + /** + * Create a temporary folder, used to generate files in during this test + */ + @Rule + public final TemporaryFolder testFolder = new TemporaryFolder(); + + /** + * Verify if the given writer is not 'null' + */ + @Test + public void testWriterNotNull() throws Exception { + final File temporaryFile = this.testFolder.newFile(); + new SimpleFileWriter(temporaryFile.getPath(), Assert::assertNotNull); + } + + /** + * Test if the {@link SimpleFileWriter} creates a file if it doesn't exist + */ + @Test + public void testNonExistentFile() throws Exception { + final File nonExistingFile = new File(this.testFolder.getRoot(), "non-existing-file"); + assertFalse(nonExistingFile.exists()); + + new SimpleFileWriter(nonExistingFile.getPath(), Assert::assertNotNull); + assertTrue(nonExistingFile.exists()); + } + + /** + * Test if the data written to the file writer actually gets in the file + */ + @Test + public void testActualWrite() throws Exception { + final String testMessage = "Test message"; + + final File temporaryFile = this.testFolder.newFile(); + assertTrue(temporaryFile.exists()); + + new SimpleFileWriter(temporaryFile.getPath(), writer -> writer.write(testMessage)); + assertTrue(Files.lines(temporaryFile.toPath()).allMatch(testMessage::equals)); + } + + /** + * Verify if an {@link IOException} during the write ripples through + */ + @Test(expected = IOException.class) + public void testIoException() throws Exception { + final File temporaryFile = this.testFolder.newFile(); + new SimpleFileWriter(temporaryFile.getPath(), writer -> { + throw new IOException(""); + }); + } + +} diff --git a/facade/index.md b/facade/README.md similarity index 89% rename from facade/index.md rename to facade/README.md index 59ff888b2..c416552c7 100644 --- a/facade/index.md +++ b/facade/README.md @@ -7,19 +7,22 @@ categories: Structural tags: - Java - Gang Of Four + - Difficulty-Beginner --- -**Intent:** Provide a unified interface to a set of interfaces in a subsystem. +## Intent +Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use. ![alt text](./etc/facade_1.png "Facade") -**Applicability:** Use the Facade pattern when +## Applicability +Use the Facade pattern when * you want to provide a simple interface to a complex subsystem. Subsystems often get more complex as they evolve. Most patterns, when applied, result in more and smaller classes. This makes the subsystem more reusable and easier to customize, but it also becomes harder to use for clients that don't need to customize it. A facade can provide a simple default view of the subsystem that is good enough for most clients. Only clients needing more customizability will need to look beyond the facade. * there are many dependencies between clients and the implementation classes of an abstraction. Introduce a facade to decouple the subsystem from clients and other subsystems, thereby promoting subsystem independence and portability. * you want to layer your subsystems. Use a facade to define an entry point to each subsystem level. If subsystems are dependent, the you can simplify the dependencies between them by making them communicate with each other solely through their facades -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/facade/pom.xml b/facade/pom.xml index cea5bf0c3..e0e0f4a4d 100644 --- a/facade/pom.xml +++ b/facade/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT facade @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/facade/src/main/java/com/iluwatar/facade/App.java b/facade/src/main/java/com/iluwatar/facade/App.java index 37cda0281..242bfc9c4 100644 --- a/facade/src/main/java/com/iluwatar/facade/App.java +++ b/facade/src/main/java/com/iluwatar/facade/App.java @@ -1,28 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014 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.facade; /** * - * The Facade design pattern is often used when a system is very complex or difficult - * to understand because the system has a large number of interdependent classes or - * its source code is unavailable. This pattern hides the complexities of the larger - * system and provides a simpler interface to the client. It typically involves a single - * wrapper class which contains a set of members required by client. These members access - * the system on behalf of the facade client and hide the implementation details. + * The Facade design pattern is often used when a system is very complex or difficult to understand + * because the system has a large number of interdependent classes or its source code is + * unavailable. This pattern hides the complexities of the larger system and provides a simpler + * interface to the client. It typically involves a single wrapper class which contains a set of + * members required by client. These members access the system on behalf of the facade client and + * hide the implementation details. *

- * In this example the Facade is ({@link DwarvenGoldmineFacade}) and it provides a simpler - * interface to the goldmine subsystem. + * In this example the Facade is ({@link DwarvenGoldmineFacade}) and it provides a simpler interface + * to the goldmine subsystem. * */ public class App { - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { - DwarvenGoldmineFacade facade = new DwarvenGoldmineFacade(); - facade.startNewDay(); - facade.digOutGold(); - facade.endDay(); - } + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + DwarvenGoldmineFacade facade = new DwarvenGoldmineFacade(); + facade.startNewDay(); + facade.digOutGold(); + facade.endDay(); + } } diff --git a/facade/src/main/java/com/iluwatar/facade/DwarvenCartOperator.java b/facade/src/main/java/com/iluwatar/facade/DwarvenCartOperator.java index 7c33fd267..bdc839f57 100644 --- a/facade/src/main/java/com/iluwatar/facade/DwarvenCartOperator.java +++ b/facade/src/main/java/com/iluwatar/facade/DwarvenCartOperator.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.facade; /** @@ -7,14 +29,13 @@ package com.iluwatar.facade; */ public class DwarvenCartOperator extends DwarvenMineWorker { - @Override - public void work() { - System.out.println(name() + " moves gold chunks out of the mine."); - } - - @Override - public String name() { - return "Dwarf cart operator"; - } + @Override + public void work() { + System.out.println(name() + " moves gold chunks out of the mine."); + } + @Override + public String name() { + return "Dwarf cart operator"; + } } diff --git a/facade/src/main/java/com/iluwatar/facade/DwarvenGoldDigger.java b/facade/src/main/java/com/iluwatar/facade/DwarvenGoldDigger.java index b503889b7..54fa821f4 100644 --- a/facade/src/main/java/com/iluwatar/facade/DwarvenGoldDigger.java +++ b/facade/src/main/java/com/iluwatar/facade/DwarvenGoldDigger.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.facade; /** @@ -7,14 +29,13 @@ package com.iluwatar.facade; */ public class DwarvenGoldDigger extends DwarvenMineWorker { - @Override - public void work() { - System.out.println(name() + " digs for gold."); - } - - @Override - public String name() { - return "Dwarf gold digger"; - } + @Override + public void work() { + System.out.println(name() + " digs for gold."); + } + @Override + public String name() { + return "Dwarf gold digger"; + } } diff --git a/facade/src/main/java/com/iluwatar/facade/DwarvenGoldmineFacade.java b/facade/src/main/java/com/iluwatar/facade/DwarvenGoldmineFacade.java index 83a989fa7..4f6e3be4c 100644 --- a/facade/src/main/java/com/iluwatar/facade/DwarvenGoldmineFacade.java +++ b/facade/src/main/java/com/iluwatar/facade/DwarvenGoldmineFacade.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.facade; import java.util.ArrayList; @@ -6,40 +28,42 @@ import java.util.List; /** * - * DwarvenGoldmineFacade provides a single interface - * through which users can operate the subsystems. + * DwarvenGoldmineFacade provides a single interface through which users can operate the subsystems. * - * This makes the goldmine easier to operate and - * cuts the dependencies from the goldmine user to - * the subsystems. + * This makes the goldmine easier to operate and cuts the dependencies from the goldmine user to the + * subsystems. * */ public class DwarvenGoldmineFacade { - private final List workers; + private final List workers; - public DwarvenGoldmineFacade() { - workers = new ArrayList<>(); - workers.add(new DwarvenGoldDigger()); - workers.add(new DwarvenCartOperator()); - workers.add(new DwarvenTunnelDigger()); - } + /** + * Constructor + */ + public DwarvenGoldmineFacade() { + workers = new ArrayList<>(); + workers.add(new DwarvenGoldDigger()); + workers.add(new DwarvenCartOperator()); + workers.add(new DwarvenTunnelDigger()); + } - public void startNewDay() { - makeActions(workers, DwarvenMineWorker.Action.WAKE_UP, DwarvenMineWorker.Action.GO_TO_MINE); - } + public void startNewDay() { + makeActions(workers, DwarvenMineWorker.Action.WAKE_UP, DwarvenMineWorker.Action.GO_TO_MINE); + } - public void digOutGold() { - makeActions(workers, DwarvenMineWorker.Action.WORK); - } + public void digOutGold() { + makeActions(workers, DwarvenMineWorker.Action.WORK); + } - public void endDay() { - makeActions(workers, DwarvenMineWorker.Action.GO_HOME, DwarvenMineWorker.Action.GO_TO_SLEEP); - } + public void endDay() { + makeActions(workers, DwarvenMineWorker.Action.GO_HOME, DwarvenMineWorker.Action.GO_TO_SLEEP); + } - private void makeActions(Collection workers, DwarvenMineWorker.Action... actions) { - for (DwarvenMineWorker worker : workers) { - worker.action(actions); - } + private static void makeActions(Collection workers, + DwarvenMineWorker.Action... actions) { + for (DwarvenMineWorker worker : workers) { + worker.action(actions); } + } } diff --git a/facade/src/main/java/com/iluwatar/facade/DwarvenMineWorker.java b/facade/src/main/java/com/iluwatar/facade/DwarvenMineWorker.java index 3dd51f907..f27054c53 100644 --- a/facade/src/main/java/com/iluwatar/facade/DwarvenMineWorker.java +++ b/facade/src/main/java/com/iluwatar/facade/DwarvenMineWorker.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.facade; /** @@ -7,56 +29,59 @@ package com.iluwatar.facade; */ public abstract class DwarvenMineWorker { - public void goToSleep() { - System.out.println(name() + " goes to sleep."); - } + public void goToSleep() { + System.out.println(name() + " goes to sleep."); + } - public void wakeUp() { - System.out.println(name() + " wakes up."); - } + public void wakeUp() { + System.out.println(name() + " wakes up."); + } - public void goHome() { - System.out.println(name() + " goes home."); - } + public void goHome() { + System.out.println(name() + " goes home."); + } - public void goToMine() { - System.out.println(name() + " goes to the mine."); - } + public void goToMine() { + System.out.println(name() + " goes to the mine."); + } - private void action(Action action) { - switch (action) { - case GO_TO_SLEEP: - goToSleep(); - break; - case WAKE_UP: - wakeUp(); - break; - case GO_HOME: - goHome(); - break; - case GO_TO_MINE: - goToMine(); - break; - case WORK: - work(); - break; - default: - System.out.println("Undefined action"); - break; - } + private void action(Action action) { + switch (action) { + case GO_TO_SLEEP: + goToSleep(); + break; + case WAKE_UP: + wakeUp(); + break; + case GO_HOME: + goHome(); + break; + case GO_TO_MINE: + goToMine(); + break; + case WORK: + work(); + break; + default: + System.out.println("Undefined action"); + break; } + } - public void action(Action... actions) { - for (Action action : actions) { - action(action); - } + /** + * Perform actions + */ + public void action(Action... actions) { + for (Action action : actions) { + action(action); } + } - public abstract void work(); + public abstract void work(); - public abstract String name(); + public abstract String name(); - static enum Action { - GO_TO_SLEEP, WAKE_UP, GO_HOME, GO_TO_MINE, WORK - } + static enum Action { + GO_TO_SLEEP, WAKE_UP, GO_HOME, GO_TO_MINE, WORK + } } diff --git a/facade/src/main/java/com/iluwatar/facade/DwarvenTunnelDigger.java b/facade/src/main/java/com/iluwatar/facade/DwarvenTunnelDigger.java index 05fe02fb6..74d8b89cc 100644 --- a/facade/src/main/java/com/iluwatar/facade/DwarvenTunnelDigger.java +++ b/facade/src/main/java/com/iluwatar/facade/DwarvenTunnelDigger.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.facade; /** @@ -7,14 +29,13 @@ package com.iluwatar.facade; */ public class DwarvenTunnelDigger extends DwarvenMineWorker { - @Override - public void work() { - System.out.println(name() + " creates another promising tunnel."); - } - - @Override - public String name() { - return "Dwarven tunnel digger"; - } + @Override + public void work() { + System.out.println(name() + " creates another promising tunnel."); + } + @Override + public String name() { + return "Dwarven tunnel digger"; + } } diff --git a/facade/src/test/java/com/iluwatar/facade/AppTest.java b/facade/src/test/java/com/iluwatar/facade/AppTest.java index bfca5473a..115fcc405 100644 --- a/facade/src/test/java/com/iluwatar/facade/AppTest.java +++ b/facade/src/test/java/com/iluwatar/facade/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.facade; import org.junit.Test; -import com.iluwatar.facade.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.facade.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/facade/src/test/java/com/iluwatar/facade/DwarvenGoldmineFacadeTest.java b/facade/src/test/java/com/iluwatar/facade/DwarvenGoldmineFacadeTest.java new file mode 100644 index 000000000..4a3b218e2 --- /dev/null +++ b/facade/src/test/java/com/iluwatar/facade/DwarvenGoldmineFacadeTest.java @@ -0,0 +1,125 @@ +/** + * The MIT License + * Copyright (c) 2014 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.facade; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.PrintStream; + +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.internal.verification.VerificationModeFactory.times; + +/** + * Date: 12/9/15 - 9:40 PM + * + * @author Jeroen Meulemeester + */ +public class DwarvenGoldmineFacadeTest { + + /** + * The mocked standard out {@link PrintStream}, required since the actions on the gold mine facade + * don't have any influence on any other accessible objects, except for writing to std-out using + * {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Test a complete day cycle in the gold mine by executing all three different steps: {@link + * DwarvenGoldmineFacade#startNewDay()}, {@link DwarvenGoldmineFacade#digOutGold()} and {@link + * DwarvenGoldmineFacade#endDay()}. + * + * See if the workers are doing what's expected from them on each step. + */ + @Test + public void testFullWorkDay() { + final DwarvenGoldmineFacade goldMine = new DwarvenGoldmineFacade(); + goldMine.startNewDay(); + + // On the start of a day, all workers should wake up ... + verify(this.stdOutMock, times(1)).println(eq("Dwarf gold digger wakes up.")); + verify(this.stdOutMock, times(1)).println(eq("Dwarf cart operator wakes up.")); + verify(this.stdOutMock, times(1)).println(eq("Dwarven tunnel digger wakes up.")); + + // ... and go to the mine + verify(this.stdOutMock, times(1)).println(eq("Dwarf gold digger goes to the mine.")); + verify(this.stdOutMock, times(1)).println(eq("Dwarf cart operator goes to the mine.")); + verify(this.stdOutMock, times(1)).println(eq("Dwarven tunnel digger goes to the mine.")); + + // No other actions were invoked, so the workers shouldn't have done (printed) anything else + verifyNoMoreInteractions(this.stdOutMock); + + // Now do some actual work, start digging gold! + goldMine.digOutGold(); + + // Since we gave the dig command, every worker should be doing it's job ... + verify(this.stdOutMock, times(1)).println(eq("Dwarf gold digger digs for gold.")); + verify(this.stdOutMock, times(1)).println(eq("Dwarf cart operator moves gold chunks out of the mine.")); + verify(this.stdOutMock, times(1)).println(eq("Dwarven tunnel digger creates another promising tunnel.")); + + // Again, they shouldn't be doing anything else. + verifyNoMoreInteractions(this.stdOutMock); + + // Enough gold, lets end the day. + goldMine.endDay(); + + // Check if the workers go home ... + verify(this.stdOutMock, times(1)).println(eq("Dwarf gold digger goes home.")); + verify(this.stdOutMock, times(1)).println(eq("Dwarf cart operator goes home.")); + verify(this.stdOutMock, times(1)).println(eq("Dwarven tunnel digger goes home.")); + + // ... and go to sleep. We need well rested workers the next day :) + verify(this.stdOutMock, times(1)).println(eq("Dwarf gold digger goes to sleep.")); + verify(this.stdOutMock, times(1)).println(eq("Dwarf cart operator goes to sleep.")); + verify(this.stdOutMock, times(1)).println(eq("Dwarven tunnel digger goes to sleep.")); + + // Every worker should be sleeping now, no other actions allowed + verifyNoMoreInteractions(this.stdOutMock); + } + +} diff --git a/factory-kit/README.md b/factory-kit/README.md new file mode 100644 index 000000000..c25701047 --- /dev/null +++ b/factory-kit/README.md @@ -0,0 +1,28 @@ +--- +layout: pattern +title: Factory Kit +folder: factory-kit +permalink: /patterns/factory-kit/ +categories: Creational +tags: + - Java + - Difficulty-Beginner + - Functional +--- + +## Intent +Define a factory of immutable content with separated builder and factory interfaces. + +![alt text](./etc/factory-kit.png "Factory Kit") + +## Applicability +Use the Factory Kit pattern when + +* a class can't anticipate the class of objects it must create +* you just want a new instance of a custom builder instead of the global one +* you explicitly want to define types of objects, that factory can build +* you want a separated builder and creator interface + +## Credits + +* [Design Pattern Reloaded by Remi Forax: ](https://www.youtube.com/watch?v=-k2X7guaArU) diff --git a/factory-kit/etc/factory-kit.png b/factory-kit/etc/factory-kit.png new file mode 100644 index 000000000..7093193cb Binary files /dev/null and b/factory-kit/etc/factory-kit.png differ diff --git a/factory-kit/etc/factory-kit.ucls b/factory-kit/etc/factory-kit.ucls new file mode 100644 index 000000000..403fb7e27 --- /dev/null +++ b/factory-kit/etc/factory-kit.ucls @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/factory-kit/pom.xml b/factory-kit/pom.xml new file mode 100644 index 000000000..6c936de5c --- /dev/null +++ b/factory-kit/pom.xml @@ -0,0 +1,48 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + factory-kit + + + junit + junit + test + + + org.mockito + mockito-core + test + + + \ No newline at end of file diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/App.java b/factory-kit/src/main/java/com/iluwatar/factorykit/App.java new file mode 100644 index 000000000..f27bee170 --- /dev/null +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/App.java @@ -0,0 +1,54 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factorykit; + +/** + * Factory-kit is a creational pattern which defines a factory of immutable content + * with separated builder and factory interfaces to deal with the problem of + * creating one of the objects specified directly in the factory-kit instance. + * + *

+ * In the given example {@link WeaponFactory} represents the factory-kit, that contains + * four {@link Builder}s for creating new objects of + * the classes implementing {@link Weapon} interface. + *
Each of them can be called with {@link WeaponFactory#create(WeaponType)} method, with + * an input representing an instance of {@link WeaponType} that needs to + * be mapped explicitly with desired class type in the factory instance. + */ +public class App { + /** + * Program entry point. + * + * @param args @param args command line args + */ + public static void main(String[] args) { + WeaponFactory factory = WeaponFactory.factory(builder -> { + builder.add(WeaponType.SWORD, Sword::new); + builder.add(WeaponType.AXE, Axe::new); + builder.add(WeaponType.SPEAR, Spear::new); + builder.add(WeaponType.BOW, Bow::new); + }); + Weapon axe = factory.create(WeaponType.AXE); + System.out.println(axe); + } +} diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/Axe.java b/factory-kit/src/main/java/com/iluwatar/factorykit/Axe.java new file mode 100644 index 000000000..826a1f9ec --- /dev/null +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/Axe.java @@ -0,0 +1,30 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factorykit; + +public class Axe implements Weapon { + @Override + public String toString() { + return "Axe"; + } +} diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/Bow.java b/factory-kit/src/main/java/com/iluwatar/factorykit/Bow.java new file mode 100644 index 000000000..5aa952c3d --- /dev/null +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/Bow.java @@ -0,0 +1,30 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factorykit; + +public class Bow implements Weapon { + @Override + public String toString() { + return "Bow"; + } +} diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/Builder.java b/factory-kit/src/main/java/com/iluwatar/factorykit/Builder.java new file mode 100644 index 000000000..1049c7b6f --- /dev/null +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/Builder.java @@ -0,0 +1,32 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factorykit; + +import java.util.function.Supplier; + +/** + * Functional interface that allows adding builder with name to the factory. + */ +public interface Builder { + void add(WeaponType name, Supplier supplier); +} diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/Spear.java b/factory-kit/src/main/java/com/iluwatar/factorykit/Spear.java new file mode 100644 index 000000000..c32811e8c --- /dev/null +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/Spear.java @@ -0,0 +1,30 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factorykit; + +public class Spear implements Weapon { + @Override + public String toString() { + return "Spear"; + } +} diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/Sword.java b/factory-kit/src/main/java/com/iluwatar/factorykit/Sword.java new file mode 100644 index 000000000..208cd6bbb --- /dev/null +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/Sword.java @@ -0,0 +1,30 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factorykit; + +public class Sword implements Weapon { + @Override + public String toString() { + return "Sword"; + } +} diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/Weapon.java b/factory-kit/src/main/java/com/iluwatar/factorykit/Weapon.java new file mode 100644 index 000000000..3d668e352 --- /dev/null +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/Weapon.java @@ -0,0 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factorykit; + +/** + * Interface representing weapon. + */ +public interface Weapon { +} diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponFactory.java b/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponFactory.java new file mode 100644 index 000000000..80a6fd9d3 --- /dev/null +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponFactory.java @@ -0,0 +1,55 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factorykit; + +import java.util.HashMap; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * Functional interface, an example of the factory-kit design pattern. + *
Instance created locally gives an opportunity to strictly define + * which objects types the instance of a factory will be able to create. + *
Factory is a placeholder for {@link Builder}s + * with {@link WeaponFactory#create(WeaponType)} method to initialize new objects. + */ +public interface WeaponFactory { + + /** + * Creates an instance of the given type. + * @param name representing enum of an object type to be created. + * @return new instance of a requested class implementing {@link Weapon} interface. + */ + Weapon create(WeaponType name); + + /** + * Creates factory - placeholder for specified {@link Builder}s. + * @param consumer for the new builder to the factory. + * @return factory with specified {@link Builder}s + */ + static WeaponFactory factory(Consumer consumer) { + HashMap> map = new HashMap<>(); + consumer.accept(map::put); + return name -> map.get(name).get(); + } +} diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponType.java b/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponType.java new file mode 100644 index 000000000..283f252de --- /dev/null +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/WeaponType.java @@ -0,0 +1,30 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factorykit; + +/** + * Enumerates {@link Weapon} types + */ +public enum WeaponType { + SWORD, AXE, BOW, SPEAR +} diff --git a/factory-kit/src/test/java/com/iluwatar/factorykit/app/AppTest.java b/factory-kit/src/test/java/com/iluwatar/factorykit/app/AppTest.java new file mode 100644 index 000000000..036326d97 --- /dev/null +++ b/factory-kit/src/test/java/com/iluwatar/factorykit/app/AppTest.java @@ -0,0 +1,36 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factorykit.app; + +import com.iluwatar.factorykit.App; +import org.junit.Test; + +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} + diff --git a/factory-kit/src/test/java/com/iluwatar/factorykit/factorykit/FactoryKitTest.java b/factory-kit/src/test/java/com/iluwatar/factorykit/factorykit/FactoryKitTest.java new file mode 100644 index 000000000..c57bee3e3 --- /dev/null +++ b/factory-kit/src/test/java/com/iluwatar/factorykit/factorykit/FactoryKitTest.java @@ -0,0 +1,81 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factorykit.factorykit; + +import com.iluwatar.factorykit.*; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class FactoryKitTest { + + private WeaponFactory factory; + + @Before + public void init() { + factory = WeaponFactory.factory(builder -> { + builder.add(WeaponType.SPEAR, Spear::new); + builder.add(WeaponType.AXE, Axe::new); + builder.add(WeaponType.SWORD, Sword::new); + }); + } + + /** + * Testing {@link WeaponFactory} to produce a SPEAR asserting that the Weapon is an instance of {@link Spear} + */ + @Test + public void testSpearWeapon() { + Weapon weapon = factory.create(WeaponType.SPEAR); + verifyWeapon(weapon, Spear.class); + } + + /** + * Testing {@link WeaponFactory} to produce a AXE asserting that the Weapon is an instance of {@link Axe} + */ + @Test + public void testAxeWeapon() { + Weapon weapon = factory.create(WeaponType.AXE); + verifyWeapon(weapon, Axe.class); + } + + + /** + * Testing {@link WeaponFactory} to produce a SWORD asserting that the Weapon is an instance of {@link Sword} + */ + @Test + public void testWeapon() { + Weapon weapon = factory.create(WeaponType.SWORD); + verifyWeapon(weapon, Sword.class); + } + + /** + * This method asserts that the weapon object that is passed is an instance of the clazz + * + * @param weapon weapon object which is to be verified + * @param clazz expected class of the weapon + */ + private void verifyWeapon(Weapon weapon, Class clazz) { + assertTrue("Weapon must be an object of: " + clazz.getName(), clazz.isInstance(weapon)); + } +} diff --git a/factory-method/index.md b/factory-method/README.md similarity index 73% rename from factory-method/index.md rename to factory-method/README.md index fee6b6e83..05549cf4f 100644 --- a/factory-method/index.md +++ b/factory-method/README.md @@ -10,18 +10,29 @@ tags: - Gang Of Four --- -**Intent:** Define an interface for creating an object, but let subclasses +## Also known as +Virtual Constructor + +## Intent +Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses. ![alt text](./etc/factory-method_1.png "Factory Method") -**Applicability:** Use the Factory Method pattern when +## Applicability +Use the Factory Method pattern when * a class can't anticipate the class of objects it must create * a class wants its subclasses to specify the objects it creates * classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate -**Credits** +## Known uses + +* java.util.Calendar +* java.util.ResourceBundle +* java.text.NumberFormat#getInstance() + +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/factory-method/pom.xml b/factory-method/pom.xml index 39e0b7b4e..5081ecf99 100644 --- a/factory-method/pom.xml +++ b/factory-method/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT factory-method @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/factory-method/src/main/java/com/iluwatar/factory/method/App.java b/factory-method/src/main/java/com/iluwatar/factory/method/App.java index 118413564..cd7a6e6e7 100644 --- a/factory-method/src/main/java/com/iluwatar/factory/method/App.java +++ b/factory-method/src/main/java/com/iluwatar/factory/method/App.java @@ -1,40 +1,77 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factory.method; /** * - * The Factory Method is a creational design pattern which uses factory methods to deal - * with the problem of creating objects without specifying the exact class of object - * that will be created. This is done by creating objects via calling a factory - * method either specified in an interface and implemented by child classes, or implemented - * in a base class and optionally overridden by derived classes—rather than by calling a - * constructor. + * The Factory Method is a creational design pattern which uses factory methods to deal with the + * problem of creating objects without specifying the exact class of object that will be created. + * This is done by creating objects via calling a factory method either specified in an interface + * and implemented by child classes, or implemented in a base class and optionally overridden by + * derived classes—rather than by calling a constructor. *

* In this Factory Method example we have an interface ({@link Blacksmith}) with a method for - * creating objects ({@link Blacksmith#manufactureWeapon}). The concrete subclasses - * ({@link OrcBlacksmith}, {@link ElfBlacksmith}) then override the method to produce - * objects of their liking. + * creating objects ({@link Blacksmith#manufactureWeapon}). The concrete subclasses ( + * {@link OrcBlacksmith}, {@link ElfBlacksmith}) then override the method to produce objects of + * their liking. * */ public class App { - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { - Blacksmith blacksmith; - Weapon weapon; - - blacksmith = new OrcBlacksmith(); - weapon = blacksmith.manufactureWeapon(WeaponType.SPEAR); - System.out.println(weapon); - weapon = blacksmith.manufactureWeapon(WeaponType.AXE); - System.out.println(weapon); - - blacksmith = new ElfBlacksmith(); - weapon = blacksmith.manufactureWeapon(WeaponType.SHORT_SWORD); - System.out.println(weapon); - weapon = blacksmith.manufactureWeapon(WeaponType.SPEAR); - System.out.println(weapon); - } + private final Blacksmith blacksmith; + + /** + * Creates an instance of App which will use blacksmith to manufacture + * the weapons for war. + * App is unaware which concrete implementation of {@link Blacksmith} it is using. + * The decision of which blacksmith implementation to use may depend on configuration, or + * the type of rival in war. + * @param blacksmith a non-null implementation of blacksmith + */ + public App(Blacksmith blacksmith) { + this.blacksmith = blacksmith; + } + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + // Lets go to war with Orc weapons + App app = new App(new OrcBlacksmith()); + app.manufactureWeapons(); + + // Lets go to war with Elf weapons + app = new App(new ElfBlacksmith()); + app.manufactureWeapons(); + } + + private void manufactureWeapons() { + Weapon weapon; + weapon = blacksmith.manufactureWeapon(WeaponType.SPEAR); + System.out.println(weapon); + weapon = blacksmith.manufactureWeapon(WeaponType.AXE); + System.out.println(weapon); + } } diff --git a/factory-method/src/main/java/com/iluwatar/factory/method/Blacksmith.java b/factory-method/src/main/java/com/iluwatar/factory/method/Blacksmith.java index 516c993a9..9d90bebbc 100644 --- a/factory-method/src/main/java/com/iluwatar/factory/method/Blacksmith.java +++ b/factory-method/src/main/java/com/iluwatar/factory/method/Blacksmith.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factory.method; /** @@ -7,6 +29,6 @@ package com.iluwatar.factory.method; */ public interface Blacksmith { - Weapon manufactureWeapon(WeaponType weaponType); + Weapon manufactureWeapon(WeaponType weaponType); } diff --git a/factory-method/src/main/java/com/iluwatar/factory/method/ElfBlacksmith.java b/factory-method/src/main/java/com/iluwatar/factory/method/ElfBlacksmith.java index 9a5e62890..52844691f 100644 --- a/factory-method/src/main/java/com/iluwatar/factory/method/ElfBlacksmith.java +++ b/factory-method/src/main/java/com/iluwatar/factory/method/ElfBlacksmith.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factory.method; /** @@ -7,8 +29,8 @@ package com.iluwatar.factory.method; */ public class ElfBlacksmith implements Blacksmith { - public Weapon manufactureWeapon(WeaponType weaponType) { - return new ElfWeapon(weaponType); - } + public Weapon manufactureWeapon(WeaponType weaponType) { + return new ElfWeapon(weaponType); + } } diff --git a/factory-method/src/main/java/com/iluwatar/factory/method/ElfWeapon.java b/factory-method/src/main/java/com/iluwatar/factory/method/ElfWeapon.java index 75bb8a9e0..c06674d49 100644 --- a/factory-method/src/main/java/com/iluwatar/factory/method/ElfWeapon.java +++ b/factory-method/src/main/java/com/iluwatar/factory/method/ElfWeapon.java @@ -1,21 +1,45 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factory.method; /** - * - * ElfWeapon - * + * ElfWeapon. */ public class ElfWeapon implements Weapon { - private WeaponType weaponType; + private WeaponType weaponType; - public ElfWeapon(WeaponType weaponType) { - this.weaponType = weaponType; - } + public ElfWeapon(WeaponType weaponType) { + this.weaponType = weaponType; + } - @Override - public String toString() { - return "Elven " + weaponType; - } + @Override + public String toString() { + return "Elven " + weaponType; + } + @Override + public WeaponType getWeaponType() { + return weaponType; + } } diff --git a/factory-method/src/main/java/com/iluwatar/factory/method/OrcBlacksmith.java b/factory-method/src/main/java/com/iluwatar/factory/method/OrcBlacksmith.java index 382507ec9..75247f4a2 100644 --- a/factory-method/src/main/java/com/iluwatar/factory/method/OrcBlacksmith.java +++ b/factory-method/src/main/java/com/iluwatar/factory/method/OrcBlacksmith.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factory.method; /** @@ -7,8 +29,7 @@ package com.iluwatar.factory.method; */ public class OrcBlacksmith implements Blacksmith { - public Weapon manufactureWeapon(WeaponType weaponType) { - return new OrcWeapon(weaponType); - } - + public Weapon manufactureWeapon(WeaponType weaponType) { + return new OrcWeapon(weaponType); + } } diff --git a/factory-method/src/main/java/com/iluwatar/factory/method/OrcWeapon.java b/factory-method/src/main/java/com/iluwatar/factory/method/OrcWeapon.java index 85500799e..abae770ed 100644 --- a/factory-method/src/main/java/com/iluwatar/factory/method/OrcWeapon.java +++ b/factory-method/src/main/java/com/iluwatar/factory/method/OrcWeapon.java @@ -1,21 +1,45 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factory.method; /** - * - * OrcWeapon - * + * OrcWeapon. */ public class OrcWeapon implements Weapon { - private WeaponType weaponType; + private WeaponType weaponType; - public OrcWeapon(WeaponType weaponType) { - this.weaponType = weaponType; - } + public OrcWeapon(WeaponType weaponType) { + this.weaponType = weaponType; + } - @Override - public String toString() { - return "Orcish " + weaponType; - } + @Override + public String toString() { + return "Orcish " + weaponType; + } + @Override + public WeaponType getWeaponType() { + return weaponType; + } } diff --git a/factory-method/src/main/java/com/iluwatar/factory/method/Weapon.java b/factory-method/src/main/java/com/iluwatar/factory/method/Weapon.java index a5ae99254..d9c8cac0c 100644 --- a/factory-method/src/main/java/com/iluwatar/factory/method/Weapon.java +++ b/factory-method/src/main/java/com/iluwatar/factory/method/Weapon.java @@ -1,10 +1,32 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factory.method; /** - * - * Weapon interface - * + * Weapon interface. */ public interface Weapon { + WeaponType getWeaponType(); + } diff --git a/factory-method/src/main/java/com/iluwatar/factory/method/WeaponType.java b/factory-method/src/main/java/com/iluwatar/factory/method/WeaponType.java index 1c0341670..34921ae5c 100644 --- a/factory-method/src/main/java/com/iluwatar/factory/method/WeaponType.java +++ b/factory-method/src/main/java/com/iluwatar/factory/method/WeaponType.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factory.method; /** @@ -7,16 +29,16 @@ package com.iluwatar.factory.method; */ public enum WeaponType { - SHORT_SWORD("short sword"), SPEAR("spear"), AXE("axe"), UNDEFINED(""); + SHORT_SWORD("short sword"), SPEAR("spear"), AXE("axe"), UNDEFINED(""); - private String title; + private String title; - WeaponType(String title) { - this.title = title; - } + WeaponType(String title) { + this.title = title; + } - @Override - public String toString() { - return title; - } + @Override + public String toString() { + return title; + } } diff --git a/factory-method/src/test/java/com/iluwatar/factory/method/AppTest.java b/factory-method/src/test/java/com/iluwatar/factory/method/AppTest.java index c6db18b3e..818ee96cd 100644 --- a/factory-method/src/test/java/com/iluwatar/factory/method/AppTest.java +++ b/factory-method/src/test/java/com/iluwatar/factory/method/AppTest.java @@ -1,19 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factory.method; import org.junit.Test; -import com.iluwatar.factory.method.App; +import java.io.IOException; /** - * - * Application test - * + * Tests that Factory Method example runs without errors. */ public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } } diff --git a/factory-method/src/test/java/com/iluwatar/factory/method/FactoryMethodTest.java b/factory-method/src/test/java/com/iluwatar/factory/method/FactoryMethodTest.java new file mode 100644 index 000000000..2f8d1c9bb --- /dev/null +++ b/factory-method/src/test/java/com/iluwatar/factory/method/FactoryMethodTest.java @@ -0,0 +1,101 @@ +/** + * The MIT License + * Copyright (c) 2014 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.factory.method; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * The Factory Method is a creational design pattern which uses factory methods to deal with the + * problem of creating objects without specifying the exact class of object that will be created. + * This is done by creating objects via calling a factory method either specified in an interface + * and implemented by child classes, or implemented in a base class and optionally overridden by + * derived classes—rather than by calling a constructor. + * + *

Factory produces the object of its liking. + * The weapon {@link Weapon} manufactured by the + * blacksmith depends on the kind of factory implementation it is referring to. + *

+ */ +public class FactoryMethodTest { + + /** + * Testing {@link OrcBlacksmith} to produce a SPEAR asserting that the Weapon is an instance + * of {@link OrcWeapon}. + */ + @Test + public void testOrcBlacksmithWithSpear() { + Blacksmith blacksmith = new OrcBlacksmith(); + Weapon weapon = blacksmith.manufactureWeapon(WeaponType.SPEAR); + verifyWeapon(weapon, WeaponType.SPEAR, OrcWeapon.class); + } + + /** + * Testing {@link OrcBlacksmith} to produce a AXE asserting that the Weapon is an instance + * of {@link OrcWeapon}. + */ + @Test + public void testOrcBlacksmithWithAxe() { + Blacksmith blacksmith = new OrcBlacksmith(); + Weapon weapon = blacksmith.manufactureWeapon(WeaponType.AXE); + verifyWeapon(weapon, WeaponType.AXE, OrcWeapon.class); + } + + /** + * Testing {@link ElfBlacksmith} to produce a SHORT_SWORD asserting that the Weapon is an + * instance of {@link ElfWeapon}. + */ + @Test + public void testElfBlacksmithWithShortSword() { + Blacksmith blacksmith = new ElfBlacksmith(); + Weapon weapon = blacksmith.manufactureWeapon(WeaponType.SHORT_SWORD); + verifyWeapon(weapon, WeaponType.SHORT_SWORD, ElfWeapon.class); + } + + /** + * Testing {@link ElfBlacksmith} to produce a SPEAR asserting that the Weapon is an instance + * of {@link ElfWeapon}. + */ + @Test + public void testElfBlacksmithWithSpear() { + Blacksmith blacksmith = new ElfBlacksmith(); + Weapon weapon = blacksmith.manufactureWeapon(WeaponType.SPEAR); + verifyWeapon(weapon, WeaponType.SPEAR, ElfWeapon.class); + } + + /** + * This method asserts that the weapon object that is passed is an instance of the clazz and the + * weapon is of type expectedWeaponType. + * + * @param weapon weapon object which is to be verified + * @param expectedWeaponType expected WeaponType of the weapon + * @param clazz expected class of the weapon + */ + private void verifyWeapon(Weapon weapon, WeaponType expectedWeaponType, Class clazz) { + assertTrue("Weapon must be an object of: " + clazz.getName(), clazz.isInstance(weapon)); + assertEquals("Weapon must be of weaponType: " + clazz.getName(), expectedWeaponType, + weapon.getWeaponType()); + } +} diff --git a/faq.md b/faq.md index b98bc7589..69f7b795e 100644 --- a/faq.md +++ b/faq.md @@ -3,7 +3,7 @@ layout: page title: FAQ permalink: /faq/ icon: fa-question -page-index: 2 +page-index: 1 --- ### Q1: What is the difference between State and Strategy patterns? {#Q1} @@ -64,4 +64,4 @@ Flyweight. ### Q7: What are the differences between FluentInterface and Builder patterns? {#Q7} -Fluent interfaces are sometimes confused with the Builder pattern, because they share method chaining and a fluent usage. However, fluent interfaces are not primarily used to create shared (mutable) objects, but to configure complex objects without having to respecify the target object on every property change. \ No newline at end of file +Fluent interfaces are sometimes confused with the Builder pattern, because they share method chaining and a fluent usage. However, fluent interfaces are not primarily used to create shared (mutable) objects, but to configure complex objects without having to respecify the target object on every property change. diff --git a/feature-toggle/README.md b/feature-toggle/README.md new file mode 100644 index 000000000..51747ac09 --- /dev/null +++ b/feature-toggle/README.md @@ -0,0 +1,32 @@ +--- +layout: pattern +title: Feature Toggle +folder: feature-toggle +permalink: /patterns/feature-toggle/ +categories: Behavioral +tags: + - Java + - Difficulty-Beginner +--- + +## Also known as +Feature Flag + +## Intent +Used to switch code execution paths based on properties or groupings. Allowing new features to be released, tested +and rolled out. Allowing switching back to the older feature quickly if needed. It should be noted that this pattern, +can easily introduce code complexity. There is also cause for concern that the old feature that the toggle is eventually +going to phase out is never removed, causing redundant code smells and increased maintainability. + +![alt text](./etc/feature-toggle.png "Feature Toggle") + +## Applicability +Use the Feature Toogle pattern when + +* Giving different features to different users. +* Rolling out a new feature incrementally. +* Switching between development and production environments. + +## Credits + +* [Martin Fowler 29 October 2010 (2010-10-29).](http://martinfowler.com/bliki/FeatureToggle.html) \ No newline at end of file diff --git a/feature-toggle/etc/feature-toggle.png b/feature-toggle/etc/feature-toggle.png new file mode 100644 index 000000000..5c118e57e Binary files /dev/null and b/feature-toggle/etc/feature-toggle.png differ diff --git a/feature-toggle/etc/feature-toggle.ucls b/feature-toggle/etc/feature-toggle.ucls new file mode 100644 index 000000000..538d3f416 --- /dev/null +++ b/feature-toggle/etc/feature-toggle.ucls @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/feature-toggle/pom.xml b/feature-toggle/pom.xml new file mode 100644 index 000000000..78c182af9 --- /dev/null +++ b/feature-toggle/pom.xml @@ -0,0 +1,48 @@ + + + + + + java-design-patterns + com.iluwatar + 1.13.0-SNAPSHOT + + 4.0.0 + + feature-toggle + + + + + junit + junit + test + + + + \ No newline at end of file diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/App.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/App.java new file mode 100644 index 000000000..debe99580 --- /dev/null +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/App.java @@ -0,0 +1,96 @@ +/** + * The MIT License + * Copyright (c) 2014 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.featuretoggle; + +import com.iluwatar.featuretoggle.pattern.Service; +import com.iluwatar.featuretoggle.pattern.propertiesversion.PropertiesFeatureToggleVersion; +import com.iluwatar.featuretoggle.user.User; +import com.iluwatar.featuretoggle.user.UserGroup; + +import java.util.Properties; + +/** + * The Feature Toggle pattern allows for complete code executions to be turned on or off with ease. This allows features + * to be controlled by either dynamic methods just as {@link User} information or by {@link Properties}. In the App + * below there are two examples. Firstly the {@link Properties} version of the feature toggle, where the enhanced + * version of the welcome message which is personalised is turned either on or off at instance creation. This method + * is not as dynamic as the {@link User} driven version where the feature of the personalised welcome message is + * dependant on the {@link UserGroup} the {@link User} is in. So if the user is a memeber of the + * {@link UserGroup#isPaid(User)} then they get an ehanced version of the welcome message. + * + * Note that this pattern can easily introduce code complexity, and if not kept in check can result in redundant + * unmaintained code within the codebase. + * + */ +public class App { + + /** + * Block 1 shows the {@link PropertiesFeatureToggleVersion} being run with {@link Properties} setting the feature + * toggle to enabled. + * + * Block 2 shows the {@link PropertiesFeatureToggleVersion} being run with {@link Properties} setting the feature + * toggle to disabled. Notice the difference with the printed welcome message the username is not included. + * + * Block 3 shows the {@link com.iluwatar.featuretoggle.pattern.tieredversion.TieredFeatureToggleVersion} being + * set up with two users on who is on the free level, while the other is on the paid level. When the + * {@link Service#getWelcomeMessage(User)} is called with the paid {@link User} note that the welcome message + * contains their username, while the same service call with the free tier user is more generic. No username is + * printed. + * + * @see User + * @see UserGroup + * @see Service + * @see PropertiesFeatureToggleVersion + * @see com.iluwatar.featuretoggle.pattern.tieredversion.TieredFeatureToggleVersion; + */ + public static void main(String[] args) { + + final Properties properties = new Properties(); + properties.put("enhancedWelcome", true); + Service service = new PropertiesFeatureToggleVersion(properties); + final String welcomeMessage = service.getWelcomeMessage(new User("Jamie No Code")); + System.out.println(welcomeMessage); + + // --------------------------------------------- + + final Properties turnedOff = new Properties(); + turnedOff.put("enhancedWelcome", false); + Service turnedOffService = new PropertiesFeatureToggleVersion(turnedOff); + final String welcomeMessageturnedOff = turnedOffService.getWelcomeMessage(new User("Jamie No Code")); + System.out.println(welcomeMessageturnedOff); + + // -------------------------------------------- + + final User paidUser = new User("Jamie Coder"); + final User freeUser = new User("Alan Defect"); + + UserGroup.addUserToPaidGroup(paidUser); + UserGroup.addUserToFreeGroup(freeUser); + + final String welcomeMessagePaidUser = service.getWelcomeMessage(paidUser); + final String welcomeMessageFreeUser = service.getWelcomeMessage(freeUser); + System.out.println(welcomeMessageFreeUser); + System.out.println(welcomeMessagePaidUser); + } +} diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java new file mode 100644 index 000000000..d2542b2b7 --- /dev/null +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/Service.java @@ -0,0 +1,54 @@ +/** + * The MIT License + * Copyright (c) 2014 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.featuretoggle.pattern; + +import com.iluwatar.featuretoggle.user.User; + +/** + * Simple interfaces to allow the calling of the method to generate the welcome message for a given user. While there is + * a helper method to gather the the status of the feature toggle. In some cases there is no need for the + * {@link Service#isEnhanced()} in {@link com.iluwatar.featuretoggle.pattern.tieredversion.TieredFeatureToggleVersion} + * where the toggle is determined by the actual {@link User}. + * + * @see com.iluwatar.featuretoggle.pattern.propertiesversion.PropertiesFeatureToggleVersion + * @see com.iluwatar.featuretoggle.pattern.tieredversion.TieredFeatureToggleVersion + * @see User + */ +public interface Service { + + /** + * Generates a welcome message for the passed user. + * + * @param user the {@link User} to be used if the message is to be personalised. + * @return Generated {@link String} welcome message + */ + String getWelcomeMessage(User user); + + /** + * Returns if the welcome message to be displayed will be the enhanced version. + * + * @return Boolean {@value true} if enhanced. + */ + boolean isEnhanced(); + +} diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java new file mode 100644 index 000000000..761d7d39a --- /dev/null +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersion.java @@ -0,0 +1,100 @@ +/** + * The MIT License + * Copyright (c) 2014 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.featuretoggle.pattern.propertiesversion; + +import com.iluwatar.featuretoggle.pattern.Service; +import com.iluwatar.featuretoggle.user.User; + +import java.util.Properties; + +/** + * This example of the Feature Toogle pattern is less dynamic version than + * {@link com.iluwatar.featuretoggle.pattern.tieredversion.TieredFeatureToggleVersion} where the feature is turned on + * or off at the time of creation of the service. This example uses simple Java {@link Properties} however it could as + * easily be done with an external configuration file loaded by Spring and so on. A good example of when to use this + * version of the feature toggle is when new features are being developed. So you could have a configuration property + * boolean named development or some sort of system environment variable. + * + * @see Service + * @see com.iluwatar.featuretoggle.pattern.tieredversion.TieredFeatureToggleVersion + * @see User + */ +public class PropertiesFeatureToggleVersion implements Service { + + private boolean isEnhanced; + + /** + * Creates an instance of {@link PropertiesFeatureToggleVersion} using the passed {@link Properties} to determine, + * the status of the feature toggle {@link PropertiesFeatureToggleVersion#isEnhanced()}. There is also some defensive + * code to ensure the {@link Properties} passed are as expected. + * + * @param properties {@link Properties} used to configure the service and toggle features. + * @throws IllegalArgumentException when the passed {@link Properties} is not as expected + * @see Properties + */ + public PropertiesFeatureToggleVersion(final Properties properties) { + if (properties == null) { + throw new IllegalArgumentException("No Properties Provided."); + } else { + try { + isEnhanced = (boolean) properties.get("enhancedWelcome"); + } catch (Exception e) { + throw new IllegalArgumentException("Invalid Enhancement Settings Provided."); + } + } + } + + /** + * Generate a welcome message based on the user being passed and the status of the feature toggle. If the enhanced + * version is enabled, then the message will be personalised with the name of the passed {@link User}. However if + * disabled then a generic version fo the message is returned. + * + * @param user the {@link User} to be displayed in the message if the enhanced version is enabled see + * {@link PropertiesFeatureToggleVersion#isEnhanced()}. If the enhanced version is enabled, then the + * message will be personalised with the name of the passed {@link User}. However if disabled then a + * generic version fo the message is returned. + * @return Resulting welcome message. + * @see User + */ + @Override + public String getWelcomeMessage(final User user) { + + if (isEnhanced()) { + return "Welcome " + user + ". You're using the enhanced welcome message."; + } + + return "Welcome to the application."; + } + + /** + * Method that checks if the welcome message to be returned is the enhanced venison or not. For this service it will + * see the value of the boolean that was set in the constructor + * {@link PropertiesFeatureToggleVersion#PropertiesFeatureToggleVersion(Properties)} + * + * @return Boolean value {@value true} if enhanced. + */ + @Override + public boolean isEnhanced() { + return isEnhanced; + } +} diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java new file mode 100644 index 000000000..124c9533f --- /dev/null +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersion.java @@ -0,0 +1,75 @@ +/** + * The MIT License + * Copyright (c) 2014 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.featuretoggle.pattern.tieredversion; + +import com.iluwatar.featuretoggle.pattern.Service; +import com.iluwatar.featuretoggle.user.User; +import com.iluwatar.featuretoggle.user.UserGroup; + +/** + * This example of the Feature Toogle pattern shows how it could be implemented based on a {@link User}. Therefore + * showing its use within a tiered application where the paying users get access to different content or + * better versions of features. So in this instance a {@link User} is passed in and if they are found to be + * on the {@link UserGroup#isPaid(User)} they are welcomed with a personalised message. While the other is more + * generic. However this pattern is limited to simple examples such as the one below. + * + * @see Service + * @see User + * @see com.iluwatar.featuretoggle.pattern.propertiesversion.PropertiesFeatureToggleVersion + * @see UserGroup + */ +public class TieredFeatureToggleVersion implements Service { + + /** + * Generates a welcome message from the passed {@link User}. The resulting message depends on the group of the + * {@link User}. So if the {@link User} is in the {@link UserGroup#paidGroup} then the enhanced version of the + * welcome message will be returned where the username is displayed. + * + * @param user the {@link User} to generate the welcome message for, different messages are displayed if the user is + * in the {@link UserGroup#isPaid(User)} or {@link UserGroup#freeGroup} + * @return Resulting welcome message. + * @see User + * @see UserGroup + */ + @Override + public String getWelcomeMessage(User user) { + if (UserGroup.isPaid(user)) { + return "You're amazing " + user + ". Thanks for paying for this awesome software."; + } + + return "I suppose you can use this software."; + } + + /** + * Method that checks if the welcome message to be returned is the enhanced version. For this instance as the logic + * is driven by the user group. This method is a little redundant. However can be used to show that there is an + * enhanced version available. + * + * @return Boolean value {@value true} if enhanced. + */ + @Override + public boolean isEnhanced() { + return true; + } + +} diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java new file mode 100644 index 000000000..ce7b54b7b --- /dev/null +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/User.java @@ -0,0 +1,49 @@ +/** + * The MIT License + * Copyright (c) 2014 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.featuretoggle.user; + +/** + * Used to demonstrate the purpose of the feature toggle. This class actually has nothing to do with the pattern. + */ +public class User { + + private String name; + + /** + * Default Constructor setting the username. + * + * @param name {@link String} to represent the name of the user. + */ + public User(String name) { + this.name = name; + } + + /** + * {@inheritDoc} + * @return The {@link String} representation of the User, in this case just return the name of the user. + */ + @Override + public String toString() { + return name; + } +} diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java new file mode 100644 index 000000000..c9d9fd027 --- /dev/null +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/user/UserGroup.java @@ -0,0 +1,84 @@ +/** + * The MIT License + * Copyright (c) 2014 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.featuretoggle.user; + +import java.util.ArrayList; +import java.util.List; + +/** + * Contains the lists of users of different groups paid and free. Used to demonstrate the tiered example of feature + * toggle. Allowing certain features to be available to only certain groups of users. + * + * @see User + */ +public class UserGroup { + + private static List freeGroup = new ArrayList<>(); + private static List paidGroup = new ArrayList<>(); + + + /** + * Add the passed {@link User} to the free user group list. + * + * @param user {@link User} to be added to the free group + * @throws IllegalArgumentException when user is already added to the paid group + * @see User + */ + public static void addUserToFreeGroup(final User user) throws IllegalArgumentException { + if (paidGroup.contains(user)) { + throw new IllegalArgumentException("User all ready member of paid group."); + } else { + if (!freeGroup.contains(user)) { + freeGroup.add(user); + } + } + } + + /** + * Add the passed {@link User} to the paid user group list. + * + * @param user {@link User} to be added to the paid group + * @throws IllegalArgumentException when the user is already added to the free group + * @see User + */ + public static void addUserToPaidGroup(final User user) throws IllegalArgumentException { + if (freeGroup.contains(user)) { + throw new IllegalArgumentException("User all ready member of free group."); + } else { + if (!paidGroup.contains(user)) { + paidGroup.add(user); + } + } + } + + /** + * Method to take a {@link User} to determine if the user is in the {@link UserGroup#paidGroup}. + * + * @param user {@link User} to check if they are in the {@link UserGroup#paidGroup} + * + * @return true if the {@link User} is in {@link UserGroup#paidGroup} + */ + public static boolean isPaid(User user) { + return paidGroup.contains(user); + } +} diff --git a/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersionTest.java b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersionTest.java new file mode 100644 index 000000000..69afc9bb4 --- /dev/null +++ b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/propertiesversion/PropertiesFeatureToggleVersionTest.java @@ -0,0 +1,69 @@ +/** + * The MIT License + * Copyright (c) 2014 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.featuretoggle.pattern.propertiesversion; + +import com.iluwatar.featuretoggle.pattern.Service; +import com.iluwatar.featuretoggle.user.User; +import org.junit.Test; + +import java.util.Properties; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class PropertiesFeatureToggleVersionTest { + + @Test(expected = IllegalArgumentException.class) + public void testNullPropertiesPassed() throws Exception { + new PropertiesFeatureToggleVersion(null); + } + + @Test(expected = IllegalArgumentException.class) + public void testNonBooleanProperty() throws Exception { + final Properties properties = new Properties(); + properties.setProperty("enhancedWelcome", "Something"); + new PropertiesFeatureToggleVersion(properties); + } + + @Test + public void testFeatureTurnedOn() throws Exception { + final Properties properties = new Properties(); + properties.put("enhancedWelcome", true); + Service service = new PropertiesFeatureToggleVersion(properties); + assertTrue(service.isEnhanced()); + final String welcomeMessage = service.getWelcomeMessage(new User("Jamie No Code")); + assertEquals("Welcome Jamie No Code. You're using the enhanced welcome message.", welcomeMessage); + } + + @Test + public void testFeatureTurnedOff() throws Exception { + final Properties properties = new Properties(); + properties.put("enhancedWelcome", false); + Service service = new PropertiesFeatureToggleVersion(properties); + assertFalse(service.isEnhanced()); + final String welcomeMessage = service.getWelcomeMessage(new User("Jamie No Code")); + assertEquals("Welcome to the application.", welcomeMessage); + } +} \ No newline at end of file diff --git a/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java new file mode 100644 index 000000000..dca1d9b82 --- /dev/null +++ b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/pattern/tieredversion/TieredFeatureToggleVersionTest.java @@ -0,0 +1,64 @@ +/** + * The MIT License + * Copyright (c) 2014 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.featuretoggle.pattern.tieredversion; + +import com.iluwatar.featuretoggle.pattern.Service; +import com.iluwatar.featuretoggle.user.User; +import com.iluwatar.featuretoggle.user.UserGroup; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class TieredFeatureToggleVersionTest { + + final User paidUser = new User("Jamie Coder"); + final User freeUser = new User("Alan Defect"); + final Service service = new TieredFeatureToggleVersion(); + + @Before + public void setUp() throws Exception { + UserGroup.addUserToPaidGroup(paidUser); + UserGroup.addUserToFreeGroup(freeUser); + } + + @Test + public void testGetWelcomeMessageForPaidUser() throws Exception { + final String welcomeMessage = service.getWelcomeMessage(paidUser); + final String expected = "You're amazing Jamie Coder. Thanks for paying for this awesome software."; + assertEquals(expected, welcomeMessage); + } + + @Test + public void testGetWelcomeMessageForFreeUser() throws Exception { + final String welcomeMessage = service.getWelcomeMessage(freeUser); + final String expected = "I suppose you can use this software."; + assertEquals(expected, welcomeMessage); + } + + @Test + public void testIsEnhancedAlwaysTrueAsTiered() throws Exception { + assertTrue(service.isEnhanced()); + } +} \ No newline at end of file diff --git a/feature-toggle/src/test/java/com/iluwatar/featuretoggle/user/UserGroupTest.java b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/user/UserGroupTest.java new file mode 100644 index 000000000..6659815d3 --- /dev/null +++ b/feature-toggle/src/test/java/com/iluwatar/featuretoggle/user/UserGroupTest.java @@ -0,0 +1,59 @@ +/** + * The MIT License + * Copyright (c) 2014 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.featuretoggle.user; + +import org.junit.Test; + +import static junit.framework.TestCase.assertFalse; +import static org.junit.Assert.assertTrue; + +public class UserGroupTest { + + @Test + public void testAddUserToFreeGroup() throws Exception { + User user = new User("Free User"); + UserGroup.addUserToFreeGroup(user); + assertFalse(UserGroup.isPaid(user)); + } + + @Test + public void testAddUserToPaidGroup() throws Exception { + User user = new User("Paid User"); + UserGroup.addUserToPaidGroup(user); + assertTrue(UserGroup.isPaid(user)); + } + + @Test(expected = IllegalArgumentException.class) + public void testAddUserToPaidWhenOnFree() throws Exception { + User user = new User("Paid User"); + UserGroup.addUserToFreeGroup(user); + UserGroup.addUserToPaidGroup(user); + } + + @Test(expected = IllegalArgumentException.class) + public void testAddUserToFreeWhenOnPaid() throws Exception { + User user = new User("Free User"); + UserGroup.addUserToPaidGroup(user); + UserGroup.addUserToFreeGroup(user); + } +} \ No newline at end of file diff --git a/fluentinterface/index.md b/fluentinterface/README.md similarity index 80% rename from fluentinterface/index.md rename to fluentinterface/README.md index 27a4d1a26..767792da7 100644 --- a/fluentinterface/index.md +++ b/fluentinterface/README.md @@ -7,11 +7,13 @@ categories: Other tags: - Java - Difficulty-Intermediate + - Functional --- -**Intent:** A fluent interface provides an easy-readable, flowing interface, that often mimics a domain specific language. Using this pattern results in code that can be read nearly as human language. +## Intent +A fluent interface provides an easy-readable, flowing interface, that often mimics a domain specific language. Using this pattern results in code that can be read nearly as human language. -**Implementation:** +## Implementation A fluent interface can be implemented using any of @@ -21,13 +23,13 @@ A fluent interface can be implemented using any of ![Fluent Interface](./etc/fluentinterface.png "Fluent Interface") - -**Applicability:** Use the Fluent Interface pattern when +## Applicability +Use the Fluent Interface pattern when * you provide an API that would benefit from a DSL-like usage * you have objects that are difficult to configure or use -**Real world examples:** +## Real world examples * [Java 8 Stream API](http://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams-2177646.html) * [Google Guava FluentInterable](https://github.com/google/guava/wiki/FunctionalExplained) @@ -35,7 +37,7 @@ A fluent interface can be implemented using any of * [Mockito](http://mockito.org/) * [Java Hamcrest](http://code.google.com/p/hamcrest/wiki/Tutorial) -**Credits** +## Credits * [Fluent Interface - Martin Fowler](http://www.martinfowler.com/bliki/FluentInterface.html) * [Evolutionary architecture and emergent design: Fluent interfaces - Neal Ford](http://www.ibm.com/developerworks/library/j-eaed14/) diff --git a/fluentinterface/pom.xml b/fluentinterface/pom.xml index 16e237bd5..ca5e115d0 100644 --- a/fluentinterface/pom.xml +++ b/fluentinterface/pom.xml @@ -1,11 +1,35 @@ + java-design-patterns com.iluwatar - 1.7.0 + 1.13.0-SNAPSHOT 4.0.0 @@ -16,5 +40,10 @@ junit test + + org.mockito + mockito-core + test + \ No newline at end of file diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/app/App.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/app/App.java index 7733df37d..1be2b1e70 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/app/App.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/app/App.java @@ -1,35 +1,64 @@ +/** + * The MIT License + * Copyright (c) 2014 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.fluentinterface.app; +import static java.lang.String.valueOf; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.StringJoiner; +import java.util.function.Function; +import java.util.function.Predicate; + import com.iluwatar.fluentinterface.fluentiterable.FluentIterable; import com.iluwatar.fluentinterface.fluentiterable.lazy.LazyFluentIterable; import com.iluwatar.fluentinterface.fluentiterable.simple.SimpleFluentIterable; -import java.util.*; -import java.util.function.Function; -import java.util.function.Predicate; - -import static java.lang.String.valueOf; - /** - * The Fluent Interface pattern is useful when you want to provide an easy readable, flowing API. Those - * interfaces tend to mimic domain specific languages, so they can nearly be read as human languages. + * The Fluent Interface pattern is useful when you want to provide an easy readable, flowing API. + * Those interfaces tend to mimic domain specific languages, so they can nearly be read as human + * languages. *

* In this example two implementations of a {@link FluentIterable} interface are given. The - * {@link SimpleFluentIterable} evaluates eagerly and would be too costly for real world applications. - * The {@link LazyFluentIterable} is evaluated on termination. Their usage is demonstrated with a - * simple number list that is filtered, transformed and collected. The result is printed afterwards. + * {@link SimpleFluentIterable} evaluates eagerly and would be too costly for real world + * applications. The {@link LazyFluentIterable} is evaluated on termination. Their usage is + * demonstrated with a simple number list that is filtered, transformed and collected. The result is + * printed afterwards. * */ public class App { + /** + * Program entry point + */ public static void main(String[] args) { List integerList = new ArrayList<>(); - integerList.addAll(Arrays.asList( - 1, -61, 14, -22, 18, -87, 6, 64, -82, 26, -98, 97, - 45, 23, 2, -68, 45 - )); - + integerList.addAll(Arrays.asList(1, -61, 14, -22, 18, -87, 6, 64, -82, 26, -98, 97, 45, 23, 2, + -68, 45)); + prettyPrint("The initial list contains: ", integerList); List firstFiveNegatives = @@ -58,7 +87,7 @@ public class App { List lastTwoOfFirstFourStringMapped = LazyFluentIterable.from(integerList).filter(positives()).first(4).last(2) - .map(number -> "String[" + String.valueOf(number) + "]").asList(); + .map(number -> "String[" + valueOf(number) + "]").asList(); prettyPrint( "The lazy list contains the last two of the first four positive numbers mapped to Strings: ", lastTwoOfFirstFourStringMapped); @@ -78,21 +107,21 @@ public class App { } private static Predicate negatives() { - return integer -> (integer < 0); + return integer -> integer < 0; } private static Predicate positives() { - return integer -> (integer > 0); + return integer -> integer > 0; } - private static void prettyPrint(String prefix, Iterable iterable) { - prettyPrint(", ", prefix, ".", iterable); + private static void prettyPrint(String prefix, Iterable iterable) { + prettyPrint(", ", prefix, iterable); } - private static void prettyPrint(String delimiter, String prefix, String suffix, - Iterable iterable) { + private static void prettyPrint(String delimiter, String prefix, + Iterable iterable) { StringJoiner joiner = new StringJoiner(delimiter, prefix, "."); - Iterator iterator = iterable.iterator(); + Iterator iterator = iterable.iterator(); while (iterator.hasNext()) { joiner.add(iterator.next().toString()); } diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java index 5c4df0391..f6d7e2e2b 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterable.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.fluentinterface.fluentiterable; import java.util.ArrayList; @@ -12,9 +34,9 @@ import java.util.function.Predicate; * the fluent interface design pattern. This interface defines common operations, but doesn't aim to * be complete. It was inspired by Guava's com.google.common.collect.FluentIterable. * - * @param is the class of objects the iterable contains + * @param is the class of objects the iterable contains */ -public interface FluentIterable extends Iterable { +public interface FluentIterable extends Iterable { /** * Filters the contents of Iterable using the given predicate, leaving only the ones which satisfy @@ -24,7 +46,7 @@ public interface FluentIterable extends Iterable { * tested object is removed by the iterator. * @return a filtered FluentIterable */ - FluentIterable filter(Predicate predicate); + FluentIterable filter(Predicate predicate); /** * Returns an Optional containing the first element of this iterable if present, else returns @@ -32,55 +54,55 @@ public interface FluentIterable extends Iterable { * * @return the first element after the iteration is evaluated */ - Optional first(); + Optional first(); /** * Evaluates the iteration and leaves only the count first elements. * * @return the first count elements as an Iterable */ - FluentIterable first(int count); + FluentIterable first(int count); /** * Evaluates the iteration and returns the last element. This is a terminating operation. * * @return the last element after the iteration is evaluated */ - Optional last(); + Optional last(); /** * Evaluates the iteration and leaves only the count last elements. * * @return the last counts elements as an Iterable */ - FluentIterable last(int count); + FluentIterable last(int count); /** - * Transforms this FluentIterable into a new one containing objects of the type NEW_TYPE. + * Transforms this FluentIterable into a new one containing objects of the type T. * - * @param function a function that transforms an instance of TYPE into an instance of NEW_TYPE - * @param the target type of the transformation + * @param function a function that transforms an instance of E into an instance of T + * @param the target type of the transformation * @return a new FluentIterable of the new type */ - FluentIterable map(Function function); + FluentIterable map(Function function); /** * Returns the contents of this Iterable as a List. * * @return a List representation of this Iterable */ - List asList(); + List asList(); /** * Utility method that iterates over iterable and adds the contents to a list. * * @param iterable the iterable to collect - * @param the type of the objects to iterate + * @param the type of the objects to iterate * @return a list with all objects of the given iterator */ - static List copyToList(Iterable iterable) { - ArrayList copy = new ArrayList<>(); - Iterator iterator = iterable.iterator(); + static List copyToList(Iterable iterable) { + List copy = new ArrayList<>(); + Iterator iterator = iterable.iterator(); while (iterator.hasNext()) { copy.add(iterator.next()); } diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java index e80356d8e..07d7f5739 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/DecoratingIterator.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.fluentinterface.fluentiterable.lazy; import java.util.Iterator; @@ -5,21 +27,17 @@ import java.util.Iterator; /** * This class is used to realize LazyFluentIterables. It decorates a given iterator. Does not * support consecutive hasNext() calls. - * - * @param */ -public abstract class DecoratingIterator implements Iterator { +public abstract class DecoratingIterator implements Iterator { - protected final Iterator fromIterator; + protected final Iterator fromIterator; - private TYPE next = null; + private E next; /** * Creates an iterator that decorates the given iterator. - * - * @param fromIterator */ - public DecoratingIterator(Iterator fromIterator) { + public DecoratingIterator(Iterator fromIterator) { this.fromIterator = fromIterator; } @@ -40,11 +58,11 @@ public abstract class DecoratingIterator implements Iterator { * @return the next element of the Iterable, or null if not present. */ @Override - public final TYPE next() { + public final E next() { if (next == null) { return fromIterator.next(); } else { - final TYPE result = next; + final E result = next; next = null; return result; } @@ -56,5 +74,5 @@ public abstract class DecoratingIterator implements Iterator { * * @return the next element of the Iterable. */ - public abstract TYPE computeNext(); + public abstract E computeNext(); } diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java index 560b10189..63247875c 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterable.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.fluentinterface.fluentiterable.lazy; import java.util.ArrayList; @@ -13,18 +35,18 @@ import com.iluwatar.fluentinterface.fluentiterable.FluentIterable; * This is a lazy implementation of the FluentIterable interface. It evaluates all chained * operations when a terminating operation is applied. * - * @param the type of the objects the iteration is about + * @param the type of the objects the iteration is about */ -public class LazyFluentIterable implements FluentIterable { +public class LazyFluentIterable implements FluentIterable { - private final Iterable iterable; + private final Iterable iterable; /** * This constructor creates a new LazyFluentIterable. It wraps the given iterable. * * @param iterable the iterable this FluentIterable works on. */ - protected LazyFluentIterable(Iterable iterable) { + protected LazyFluentIterable(Iterable iterable) { this.iterable = iterable; } @@ -44,19 +66,18 @@ public class LazyFluentIterable implements FluentIterable { * @return a new FluentIterable object that decorates the source iterable */ @Override - public FluentIterable filter(Predicate predicate) { - return new LazyFluentIterable() { + public FluentIterable filter(Predicate predicate) { + return new LazyFluentIterable() { @Override - public Iterator iterator() { - return new DecoratingIterator(iterable.iterator()) { + public Iterator iterator() { + return new DecoratingIterator(iterable.iterator()) { @Override - public TYPE computeNext() { + public E computeNext() { while (fromIterator.hasNext()) { - TYPE candidate = fromIterator.next(); - if (!predicate.test(candidate)) { - continue; + E candidate = fromIterator.next(); + if (predicate.test(candidate)) { + return candidate; } - return candidate; } return null; @@ -72,8 +93,8 @@ public class LazyFluentIterable implements FluentIterable { * @return an Optional containing the first object of this Iterable */ @Override - public Optional first() { - Iterator resultIterator = first(1).iterator(); + public Optional first() { + Iterator resultIterator = first(1).iterator(); return resultIterator.hasNext() ? Optional.of(resultIterator.next()) : Optional.empty(); } @@ -85,21 +106,19 @@ public class LazyFluentIterable implements FluentIterable { * objects. */ @Override - public FluentIterable first(int count) { - return new LazyFluentIterable() { + public FluentIterable first(int count) { + return new LazyFluentIterable() { @Override - public Iterator iterator() { - return new DecoratingIterator(iterable.iterator()) { - int currentIndex = 0; + public Iterator iterator() { + return new DecoratingIterator(iterable.iterator()) { + int currentIndex; @Override - public TYPE computeNext() { - if (currentIndex < count) { - if (fromIterator.hasNext()) { - TYPE candidate = fromIterator.next(); - currentIndex++; - return candidate; - } + public E computeNext() { + if (currentIndex < count && fromIterator.hasNext()) { + E candidate = fromIterator.next(); + currentIndex++; + return candidate; } return null; } @@ -114,8 +133,8 @@ public class LazyFluentIterable implements FluentIterable { * @return an Optional containing the last object of this Iterable */ @Override - public Optional last() { - Iterator resultIterator = last(1).iterator(); + public Optional last() { + Iterator resultIterator = last(1).iterator(); return resultIterator.hasNext() ? Optional.of(resultIterator.next()) : Optional.empty(); } @@ -129,21 +148,21 @@ public class LazyFluentIterable implements FluentIterable { * objects */ @Override - public FluentIterable last(int count) { - return new LazyFluentIterable() { + public FluentIterable last(int count) { + return new LazyFluentIterable() { @Override - public Iterator iterator() { - return new DecoratingIterator(iterable.iterator()) { + public Iterator iterator() { + return new DecoratingIterator(iterable.iterator()) { private int stopIndex; private int totalElementsCount; - private List list; - private int currentIndex = 0; + private List list; + private int currentIndex; @Override - public TYPE computeNext() { + public E computeNext() { initialize(); - TYPE candidate = null; + E candidate = null; while (currentIndex < stopIndex && fromIterator.hasNext()) { currentIndex++; fromIterator.next(); @@ -157,7 +176,7 @@ public class LazyFluentIterable implements FluentIterable { private void initialize() { if (list == null) { list = new ArrayList<>(); - Iterator newIterator = iterable.iterator(); + Iterator newIterator = iterable.iterator(); while (newIterator.hasNext()) { list.add(newIterator.next()); } @@ -172,27 +191,28 @@ public class LazyFluentIterable implements FluentIterable { } /** - * Transforms this FluentIterable into a new one containing objects of the type NEW_TYPE. + * Transforms this FluentIterable into a new one containing objects of the type T. * - * @param function a function that transforms an instance of TYPE into an instance of NEW_TYPE - * @param the target type of the transformation + * @param function a function that transforms an instance of E into an instance of T + * @param the target type of the transformation * @return a new FluentIterable of the new type */ @Override - public FluentIterable map(Function function) { - return new LazyFluentIterable() { + public FluentIterable map(Function function) { + return new LazyFluentIterable() { @Override - public Iterator iterator() { - return new DecoratingIterator(null) { - Iterator oldTypeIterator = iterable.iterator(); + public Iterator iterator() { + return new DecoratingIterator(null) { + Iterator oldTypeIterator = iterable.iterator(); @Override - public NEW_TYPE computeNext() { - while (oldTypeIterator.hasNext()) { - TYPE candidate = oldTypeIterator.next(); + public T computeNext() { + if (oldTypeIterator.hasNext()) { + E candidate = oldTypeIterator.next(); return function.apply(candidate); + } else { + return null; } - return null; } }; } @@ -205,17 +225,16 @@ public class LazyFluentIterable implements FluentIterable { * @return a list with all remaining objects of this iteration */ @Override - public List asList() { - List copy = FluentIterable.copyToList(iterable); - return copy; + public List asList() { + return FluentIterable.copyToList(iterable); } @Override - public Iterator iterator() { - return new DecoratingIterator(iterable.iterator()) { + public Iterator iterator() { + return new DecoratingIterator(iterable.iterator()) { @Override - public TYPE computeNext() { - return fromIterator.next(); + public E computeNext() { + return fromIterator.hasNext() ? fromIterator.next() : null; } }; } @@ -223,7 +242,7 @@ public class LazyFluentIterable implements FluentIterable { /** * @return a FluentIterable from a given iterable. Calls the LazyFluentIterable constructor. */ - public static final FluentIterable from(Iterable iterable) { + public static final FluentIterable from(Iterable iterable) { return new LazyFluentIterable<>(iterable); } diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java index 19283152e..153f3faa5 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterable.java @@ -1,28 +1,54 @@ +/** + * The MIT License + * Copyright (c) 2014 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.fluentinterface.fluentiterable.simple; -import com.iluwatar.fluentinterface.fluentiterable.FluentIterable; - -import java.util.*; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.Spliterator; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; +import com.iluwatar.fluentinterface.fluentiterable.FluentIterable; + /** * This is a simple implementation of the FluentIterable interface. It evaluates all chained * operations eagerly. This implementation would be costly to be utilized in real applications. * - * @param the type of the objects the iteration is about + * @param the type of the objects the iteration is about */ -public class SimpleFluentIterable implements FluentIterable { +public class SimpleFluentIterable implements FluentIterable { - private final Iterable iterable; + private final Iterable iterable; /** * This constructor creates a copy of a given iterable's contents. * * @param iterable the iterable this interface copies to work on. */ - protected SimpleFluentIterable(Iterable iterable) { + protected SimpleFluentIterable(Iterable iterable) { this.iterable = iterable; } @@ -35,10 +61,10 @@ public class SimpleFluentIterable implements FluentIterable { * @return the same FluentIterable with a filtered collection */ @Override - public final FluentIterable filter(Predicate predicate) { - Iterator iterator = iterator(); + public final FluentIterable filter(Predicate predicate) { + Iterator iterator = iterator(); while (iterator.hasNext()) { - TYPE nextElement = iterator.next(); + E nextElement = iterator.next(); if (!predicate.test(nextElement)) { iterator.remove(); } @@ -52,8 +78,8 @@ public class SimpleFluentIterable implements FluentIterable { * @return an option of the first object of the Iterable */ @Override - public final Optional first() { - Iterator resultIterator = first(1).iterator(); + public final Optional first() { + Iterator resultIterator = first(1).iterator(); return resultIterator.hasNext() ? Optional.of(resultIterator.next()) : Optional.empty(); } @@ -65,8 +91,8 @@ public class SimpleFluentIterable implements FluentIterable { * objects. */ @Override - public final FluentIterable first(int count) { - Iterator iterator = iterator(); + public final FluentIterable first(int count) { + Iterator iterator = iterator(); int currentCount = 0; while (iterator.hasNext()) { iterator.next(); @@ -84,8 +110,8 @@ public class SimpleFluentIterable implements FluentIterable { * @return an option of the last object of the Iterable */ @Override - public final Optional last() { - List list = last(1).asList(); + public final Optional last() { + List list = last(1).asList(); if (list.isEmpty()) { return Optional.empty(); } @@ -100,9 +126,9 @@ public class SimpleFluentIterable implements FluentIterable { * objects */ @Override - public final FluentIterable last(int count) { + public final FluentIterable last(int count) { int remainingElementsCount = getRemainingElementsCount(); - Iterator iterator = iterator(); + Iterator iterator = iterator(); int currentIndex = 0; while (iterator.hasNext()) { iterator.next(); @@ -116,16 +142,16 @@ public class SimpleFluentIterable implements FluentIterable { } /** - * Transforms this FluentIterable into a new one containing objects of the type NEW_TYPE. + * Transforms this FluentIterable into a new one containing objects of the type T. * - * @param function a function that transforms an instance of TYPE into an instance of NEW_TYPE - * @param the target type of the transformation + * @param function a function that transforms an instance of E into an instance of T + * @param the target type of the transformation * @return a new FluentIterable of the new type */ @Override - public final FluentIterable map(Function function) { - List temporaryList = new ArrayList<>(); - Iterator iterator = iterator(); + public final FluentIterable map(Function function) { + List temporaryList = new ArrayList<>(); + Iterator iterator = iterator(); while (iterator.hasNext()) { temporaryList.add(function.apply(iterator.next())); } @@ -138,35 +164,35 @@ public class SimpleFluentIterable implements FluentIterable { * @return a list with all remaining objects of this Iterable */ @Override - public List asList() { + public List asList() { return toList(iterable.iterator()); } /** * @return a FluentIterable from a given iterable. Calls the SimpleFluentIterable constructor. */ - public static final FluentIterable from(Iterable iterable) { + public static final FluentIterable from(Iterable iterable) { return new SimpleFluentIterable<>(iterable); } - public static final FluentIterable fromCopyOf(Iterable iterable) { - List copy = FluentIterable.copyToList(iterable); + public static final FluentIterable fromCopyOf(Iterable iterable) { + List copy = FluentIterable.copyToList(iterable); return new SimpleFluentIterable<>(copy); } @Override - public Iterator iterator() { + public Iterator iterator() { return iterable.iterator(); } @Override - public void forEach(Consumer action) { + public void forEach(Consumer action) { iterable.forEach(action); } @Override - public Spliterator spliterator() { + public Spliterator spliterator() { return iterable.spliterator(); } @@ -175,7 +201,7 @@ public class SimpleFluentIterable implements FluentIterable { */ public final int getRemainingElementsCount() { int counter = 0; - Iterator iterator = iterator(); + Iterator iterator = iterator(); while (iterator.hasNext()) { iterator.next(); counter++; @@ -188,8 +214,8 @@ public class SimpleFluentIterable implements FluentIterable { * * @return a new List with the remaining objects. */ - public static List toList(Iterator iterator) { - List copy = new ArrayList<>(); + public static List toList(Iterator iterator) { + List copy = new ArrayList<>(); while (iterator.hasNext()) { copy.add(iterator.next()); } diff --git a/fluentinterface/src/test/java/com/iluwatar/fluentinterface/app/AppTest.java b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/app/AppTest.java index 29ad885c0..5b750c604 100644 --- a/fluentinterface/src/test/java/com/iluwatar/fluentinterface/app/AppTest.java +++ b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/app/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.fluentinterface.app; import org.junit.Test; -import com.iluwatar.fluentinterface.app.App; - public class AppTest { @Test diff --git a/fluentinterface/src/test/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterableTest.java b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterableTest.java new file mode 100644 index 000000000..4037bed49 --- /dev/null +++ b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/fluentiterable/FluentIterableTest.java @@ -0,0 +1,192 @@ +/** + * The MIT License + * Copyright (c) 2014 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.fluentinterface.fluentiterable; + +import org.junit.Test; + +import java.util.*; +import java.util.function.Consumer; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +/** + * Date: 12/12/15 - 7:00 PM + * + * @author Jeroen Meulemeester + */ +public abstract class FluentIterableTest { + + /** + * Create a new {@link FluentIterable} from the given integers + * + * @param integers The integers + * @return The new iterable, use for testing + */ + protected abstract FluentIterable createFluentIterable(final Iterable integers); + + @Test + public void testFirst() throws Exception { + final List integers = Arrays.asList(1, 2, 3, 10, 9, 8); + final Optional first = createFluentIterable(integers).first(); + assertNotNull(first); + assertTrue(first.isPresent()); + assertEquals(integers.get(0), first.get()); + } + + @Test + public void testFirstEmptyCollection() throws Exception { + final List integers = Collections.emptyList(); + final Optional first = createFluentIterable(integers).first(); + assertNotNull(first); + assertFalse(first.isPresent()); + } + + @Test + public void testFirstCount() throws Exception { + final List integers = Arrays.asList(1, 2, 3, 10, 9, 8); + final List first4 = createFluentIterable(integers) + .first(4) + .asList(); + + assertNotNull(first4); + assertEquals(4, first4.size()); + + assertEquals(integers.get(0), first4.get(0)); + assertEquals(integers.get(1), first4.get(1)); + assertEquals(integers.get(2), first4.get(2)); + assertEquals(integers.get(3), first4.get(3)); + } + + @Test + public void testFirstCountLessItems() throws Exception { + final List integers = Arrays.asList(1, 2, 3); + final List first4 = createFluentIterable(integers) + .first(4) + .asList(); + + assertNotNull(first4); + assertEquals(3, first4.size()); + + assertEquals(integers.get(0), first4.get(0)); + assertEquals(integers.get(1), first4.get(1)); + assertEquals(integers.get(2), first4.get(2)); + } + + @Test + public void testLast() throws Exception { + final List integers = Arrays.asList(1, 2, 3, 10, 9, 8); + final Optional last = createFluentIterable(integers).last(); + assertNotNull(last); + assertTrue(last.isPresent()); + assertEquals(integers.get(integers.size() - 1), last.get()); + } + + @Test + public void testLastEmptyCollection() throws Exception { + final List integers = Collections.emptyList(); + final Optional last = createFluentIterable(integers).last(); + assertNotNull(last); + assertFalse(last.isPresent()); + } + + @Test + public void testLastCount() throws Exception { + final List integers = Arrays.asList(1, 2, 3, 10, 9, 8); + final List last4 = createFluentIterable(integers) + .last(4) + .asList(); + + assertNotNull(last4); + assertEquals(4, last4.size()); + assertEquals(Integer.valueOf(3), last4.get(0)); + assertEquals(Integer.valueOf(10), last4.get(1)); + assertEquals(Integer.valueOf(9), last4.get(2)); + assertEquals(Integer.valueOf(8), last4.get(3)); + } + + @Test + public void testLastCountLessItems() throws Exception { + final List integers = Arrays.asList(1, 2, 3); + final List last4 = createFluentIterable(integers) + .last(4) + .asList(); + + assertNotNull(last4); + assertEquals(3, last4.size()); + + assertEquals(Integer.valueOf(1), last4.get(0)); + assertEquals(Integer.valueOf(2), last4.get(1)); + assertEquals(Integer.valueOf(3), last4.get(2)); + } + + @Test + public void testFilter() throws Exception { + final List integers = Arrays.asList(1, 2, 3, 10, 9, 8); + final List evenItems = createFluentIterable(integers) + .filter(i -> i % 2 == 0) + .asList(); + + assertNotNull(evenItems); + assertEquals(3, evenItems.size()); + assertEquals(Integer.valueOf(2), evenItems.get(0)); + assertEquals(Integer.valueOf(10), evenItems.get(1)); + assertEquals(Integer.valueOf(8), evenItems.get(2)); + } + + @Test + public void testMap() throws Exception { + final List integers = Arrays.asList(1, 2, 3); + final List longs = createFluentIterable(integers) + .map(Integer::longValue) + .asList(); + + assertNotNull(longs); + assertEquals(integers.size(), longs.size()); + assertEquals(Long.valueOf(1), longs.get(0)); + assertEquals(Long.valueOf(2), longs.get(1)); + assertEquals(Long.valueOf(3), longs.get(2)); + } + + @Test + public void testForEach() throws Exception { + final List integers = Arrays.asList(1, 2, 3); + + final Consumer consumer = mock(Consumer.class); + createFluentIterable(integers).forEach(consumer); + + verify(consumer, times(1)).accept(Integer.valueOf(1)); + verify(consumer, times(1)).accept(Integer.valueOf(2)); + verify(consumer, times(1)).accept(Integer.valueOf(3)); + verifyNoMoreInteractions(consumer); + + } + + @Test + public void testSpliterator() throws Exception { + final List integers = Arrays.asList(1, 2, 3); + final Spliterator split = createFluentIterable(integers).spliterator(); + assertNotNull(split); + } + +} \ No newline at end of file diff --git a/fluentinterface/src/test/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterableTest.java b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterableTest.java new file mode 100644 index 000000000..c422903c8 --- /dev/null +++ b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/fluentiterable/lazy/LazyFluentIterableTest.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 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.fluentinterface.fluentiterable.lazy; + +import com.iluwatar.fluentinterface.fluentiterable.FluentIterable; +import com.iluwatar.fluentinterface.fluentiterable.FluentIterableTest; + +/** + * Date: 12/12/15 - 7:56 PM + * + * @author Jeroen Meulemeester + */ +public class LazyFluentIterableTest extends FluentIterableTest { + + @Override + protected FluentIterable createFluentIterable(Iterable integers) { + return LazyFluentIterable.from(integers); + } + +} diff --git a/fluentinterface/src/test/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterableTest.java b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterableTest.java new file mode 100644 index 000000000..e4e80641c --- /dev/null +++ b/fluentinterface/src/test/java/com/iluwatar/fluentinterface/fluentiterable/simple/SimpleFluentIterableTest.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 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.fluentinterface.fluentiterable.simple; + +import com.iluwatar.fluentinterface.fluentiterable.FluentIterable; +import com.iluwatar.fluentinterface.fluentiterable.FluentIterableTest; + +/** + * Date: 12/12/15 - 7:56 PM + * + * @author Jeroen Meulemeester + */ +public class SimpleFluentIterableTest extends FluentIterableTest { + + @Override + protected FluentIterable createFluentIterable(Iterable integers) { + return SimpleFluentIterable.fromCopyOf(integers); + } + +} diff --git a/flux/index.md b/flux/README.md similarity index 79% rename from flux/index.md rename to flux/README.md index 227237168..7ac312c44 100644 --- a/flux/index.md +++ b/flux/README.md @@ -4,20 +4,24 @@ title: Flux folder: flux permalink: /patterns/flux/ categories: Presentation Tier -tags: Java +tags: + - Java + - Difficulty-Intermediate --- -**Intent:** Flux eschews MVC in favor of a unidirectional data flow. When a +## Intent +Flux eschews MVC in favor of a unidirectional data flow. When a user interacts with a view, the view propagates an action through a central dispatcher, to the various stores that hold the application's data and business logic, which updates all of the views that are affected. ![alt text](./etc/flux.png "Flux") -**Applicability:** Use the Flux pattern when +## Applicability +Use the Flux pattern when * you want to focus on creating explicit and understandable update paths for your application's data, which makes tracing changes during development simpler and makes bugs easier to track down and fix. -**Credits:** +## Credits * [Flux - Application architecture for building user interfaces](http://facebook.github.io/flux/) diff --git a/flux/pom.xml b/flux/pom.xml index f706b49ed..c07cca157 100644 --- a/flux/pom.xml +++ b/flux/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT flux @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/flux/src/main/java/com/iluwatar/flux/action/Action.java b/flux/src/main/java/com/iluwatar/flux/action/Action.java index 24d294be7..7fc0fe51a 100644 --- a/flux/src/main/java/com/iluwatar/flux/action/Action.java +++ b/flux/src/main/java/com/iluwatar/flux/action/Action.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flux.action; /** @@ -7,13 +29,13 @@ package com.iluwatar.flux.action; */ public abstract class Action { - private ActionType type; - - public Action(ActionType type) { - this.type = type; - } - - public ActionType getType() { - return type; - } + private ActionType type; + + public Action(ActionType type) { + this.type = type; + } + + public ActionType getType() { + return type; + } } diff --git a/flux/src/main/java/com/iluwatar/flux/action/ActionType.java b/flux/src/main/java/com/iluwatar/flux/action/ActionType.java index 318ca1b12..db8046d5e 100644 --- a/flux/src/main/java/com/iluwatar/flux/action/ActionType.java +++ b/flux/src/main/java/com/iluwatar/flux/action/ActionType.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flux.action; /** @@ -7,6 +29,6 @@ package com.iluwatar.flux.action; */ public enum ActionType { - MENU_ITEM_SELECTED, CONTENT_CHANGED; - + MENU_ITEM_SELECTED, CONTENT_CHANGED; + } diff --git a/flux/src/main/java/com/iluwatar/flux/action/Content.java b/flux/src/main/java/com/iluwatar/flux/action/Content.java index e53871d89..49a06b5ad 100644 --- a/flux/src/main/java/com/iluwatar/flux/action/Content.java +++ b/flux/src/main/java/com/iluwatar/flux/action/Content.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flux.action; /** @@ -6,17 +28,18 @@ package com.iluwatar.flux.action; * */ public enum Content { - - PRODUCTS("Products - This page lists the company's products."), COMPANY("Company - This page displays information about the company."); - - private String title; - private Content(String title) { - this.title = title; - } - - @Override - public String toString() { - return title; - } + PRODUCTS("Products - This page lists the company's products."), COMPANY( + "Company - This page displays information about the company."); + + private String title; + + private Content(String title) { + this.title = title; + } + + @Override + public String toString() { + return title; + } } diff --git a/flux/src/main/java/com/iluwatar/flux/action/ContentAction.java b/flux/src/main/java/com/iluwatar/flux/action/ContentAction.java index 2c1a40e6e..c066ba933 100644 --- a/flux/src/main/java/com/iluwatar/flux/action/ContentAction.java +++ b/flux/src/main/java/com/iluwatar/flux/action/ContentAction.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flux.action; /** @@ -7,14 +29,14 @@ package com.iluwatar.flux.action; */ public class ContentAction extends Action { - private Content content; + private Content content; - public ContentAction(Content content) { - super(ActionType.CONTENT_CHANGED); - this.content = content; - } - - public Content getContent() { - return content; - } + public ContentAction(Content content) { + super(ActionType.CONTENT_CHANGED); + this.content = content; + } + + public Content getContent() { + return content; + } } diff --git a/flux/src/main/java/com/iluwatar/flux/action/MenuAction.java b/flux/src/main/java/com/iluwatar/flux/action/MenuAction.java index a3dd9875e..09c720503 100644 --- a/flux/src/main/java/com/iluwatar/flux/action/MenuAction.java +++ b/flux/src/main/java/com/iluwatar/flux/action/MenuAction.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flux.action; @@ -8,14 +30,14 @@ package com.iluwatar.flux.action; */ public class MenuAction extends Action { - private MenuItem menuItem; + private MenuItem menuItem; - public MenuAction(MenuItem menuItem) { - super(ActionType.MENU_ITEM_SELECTED); - this.menuItem = menuItem; - } - - public MenuItem getMenuItem() { - return menuItem; - } + public MenuAction(MenuItem menuItem) { + super(ActionType.MENU_ITEM_SELECTED); + this.menuItem = menuItem; + } + + public MenuItem getMenuItem() { + return menuItem; + } } diff --git a/flux/src/main/java/com/iluwatar/flux/action/MenuItem.java b/flux/src/main/java/com/iluwatar/flux/action/MenuItem.java index d842fca78..ce8c5ed64 100644 --- a/flux/src/main/java/com/iluwatar/flux/action/MenuItem.java +++ b/flux/src/main/java/com/iluwatar/flux/action/MenuItem.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flux.action; /** @@ -6,17 +28,17 @@ package com.iluwatar.flux.action; * */ public enum MenuItem { - - HOME("Home"), PRODUCTS("Products"), COMPANY("Company"); - - private String title; - MenuItem(String title) { - this.title = title; - } - - @Override - public String toString() { - return title; - } + HOME("Home"), PRODUCTS("Products"), COMPANY("Company"); + + private String title; + + MenuItem(String title) { + this.title = title; + } + + @Override + public String toString() { + return title; + } } diff --git a/flux/src/main/java/com/iluwatar/flux/app/App.java b/flux/src/main/java/com/iluwatar/flux/app/App.java index a567a92d3..81aac980c 100644 --- a/flux/src/main/java/com/iluwatar/flux/app/App.java +++ b/flux/src/main/java/com/iluwatar/flux/app/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flux.app; import com.iluwatar.flux.action.MenuItem; @@ -9,43 +31,45 @@ import com.iluwatar.flux.view.MenuView; /** * - * Flux is the application architecture that Facebook uses for building client-side web - * applications. Flux eschews MVC in favor of a unidirectional data flow. When a user interacts with - * a React view, the view propagates an action through a central dispatcher, to the various stores that - * hold the application's data and business logic, which updates all of the views that are affected. + * Flux is the application architecture that Facebook uses for building client-side web + * applications. Flux eschews MVC in favor of a unidirectional data flow. When a user interacts with + * a React view, the view propagates an action through a central dispatcher, to the various stores + * that hold the application's data and business logic, which updates all of the views that are + * affected. *

- * This example has two views: menu and content. They represent typical main menu and content area of - * a web page. When menu item is clicked it triggers events through the dispatcher. The events are - * received and handled by the stores updating their data as needed. The stores then notify the views - * that they should rerender themselves. + * This example has two views: menu and content. They represent typical main menu and content area + * of a web page. When menu item is clicked it triggers events through the dispatcher. The events + * are received and handled by the stores updating their data as needed. The stores then notify the + * views that they should rerender themselves. *

* http://facebook.github.io/flux/docs/overview.html * */ public class App { - - /** - * Program entry point - * @param args command line args - */ - public static void main( String[] args ) { - - // initialize and wire the system - MenuStore menuStore = new MenuStore(); - Dispatcher.getInstance().registerStore(menuStore); - ContentStore contentStore = new ContentStore(); - Dispatcher.getInstance().registerStore(contentStore); - MenuView menuView = new MenuView(); - menuStore.registerView(menuView); - ContentView contentView = new ContentView(); - contentStore.registerView(contentView); - - // render initial view - menuView.render(); - contentView.render(); - - // user clicks another menu item - // this triggers action dispatching and eventually causes views to render with new content - menuView.itemClicked(MenuItem.COMPANY); - } + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + + // initialize and wire the system + MenuStore menuStore = new MenuStore(); + Dispatcher.getInstance().registerStore(menuStore); + ContentStore contentStore = new ContentStore(); + Dispatcher.getInstance().registerStore(contentStore); + MenuView menuView = new MenuView(); + menuStore.registerView(menuView); + ContentView contentView = new ContentView(); + contentStore.registerView(contentView); + + // render initial view + menuView.render(); + contentView.render(); + + // user clicks another menu item + // this triggers action dispatching and eventually causes views to render with new content + menuView.itemClicked(MenuItem.COMPANY); + } } diff --git a/flux/src/main/java/com/iluwatar/flux/dispatcher/Dispatcher.java b/flux/src/main/java/com/iluwatar/flux/dispatcher/Dispatcher.java index 8bf03e4b0..10f064efa 100644 --- a/flux/src/main/java/com/iluwatar/flux/dispatcher/Dispatcher.java +++ b/flux/src/main/java/com/iluwatar/flux/dispatcher/Dispatcher.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flux.dispatcher; import java.util.LinkedList; @@ -15,38 +37,40 @@ import com.iluwatar.flux.store.Store; * Dispatcher sends Actions to registered Stores. * */ -public class Dispatcher { - - private static Dispatcher instance = new Dispatcher(); - - private List stores = new LinkedList<>(); - - private Dispatcher() { - } +public final class Dispatcher { - public static Dispatcher getInstance() { - return instance; - } - - public void registerStore(Store store) { - stores.add(store); - } - - public void menuItemSelected(MenuItem menuItem) { - dispatchAction(new MenuAction(menuItem)); - switch (menuItem) { - case HOME: - case PRODUCTS: - default: - dispatchAction(new ContentAction(Content.PRODUCTS)); - break; - case COMPANY: - dispatchAction(new ContentAction(Content.COMPANY)); - break; - } - } - - private void dispatchAction(Action action) { - stores.stream().forEach((store) -> store.onAction(action)); - } + private static Dispatcher instance = new Dispatcher(); + + private List stores = new LinkedList<>(); + + private Dispatcher() {} + + public static Dispatcher getInstance() { + return instance; + } + + public void registerStore(Store store) { + stores.add(store); + } + + /** + * Menu item selected handler + */ + public void menuItemSelected(MenuItem menuItem) { + dispatchAction(new MenuAction(menuItem)); + switch (menuItem) { + case HOME: + case PRODUCTS: + default: + dispatchAction(new ContentAction(Content.PRODUCTS)); + break; + case COMPANY: + dispatchAction(new ContentAction(Content.COMPANY)); + break; + } + } + + private void dispatchAction(Action action) { + stores.stream().forEach(store -> store.onAction(action)); + } } diff --git a/flux/src/main/java/com/iluwatar/flux/store/ContentStore.java b/flux/src/main/java/com/iluwatar/flux/store/ContentStore.java index 15d031abd..bca8e29bf 100644 --- a/flux/src/main/java/com/iluwatar/flux/store/ContentStore.java +++ b/flux/src/main/java/com/iluwatar/flux/store/ContentStore.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flux.store; import com.iluwatar.flux.action.Action; @@ -12,18 +34,18 @@ import com.iluwatar.flux.action.ContentAction; */ public class ContentStore extends Store { - private Content content = Content.PRODUCTS; + private Content content = Content.PRODUCTS; - @Override - public void onAction(Action action) { - if (action.getType().equals(ActionType.CONTENT_CHANGED)) { - ContentAction contentAction = (ContentAction) action; - content = contentAction.getContent(); - notifyChange(); - } - } - - public Content getContent() { - return content; - } + @Override + public void onAction(Action action) { + if (action.getType().equals(ActionType.CONTENT_CHANGED)) { + ContentAction contentAction = (ContentAction) action; + content = contentAction.getContent(); + notifyChange(); + } + } + + public Content getContent() { + return content; + } } diff --git a/flux/src/main/java/com/iluwatar/flux/store/MenuStore.java b/flux/src/main/java/com/iluwatar/flux/store/MenuStore.java index 3e614ac73..2b3b418b3 100644 --- a/flux/src/main/java/com/iluwatar/flux/store/MenuStore.java +++ b/flux/src/main/java/com/iluwatar/flux/store/MenuStore.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flux.store; import com.iluwatar.flux.action.Action; @@ -12,18 +34,18 @@ import com.iluwatar.flux.action.MenuItem; */ public class MenuStore extends Store { - private MenuItem selected = MenuItem.HOME; - - @Override - public void onAction(Action action) { - if (action.getType().equals(ActionType.MENU_ITEM_SELECTED)) { - MenuAction menuAction = (MenuAction) action; - selected = menuAction.getMenuItem(); - notifyChange(); - } - } - - public MenuItem getSelected() { - return selected; - } + private MenuItem selected = MenuItem.HOME; + + @Override + public void onAction(Action action) { + if (action.getType().equals(ActionType.MENU_ITEM_SELECTED)) { + MenuAction menuAction = (MenuAction) action; + selected = menuAction.getMenuItem(); + notifyChange(); + } + } + + public MenuItem getSelected() { + return selected; + } } diff --git a/flux/src/main/java/com/iluwatar/flux/store/Store.java b/flux/src/main/java/com/iluwatar/flux/store/Store.java index 326af404b..f7cc2ccbf 100644 --- a/flux/src/main/java/com/iluwatar/flux/store/Store.java +++ b/flux/src/main/java/com/iluwatar/flux/store/Store.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flux.store; import java.util.LinkedList; @@ -12,16 +34,16 @@ import com.iluwatar.flux.view.View; * */ public abstract class Store { - - private List views = new LinkedList<>(); - - public abstract void onAction(Action action); - public void registerView(View view) { - views.add(view); - } - - protected void notifyChange() { - views.stream().forEach((view) -> view.storeChanged(this)); - } + private List views = new LinkedList<>(); + + public abstract void onAction(Action action); + + public void registerView(View view) { + views.add(view); + } + + protected void notifyChange() { + views.stream().forEach(view -> view.storeChanged(this)); + } } diff --git a/flux/src/main/java/com/iluwatar/flux/view/ContentView.java b/flux/src/main/java/com/iluwatar/flux/view/ContentView.java index b12c8b7a2..cb351bfae 100644 --- a/flux/src/main/java/com/iluwatar/flux/view/ContentView.java +++ b/flux/src/main/java/com/iluwatar/flux/view/ContentView.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flux.view; import com.iluwatar.flux.action.Content; @@ -11,17 +33,17 @@ import com.iluwatar.flux.store.Store; */ public class ContentView implements View { - private Content content = Content.PRODUCTS; + private Content content = Content.PRODUCTS; - @Override - public void storeChanged(Store store) { - ContentStore contentStore = (ContentStore) store; - content = contentStore.getContent(); - render(); - } + @Override + public void storeChanged(Store store) { + ContentStore contentStore = (ContentStore) store; + content = contentStore.getContent(); + render(); + } - @Override - public void render() { - System.out.println(content.toString()); - } + @Override + public void render() { + System.out.println(content.toString()); + } } diff --git a/flux/src/main/java/com/iluwatar/flux/view/MenuView.java b/flux/src/main/java/com/iluwatar/flux/view/MenuView.java index f37d21bb9..6cd9005dc 100644 --- a/flux/src/main/java/com/iluwatar/flux/view/MenuView.java +++ b/flux/src/main/java/com/iluwatar/flux/view/MenuView.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flux.view; import com.iluwatar.flux.action.MenuItem; @@ -12,27 +34,27 @@ import com.iluwatar.flux.store.Store; */ public class MenuView implements View { - private MenuItem selected = MenuItem.HOME; - - @Override - public void storeChanged(Store store) { - MenuStore menuStore = (MenuStore) store; - selected = menuStore.getSelected(); - render(); - } + private MenuItem selected = MenuItem.HOME; - @Override - public void render() { - for (MenuItem item: MenuItem.values()) { - if (selected.equals(item)) { - System.out.println(String.format("* %s", item.toString())); - } else { - System.out.println(item.toString()); - } - } - } - - public void itemClicked(MenuItem item) { - Dispatcher.getInstance().menuItemSelected(item); - } + @Override + public void storeChanged(Store store) { + MenuStore menuStore = (MenuStore) store; + selected = menuStore.getSelected(); + render(); + } + + @Override + public void render() { + for (MenuItem item : MenuItem.values()) { + if (selected.equals(item)) { + System.out.println(String.format("* %s", item.toString())); + } else { + System.out.println(item.toString()); + } + } + } + + public void itemClicked(MenuItem item) { + Dispatcher.getInstance().menuItemSelected(item); + } } diff --git a/flux/src/main/java/com/iluwatar/flux/view/View.java b/flux/src/main/java/com/iluwatar/flux/view/View.java index 4eb6ee3fb..0a34f1fdf 100644 --- a/flux/src/main/java/com/iluwatar/flux/view/View.java +++ b/flux/src/main/java/com/iluwatar/flux/view/View.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flux.view; import com.iluwatar.flux.store.Store; @@ -9,7 +31,7 @@ import com.iluwatar.flux.store.Store; */ public interface View { - public void storeChanged(Store store); + void storeChanged(Store store); - public void render(); + void render(); } diff --git a/flux/src/test/java/com/iluwatar/flux/action/ContentTest.java b/flux/src/test/java/com/iluwatar/flux/action/ContentTest.java new file mode 100644 index 000000000..90e66d69a --- /dev/null +++ b/flux/src/test/java/com/iluwatar/flux/action/ContentTest.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flux.action; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/12/15 - 10:11 PM + * + * @author Jeroen Meulemeester + */ +public class ContentTest { + + @Test + public void testToString() throws Exception { + for (final Content content : Content.values()) { + final String toString = content.toString(); + assertNotNull(toString); + assertFalse(toString.trim().isEmpty()); + } + } + +} diff --git a/flux/src/test/java/com/iluwatar/flux/action/MenuItemTest.java b/flux/src/test/java/com/iluwatar/flux/action/MenuItemTest.java new file mode 100644 index 000000000..02c8e972b --- /dev/null +++ b/flux/src/test/java/com/iluwatar/flux/action/MenuItemTest.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flux.action; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/12/15 - 10:15 PM + * + * @author Jeroen Meulemeester + */ +public class MenuItemTest { + + @Test + public void testToString() throws Exception { + for (final MenuItem menuItem : MenuItem.values()) { + final String toString = menuItem.toString(); + assertNotNull(toString); + assertFalse(toString.trim().isEmpty()); + } + } + +} diff --git a/flux/src/test/java/com/iluwatar/flux/app/AppTest.java b/flux/src/test/java/com/iluwatar/flux/app/AppTest.java index ba4b592a1..f25beaefa 100644 --- a/flux/src/test/java/com/iluwatar/flux/app/AppTest.java +++ b/flux/src/test/java/com/iluwatar/flux/app/AppTest.java @@ -1,19 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flux.app; import org.junit.Test; -import com.iluwatar.flux.app.App; - /** * * Application test * */ public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } + + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/flux/src/test/java/com/iluwatar/flux/dispatcher/DispatcherTest.java b/flux/src/test/java/com/iluwatar/flux/dispatcher/DispatcherTest.java new file mode 100644 index 000000000..9f3f610d1 --- /dev/null +++ b/flux/src/test/java/com/iluwatar/flux/dispatcher/DispatcherTest.java @@ -0,0 +1,113 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flux.dispatcher; + +import com.iluwatar.flux.action.Action; +import com.iluwatar.flux.action.ActionType; +import com.iluwatar.flux.action.Content; +import com.iluwatar.flux.action.ContentAction; +import com.iluwatar.flux.action.MenuAction; +import com.iluwatar.flux.action.MenuItem; +import com.iluwatar.flux.store.Store; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.util.List; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/12/15 - 8:22 PM + * + * @author Jeroen Meulemeester + */ +public class DispatcherTest { + + /** + * Dispatcher is a singleton with no way to reset it's internal state back to the beginning. + * Replace the instance with a fresh one before each test to make sure test cases have no + * influence on each other. + */ + @Before + public void setUp() throws Exception { + final Constructor constructor; + constructor = Dispatcher.class.getDeclaredConstructor(); + constructor.setAccessible(true); + + final Field field = Dispatcher.class.getDeclaredField("instance"); + field.setAccessible(true); + field.set(Dispatcher.getInstance(), constructor.newInstance()); + } + + @Test + public void testGetInstance() throws Exception { + assertNotNull(Dispatcher.getInstance()); + assertSame(Dispatcher.getInstance(), Dispatcher.getInstance()); + } + + @Test + public void testMenuItemSelected() throws Exception { + final Dispatcher dispatcher = Dispatcher.getInstance(); + + final Store store = mock(Store.class); + dispatcher.registerStore(store); + dispatcher.menuItemSelected(MenuItem.HOME); + dispatcher.menuItemSelected(MenuItem.COMPANY); + + // We expect 4 events, 2 menu selections and 2 content change actions + final ArgumentCaptor actionCaptor = ArgumentCaptor.forClass(Action.class); + verify(store, times(4)).onAction(actionCaptor.capture()); + verifyNoMoreInteractions(store); + + final List actions = actionCaptor.getAllValues(); + final List menuActions = actions.stream() + .filter(a -> a.getType().equals(ActionType.MENU_ITEM_SELECTED)) + .map(a -> (MenuAction) a) + .collect(Collectors.toList()); + + final List contentActions = actions.stream() + .filter(a -> a.getType().equals(ActionType.CONTENT_CHANGED)) + .map(a -> (ContentAction) a) + .collect(Collectors.toList()); + + assertEquals(2, menuActions.size()); + assertEquals(1, menuActions.stream().map(MenuAction::getMenuItem).filter(MenuItem.HOME::equals).count()); + assertEquals(1, menuActions.stream().map(MenuAction::getMenuItem).filter(MenuItem.COMPANY::equals).count()); + + assertEquals(2, contentActions.size()); + assertEquals(1, contentActions.stream().map(ContentAction::getContent).filter(Content.PRODUCTS::equals).count()); + assertEquals(1, contentActions.stream().map(ContentAction::getContent).filter(Content.COMPANY::equals).count()); + + } + +} diff --git a/flux/src/test/java/com/iluwatar/flux/store/ContentStoreTest.java b/flux/src/test/java/com/iluwatar/flux/store/ContentStoreTest.java new file mode 100644 index 000000000..6e2cc5b35 --- /dev/null +++ b/flux/src/test/java/com/iluwatar/flux/store/ContentStoreTest.java @@ -0,0 +1,68 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flux.store; + +import com.iluwatar.flux.action.Content; +import com.iluwatar.flux.action.ContentAction; +import com.iluwatar.flux.action.MenuAction; +import com.iluwatar.flux.action.MenuItem; +import com.iluwatar.flux.view.View; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/12/15 - 10:18 PM + * + * @author Jeroen Meulemeester + */ +public class ContentStoreTest { + + @Test + public void testOnAction() throws Exception { + final ContentStore contentStore = new ContentStore(); + + final View view = mock(View.class); + contentStore.registerView(view); + + verifyZeroInteractions(view); + + // Content should not react on menu action ... + contentStore.onAction(new MenuAction(MenuItem.PRODUCTS)); + verifyZeroInteractions(view); + + // ... but it should react on a content action + contentStore.onAction(new ContentAction(Content.COMPANY)); + verify(view, times(1)).storeChanged(eq(contentStore)); + verifyNoMoreInteractions(view); + assertEquals(Content.COMPANY, contentStore.getContent()); + + } + +} diff --git a/flux/src/test/java/com/iluwatar/flux/store/MenuStoreTest.java b/flux/src/test/java/com/iluwatar/flux/store/MenuStoreTest.java new file mode 100644 index 000000000..7d68656a2 --- /dev/null +++ b/flux/src/test/java/com/iluwatar/flux/store/MenuStoreTest.java @@ -0,0 +1,68 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flux.store; + +import com.iluwatar.flux.action.Content; +import com.iluwatar.flux.action.ContentAction; +import com.iluwatar.flux.action.MenuAction; +import com.iluwatar.flux.action.MenuItem; +import com.iluwatar.flux.view.View; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/12/15 - 10:18 PM + * + * @author Jeroen Meulemeester + */ +public class MenuStoreTest { + + @Test + public void testOnAction() throws Exception { + final MenuStore menuStore = new MenuStore(); + + final View view = mock(View.class); + menuStore.registerView(view); + + verifyZeroInteractions(view); + + // Menu should not react on content action ... + menuStore.onAction(new ContentAction(Content.COMPANY)); + verifyZeroInteractions(view); + + // ... but it should react on a menu action + menuStore.onAction(new MenuAction(MenuItem.PRODUCTS)); + verify(view, times(1)).storeChanged(eq(menuStore)); + verifyNoMoreInteractions(view); + assertEquals(MenuItem.PRODUCTS, menuStore.getSelected()); + + } + +} diff --git a/flux/src/test/java/com/iluwatar/flux/view/ContentViewTest.java b/flux/src/test/java/com/iluwatar/flux/view/ContentViewTest.java new file mode 100644 index 000000000..cb26f97d6 --- /dev/null +++ b/flux/src/test/java/com/iluwatar/flux/view/ContentViewTest.java @@ -0,0 +1,54 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flux.view; + +import com.iluwatar.flux.action.Content; +import com.iluwatar.flux.store.ContentStore; +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/12/15 - 10:31 PM + * + * @author Jeroen Meulemeester + */ +public class ContentViewTest { + + @Test + public void testStoreChanged() throws Exception { + final ContentStore store = mock(ContentStore.class); + when(store.getContent()).thenReturn(Content.PRODUCTS); + + final ContentView view = new ContentView(); + view.storeChanged(store); + + verify(store, times(1)).getContent(); + verifyNoMoreInteractions(store); + } + +} diff --git a/flux/src/test/java/com/iluwatar/flux/view/MenuViewTest.java b/flux/src/test/java/com/iluwatar/flux/view/MenuViewTest.java new file mode 100644 index 000000000..8a5d62f24 --- /dev/null +++ b/flux/src/test/java/com/iluwatar/flux/view/MenuViewTest.java @@ -0,0 +1,71 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flux.view; + +import com.iluwatar.flux.action.Action; +import com.iluwatar.flux.action.MenuItem; +import com.iluwatar.flux.dispatcher.Dispatcher; +import com.iluwatar.flux.store.MenuStore; +import com.iluwatar.flux.store.Store; +import org.junit.Test; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +/** + * Date: 12/12/15 - 10:31 PM + * + * @author Jeroen Meulemeester + */ +public class MenuViewTest { + + @Test + public void testStoreChanged() throws Exception { + final MenuStore store = mock(MenuStore.class); + when(store.getSelected()).thenReturn(MenuItem.HOME); + + final MenuView view = new MenuView(); + view.storeChanged(store); + + verify(store, times(1)).getSelected(); + verifyNoMoreInteractions(store); + } + + @Test + public void testItemClicked() throws Exception { + final Store store = mock(Store.class); + Dispatcher.getInstance().registerStore(store); + + final MenuView view = new MenuView(); + view.itemClicked(MenuItem.PRODUCTS); + + // We should receive a menu click action and a content changed action + verify(store, times(2)).onAction(any(Action.class)); + + } + +} diff --git a/flyweight/index.md b/flyweight/README.md similarity index 81% rename from flyweight/index.md rename to flyweight/README.md index e2273c197..a98dced8e 100644 --- a/flyweight/index.md +++ b/flyweight/README.md @@ -7,14 +7,18 @@ categories: Structural tags: - Java - Gang Of Four + - Difficulty-Intermediate + - Performance --- -**Intent:** Use sharing to support large numbers of fine-grained objects +## Intent +Use sharing to support large numbers of fine-grained objects efficiently. ![alt text](./etc/flyweight_1.png "Flyweight") -**Applicability:** The Flyweight pattern's effectiveness depends heavily on how +## Applicability +The Flyweight pattern's effectiveness depends heavily on how and where it's used. Apply the Flyweight pattern when all of the following are true @@ -24,10 +28,10 @@ true * many groups of objects may be replaced by relatively few shared objects once extrinsic state is removed * the application doesn't depend on object identity. Since flyweight objects may be shared, identity tests will return true for conceptually distinct objects. -**Real world examples:** +## Real world examples * [java.lang.Integer#valueOf(int)](http://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html#valueOf%28int%29) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/flyweight/pom.xml b/flyweight/pom.xml index 7c0f3e2b0..959fa5548 100644 --- a/flyweight/pom.xml +++ b/flyweight/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT flyweight diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/AlchemistShop.java b/flyweight/src/main/java/com/iluwatar/flyweight/AlchemistShop.java index e87f2e6cf..507de7a6a 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/AlchemistShop.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/AlchemistShop.java @@ -1,57 +1,103 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flyweight; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** * - * AlchemistShop holds potions on its shelves. - * It uses PotionFactory to provide the potions. + * AlchemistShop holds potions on its shelves. It uses PotionFactory to provide the potions. * */ public class AlchemistShop { - private List topShelf; - private List bottomShelf; + private List topShelf; + private List bottomShelf; - public AlchemistShop() { - topShelf = new ArrayList<>(); - bottomShelf = new ArrayList<>(); - fillShelves(); - } + /** + * Constructor + */ + public AlchemistShop() { + topShelf = new ArrayList<>(); + bottomShelf = new ArrayList<>(); + fillShelves(); + } - private void fillShelves() { + private void fillShelves() { - PotionFactory factory = new PotionFactory(); + PotionFactory factory = new PotionFactory(); - topShelf.add(factory.createPotion(PotionType.INVISIBILITY)); - topShelf.add(factory.createPotion(PotionType.INVISIBILITY)); - topShelf.add(factory.createPotion(PotionType.STRENGTH)); - topShelf.add(factory.createPotion(PotionType.HEALING)); - topShelf.add(factory.createPotion(PotionType.INVISIBILITY)); - topShelf.add(factory.createPotion(PotionType.STRENGTH)); - topShelf.add(factory.createPotion(PotionType.HEALING)); - topShelf.add(factory.createPotion(PotionType.HEALING)); + topShelf.add(factory.createPotion(PotionType.INVISIBILITY)); + topShelf.add(factory.createPotion(PotionType.INVISIBILITY)); + topShelf.add(factory.createPotion(PotionType.STRENGTH)); + topShelf.add(factory.createPotion(PotionType.HEALING)); + topShelf.add(factory.createPotion(PotionType.INVISIBILITY)); + topShelf.add(factory.createPotion(PotionType.STRENGTH)); + topShelf.add(factory.createPotion(PotionType.HEALING)); + topShelf.add(factory.createPotion(PotionType.HEALING)); - bottomShelf.add(factory.createPotion(PotionType.POISON)); - bottomShelf.add(factory.createPotion(PotionType.POISON)); - bottomShelf.add(factory.createPotion(PotionType.POISON)); - bottomShelf.add(factory.createPotion(PotionType.HOLY_WATER)); - bottomShelf.add(factory.createPotion(PotionType.HOLY_WATER)); - } + bottomShelf.add(factory.createPotion(PotionType.POISON)); + bottomShelf.add(factory.createPotion(PotionType.POISON)); + bottomShelf.add(factory.createPotion(PotionType.POISON)); + bottomShelf.add(factory.createPotion(PotionType.HOLY_WATER)); + bottomShelf.add(factory.createPotion(PotionType.HOLY_WATER)); + } - public void enumerate() { + /** + * Get a read-only list of all the items on the top shelf + * + * @return The top shelf potions + */ + public final List getTopShelf() { + return Collections.unmodifiableList(this.topShelf); + } - System.out.println("Enumerating top shelf potions\n"); + /** + * Get a read-only list of all the items on the bottom shelf + * + * @return The bottom shelf potions + */ + public final List getBottomShelf() { + return Collections.unmodifiableList(this.bottomShelf); + } - for (Potion p : topShelf) { - p.drink(); - } + /** + * Enumerate potions + */ + public void enumerate() { - System.out.println("\nEnumerating bottom shelf potions\n"); + System.out.println("Enumerating top shelf potions\n"); - for (Potion p : bottomShelf) { - p.drink(); - } - } + for (Potion p : topShelf) { + p.drink(); + } + + System.out.println("\nEnumerating bottom shelf potions\n"); + + for (Potion p : bottomShelf) { + p.drink(); + } + } } diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/App.java b/flyweight/src/main/java/com/iluwatar/flyweight/App.java index c08ba78a3..49c17e465 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/App.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/App.java @@ -1,27 +1,50 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flyweight; /** * - * Flyweight pattern is useful when the program needs a huge amount of objects. - * It provides means to decrease resource usage by sharing object instances. + * Flyweight pattern is useful when the program needs a huge amount of objects. It provides means to + * decrease resource usage by sharing object instances. *

- * In this example {@link AlchemistShop} has great amount of potions on its shelves. - * To fill the shelves {@link AlchemistShop} uses {@link PotionFactory} (which represents - * the Flyweight in this example). Internally {@link PotionFactory} holds a map - * of the potions and lazily creates new ones when requested. + * In this example {@link AlchemistShop} has great amount of potions on its shelves. To fill the + * shelves {@link AlchemistShop} uses {@link PotionFactory} (which represents the Flyweight in this + * example). Internally {@link PotionFactory} holds a map of the potions and lazily creates new ones + * when requested. *

- * To enable safe sharing, between clients and threads, Flyweight objects must - * be immutable. Flyweight objects are by definition value objects. + * To enable safe sharing, between clients and threads, Flyweight objects must be immutable. + * Flyweight objects are by definition value objects. * */ public class App { - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { - AlchemistShop alchemistShop = new AlchemistShop(); - alchemistShop.enumerate(); - } + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + AlchemistShop alchemistShop = new AlchemistShop(); + alchemistShop.enumerate(); + } } diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/HealingPotion.java b/flyweight/src/main/java/com/iluwatar/flyweight/HealingPotion.java index a5f8f4fb8..464675a61 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/HealingPotion.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/HealingPotion.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flyweight; /** @@ -7,10 +29,8 @@ package com.iluwatar.flyweight; */ public class HealingPotion implements Potion { - @Override - public void drink() { - System.out.println("You feel healed. (Potion=" - + System.identityHashCode(this) + ")"); - } - + @Override + public void drink() { + System.out.println("You feel healed. (Potion=" + System.identityHashCode(this) + ")"); + } } diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/HolyWaterPotion.java b/flyweight/src/main/java/com/iluwatar/flyweight/HolyWaterPotion.java index 750e3c568..b05b4af11 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/HolyWaterPotion.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/HolyWaterPotion.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flyweight; /** @@ -7,10 +29,8 @@ package com.iluwatar.flyweight; */ public class HolyWaterPotion implements Potion { - @Override - public void drink() { - System.out.println("You feel blessed. (Potion=" - + System.identityHashCode(this) + ")"); - } - + @Override + public void drink() { + System.out.println("You feel blessed. (Potion=" + System.identityHashCode(this) + ")"); + } } diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/InvisibilityPotion.java b/flyweight/src/main/java/com/iluwatar/flyweight/InvisibilityPotion.java index db9d261d5..5aeb5d3a4 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/InvisibilityPotion.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/InvisibilityPotion.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flyweight; /** @@ -7,10 +29,8 @@ package com.iluwatar.flyweight; */ public class InvisibilityPotion implements Potion { - @Override - public void drink() { - System.out.println("You become invisible. (Potion=" - + System.identityHashCode(this) + ")"); - } - + @Override + public void drink() { + System.out.println("You become invisible. (Potion=" + System.identityHashCode(this) + ")"); + } } diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/PoisonPotion.java b/flyweight/src/main/java/com/iluwatar/flyweight/PoisonPotion.java index dfcd3c38d..a9d13088e 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/PoisonPotion.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/PoisonPotion.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flyweight; /** @@ -7,10 +29,8 @@ package com.iluwatar.flyweight; */ public class PoisonPotion implements Potion { - @Override - public void drink() { - System.out.println("Urgh! This is poisonous. (Potion=" - + System.identityHashCode(this) + ")"); - } - + @Override + public void drink() { + System.out.println("Urgh! This is poisonous. (Potion=" + System.identityHashCode(this) + ")"); + } } diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/Potion.java b/flyweight/src/main/java/com/iluwatar/flyweight/Potion.java index c4110201c..d2520c316 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/Potion.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/Potion.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flyweight; /** @@ -7,5 +29,5 @@ package com.iluwatar.flyweight; */ public interface Potion { - void drink(); + void drink(); } diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/PotionFactory.java b/flyweight/src/main/java/com/iluwatar/flyweight/PotionFactory.java index 20ec110e3..436095081 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/PotionFactory.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/PotionFactory.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flyweight; import java.util.EnumMap; @@ -5,48 +27,47 @@ import java.util.Map; /** * - * PotionFactory is the Flyweight in this example. - * It minimizes memory use by sharing object instances. - * It holds a map of potion instances and new potions - * are created only when none of the type already exists. + * PotionFactory is the Flyweight in this example. It minimizes memory use by sharing object + * instances. It holds a map of potion instances and new potions are created only when none of the + * type already exists. * */ public class PotionFactory { - private final Map potions; + private final Map potions; - public PotionFactory() { - potions = new EnumMap<>(PotionType.class); - } + public PotionFactory() { + potions = new EnumMap<>(PotionType.class); + } - Potion createPotion(PotionType type) { - Potion potion = potions.get(type); - if (potion == null) { - switch (type) { - case HEALING: - potion = new HealingPotion(); - potions.put(type, potion); - break; - case HOLY_WATER: - potion = new HolyWaterPotion(); - potions.put(type, potion); - break; - case INVISIBILITY: - potion = new InvisibilityPotion(); - potions.put(type, potion); - break; - case POISON: - potion = new PoisonPotion(); - potions.put(type, potion); - break; - case STRENGTH: - potion = new StrengthPotion(); - potions.put(type, potion); - break; - default: - break; - } - } - return potion; - } + Potion createPotion(PotionType type) { + Potion potion = potions.get(type); + if (potion == null) { + switch (type) { + case HEALING: + potion = new HealingPotion(); + potions.put(type, potion); + break; + case HOLY_WATER: + potion = new HolyWaterPotion(); + potions.put(type, potion); + break; + case INVISIBILITY: + potion = new InvisibilityPotion(); + potions.put(type, potion); + break; + case POISON: + potion = new PoisonPotion(); + potions.put(type, potion); + break; + case STRENGTH: + potion = new StrengthPotion(); + potions.put(type, potion); + break; + default: + break; + } + } + return potion; + } } diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/PotionType.java b/flyweight/src/main/java/com/iluwatar/flyweight/PotionType.java index bbb9b6521..89c6fdbe3 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/PotionType.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/PotionType.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flyweight; /** @@ -7,5 +29,5 @@ package com.iluwatar.flyweight; */ public enum PotionType { - HEALING, INVISIBILITY, STRENGTH, HOLY_WATER, POISON + HEALING, INVISIBILITY, STRENGTH, HOLY_WATER, POISON } diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/StrengthPotion.java b/flyweight/src/main/java/com/iluwatar/flyweight/StrengthPotion.java index 49083cf7c..2c21e7df1 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/StrengthPotion.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/StrengthPotion.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flyweight; /** @@ -7,9 +29,8 @@ package com.iluwatar.flyweight; */ public class StrengthPotion implements Potion { - @Override - public void drink() { - System.out.println("You feel strong. (Potion=" - + System.identityHashCode(this) + ")"); - } + @Override + public void drink() { + System.out.println("You feel strong. (Potion=" + System.identityHashCode(this) + ")"); + } } diff --git a/flyweight/src/test/java/com/iluwatar/flyweight/AlchemistShopTest.java b/flyweight/src/test/java/com/iluwatar/flyweight/AlchemistShopTest.java new file mode 100644 index 000000000..efb0a6e2e --- /dev/null +++ b/flyweight/src/test/java/com/iluwatar/flyweight/AlchemistShopTest.java @@ -0,0 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flyweight; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/12/15 - 10:54 PM + * + * @author Jeroen Meulemeester + */ +public class AlchemistShopTest { + + @Test + public void testShop() throws Exception { + final AlchemistShop shop = new AlchemistShop(); + + final List bottomShelf = shop.getBottomShelf(); + assertNotNull(bottomShelf); + assertEquals(5, bottomShelf.size()); + + final List topShelf = shop.getTopShelf(); + assertNotNull(topShelf); + assertEquals(8, topShelf.size()); + + final List allPotions = new ArrayList<>(); + allPotions.addAll(topShelf); + allPotions.addAll(bottomShelf); + + // There are 13 potion instances, but only 5 unique instance types + assertEquals(13, allPotions.size()); + assertEquals(5, allPotions.stream().map(System::identityHashCode).distinct().count()); + + } + +} diff --git a/flyweight/src/test/java/com/iluwatar/flyweight/AppTest.java b/flyweight/src/test/java/com/iluwatar/flyweight/AppTest.java index f3b033ba7..282311920 100644 --- a/flyweight/src/test/java/com/iluwatar/flyweight/AppTest.java +++ b/flyweight/src/test/java/com/iluwatar/flyweight/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.flyweight; import org.junit.Test; -import com.iluwatar.flyweight.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.flyweight.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/front-controller/index.md b/front-controller/README.md similarity index 83% rename from front-controller/index.md rename to front-controller/README.md index ba593a157..a462a08e0 100644 --- a/front-controller/index.md +++ b/front-controller/README.md @@ -4,26 +4,30 @@ title: Front Controller folder: front-controller permalink: /patterns/front-controller/ categories: Presentation Tier -tags: Java +tags: + - Java + - Difficulty-Intermediate --- -**Intent:** Introduce a common handler for all requests for a web site. This +## Intent +Introduce a common handler for all requests for a web site. This way we can encapsulate common functionality such as security, internationalization, routing and logging in a single place. ![alt text](./etc/front-controller.png "Front Controller") -**Applicability:** Use the Front Controller pattern when +## Applicability +Use the Front Controller pattern when * you want to encapsulate common request handling functionality in single place * you want to implements dynamic request handling i.e. change routing without modifying code * make web server configuration portable, you only need to register the handler web server specific way -**Real world examples:** +## Real world examples * [Apache Struts](https://struts.apache.org/) -**Credits:** +## Credits * [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) * [Presentation Tier Patterns](http://www.javagyan.com/tutorials/corej2eepatterns/presentation-tier-patterns) diff --git a/front-controller/pom.xml b/front-controller/pom.xml index c80121df2..2a448cffe 100644 --- a/front-controller/pom.xml +++ b/front-controller/pom.xml @@ -1,4 +1,28 @@ + @@ -6,7 +30,7 @@ com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT front-controller @@ -15,5 +39,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/App.java b/front-controller/src/main/java/com/iluwatar/front/controller/App.java index 18a92d37d..909578b3b 100644 --- a/front-controller/src/main/java/com/iluwatar/front/controller/App.java +++ b/front-controller/src/main/java/com/iluwatar/front/controller/App.java @@ -1,33 +1,57 @@ +/** + * The MIT License + * Copyright (c) 2014 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.front.controller; /** * - * The Front Controller is a presentation tier pattern. Essentially it defines a - * controller that handles all requests for a web site. + * The Front Controller is a presentation tier pattern. Essentially it defines a controller that + * handles all requests for a web site. *

- * The Front Controller pattern consolidates request handling through a single handler - * object ({@link FrontController}). This object can carry out the common the behavior such as + * The Front Controller pattern consolidates request handling through a single handler object ( + * {@link FrontController}). This object can carry out the common the behavior such as * authorization, request logging and routing requests to corresponding views. *

- * Typically the requests are mapped to command objects ({@link Command}) which then display - * the correct view ({@link View}). + * Typically the requests are mapped to command objects ({@link Command}) which then display the + * correct view ({@link View}). *

* In this example we have implemented two views: {@link ArcherView} and {@link CatapultView}. These - * are displayed by sending correct request to the {@link FrontController} object. For example, - * the {@link ArcherView} gets displayed when {@link FrontController} receives request "Archer". When + * are displayed by sending correct request to the {@link FrontController} object. For example, the + * {@link ArcherView} gets displayed when {@link FrontController} receives request "Archer". When * the request is unknown, we display the error view ({@link ErrorView}). * */ public class App { - - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { - FrontController controller = new FrontController(); - controller.handleRequest("Archer"); - controller.handleRequest("Catapult"); - controller.handleRequest("foobar"); - } + + /** + * Program entry point + * + * @param args + * command line args + */ + public static void main(String[] args) { + FrontController controller = new FrontController(); + controller.handleRequest("Archer"); + controller.handleRequest("Catapult"); + controller.handleRequest("foobar"); + } } diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/ApplicationException.java b/front-controller/src/main/java/com/iluwatar/front/controller/ApplicationException.java index b3963d8e9..aa50fe2ce 100644 --- a/front-controller/src/main/java/com/iluwatar/front/controller/ApplicationException.java +++ b/front-controller/src/main/java/com/iluwatar/front/controller/ApplicationException.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.front.controller; /** @@ -7,9 +29,9 @@ package com.iluwatar.front.controller; */ public class ApplicationException extends RuntimeException { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - public ApplicationException(Throwable cause) { - super(cause); - } + public ApplicationException(Throwable cause) { + super(cause); + } } diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/ArcherCommand.java b/front-controller/src/main/java/com/iluwatar/front/controller/ArcherCommand.java index 117aa0c8c..a1a391979 100644 --- a/front-controller/src/main/java/com/iluwatar/front/controller/ArcherCommand.java +++ b/front-controller/src/main/java/com/iluwatar/front/controller/ArcherCommand.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.front.controller; /** @@ -7,8 +29,8 @@ package com.iluwatar.front.controller; */ public class ArcherCommand implements Command { - @Override - public void process() { - new ArcherView().display(); - } + @Override + public void process() { + new ArcherView().display(); + } } diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/ArcherView.java b/front-controller/src/main/java/com/iluwatar/front/controller/ArcherView.java index d8cae33c1..a7cf8ca27 100644 --- a/front-controller/src/main/java/com/iluwatar/front/controller/ArcherView.java +++ b/front-controller/src/main/java/com/iluwatar/front/controller/ArcherView.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.front.controller; /** @@ -7,8 +29,8 @@ package com.iluwatar.front.controller; */ public class ArcherView implements View { - @Override - public void display() { - System.out.println("Displaying archers"); - } + @Override + public void display() { + System.out.println("Displaying archers"); + } } diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/CatapultCommand.java b/front-controller/src/main/java/com/iluwatar/front/controller/CatapultCommand.java index fae5d1753..c4b3c6ea3 100644 --- a/front-controller/src/main/java/com/iluwatar/front/controller/CatapultCommand.java +++ b/front-controller/src/main/java/com/iluwatar/front/controller/CatapultCommand.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.front.controller; /** @@ -7,8 +29,8 @@ package com.iluwatar.front.controller; */ public class CatapultCommand implements Command { - @Override - public void process() { - new CatapultView().display(); - } + @Override + public void process() { + new CatapultView().display(); + } } diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/CatapultView.java b/front-controller/src/main/java/com/iluwatar/front/controller/CatapultView.java index 9ad94d522..90e4c7896 100644 --- a/front-controller/src/main/java/com/iluwatar/front/controller/CatapultView.java +++ b/front-controller/src/main/java/com/iluwatar/front/controller/CatapultView.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.front.controller; /** @@ -7,8 +29,8 @@ package com.iluwatar.front.controller; */ public class CatapultView implements View { - @Override - public void display() { - System.out.println("Displaying catapults"); - } + @Override + public void display() { + System.out.println("Displaying catapults"); + } } diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/Command.java b/front-controller/src/main/java/com/iluwatar/front/controller/Command.java index 95bd00129..ed49572e4 100644 --- a/front-controller/src/main/java/com/iluwatar/front/controller/Command.java +++ b/front-controller/src/main/java/com/iluwatar/front/controller/Command.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.front.controller; /** @@ -6,6 +28,6 @@ package com.iluwatar.front.controller; * */ public interface Command { - - void process(); + + void process(); } diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/ErrorView.java b/front-controller/src/main/java/com/iluwatar/front/controller/ErrorView.java index 04c31dd34..cbfd4bd2e 100644 --- a/front-controller/src/main/java/com/iluwatar/front/controller/ErrorView.java +++ b/front-controller/src/main/java/com/iluwatar/front/controller/ErrorView.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.front.controller; /** @@ -7,8 +29,8 @@ package com.iluwatar.front.controller; */ public class ErrorView implements View { - @Override - public void display() { - System.out.println("Error 500"); - } + @Override + public void display() { + System.out.println("Error 500"); + } } diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/FrontController.java b/front-controller/src/main/java/com/iluwatar/front/controller/FrontController.java index 5a6dc2a78..6e2cccb0f 100644 --- a/front-controller/src/main/java/com/iluwatar/front/controller/FrontController.java +++ b/front-controller/src/main/java/com/iluwatar/front/controller/FrontController.java @@ -1,34 +1,56 @@ +/** + * The MIT License + * Copyright (c) 2014 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.front.controller; /** * - * FrontController is the handler class that takes in all the requests and - * renders the correct response. + * FrontController is the handler class that takes in all the requests and renders the correct + * response. * */ public class FrontController { - - public void handleRequest(String request) { - Command command = getCommand(request); - command.process(); - } - - private Command getCommand(String request) { - Class commandClass = getCommandClass(request); - try { - return (Command) commandClass.newInstance(); - } catch (Exception e) { - throw new ApplicationException(e); - } - } - - private Class getCommandClass(String request) { - Class result; - try { - result = Class.forName("com.iluwatar." + request + "Command"); - } catch (ClassNotFoundException e) { - result = UnknownCommand.class; - } - return result; - } + + public void handleRequest(String request) { + Command command = getCommand(request); + command.process(); + } + + private Command getCommand(String request) { + Class commandClass = getCommandClass(request); + try { + return (Command) commandClass.newInstance(); + } catch (Exception e) { + throw new ApplicationException(e); + } + } + + private static Class getCommandClass(String request) { + Class result; + try { + result = Class.forName("com.iluwatar.front.controller." + request + "Command"); + } catch (ClassNotFoundException e) { + result = UnknownCommand.class; + } + return result; + } } diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/UnknownCommand.java b/front-controller/src/main/java/com/iluwatar/front/controller/UnknownCommand.java index f8f93e7e0..6e5b33987 100644 --- a/front-controller/src/main/java/com/iluwatar/front/controller/UnknownCommand.java +++ b/front-controller/src/main/java/com/iluwatar/front/controller/UnknownCommand.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.front.controller; /** @@ -7,8 +29,8 @@ package com.iluwatar.front.controller; */ public class UnknownCommand implements Command { - @Override - public void process() { - new ErrorView().display(); - } + @Override + public void process() { + new ErrorView().display(); + } } diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/View.java b/front-controller/src/main/java/com/iluwatar/front/controller/View.java index 29c5f0fcb..536bb9750 100644 --- a/front-controller/src/main/java/com/iluwatar/front/controller/View.java +++ b/front-controller/src/main/java/com/iluwatar/front/controller/View.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.front.controller; /** @@ -7,5 +29,5 @@ package com.iluwatar.front.controller; */ public interface View { - void display(); + void display(); } diff --git a/front-controller/src/test/java/com/iluwatar/front/controller/AppTest.java b/front-controller/src/test/java/com/iluwatar/front/controller/AppTest.java index 2c28aa8ce..8eec47b0a 100644 --- a/front-controller/src/test/java/com/iluwatar/front/controller/AppTest.java +++ b/front-controller/src/test/java/com/iluwatar/front/controller/AppTest.java @@ -1,19 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 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.front.controller; import org.junit.Test; -import com.iluwatar.front.controller.App; - /** * * Application test * */ public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } + + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/front-controller/src/test/java/com/iluwatar/front/controller/ApplicationExceptionTest.java b/front-controller/src/test/java/com/iluwatar/front/controller/ApplicationExceptionTest.java new file mode 100644 index 000000000..26157acde --- /dev/null +++ b/front-controller/src/test/java/com/iluwatar/front/controller/ApplicationExceptionTest.java @@ -0,0 +1,42 @@ +/** + * The MIT License + * Copyright (c) 2014 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.front.controller; + +import static org.junit.Assert.assertSame; + +import org.junit.Test; + +/** + * Date: 12/13/15 - 1:35 PM + * + * @author Jeroen Meulemeester + */ +public class ApplicationExceptionTest { + + @Test + public void testCause() throws Exception { + final Exception cause = new Exception(); + assertSame(cause, new ApplicationException(cause).getCause()); + } + +} \ No newline at end of file diff --git a/front-controller/src/test/java/com/iluwatar/front/controller/CommandTest.java b/front-controller/src/test/java/com/iluwatar/front/controller/CommandTest.java new file mode 100644 index 000000000..c4d9ae625 --- /dev/null +++ b/front-controller/src/test/java/com/iluwatar/front/controller/CommandTest.java @@ -0,0 +1,84 @@ +/** + * The MIT License + * Copyright (c) 2014 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.front.controller; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/13/15 - 1:39 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class CommandTest extends StdOutTest { + + @Parameters + public static List data() { + final List parameters = new ArrayList<>(); + parameters.add(new Object[]{"Archer", "Displaying archers"}); + parameters.add(new Object[]{"Catapult", "Displaying catapults"}); + parameters.add(new Object[]{"NonExistentCommand", "Error 500"}); + return parameters; + } + + /** + * The view that's been tested + */ + private final String request; + + /** + * The expected display message + */ + private final String displayMessage; + + /** + * Create a new instance of the {@link CommandTest} with the given view and expected message + * + * @param request The request that's been tested + * @param displayMessage The expected display message + */ + public CommandTest(final String request, final String displayMessage) { + this.displayMessage = displayMessage; + this.request = request; + } + + @Test + public void testDisplay() { + final FrontController frontController = new FrontController(); + verifyZeroInteractions(getStdOutMock()); + frontController.handleRequest(request); + verify(getStdOutMock()).println(displayMessage); + verifyNoMoreInteractions(getStdOutMock()); + } + +} diff --git a/front-controller/src/test/java/com/iluwatar/front/controller/FrontControllerTest.java b/front-controller/src/test/java/com/iluwatar/front/controller/FrontControllerTest.java new file mode 100644 index 000000000..0822ffcd0 --- /dev/null +++ b/front-controller/src/test/java/com/iluwatar/front/controller/FrontControllerTest.java @@ -0,0 +1,83 @@ +/** + * The MIT License + * Copyright (c) 2014 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.front.controller; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/13/15 - 1:39 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class FrontControllerTest extends StdOutTest { + + @Parameters + public static List data() { + final List parameters = new ArrayList<>(); + parameters.add(new Object[]{new ArcherCommand(), "Displaying archers"}); + parameters.add(new Object[]{new CatapultCommand(), "Displaying catapults"}); + parameters.add(new Object[]{new UnknownCommand(), "Error 500"}); + return parameters; + } + + /** + * The view that's been tested + */ + private final Command command; + + /** + * The expected display message + */ + private final String displayMessage; + + /** + * Create a new instance of the {@link FrontControllerTest} with the given view and expected message + * + * @param command The command that's been tested + * @param displayMessage The expected display message + */ + public FrontControllerTest(final Command command, final String displayMessage) { + this.displayMessage = displayMessage; + this.command = command; + } + + @Test + public void testDisplay() { + verifyZeroInteractions(getStdOutMock()); + this.command.process(); + verify(getStdOutMock()).println(displayMessage); + verifyNoMoreInteractions(getStdOutMock()); + } + +} diff --git a/front-controller/src/test/java/com/iluwatar/front/controller/StdOutTest.java b/front-controller/src/test/java/com/iluwatar/front/controller/StdOutTest.java new file mode 100644 index 000000000..bc32a1b3c --- /dev/null +++ b/front-controller/src/test/java/com/iluwatar/front/controller/StdOutTest.java @@ -0,0 +1,76 @@ +/** + * The MIT License + * Copyright (c) 2014 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.front.controller; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since the actions of the views don't have + * any influence on any other accessible objects, except for writing to std-out using {@link + * System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/front-controller/src/test/java/com/iluwatar/front/controller/ViewTest.java b/front-controller/src/test/java/com/iluwatar/front/controller/ViewTest.java new file mode 100644 index 000000000..cdabd66ef --- /dev/null +++ b/front-controller/src/test/java/com/iluwatar/front/controller/ViewTest.java @@ -0,0 +1,83 @@ +/** + * The MIT License + * Copyright (c) 2014 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.front.controller; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/13/15 - 1:39 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class ViewTest extends StdOutTest { + + @Parameters + public static List data() { + final List parameters = new ArrayList<>(); + parameters.add(new Object[]{new ArcherView(), "Displaying archers"}); + parameters.add(new Object[]{new CatapultView(), "Displaying catapults"}); + parameters.add(new Object[]{new ErrorView(), "Error 500"}); + return parameters; + } + + /** + * The view that's been tested + */ + private final View view; + + /** + * The expected display message + */ + private final String displayMessage; + + /** + * Create a new instance of the {@link ViewTest} with the given view and expected message + * + * @param view The view that's been tested + * @param displayMessage The expected display message + */ + public ViewTest(final View view, final String displayMessage) { + this.displayMessage = displayMessage; + this.view = view; + } + + @Test + public void testDisplay() { + verifyZeroInteractions(getStdOutMock()); + this.view.display(); + verify(getStdOutMock()).println(displayMessage); + verifyNoMoreInteractions(getStdOutMock()); + } + +} \ No newline at end of file diff --git a/half-sync-half-async/index.md b/half-sync-half-async/README.md similarity index 87% rename from half-sync-half-async/index.md rename to half-sync-half-async/README.md index dc1930e3b..8a091f813 100644 --- a/half-sync-half-async/index.md +++ b/half-sync-half-async/README.md @@ -4,16 +4,20 @@ title: Half-Sync/Half-Async folder: half-sync-half-async permalink: /patterns/half-sync-half-async/ categories: Concurrency -tags: Java +tags: + - Java + - Difficulty-Intermediate --- -**Intent:** The Half-Sync/Half-Async pattern decouples synchronous I/O from +## Intent +The Half-Sync/Half-Async pattern decouples synchronous I/O from asynchronous I/O in a system to simplify concurrent programming effort without degrading execution efficiency. ![Half-Sync/Half-Async class diagram](./etc/half-sync-half-async.png) -**Applicability:** Use Half-Sync/Half-Async pattern when +## Applicability +Use Half-Sync/Half-Async pattern when * a system possesses following characteristics: * the system must perform tasks in response to external events that occur asynchronously, like hardware interrupts in OS @@ -21,13 +25,13 @@ degrading execution efficiency. * the higher level tasks in the system can be simplified significantly if I/O is performed synchronously. * one or more tasks in a system must run in a single thread of control, while other tasks may benefit from multi-threading. -**Real world examples:** +## Real world examples * [BSD Unix networking subsystem](http://www.cs.wustl.edu/~schmidt/PDF/PLoP-95.pdf) * [Real Time CORBA](http://www.omg.org/news/meetings/workshops/presentations/realtime2001/4-3_Pyarali_thread-pool.pdf) * [Android AsyncTask framework](http://developer.android.com/reference/android/os/AsyncTask.html) -**Credits:** +## Credits * [Douglas C. Schmidt and Charles D. Cranor - Half Sync/Half Async](http://www.cs.wustl.edu/~schmidt/PDF/PLoP-95.pdf) * [Pattern Oriented Software Architecture Vol I-V](http://www.amazon.com/Pattern-Oriented-Software-Architecture-Volume-Patterns/dp/0471958697) diff --git a/half-sync-half-async/pom.xml b/half-sync-half-async/pom.xml index e7436d29f..076dfb44d 100644 --- a/half-sync-half-async/pom.xml +++ b/half-sync-half-async/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT half-sync-half-async @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/App.java b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/App.java index b8cec89e3..17839bb32 100644 --- a/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/App.java +++ b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.halfsynchalfasync; import java.util.concurrent.LinkedBlockingQueue; @@ -8,122 +30,119 @@ import java.util.concurrent.LinkedBlockingQueue; * {@link AsyncTask} and {@link AsynchronousService}. * *

- * PROBLEM - *
- * A concurrent system have a mixture of short duration, mid duration and long duration tasks. - * Mid or long duration tasks should be performed asynchronously to meet quality of service + * PROBLEM
+ * A concurrent system have a mixture of short duration, mid duration and long duration tasks. Mid + * or long duration tasks should be performed asynchronously to meet quality of service * requirements. - * - *

INTENT - *
- * The intent of this pattern is to separate the the synchronous and asynchronous processing - * in the concurrent application by introducing two intercommunicating layers - one for sync - * and one for async. This simplifies the programming without unduly affecting the performance. - * + * *

- * APPLICABILITY - *
- *

    - *
  • UNIX network subsystems - In operating systems network operations are carried out - * asynchronously with help of hardware level interrupts.
  • - *
  • CORBA - At the asynchronous layer one thread is associated with each socket that is - * connected to the client. Thread blocks waiting for CORBA requests from the client. On receiving - * request it is inserted in the queuing layer which is then picked up by synchronous layer which - * processes the request and sends response back to the client.
  • - *
  • Android AsyncTask framework - Framework provides a way to execute long running blocking calls, - * such as downloading a file, in background threads so that the UI thread remains free to respond - * to user inputs. - *
- * + * INTENT
+ * The intent of this pattern is to separate the the synchronous and asynchronous processing in the + * concurrent application by introducing two intercommunicating layers - one for sync and one for + * async. This simplifies the programming without unduly affecting the performance. + * *

- * IMPLEMENTATION - *
- * The main method creates an asynchronous service which does not block the main thread while - * the task is being performed. The main thread continues its work which is similar to Async Method - * Invocation pattern. The difference between them is that there is a queuing layer between Asynchronous - * layer and synchronous layer, which allows for different communication patterns between both layers. - * Such as Priority Queue can be used as queuing layer to prioritize the way tasks are executed. - * Our implementation is just one simple way of implementing this pattern, there are many variants possible - * as described in its applications. + * APPLICABILITY
+ * UNIX network subsystems - In operating systems network operations are carried out + * asynchronously with help of hardware level interrupts.
+ * CORBA - At the asynchronous layer one thread is associated with each socket that is connected + * to the client. Thread blocks waiting for CORBA requests from the client. On receiving request it + * is inserted in the queuing layer which is then picked up by synchronous layer which processes the + * request and sends response back to the client.
+ * Android AsyncTask framework - Framework provides a way to execute long running blocking + * calls, such as downloading a file, in background threads so that the UI thread remains free to + * respond to user inputs.
+ * + *

+ * IMPLEMENTATION
+ * The main method creates an asynchronous service which does not block the main thread while the + * task is being performed. The main thread continues its work which is similar to Async Method + * Invocation pattern. The difference between them is that there is a queuing layer between + * Asynchronous layer and synchronous layer, which allows for different communication patterns + * between both layers. Such as Priority Queue can be used as queuing layer to prioritize the way + * tasks are executed. Our implementation is just one simple way of implementing this pattern, there + * are many variants possible as described in its applications. * */ public class App { - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { - AsynchronousService service = new AsynchronousService(new LinkedBlockingQueue<>()); - /* - * A new task to calculate sum is received but as this is main thread, it should not block. - * So it passes it to the asynchronous task layer to compute and proceeds with handling other - * incoming requests. This is particularly useful when main thread is waiting on Socket to receive - * new incoming requests and does not wait for particular request to be completed before responding - * to new request. - */ - service.execute(new ArithmeticSumTask(1000)); - - /* New task received, lets pass that to async layer for computation. So both requests will be - * executed in parallel. - */ - service.execute(new ArithmeticSumTask(500)); - service.execute(new ArithmeticSumTask(2000)); - service.execute(new ArithmeticSumTask(1)); - } - - /** - * - * ArithmeticSumTask - * - */ - static class ArithmeticSumTask implements AsyncTask { - private long n; + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + AsynchronousService service = new AsynchronousService(new LinkedBlockingQueue<>()); + /* + * A new task to calculate sum is received but as this is main thread, it should not block. So + * it passes it to the asynchronous task layer to compute and proceeds with handling other + * incoming requests. This is particularly useful when main thread is waiting on Socket to + * receive new incoming requests and does not wait for particular request to be completed before + * responding to new request. + */ + service.execute(new ArithmeticSumTask(1000)); - public ArithmeticSumTask(long n) { - this.n = n; - } + /* + * New task received, lets pass that to async layer for computation. So both requests will be + * executed in parallel. + */ + service.execute(new ArithmeticSumTask(500)); + service.execute(new ArithmeticSumTask(2000)); + service.execute(new ArithmeticSumTask(1)); + } - /* - * This is the long running task that is performed in background. In our example - * the long running task is calculating arithmetic sum with artificial delay. - */ - @Override - public Long call() throws Exception { - return ap(n); - } + /** + * + * ArithmeticSumTask + * + */ + static class ArithmeticSumTask implements AsyncTask { + private long n; - /* - * This will be called in context of the main thread where some validations can be - * done regarding the inputs. Such as it must be greater than 0. It's a small - * computation which can be performed in main thread. If we did validated the input - * in background thread then we pay the cost of context switching - * which is much more than validating it in main thread. - */ - @Override - public void onPreCall() { - if (n < 0) { - throw new IllegalArgumentException("n is less than 0"); - } - } + public ArithmeticSumTask(long n) { + this.n = n; + } - @Override - public void onPostCall(Long result) { - // Handle the result of computation - System.out.println(result); - } + /* + * This is the long running task that is performed in background. In our example the long + * running task is calculating arithmetic sum with artificial delay. + */ + @Override + public Long call() throws Exception { + return ap(n); + } - @Override - public void onError(Throwable throwable) { - throw new IllegalStateException("Should not occur"); - } - } - - private static long ap(long i) { - try { - Thread.sleep(i); - } catch (InterruptedException e) { - } - return (i) * (i + 1) / 2; - } + /* + * This will be called in context of the main thread where some validations can be done + * regarding the inputs. Such as it must be greater than 0. It's a small computation which can + * be performed in main thread. If we did validated the input in background thread then we pay + * the cost of context switching which is much more than validating it in main thread. + */ + @Override + public void onPreCall() { + if (n < 0) { + throw new IllegalArgumentException("n is less than 0"); + } + } + + @Override + public void onPostCall(Long result) { + // Handle the result of computation + System.out.println(result); + } + + @Override + public void onError(Throwable throwable) { + throw new IllegalStateException("Should not occur"); + } + } + + private static long ap(long i) { + try { + Thread.sleep(i); + } catch (InterruptedException e) { + System.out.println(e); + } + return i * (i + 1) / 2; + } } diff --git a/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsyncTask.java b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsyncTask.java index 8ed7376b6..91e36069e 100644 --- a/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsyncTask.java +++ b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsyncTask.java @@ -1,44 +1,66 @@ +/** + * The MIT License + * Copyright (c) 2014 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.halfsynchalfasync; import java.util.concurrent.Callable; /** - * Represents some computation that is performed asynchronously and its result. - * The computation is typically done is background threads and the result is posted - * back in form of callback. The callback does not implement {@code isComplete}, {@code cancel} - * as it is out of scope of this pattern. + * Represents some computation that is performed asynchronously and its result. The computation is + * typically done is background threads and the result is posted back in form of callback. The + * callback does not implement {@code isComplete}, {@code cancel} as it is out of scope of this + * pattern. * * @param type of result */ public interface AsyncTask extends Callable { - /** - * Is called in context of caller thread before call to {@link #call()}. Large - * tasks should not be performed in this method as it will block the caller thread. - * Small tasks such as validations can be performed here so that the performance penalty - * of context switching is not incurred in case of invalid requests. - */ - void onPreCall(); - - /** - * A callback called after the result is successfully computed by {@link #call()}. In our - * implementation this method is called in context of background thread but in some variants, - * such as Android where only UI thread can change the state of UI widgets, this method is called - * in context of UI thread. - */ - void onPostCall(O result); - - /** - * A callback called if computing the task resulted in some exception. This method - * is called when either of {@link #call()} or {@link #onPreCall()} throw any exception. - * - * @param throwable error cause - */ - void onError(Throwable throwable); - - /** - * This is where the computation of task should reside. This method is called in context - * of background thread. - */ - @Override - O call() throws Exception; + /** + * Is called in context of caller thread before call to {@link #call()}. Large tasks should not be + * performed in this method as it will block the caller thread. Small tasks such as validations + * can be performed here so that the performance penalty of context switching is not incurred in + * case of invalid requests. + */ + void onPreCall(); + + /** + * A callback called after the result is successfully computed by {@link #call()}. In our + * implementation this method is called in context of background thread but in some variants, such + * as Android where only UI thread can change the state of UI widgets, this method is called in + * context of UI thread. + */ + void onPostCall(O result); + + /** + * A callback called if computing the task resulted in some exception. This method is called when + * either of {@link #call()} or {@link #onPreCall()} throw any exception. + * + * @param throwable error cause + */ + void onError(Throwable throwable); + + /** + * This is where the computation of task should reside. This method is called in context of + * background thread. + */ + @Override + O call() throws Exception; } diff --git a/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsynchronousService.java b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsynchronousService.java index 6c36354d0..b42f551f2 100644 --- a/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsynchronousService.java +++ b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/AsynchronousService.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.halfsynchalfasync; import java.util.concurrent.BlockingQueue; @@ -9,67 +31,68 @@ import java.util.concurrent.TimeUnit; /** * This is the asynchronous layer which does not block when a new request arrives. It just passes - * the request to the synchronous layer which consists of a queue i.e. a {@link BlockingQueue} and - * a pool of threads i.e. {@link ThreadPoolExecutor}. Out of this pool of worker threads one of the - * thread picks up the task and executes it synchronously in background and the result is posted back - * to the caller via callback. + * the request to the synchronous layer which consists of a queue i.e. a {@link BlockingQueue} and a + * pool of threads i.e. {@link ThreadPoolExecutor}. Out of this pool of worker threads one of the + * thread picks up the task and executes it synchronously in background and the result is posted + * back to the caller via callback. */ public class AsynchronousService { - - /* - * This represents the queuing layer as well as synchronous layer of the pattern. The thread - * pool contains worker threads which execute the tasks in blocking/synchronous manner. Long - * running tasks should be performed in the background which does not affect the performance of - * main thread. - */ - private ExecutorService service; - /** - * Creates an asynchronous service using {@code workQueue} as communication channel between - * asynchronous layer and synchronous layer. Different types of queues such as Priority queue, - * can be used to control the pattern of communication between the layers. - */ - public AsynchronousService(BlockingQueue workQueue) { - service = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, workQueue); - } - + /* + * This represents the queuing layer as well as synchronous layer of the pattern. The thread pool + * contains worker threads which execute the tasks in blocking/synchronous manner. Long running + * tasks should be performed in the background which does not affect the performance of main + * thread. + */ + private ExecutorService service; - /** - * A non-blocking method which performs the task provided in background and returns immediately. - *

- * On successful completion of task the result is posted back using callback method - * {@link AsyncTask#onPostCall(Object)}, if task execution is unable to complete normally - * due to some exception then the reason for error is posted back using callback method - * {@link AsyncTask#onError(Throwable)}. - *

- * NOTE: The results are posted back in the context of background thread in this implementation. - */ - public void execute(final AsyncTask task) { - try { - // some small tasks such as validation can be performed here. - task.onPreCall(); - } catch (Exception e) { - task.onError(e); - } + /** + * Creates an asynchronous service using {@code workQueue} as communication channel between + * asynchronous layer and synchronous layer. Different types of queues such as Priority queue, can + * be used to control the pattern of communication between the layers. + */ + public AsynchronousService(BlockingQueue workQueue) { + service = new ThreadPoolExecutor(10, 10, 10, TimeUnit.SECONDS, workQueue); + } - service.submit(new FutureTask(task) { - @Override - protected void done() { - super.done(); - try { - /* called in context of background thread. There is other variant possible - * where result is posted back and sits in the queue of caller thread which - * then picks it up for processing. An example of such a system is Android OS, - * where the UI elements can only be updated using UI thread. So result must be - * posted back in UI thread. - */ - task.onPostCall(get()); - } catch (InterruptedException e) { - // should not occur - } catch (ExecutionException e) { - task.onError(e.getCause()); - } - } - }); - } + + /** + * A non-blocking method which performs the task provided in background and returns immediately. + *

+ * On successful completion of task the result is posted back using callback method + * {@link AsyncTask#onPostCall(Object)}, if task execution is unable to complete normally due to + * some exception then the reason for error is posted back using callback method + * {@link AsyncTask#onError(Throwable)}. + *

+ * NOTE: The results are posted back in the context of background thread in this implementation. + */ + public void execute(final AsyncTask task) { + try { + // some small tasks such as validation can be performed here. + task.onPreCall(); + } catch (Exception e) { + task.onError(e); + return; + } + + service.submit(new FutureTask(task) { + @Override + protected void done() { + super.done(); + try { + /* + * called in context of background thread. There is other variant possible where result is + * posted back and sits in the queue of caller thread which then picks it up for + * processing. An example of such a system is Android OS, where the UI elements can only + * be updated using UI thread. So result must be posted back in UI thread. + */ + task.onPostCall(get()); + } catch (InterruptedException e) { + // should not occur + } catch (ExecutionException e) { + task.onError(e.getCause()); + } + } + }); + } } diff --git a/half-sync-half-async/src/test/java/com/iluwatar/halfsynchalfasync/AppTest.java b/half-sync-half-async/src/test/java/com/iluwatar/halfsynchalfasync/AppTest.java index a72417aff..e791cc310 100644 --- a/half-sync-half-async/src/test/java/com/iluwatar/halfsynchalfasync/AppTest.java +++ b/half-sync-half-async/src/test/java/com/iluwatar/halfsynchalfasync/AppTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.halfsynchalfasync; import java.util.concurrent.ExecutionException; @@ -11,8 +33,8 @@ import org.junit.Test; */ public class AppTest { - @Test - public void test() throws InterruptedException, ExecutionException { - App.main(null); - } + @Test + public void test() throws InterruptedException, ExecutionException { + App.main(null); + } } diff --git a/half-sync-half-async/src/test/java/com/iluwatar/halfsynchalfasync/AsynchronousServiceTest.java b/half-sync-half-async/src/test/java/com/iluwatar/halfsynchalfasync/AsynchronousServiceTest.java new file mode 100644 index 000000000..7fcbd1541 --- /dev/null +++ b/half-sync-half-async/src/test/java/com/iluwatar/halfsynchalfasync/AsynchronousServiceTest.java @@ -0,0 +1,94 @@ +/** + * The MIT License + * Copyright (c) 2014 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.halfsynchalfasync; + +import org.junit.Test; +import org.mockito.InOrder; + +import java.io.IOException; +import java.util.concurrent.LinkedBlockingQueue; + +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; + +/** + * Date: 12/12/15 - 11:15 PM + * + * @author Jeroen Meulemeester + */ +public class AsynchronousServiceTest { + + @Test + public void testPerfectExecution() throws Exception { + final AsynchronousService service = new AsynchronousService(new LinkedBlockingQueue<>()); + final AsyncTask task = mock(AsyncTask.class); + final Object result = new Object(); + when(task.call()).thenReturn(result); + service.execute(task); + + verify(task, timeout(2000)).onPostCall(eq(result)); + + final InOrder inOrder = inOrder(task); + inOrder.verify(task, times(1)).onPreCall(); + inOrder.verify(task, times(1)).call(); + inOrder.verify(task, times(1)).onPostCall(eq(result)); + + verifyNoMoreInteractions(task); + } + + @Test + public void testCallException() throws Exception { + final AsynchronousService service = new AsynchronousService(new LinkedBlockingQueue<>()); + final AsyncTask task = mock(AsyncTask.class); + final IOException exception = new IOException(); + when(task.call()).thenThrow(exception); + service.execute(task); + + verify(task, timeout(2000)).onError(eq(exception)); + + final InOrder inOrder = inOrder(task); + inOrder.verify(task, times(1)).onPreCall(); + inOrder.verify(task, times(1)).call(); + inOrder.verify(task, times(1)).onError(exception); + + verifyNoMoreInteractions(task); + } + + @Test + public void testPreCallException() throws Exception { + final AsynchronousService service = new AsynchronousService(new LinkedBlockingQueue<>()); + final AsyncTask task = mock(AsyncTask.class); + final IllegalStateException exception = new IllegalStateException(); + doThrow(exception).when(task).onPreCall(); + service.execute(task); + + verify(task, timeout(2000)).onError(eq(exception)); + + final InOrder inOrder = inOrder(task); + inOrder.verify(task, times(1)).onPreCall(); + inOrder.verify(task, times(1)).onError(exception); + + verifyNoMoreInteractions(task); + } + +} \ No newline at end of file diff --git a/hexagonal/README.md b/hexagonal/README.md new file mode 100644 index 000000000..b1d0a7948 --- /dev/null +++ b/hexagonal/README.md @@ -0,0 +1,34 @@ +--- +layout: pattern +title: Hexagonal Architecture +folder: hexagonal +permalink: /patterns/hexagonal/ +categories: Architectural +tags: + - Java + - Difficulty-Expert +--- + +## Also known as +* Ports and Adapters +* Clean Architecture +* Onion Architecture + +## Intent +Allow an application to equally be driven by users, programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases. + +![Hexagonal Architecture class diagram](./etc/hexagonal.png) + +## Applicability +Use Hexagonal Architecture pattern when + +* it is important that the application is fully testable +* you use Domain Driven Design methodology and/or Microservices architectural style + +## Real world examples + +* [Apache Isis](https://isis.apache.org/) + +## Credits + +* [Alistair Cockburn - Hexagonal Architecture](http://alistair.cockburn.us/Hexagonal+architecture) diff --git a/hexagonal/etc/hexagonal.png b/hexagonal/etc/hexagonal.png new file mode 100644 index 000000000..8c03d375f Binary files /dev/null and b/hexagonal/etc/hexagonal.png differ diff --git a/hexagonal/etc/hexagonal.ucls b/hexagonal/etc/hexagonal.ucls new file mode 100644 index 000000000..b54e2abf7 --- /dev/null +++ b/hexagonal/etc/hexagonal.ucls @@ -0,0 +1,325 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hexagonal/pom.xml b/hexagonal/pom.xml new file mode 100644 index 000000000..e9e2a502d --- /dev/null +++ b/hexagonal/pom.xml @@ -0,0 +1,43 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + hexagonal + + + junit + junit + test + + + diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java new file mode 100644 index 000000000..3ae55b706 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -0,0 +1,151 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import com.iluwatar.hexagonal.administration.LotteryAdministration; +import com.iluwatar.hexagonal.administration.LotteryAdministrationImpl; +import com.iluwatar.hexagonal.banking.WireTransfersImpl; +import com.iluwatar.hexagonal.domain.LotteryConstants; +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.PlayerDetails; +import com.iluwatar.hexagonal.service.LotteryService; +import com.iluwatar.hexagonal.service.LotteryServiceImpl; + +/** + * + * Hexagonal Architecture pattern decouples the application core from the + * services it uses. This allows the services to be plugged in and the + * application will run with or without the services.

+ * + * The core logic, or business logic, of an application consists of the + * algorithms that are essential to its purpose. They implement the use + * cases that are the heart of the application. When you change them, you + * change the essence of the application.

+ * + * The services are not essential. They can be replaced without changing + * the purpose of the application. Examples: database access and other + * types of storage, user interface components, e-mail and other + * communication components, hardware devices.

+ * + * This example demonstrates Hexagonal Architecture with a lottery system. + * The application core is separate from the services that drive it and + * from the services it uses.

+ * + * The primary ports for the application are {@link LotteryAdministration} + * through which the lottery round is initiated and run and + * {@link LotteryService} that allows players to submit lottery tickets for + * the draw.

+ * + * The secondary ports that application core uses are {@link WireTransfers} + * which is a banking service, {@link LotteryNotifications} that delivers + * notifications as lottery events occur and {@link LotteryTicketRepository} + * that is the storage for the lottery tickets. + * + */ +public class App { + + private static List allPlayerDetails; + + static { + allPlayerDetails = new ArrayList<>(); + allPlayerDetails.add(PlayerDetails.create("john@google.com", "312-342", "+3242434242")); + allPlayerDetails.add(PlayerDetails.create("mary@google.com", "234-987", "+23452346")); + allPlayerDetails.add(PlayerDetails.create("steve@google.com", "833-836", "+63457543")); + allPlayerDetails.add(PlayerDetails.create("wayne@google.com", "319-826", "+24626")); + allPlayerDetails.add(PlayerDetails.create("johnie@google.com", "983-322", "+3635635")); + allPlayerDetails.add(PlayerDetails.create("andy@google.com", "934-734", "+0898245")); + allPlayerDetails.add(PlayerDetails.create("richard@google.com", "536-738", "+09845325")); + allPlayerDetails.add(PlayerDetails.create("kevin@google.com", "453-936", "+2423532")); + allPlayerDetails.add(PlayerDetails.create("arnold@google.com", "114-988", "+5646346524")); + allPlayerDetails.add(PlayerDetails.create("ian@google.com", "663-765", "+928394235")); + allPlayerDetails.add(PlayerDetails.create("robin@google.com", "334-763", "+35448")); + allPlayerDetails.add(PlayerDetails.create("ted@google.com", "735-964", "+98752345")); + allPlayerDetails.add(PlayerDetails.create("larry@google.com", "734-853", "+043842423")); + allPlayerDetails.add(PlayerDetails.create("calvin@google.com", "334-746", "+73294135")); + allPlayerDetails.add(PlayerDetails.create("jacob@google.com", "444-766", "+358042354")); + allPlayerDetails.add(PlayerDetails.create("edwin@google.com", "895-345", "+9752435")); + allPlayerDetails.add(PlayerDetails.create("mary@google.com", "760-009", "+34203542")); + allPlayerDetails.add(PlayerDetails.create("lolita@google.com", "425-907", "+9872342")); + allPlayerDetails.add(PlayerDetails.create("bruno@google.com", "023-638", "+673824122")); + allPlayerDetails.add(PlayerDetails.create("peter@google.com", "335-886", "+5432503945")); + allPlayerDetails.add(PlayerDetails.create("warren@google.com", "225-946", "+9872341324")); + allPlayerDetails.add(PlayerDetails.create("monica@google.com", "265-748", "+134124")); + allPlayerDetails.add(PlayerDetails.create("ollie@google.com", "190-045", "+34453452")); + allPlayerDetails.add(PlayerDetails.create("yngwie@google.com", "241-465", "+9897641231")); + allPlayerDetails.add(PlayerDetails.create("lars@google.com", "746-936", "+42345298345")); + allPlayerDetails.add(PlayerDetails.create("bobbie@google.com", "946-384", "+79831742")); + allPlayerDetails.add(PlayerDetails.create("tyron@google.com", "310-992", "+0498837412")); + allPlayerDetails.add(PlayerDetails.create("tyrell@google.com", "032-045", "+67834134")); + allPlayerDetails.add(PlayerDetails.create("nadja@google.com", "000-346", "+498723")); + allPlayerDetails.add(PlayerDetails.create("wendy@google.com", "994-989", "+987324454")); + allPlayerDetails.add(PlayerDetails.create("luke@google.com", "546-634", "+987642435")); + allPlayerDetails.add(PlayerDetails.create("bjorn@google.com", "342-874", "+7834325")); + allPlayerDetails.add(PlayerDetails.create("lisa@google.com", "024-653", "+980742154")); + allPlayerDetails.add(PlayerDetails.create("anton@google.com", "834-935", "+876423145")); + allPlayerDetails.add(PlayerDetails.create("bruce@google.com", "284-936", "+09843212345")); + allPlayerDetails.add(PlayerDetails.create("ray@google.com", "843-073", "+678324123")); + allPlayerDetails.add(PlayerDetails.create("ron@google.com", "637-738", "+09842354")); + allPlayerDetails.add(PlayerDetails.create("xavier@google.com", "143-947", "+375245")); + allPlayerDetails.add(PlayerDetails.create("harriet@google.com", "842-404", "+131243252")); + WireTransfersImpl wireTransfers = new WireTransfersImpl(); + Random random = new Random(); + for (int i = 0; i < allPlayerDetails.size(); i++) { + wireTransfers.setFunds(allPlayerDetails.get(i).getBankAccount(), + random.nextInt(LotteryConstants.PLAYER_MAX_SALDO)); + } + } + + /** + * Program entry point + */ + public static void main(String[] args) { + // start new lottery round + LotteryAdministration administartion = new LotteryAdministrationImpl(); + administartion.resetLottery(); + + // submit some lottery tickets + LotteryServiceImpl service = new LotteryServiceImpl(); + submitTickets(service, 20); + + // perform lottery + administartion.performLottery(); + } + + private static void submitTickets(LotteryService lotteryService, int numTickets) { + for (int i = 0; i < numTickets; i++) { + LotteryTicket ticket = LotteryTicket.create(getRandomPlayerDetails(), LotteryNumbers.createRandom()); + lotteryService.submitTicket(ticket); + } + } + + private static PlayerDetails getRandomPlayerDetails() { + Random random = new Random(); + int idx = random.nextInt(allPlayerDetails.size()); + return allPlayerDetails.get(idx); + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java new file mode 100644 index 000000000..bc625b230 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java @@ -0,0 +1,53 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.administration; + +import java.util.Map; + +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketId; + +/** + * + * Administrator interface for lottery service. + * + */ +public interface LotteryAdministration { + + /** + * Get all the lottery tickets submitted for lottery + */ + Map getAllSubmittedTickets(); + + /** + * Draw lottery numbers + */ + LotteryNumbers performLottery(); + + /** + * Begin new lottery round + */ + void resetLottery(); + +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java new file mode 100644 index 000000000..a452600aa --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java @@ -0,0 +1,87 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.administration; + +import java.util.Map; + +import com.iluwatar.hexagonal.banking.WireTransfers; +import com.iluwatar.hexagonal.banking.WireTransfersImpl; +import com.iluwatar.hexagonal.database.LotteryTicketRepository; +import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; +import com.iluwatar.hexagonal.domain.LotteryConstants; +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; +import com.iluwatar.hexagonal.domain.LotteryTicketId; +import com.iluwatar.hexagonal.notifications.LotteryNotifications; +import com.iluwatar.hexagonal.notifications.LotteryNotificationsImpl; +import com.iluwatar.hexagonal.service.LotteryService; +import com.iluwatar.hexagonal.service.LotteryServiceImpl; + +/** + * + * Lottery administration implementation + * + */ +public class LotteryAdministrationImpl implements LotteryAdministration { + + private final LotteryTicketRepository repository; + private final LotteryService service = new LotteryServiceImpl(); + private final LotteryNotifications notifications = new LotteryNotificationsImpl(); + private final WireTransfers bank = new WireTransfersImpl(); + public LotteryAdministrationImpl() { + repository = new LotteryTicketInMemoryRepository(); + } + + @Override + public Map getAllSubmittedTickets() { + return repository.findAll(); + } + + @Override + public LotteryNumbers performLottery() { + LotteryNumbers numbers = LotteryNumbers.createRandom(); + Map tickets = getAllSubmittedTickets(); + for (LotteryTicketId id: tickets.keySet()) { + LotteryTicketCheckResult result = service.checkTicketForPrize(id, numbers); + if (result.getResult().equals(CheckResult.WIN_PRIZE)) { + boolean transferred = bank.transferFunds(LotteryConstants.PRIZE_AMOUNT, LotteryConstants.SERVICE_BANK_ACCOUNT, + tickets.get(id).getPlayerDetails().getBankAccount()); + if (transferred) { + notifications.notifyPrize(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); + } else { + notifications.notifyPrizeError(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); + } + } else if (result.getResult().equals(CheckResult.NO_PRIZE)) { + notifications.notifyNoWin(tickets.get(id).getPlayerDetails()); + } + } + return numbers; + } + + @Override + public void resetLottery() { + repository.deleteAll(); + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfers.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfers.java new file mode 100644 index 000000000..7d21e7bf3 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfers.java @@ -0,0 +1,47 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.banking; + +/** + * + * Interface to bank accounts. + * + */ +public interface WireTransfers { + + /** + * Set amount of funds for bank account + */ + void setFunds(String bankAccount, int amount); + + /** + * Get amount of funds for bank account + */ + int getFunds(String bankAccount); + + /** + * Transfer funds from one bank account to another + */ + boolean transferFunds(int amount, String sourceBackAccount, String destinationBankAccount); + +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfersImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfersImpl.java new file mode 100644 index 000000000..19c1bb1c0 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/WireTransfersImpl.java @@ -0,0 +1,63 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.banking; + +import java.util.HashMap; +import java.util.Map; + +import com.iluwatar.hexagonal.domain.LotteryConstants; + +/** + * + * Banking implementation + * + */ +public class WireTransfersImpl implements WireTransfers { + + private static Map accounts = new HashMap<>(); + + static { + accounts.put(LotteryConstants.SERVICE_BANK_ACCOUNT, LotteryConstants.SERVICE_BANK_ACCOUNT_SALDO); + } + + @Override + public void setFunds(String bankAccount, int amount) { + accounts.put(bankAccount, amount); + } + + @Override + public int getFunds(String bankAccount) { + return accounts.getOrDefault(bankAccount, 0); + } + + @Override + public boolean transferFunds(int amount, String sourceBackAccount, String destinationBankAccount) { + if (accounts.getOrDefault(sourceBackAccount, 0) >= amount) { + accounts.put(sourceBackAccount, accounts.get(sourceBackAccount) - amount); + accounts.put(destinationBankAccount, accounts.get(destinationBankAccount) + amount); + return true; + } else { + return false; + } + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketInMemoryRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketInMemoryRepository.java new file mode 100644 index 000000000..fe17d8cdf --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketInMemoryRepository.java @@ -0,0 +1,67 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.database; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketId; + +/** + * + * Mock database for lottery tickets. + * + */ +public class LotteryTicketInMemoryRepository implements LotteryTicketRepository { + + private static Map tickets = new HashMap<>(); + + @Override + public Optional findById(LotteryTicketId id) { + LotteryTicket ticket = tickets.get(id); + if (ticket == null) { + return Optional.empty(); + } else { + return Optional.of(ticket); + } + } + + @Override + public Optional save(LotteryTicket ticket) { + LotteryTicketId id = new LotteryTicketId(); + tickets.put(id, ticket); + return Optional.of(id); + } + + @Override + public Map findAll() { + return tickets; + } + + @Override + public void deleteAll() { + tickets.clear(); + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepository.java new file mode 100644 index 000000000..4c6522838 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketRepository.java @@ -0,0 +1,58 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.database; + +import java.util.Map; +import java.util.Optional; + +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketId; + +/** + * + * Interface for accessing lottery tickets in database. + * + */ +public interface LotteryTicketRepository { + + /** + * Find lottery ticket by id + */ + Optional findById(LotteryTicketId id); + + /** + * Save lottery ticket + */ + Optional save(LotteryTicket ticket); + + /** + * Get all lottery tickets + */ + Map findAll(); + + /** + * Delete all lottery tickets + */ + void deleteAll(); + +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryConstants.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryConstants.java new file mode 100644 index 000000000..fb4c8025f --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryConstants.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +/** + * + * Lottery domain constants + * + */ +public class LotteryConstants { + + public static final int PRIZE_AMOUNT = 100000; + public static final String SERVICE_BANK_ACCOUNT = "123-123"; + public static final int TICKET_PRIZE = 3; + public static final int SERVICE_BANK_ACCOUNT_SALDO = 150000; + public static final int PLAYER_MAX_SALDO = 100; + +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java new file mode 100644 index 000000000..6f54e743b --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java @@ -0,0 +1,152 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +import java.util.Collections; +import java.util.HashSet; +import java.util.PrimitiveIterator; +import java.util.Random; +import java.util.Set; + +/** + * + * Value object representing lottery numbers. This lottery uses sets of 4 numbers. The numbers must be unique and + * between 1 and 20. + * + */ +public class LotteryNumbers { + + private final Set numbers; + + public static final int MIN_NUMBER = 1; + public static final int MAX_NUMBER = 20; + public static final int NUM_NUMBERS = 4; + + /** + * Constructor. Creates random lottery numbers. + */ + private LotteryNumbers() { + numbers = new HashSet<>(); + generateRandomNumbers(); + } + + /** + * Constructor. Uses given numbers. + */ + private LotteryNumbers(Set givenNumbers) { + numbers = new HashSet<>(); + numbers.addAll(givenNumbers); + } + + /** + * @return random LotteryNumbers + */ + public static LotteryNumbers createRandom() { + return new LotteryNumbers(); + } + + /** + * @return given LotteryNumbers + */ + public static LotteryNumbers create(Set givenNumbers) { + return new LotteryNumbers(givenNumbers); + } + + /** + * @return lottery numbers + */ + public Set getNumbers() { + return Collections.unmodifiableSet(numbers); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((numbers == null) ? 0 : numbers.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + LotteryNumbers other = (LotteryNumbers) obj; + if (numbers == null) { + if (other.numbers != null) { + return false; + } + } else if (!numbers.equals(other.numbers)) { + return false; + } + return true; + } + + /** + * Generates 4 unique random numbers between 1-20 into numbers set. + */ + private void generateRandomNumbers() { + numbers.clear(); + RandomNumberGenerator generator = new RandomNumberGenerator(MIN_NUMBER, MAX_NUMBER); + while (numbers.size() < NUM_NUMBERS) { + int num = generator.nextInt(); + if (!numbers.contains(num)) { + numbers.add(num); + } + } + } + + /** + * + * Helper class for generating random numbers. + * + */ + private static class RandomNumberGenerator { + + private PrimitiveIterator.OfInt randomIterator; + + /** + * Initialize a new random number generator that generates random numbers in the range [min, max] + * + * @param min the min value (inclusive) + * @param max the max value (inclusive) + */ + public RandomNumberGenerator(int min, int max) { + randomIterator = new Random().ints(min, max + 1).iterator(); + } + + /** + * @return a random number in the range (min, max) + */ + public int nextInt() { + return randomIterator.nextInt(); + } + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java new file mode 100644 index 000000000..e5828cfbf --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java @@ -0,0 +1,101 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +/** + * + * Immutable value object representing lottery ticket. + * + */ +public class LotteryTicket { + + private final PlayerDetails playerDetails; + private final LotteryNumbers lotteryNumbers; + + /** + * Constructor. + */ + private LotteryTicket(PlayerDetails details, LotteryNumbers numbers) { + playerDetails = details; + lotteryNumbers = numbers; + } + + /** + * Factory for creating lottery tickets; + */ + public static LotteryTicket create(PlayerDetails details, LotteryNumbers numbers) { + return new LotteryTicket(details, numbers); + } + + /** + * @return player details + */ + public PlayerDetails getPlayerDetails() { + return playerDetails; + } + + /** + * @return lottery numbers + */ + public LotteryNumbers getNumbers() { + return lotteryNumbers; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((lotteryNumbers == null) ? 0 : lotteryNumbers.hashCode()); + result = prime * result + ((playerDetails == null) ? 0 : playerDetails.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + LotteryTicket other = (LotteryTicket) obj; + if (lotteryNumbers == null) { + if (other.lotteryNumbers != null) { + return false; + } + } else if (!lotteryNumbers.equals(other.lotteryNumbers)) { + return false; + } + if (playerDetails == null) { + if (other.playerDetails != null) { + return false; + } + } else if (!playerDetails.equals(other.playerDetails)) { + return false; + } + return true; + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java new file mode 100644 index 000000000..ded65d270 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResult.java @@ -0,0 +1,96 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +/** + * + * Represents lottery ticket check result. + * + */ +public class LotteryTicketCheckResult { + + public enum CheckResult { WIN_PRIZE, NO_PRIZE, TICKET_NOT_SUBMITTED }; + + private final CheckResult checkResult; + private final int prizeAmount; + + /** + * Constructor. + */ + public LotteryTicketCheckResult(CheckResult result) { + checkResult = result; + prizeAmount = 0; + } + + /** + * Constructor. + */ + public LotteryTicketCheckResult(CheckResult result, int amount) { + checkResult = result; + prizeAmount = amount; + } + + /** + * @return check result + */ + public CheckResult getResult() { + return checkResult; + } + + /** + * @return prize amount + */ + public int getPrizeAmount() { + return prizeAmount; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((checkResult == null) ? 0 : checkResult.hashCode()); + result = prime * result + prizeAmount; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + LotteryTicketCheckResult other = (LotteryTicketCheckResult) obj; + if (checkResult != other.checkResult) { + return false; + } + if (prizeAmount != other.prizeAmount) { + return false; + } + return true; + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java new file mode 100644 index 000000000..710091222 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java @@ -0,0 +1,41 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +import java.util.UUID; + +/** + * Lottery ticked id + */ +public class LotteryTicketId { + + private final UUID id; + + public LotteryTicketId() { + id = UUID.randomUUID(); + } + + public UUID getId() { + return id; + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java new file mode 100644 index 000000000..2fcdb6eb3 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java @@ -0,0 +1,118 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +/** + * + * Immutable value object containing lottery player details. + * + */ +public class PlayerDetails { + + private final String emailAddress; + private final String bankAccountNumber; + private final String phoneNumber; + + /** + * Constructor. + */ + private PlayerDetails(String email, String bankAccount, String phone) { + emailAddress = email; + bankAccountNumber = bankAccount; + phoneNumber = phone; + } + + /** + * Factory for creating new objects. + */ + public static PlayerDetails create(String email, String bankAccount, String phone) { + return new PlayerDetails(email, bankAccount, phone); + } + + /** + * @return email + */ + public String getEmail() { + return emailAddress; + } + + /** + * @return bank account number + */ + public String getBankAccount() { + return bankAccountNumber; + } + + /** + * @return phone number + */ + public String getPhoneNumber() { + return phoneNumber; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((bankAccountNumber == null) ? 0 : bankAccountNumber.hashCode()); + result = prime * result + ((emailAddress == null) ? 0 : emailAddress.hashCode()); + result = prime * result + ((phoneNumber == null) ? 0 : phoneNumber.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + PlayerDetails other = (PlayerDetails) obj; + if (bankAccountNumber == null) { + if (other.bankAccountNumber != null) { + return false; + } + } else if (!bankAccountNumber.equals(other.bankAccountNumber)) { + return false; + } + if (emailAddress == null) { + if (other.emailAddress != null) { + return false; + } + } else if (!emailAddress.equals(other.emailAddress)) { + return false; + } + if (phoneNumber == null) { + if (other.phoneNumber != null) { + return false; + } + } else if (!phoneNumber.equals(other.phoneNumber)) { + return false; + } + return true; + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java new file mode 100644 index 000000000..d7a0cc870 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java @@ -0,0 +1,59 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.notifications; + +import com.iluwatar.hexagonal.domain.PlayerDetails; + +/** + * + * Provides notifications for lottery events. + * + */ +public interface LotteryNotifications { + + /** + * Notify lottery ticket was submitted + */ + void notifyTicketSubmitted(PlayerDetails details); + + /** + * Notify there was an error submitting lottery ticket + */ + void notifyTicketSubmitError(PlayerDetails details); + + /** + * Notify lottery ticket did not win + */ + void notifyNoWin(PlayerDetails details); + + /** + * Notify that prize has been paid + */ + void notifyPrize(PlayerDetails details, int prizeAmount); + + /** + * Notify that there was an error paying the prize + */ + void notifyPrizeError(PlayerDetails details, int prizeAmount); + +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java new file mode 100644 index 000000000..c59a47970 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java @@ -0,0 +1,61 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.notifications; + +import com.iluwatar.hexagonal.domain.PlayerDetails; + +public class LotteryNotificationsImpl implements LotteryNotifications { + + @Override + public void notifyTicketSubmitted(PlayerDetails details) { + System.out.println(String.format("Lottery ticket for %s was submitted. Bank account %s was charged for 3 credits.", + details.getEmail(), details.getBankAccount())); + } + + @Override + public void notifyNoWin(PlayerDetails details) { + System.out.println(String.format("Lottery ticket for %s was checked and unfortunately did not win this time.", + details.getEmail())); + } + + @Override + public void notifyPrize(PlayerDetails details, int prizeAmount) { + System.out + .println(String.format("Lottery ticket for %s has won! The bank account %s was deposited with %d credits.", + details.getEmail(), details.getBankAccount(), prizeAmount)); + } + + @Override + public void notifyPrizeError(PlayerDetails details, int prizeAmount) { + System.out + .println(String.format("Lottery ticket for %s has won! Unfortunately the bank credit transfer of %d failed.", + details.getEmail(), prizeAmount)); + } + + @Override + public void notifyTicketSubmitError(PlayerDetails details) { + System.out.println( + String.format("Lottery ticket for %s could not be submitted because the credit transfer of 3 credits failed.", + details.getEmail())); + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java new file mode 100644 index 000000000..0056e794b --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java @@ -0,0 +1,48 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.service; + +import java.util.Optional; + +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; +import com.iluwatar.hexagonal.domain.LotteryTicketId; + +/** + * + * Interface for submitting and checking lottery tickets. + * + */ +public interface LotteryService { + + /** + * Submit lottery ticket to participate in the lottery + */ + Optional submitTicket(LotteryTicket ticket); + + /** + * Check if lottery ticket has won + */ + LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers); +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java new file mode 100644 index 000000000..58df1c7c8 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java @@ -0,0 +1,88 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.service; + +import java.util.Optional; + +import com.iluwatar.hexagonal.banking.WireTransfers; +import com.iluwatar.hexagonal.banking.WireTransfersImpl; +import com.iluwatar.hexagonal.database.LotteryTicketRepository; +import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; +import com.iluwatar.hexagonal.domain.LotteryConstants; +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; +import com.iluwatar.hexagonal.domain.LotteryTicketId; +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; +import com.iluwatar.hexagonal.notifications.LotteryNotifications; +import com.iluwatar.hexagonal.notifications.LotteryNotificationsImpl; + +/** + * + * Implementation for lottery service + * + */ +public class LotteryServiceImpl implements LotteryService { + + private final LotteryTicketRepository repository; + + private final WireTransfers bank = new WireTransfersImpl(); + + private final LotteryNotifications notifications = new LotteryNotificationsImpl(); + + /** + * Constructor + */ + public LotteryServiceImpl() { + repository = new LotteryTicketInMemoryRepository(); + } + + @Override + public Optional submitTicket(LotteryTicket ticket) { + boolean result = bank.transferFunds(LotteryConstants.TICKET_PRIZE, ticket.getPlayerDetails().getBankAccount(), + LotteryConstants.SERVICE_BANK_ACCOUNT); + if (result == false) { + notifications.notifyTicketSubmitError(ticket.getPlayerDetails()); + return Optional.empty(); + } + Optional optional = repository.save(ticket); + if (optional.isPresent()) { + notifications.notifyTicketSubmitted(ticket.getPlayerDetails()); + } + return optional; + } + + @Override + public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { + Optional optional = repository.findById(id); + if (optional.isPresent()) { + if (optional.get().getNumbers().equals(winningNumbers)) { + return new LotteryTicketCheckResult(CheckResult.WIN_PRIZE, 1000); + } else { + return new LotteryTicketCheckResult(CheckResult.NO_PRIZE); + } + } else { + return new LotteryTicketCheckResult(CheckResult.TICKET_NOT_SUBMITTED); + } + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java new file mode 100644 index 000000000..8e19cca35 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/AppTest.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal; + +import org.junit.Test; + +/** + * Unit test for simple App. + */ +public class AppTest { + + @Test + public void testApp() { + String[] args = {}; + App.main(args); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/WireTransfersTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/WireTransfersTest.java new file mode 100644 index 000000000..0274feca3 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/WireTransfersTest.java @@ -0,0 +1,50 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.banking; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * + * Tests for banking + * + */ +public class WireTransfersTest { + + private final WireTransfers bank = new WireTransfersImpl(); + + @Test + public void testInit() { + assertEquals(bank.getFunds("foo"), 0); + bank.setFunds("foo", 100); + assertEquals(bank.getFunds("foo"), 100); + bank.setFunds("bar", 150); + assertEquals(bank.getFunds("bar"), 150); + assertTrue(bank.transferFunds(50, "bar", "foo")); + assertEquals(bank.getFunds("foo"), 150); + assertEquals(bank.getFunds("bar"), 100); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java new file mode 100644 index 000000000..b20e928c8 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java @@ -0,0 +1,64 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.database; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Optional; + +import org.junit.Before; +import org.junit.Test; + +import com.iluwatar.hexagonal.database.LotteryTicketRepository; +import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketId; +import com.iluwatar.hexagonal.test.LotteryTestUtils; + +/** + * + * Tests for {@link LotteryTicketRepository} + * + */ +public class LotteryTicketRepositoryTest { + + private final LotteryTicketRepository repository = new LotteryTicketInMemoryRepository(); + + @Before + public void clear() { + repository.deleteAll(); + } + + @Test + public void testCrudOperations() { + LotteryTicketRepository repository = new LotteryTicketInMemoryRepository(); + assertEquals(repository.findAll().size(), 0); + LotteryTicket ticket = LotteryTestUtils.createLotteryTicket(); + Optional id = repository.save(ticket); + assertTrue(id.isPresent()); + assertEquals(repository.findAll().size(), 1); + Optional optionalTicket = repository.findById(id.get()); + assertTrue(optionalTicket.isPresent()); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryNumbersTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryNumbersTest.java new file mode 100644 index 000000000..4f2751167 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryNumbersTest.java @@ -0,0 +1,76 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.HashSet; + +import org.junit.Test; + +/** + * + * Unit tests for {@link LotteryNumbers} + * + */ +public class LotteryNumbersTest { + + @Test + public void testGivenNumbers() { + LotteryNumbers numbers = LotteryNumbers.create( + new HashSet<>(Arrays.asList(1, 2, 3, 4))); + assertEquals(numbers.getNumbers().size(), 4); + assertTrue(numbers.getNumbers().contains(1)); + assertTrue(numbers.getNumbers().contains(2)); + assertTrue(numbers.getNumbers().contains(3)); + assertTrue(numbers.getNumbers().contains(4)); + } + + @Test(expected = UnsupportedOperationException.class) + public void testNumbersCantBeModified() { + LotteryNumbers numbers = LotteryNumbers.create( + new HashSet<>(Arrays.asList(1, 2, 3, 4))); + numbers.getNumbers().add(5); + } + + @Test + public void testRandomNumbers() { + LotteryNumbers numbers = LotteryNumbers.createRandom(); + assertEquals(numbers.getNumbers().size(), LotteryNumbers.NUM_NUMBERS); + } + + @Test + public void testEquals() { + LotteryNumbers numbers1 = LotteryNumbers.create( + new HashSet<>(Arrays.asList(1, 2, 3, 4))); + LotteryNumbers numbers2 = LotteryNumbers.create( + new HashSet<>(Arrays.asList(1, 2, 3, 4))); + assertTrue(numbers1.equals(numbers2)); + LotteryNumbers numbers3 = LotteryNumbers.create( + new HashSet<>(Arrays.asList(11, 12, 13, 14))); + assertFalse(numbers1.equals(numbers3)); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResultTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResultTest.java new file mode 100644 index 000000000..237ab85d3 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketCheckResultTest.java @@ -0,0 +1,47 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import org.junit.Test; + +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; + +/** + * + * Unit tests for {@link LotteryTicketCheckResult} + * + */ +public class LotteryTicketCheckResultTest { + + @Test + public void testEquals() { + LotteryTicketCheckResult result1 = new LotteryTicketCheckResult(CheckResult.NO_PRIZE); + LotteryTicketCheckResult result2 = new LotteryTicketCheckResult(CheckResult.NO_PRIZE); + assertEquals(result1, result2); + LotteryTicketCheckResult result3 = new LotteryTicketCheckResult(CheckResult.WIN_PRIZE, 300000); + assertFalse(result1.equals(result3)); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java new file mode 100644 index 000000000..e1918686a --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java @@ -0,0 +1,49 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import java.util.Arrays; +import java.util.HashSet; + +import org.junit.Test; + +public class LotteryTicketTest { + + @Test + public void testEquals() { + PlayerDetails details1 = PlayerDetails.create("bob@foo.bar", "1212-121212", "+34332322"); + LotteryNumbers numbers1 = LotteryNumbers.create(new HashSet(Arrays.asList(1, 2, 3, 4))); + LotteryTicket ticket1 = LotteryTicket.create(details1, numbers1); + PlayerDetails details2 = PlayerDetails.create("bob@foo.bar", "1212-121212", "+34332322"); + LotteryNumbers numbers2 = LotteryNumbers.create(new HashSet(Arrays.asList(1, 2, 3, 4))); + LotteryTicket ticket2 = LotteryTicket.create(details2, numbers2); + assertEquals(ticket1, ticket2); + PlayerDetails details3 = PlayerDetails.create("elsa@foo.bar", "1223-121212", "+49332322"); + LotteryNumbers numbers3 = LotteryNumbers.create(new HashSet(Arrays.asList(1, 2, 3, 8))); + LotteryTicket ticket3 = LotteryTicket.create(details3, numbers3); + assertFalse(ticket1.equals(ticket3)); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java new file mode 100644 index 000000000..813b035a2 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java @@ -0,0 +1,45 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.domain; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import org.junit.Test; + +/** + * + * Unit tests for {@link PlayerDetails} + * + */ +public class PlayerDetailsTest { + + @Test + public void testEquals() { + PlayerDetails details1 = PlayerDetails.create("tom@foo.bar", "11212-123434", "+12323425"); + PlayerDetails details2 = PlayerDetails.create("tom@foo.bar", "11212-123434", "+12323425"); + assertEquals(details1, details2); + PlayerDetails details3 = PlayerDetails.create("john@foo.bar", "16412-123439", "+34323432"); + assertFalse(details1.equals(details3)); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java new file mode 100644 index 000000000..27e5bb6e4 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java @@ -0,0 +1,116 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.lottery; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; + +import org.junit.Before; +import org.junit.Test; + +import com.iluwatar.hexagonal.administration.LotteryAdministration; +import com.iluwatar.hexagonal.administration.LotteryAdministrationImpl; +import com.iluwatar.hexagonal.banking.WireTransfers; +import com.iluwatar.hexagonal.banking.WireTransfersImpl; +import com.iluwatar.hexagonal.database.LotteryTicketRepository; +import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; +import com.iluwatar.hexagonal.domain.LotteryTicketId; +import com.iluwatar.hexagonal.service.LotteryService; +import com.iluwatar.hexagonal.service.LotteryServiceImpl; +import com.iluwatar.hexagonal.test.LotteryTestUtils; + +/** + * + * Test the lottery system + * + */ +public class LotteryTest { + + private final LotteryAdministration admin = new LotteryAdministrationImpl(); + private final LotteryService service = new LotteryServiceImpl(); + private final LotteryTicketRepository repository = new LotteryTicketInMemoryRepository(); + private final WireTransfers wireTransfers = new WireTransfersImpl(); + + @Before + public void clear() { + repository.deleteAll(); + } + + @Test + public void testLottery() { + + // setup bank account with funds + wireTransfers.setFunds("123-12312", 100); + + // admin resets the lottery + admin.resetLottery(); + assertEquals(admin.getAllSubmittedTickets().size(), 0); + + // players submit the lottery tickets + Optional ticket1 = service.submitTicket(LotteryTestUtils.createLotteryTicket("cvt@bbb.com", + "123-12312", "+32425255", new HashSet<>(Arrays.asList(1, 2, 3, 4)))); + assertTrue(ticket1.isPresent()); + Optional ticket2 = service.submitTicket(LotteryTestUtils.createLotteryTicket("ant@bac.com", + "123-12312", "+32423455", new HashSet<>(Arrays.asList(11, 12, 13, 14)))); + assertTrue(ticket2.isPresent()); + Optional ticket3 = service.submitTicket(LotteryTestUtils.createLotteryTicket("arg@boo.com", + "123-12312", "+32421255", new HashSet<>(Arrays.asList(6, 8, 13, 19)))); + assertTrue(ticket3.isPresent()); + assertEquals(admin.getAllSubmittedTickets().size(), 3); + + // perform lottery + LotteryNumbers winningNumbers = admin.performLottery(); + + // cheat a bit for testing sake, use winning numbers to submit another ticket + Optional ticket4 = service.submitTicket(LotteryTestUtils.createLotteryTicket("lucky@orb.com", + "123-12312", "+12421255", winningNumbers.getNumbers())); + assertTrue(ticket4.isPresent()); + assertEquals(admin.getAllSubmittedTickets().size(), 4); + + // check winners + Map tickets = admin.getAllSubmittedTickets(); + for (LotteryTicketId id: tickets.keySet()) { + LotteryTicketCheckResult checkResult = service.checkTicketForPrize(id, winningNumbers); + assertTrue(checkResult.getResult() != CheckResult.TICKET_NOT_SUBMITTED); + if (checkResult.getResult().equals(CheckResult.WIN_PRIZE)) { + assertTrue(checkResult.getPrizeAmount() > 0); + } else if (checkResult.getResult().equals(CheckResult.WIN_PRIZE)) { + assertEquals(checkResult.getPrizeAmount(), 0); + } + } + + // check another ticket that has not been submitted + LotteryTicketCheckResult checkResult = service.checkTicketForPrize(new LotteryTicketId(), winningNumbers); + assertTrue(checkResult.getResult() == CheckResult.TICKET_NOT_SUBMITTED); + assertEquals(checkResult.getPrizeAmount(), 0); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java new file mode 100644 index 000000000..883c8127f --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java @@ -0,0 +1,56 @@ +/** + * The MIT License + * Copyright (c) 2014 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.hexagonal.test; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.PlayerDetails; + +/** + * + * Utilities for lottery tests + * + */ +public class LotteryTestUtils { + + /** + * @return lottery ticket + */ + public static LotteryTicket createLotteryTicket() { + return createLotteryTicket("foo@bar.com", "12231-213132", "+99324554", new HashSet<>(Arrays.asList(1, 2, 3, 4))); + } + + /** + * @return lottery ticket + */ + public static LotteryTicket createLotteryTicket(String email, String account, String phone, + Set givenNumbers) { + PlayerDetails details = PlayerDetails.create(email, account, phone); + LotteryNumbers numbers = LotteryNumbers.create(givenNumbers); + return LotteryTicket.create(details, numbers); + } +} diff --git a/intercepting-filter/index.md b/intercepting-filter/README.md similarity index 66% rename from intercepting-filter/index.md rename to intercepting-filter/README.md index 41825745b..7d53472a0 100644 --- a/intercepting-filter/index.md +++ b/intercepting-filter/README.md @@ -4,25 +4,30 @@ title: Intercepting Filter folder: intercepting-filter permalink: /patterns/intercepting-filter/ categories: Behavioral -tags: Java +tags: + - Java + - Difficulty-Intermediate --- -**Intent:** Provide pluggable filters to conduct necessary pre-processing and +## Intent +Provide pluggable filters to conduct necessary pre-processing and post-processing to requests from a client to a target ![alt text](./etc/intercepting-filter.png "Intercepting Filter") -**Applicability:** Use the Intercepting Filter pattern when +## Applicability +Use the Intercepting Filter pattern when * a system uses pre-processing or post-processing requests * a system should do the authentication/ authorization/ logging or tracking of request and then pass the requests to corresponding handlers * you want a modular approach to configuring pre-processing and post-processing schemes -**Real world examples:** +## Real world examples +* [javax.servlet.FilterChain](https://tomcat.apache.org/tomcat-8.0-doc/servletapi/javax/servlet/FilterChain.html) and [javax.servlet.Filter](https://tomcat.apache.org/tomcat-8.0-doc/servletapi/javax/servlet/Filter.html) * [Struts 2 - Interceptors](https://struts.apache.org/docs/interceptors.html) -**Credits:** +## Credits * [TutorialsPoint - Intercepting Filter](http://www.tutorialspoint.com/design_pattern/intercepting_filter_pattern.htm) * [Presentation Tier Patterns](http://www.javagyan.com/tutorials/corej2eepatterns/presentation-tier-patterns) diff --git a/intercepting-filter/pom.xml b/intercepting-filter/pom.xml index 230f76ca3..6bb6f95cc 100644 --- a/intercepting-filter/pom.xml +++ b/intercepting-filter/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT intercepting-filter @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/AbstractFilter.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/AbstractFilter.java index 2c32772fa..e81531297 100644 --- a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/AbstractFilter.java +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/AbstractFilter.java @@ -1,46 +1,66 @@ +/** + * The MIT License + * Copyright (c) 2014 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.intercepting.filter; /** - * Base class for order processing filters. - * Handles chain management. + * Base class for order processing filters. Handles chain management. * */ public abstract class AbstractFilter implements Filter { - private Filter next; - - public AbstractFilter() { - } + private Filter next; - public AbstractFilter(Filter next) { - this.next = next; - } - - @Override - public void setNext(Filter filter) { - this.next = filter; - } - - @Override - public Filter getNext() { - return next; - } + public AbstractFilter() {} - @Override - public Filter getLast() { - Filter last = this; - while (last.getNext() != null) { - last = last.getNext(); - } - return last; - } - - @Override - public String execute(Order order) { - if (getNext() != null) { - return getNext().execute(order); - } else { - return ""; - } - } + public AbstractFilter(Filter next) { + this.next = next; + } + + @Override + public void setNext(Filter filter) { + this.next = filter; + } + + @Override + public Filter getNext() { + return next; + } + + @Override + public Filter getLast() { + Filter last = this; + while (last.getNext() != null) { + last = last.getNext(); + } + return last; + } + + @Override + public String execute(Order order) { + if (getNext() != null) { + return getNext().execute(order); + } else { + return ""; + } + } } diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/AddressFilter.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/AddressFilter.java index 0bd66c047..5b6ff4155 100644 --- a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/AddressFilter.java +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/AddressFilter.java @@ -1,19 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 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.intercepting.filter; /** - * Concrete implementation of filter - * This filter is responsible for checking/filtering the input in the address field. + * Concrete implementation of filter This filter is responsible for checking/filtering the input in + * the address field. + * * @author joshzambales * */ public class AddressFilter extends AbstractFilter { - - @Override - public String execute(Order order) { - String result = super.execute(order); - if (order.getAddress() == null || order.getAddress().isEmpty()) { - return result + "Invalid address! "; - } else - return result; - } + + @Override + public String execute(Order order) { + String result = super.execute(order); + if (order.getAddress() == null || order.getAddress().isEmpty()) { + return result + "Invalid address! "; + } else { + return result; + } + } } diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/App.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/App.java index c913da66c..9d16737d0 100644 --- a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/App.java +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/App.java @@ -1,51 +1,69 @@ +/** + * The MIT License + * Copyright (c) 2014 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.intercepting.filter; /** * - * When a request enters a Web application, it often must pass several entrance - * tests prior to the main processing stage. For example, - * - Has the client been authenticated? - * - Does the client have a valid session? - * - Is the client's IP address from a trusted network? - * - Does the request path violate any constraints? - * - What encoding does the client use to send the data? - * - Do we support the browser type of the client? - * Some of these checks are tests, resulting in a yes or no answer that determines - * whether processing will continue. Other checks manipulate the incoming data + * When a request enters a Web application, it often must pass several entrance tests prior to the + * main processing stage. For example, - Has the client been authenticated? - Does the client have a + * valid session? - Is the client's IP address from a trusted network? - Does the request path + * violate any constraints? - What encoding does the client use to send the data? - Do we support + * the browser type of the client? Some of these checks are tests, resulting in a yes or no answer + * that determines whether processing will continue. Other checks manipulate the incoming data * stream into a form suitable for processing. *

- * The classic solution consists of a series of conditional checks, with any failed - * check aborting the request. Nested if/else statements are a standard strategy, - * but this solution leads to code fragility and a copy-and-paste style of programming, - * because the flow of the filtering and the action of the filters is compiled into - * the application. + * The classic solution consists of a series of conditional checks, with any failed check aborting + * the request. Nested if/else statements are a standard strategy, but this solution leads to code + * fragility and a copy-and-paste style of programming, because the flow of the filtering and the + * action of the filters is compiled into the application. *

- * The key to solving this problem in a flexible and unobtrusive manner is to have a - * simple mechanism for adding and removing processing components, in which each - * component completes a specific filtering action. This is the Intercepting Filter - * pattern in action. + * The key to solving this problem in a flexible and unobtrusive manner is to have a simple + * mechanism for adding and removing processing components, in which each component completes a + * specific filtering action. This is the Intercepting Filter pattern in action. *

- * In this example we check whether the order request is valid through pre-processing - * done via {@link Filter}. Each field has its own corresponding {@link Filter} + * In this example we check whether the order request is valid through pre-processing done via + * {@link Filter}. Each field has its own corresponding {@link Filter} *

+ * * @author joshzambales * */ -public class App{ - - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { - FilterManager filterManager = new FilterManager(new Target()); - filterManager.addFilter(new NameFilter()); - filterManager.addFilter(new ContactFilter()); - filterManager.addFilter(new AddressFilter()); - filterManager.addFilter(new DepositFilter()); - filterManager.addFilter(new OrderFilter()); +public class App { - Client client = new Client(); - client.setFilterManager(filterManager); - } + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + FilterManager filterManager = new FilterManager(); + filterManager.addFilter(new NameFilter()); + filterManager.addFilter(new ContactFilter()); + filterManager.addFilter(new AddressFilter()); + filterManager.addFilter(new DepositFilter()); + filterManager.addFilter(new OrderFilter()); + + Client client = new Client(); + client.setFilterManager(filterManager); + } } diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Client.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Client.java index 0125d1b9d..f2f1a6fbc 100644 --- a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Client.java +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Client.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.intercepting.filter; import java.awt.BorderLayout; @@ -15,93 +37,99 @@ import javax.swing.JTextField; import javax.swing.SwingUtilities; /** - * The Client class is responsible for handling the input and running them through filters inside the {@link FilterManager}. + * The Client class is responsible for handling the input and running them through filters inside + * the {@link FilterManager}. * - * This is where {@link Filter}s come to play as the client pre-processes the request before being displayed in the {@link Target}. + * This is where {@link Filter}s come to play as the client pre-processes the request before being + * displayed in the {@link Target}. * * @author joshzambales * */ public class Client extends JFrame { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - private FilterManager filterManager; - private JLabel jl; - private JTextField[] jtFields; - private JTextArea[] jtAreas; - private JButton clearButton, processButton; + private FilterManager filterManager; + private JLabel jl; + private JTextField[] jtFields; + private JTextArea[] jtAreas; + private JButton clearButton; + private JButton processButton; - public Client() { - super("Client System"); - setDefaultCloseOperation(EXIT_ON_CLOSE); - setSize(300, 300); - jl = new JLabel("RUNNING..."); - jtFields = new JTextField[3]; - for (int i = 0; i < 3; i++) { - jtFields[i] = new JTextField(); - } - jtAreas = new JTextArea[2]; - for (int i = 0; i < 2; i++) { - jtAreas[i] = new JTextArea(); - } - clearButton = new JButton("Clear"); - processButton = new JButton("Process"); + /** + * Constructor + */ + public Client() { + super("Client System"); + setDefaultCloseOperation(EXIT_ON_CLOSE); + setSize(300, 300); + jl = new JLabel("RUNNING..."); + jtFields = new JTextField[3]; + for (int i = 0; i < 3; i++) { + jtFields[i] = new JTextField(); + } + jtAreas = new JTextArea[2]; + for (int i = 0; i < 2; i++) { + jtAreas[i] = new JTextArea(); + } + clearButton = new JButton("Clear"); + processButton = new JButton("Process"); - setup(); - } + setup(); + } - private void setup() { - setLayout(new BorderLayout()); - JPanel panel = new JPanel(); - add(jl, BorderLayout.SOUTH); - add(panel, BorderLayout.CENTER); - panel.setLayout(new GridLayout(6, 2)); - panel.add(new JLabel("Name")); - panel.add(jtFields[0]); - panel.add(new JLabel("Contact Number")); - panel.add(jtFields[1]); - panel.add(new JLabel("Address")); - panel.add(jtAreas[0]); - panel.add(new JLabel("Deposit Number")); - panel.add(jtFields[2]); - panel.add(new JLabel("Order")); - panel.add(jtAreas[1]); - panel.add(clearButton); - panel.add(processButton); + private void setup() { + setLayout(new BorderLayout()); + JPanel panel = new JPanel(); + add(jl, BorderLayout.SOUTH); + add(panel, BorderLayout.CENTER); + panel.setLayout(new GridLayout(6, 2)); + panel.add(new JLabel("Name")); + panel.add(jtFields[0]); + panel.add(new JLabel("Contact Number")); + panel.add(jtFields[1]); + panel.add(new JLabel("Address")); + panel.add(jtAreas[0]); + panel.add(new JLabel("Deposit Number")); + panel.add(jtFields[2]); + panel.add(new JLabel("Order")); + panel.add(jtAreas[1]); + panel.add(clearButton); + panel.add(processButton); - clearButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - for (JTextArea i : jtAreas) { - i.setText(""); - } - for (JTextField i : jtFields) { - i.setText(""); - } - } - }); + clearButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + for (JTextArea i : jtAreas) { + i.setText(""); + } + for (JTextField i : jtFields) { + i.setText(""); + } + } + }); - processButton.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - Order order = new Order(jtFields[0].getText(), jtFields[1] - .getText(), jtAreas[0].getText(), - jtFields[2].getText(), jtAreas[1].getText()); - jl.setText(sendRequest(order)); - } - }); + processButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + Order order = + new Order(jtFields[0].getText(), jtFields[1].getText(), jtAreas[0].getText(), + jtFields[2].getText(), jtAreas[1].getText()); + jl.setText(sendRequest(order)); + } + }); - JRootPane rootPane = SwingUtilities.getRootPane(processButton); - rootPane.setDefaultButton(processButton); - setVisible(true); - } + JRootPane rootPane = SwingUtilities.getRootPane(processButton); + rootPane.setDefaultButton(processButton); + setVisible(true); + } - public void setFilterManager(FilterManager filterManager) { - this.filterManager = filterManager; - } + public void setFilterManager(FilterManager filterManager) { + this.filterManager = filterManager; + } - public String sendRequest(Order order) { - return filterManager.filterRequest(order); - } + public String sendRequest(Order order) { + return filterManager.filterRequest(order); + } } diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/ContactFilter.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/ContactFilter.java index 214376263..098de178b 100644 --- a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/ContactFilter.java +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/ContactFilter.java @@ -1,24 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 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.intercepting.filter; /** - * Concrete implementation of filter - * This filter checks for the contact field in which it checks if the input consist of numbers - * and it also checks if the input follows the length constraint (11 digits) + * Concrete implementation of filter This filter checks for the contact field in which it checks if + * the input consist of numbers and it also checks if the input follows the length constraint (11 + * digits) + * * @author joshzambales * */ public class ContactFilter extends AbstractFilter { - - @Override - public String execute(Order order) { - String result = super.execute(order); - if (order.getContactNumber() == null - || order.getContactNumber().isEmpty() - || order.getContactNumber().matches(".*[^\\d]+.*") - || order.getContactNumber().length() != 11) { - return result + "Invalid contact number! "; - } else { - return result; - } - } + + @Override + public String execute(Order order) { + String result = super.execute(order); + if (order.getContactNumber() == null || order.getContactNumber().isEmpty() + || order.getContactNumber().matches(".*[^\\d]+.*") + || order.getContactNumber().length() != 11) { + return result + "Invalid contact number! "; + } else { + return result; + } + } } diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/DepositFilter.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/DepositFilter.java index 129c07cd7..08285f79d 100644 --- a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/DepositFilter.java +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/DepositFilter.java @@ -1,20 +1,42 @@ +/** + * The MIT License + * Copyright (c) 2014 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.intercepting.filter; /** - * Concrete implementation of filter - * This checks for the deposit code + * Concrete implementation of filter This checks for the deposit code + * * @author joshzambales * */ public class DepositFilter extends AbstractFilter { - - @Override - public String execute(Order order) { - String result = super.execute(order); - if (order.getDepositNumber() == null || order.getDepositNumber().isEmpty()) { - return result + "Invalid deposit number! "; - } else { - return result; - } - } + + @Override + public String execute(Order order) { + String result = super.execute(order); + if (order.getDepositNumber() == null || order.getDepositNumber().isEmpty()) { + return result + "Invalid deposit number! "; + } else { + return result; + } + } } diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Filter.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Filter.java index a71be5154..9979df0e7 100644 --- a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Filter.java +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Filter.java @@ -1,37 +1,53 @@ +/** + * The MIT License + * Copyright (c) 2014 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.intercepting.filter; /** - * Filters perform certain tasks prior or after execution of - * request by request handler. In this case, before the request is handled by - * the target, the request undergoes through each Filter + * Filters perform certain tasks prior or after execution of request by request handler. In this + * case, before the request is handled by the target, the request undergoes through each Filter * * @author joshzambales * */ public interface Filter { - - /** - * Execute order processing filter. - * @param order - * @return empty string on success, otherwise error message. - */ - String execute(Order order); - - /** - * Set next filter in chain after this. - * @param filter - */ - void setNext(Filter filter); - - /** - * Get next filter in chain after this. - * @return - */ - Filter getNext(); - - /** - * Get last filter in the chain. - * @return - */ - Filter getLast(); + + /** + * Execute order processing filter. + */ + String execute(Order order); + + /** + * Set next filter in chain after this. + */ + void setNext(Filter filter); + + /** + * Get next filter in chain after this. + */ + Filter getNext(); + + /** + * Get last filter in the chain. + */ + Filter getLast(); } diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/FilterChain.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/FilterChain.java index e11a58ea0..7c5d74b4c 100644 --- a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/FilterChain.java +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/FilterChain.java @@ -1,34 +1,62 @@ - package com.iluwatar.intercepting.filter; - - +/** + * The MIT License + * Copyright (c) 2014 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.intercepting.filter; + + /** * Filter Chain carries multiple filters and help to execute them in defined order on target. * * @author joshzambales */ public class FilterChain { - - private Filter chain; - - private final Target target; - public FilterChain(Target target) { - this.target = target; - } + private Filter chain; - public void addFilter(Filter filter) { - if (chain == null) { - chain = filter; - } else { - chain.getLast().setNext(filter); - } - } + /** + * Constructor + */ + public FilterChain() { + } - public String execute(Order order) { - if (chain != null) { - return chain.execute(order); - } else { - return "RUNNING..."; - } - } + /** + * Adds filter + */ + public void addFilter(Filter filter) { + if (chain == null) { + chain = filter; + } else { + chain.getLast().setNext(filter); + } + } + + /** + * Execute filter chain + */ + public String execute(Order order) { + if (chain != null) { + return chain.execute(order); + } else { + return "RUNNING..."; + } + } } diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/FilterManager.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/FilterManager.java index d15df424a..b0e125522 100644 --- a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/FilterManager.java +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/FilterManager.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.intercepting.filter; /** @@ -7,18 +29,18 @@ package com.iluwatar.intercepting.filter; * */ public class FilterManager { - - private FilterChain filterChain; - public FilterManager(Target target) { - filterChain = new FilterChain(target); - } + private FilterChain filterChain; - public void addFilter(Filter filter) { - filterChain.addFilter(filter); - } + public FilterManager() { + filterChain = new FilterChain(); + } - public String filterRequest(Order order) { - return filterChain.execute(order); - } + public void addFilter(Filter filter) { + filterChain.addFilter(filter); + } + + public String filterRequest(Order order) { + return filterChain.execute(order); + } } diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/NameFilter.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/NameFilter.java index 201c68bca..ea8d17413 100644 --- a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/NameFilter.java +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/NameFilter.java @@ -1,21 +1,44 @@ +/** + * The MIT License + * Copyright (c) 2014 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.intercepting.filter; /** - * Concrete implementation of filter. This filter checks if the input in the Name - * field is valid. (alphanumeric) + * Concrete implementation of filter. This filter checks if the input in the Name field is valid. + * (alphanumeric) * * @author joshzambales * */ public class NameFilter extends AbstractFilter { - - @Override - public String execute(Order order) { - String result = super.execute(order); - if (order.getName() == null || order.getName().isEmpty() || order.getName().matches(".*[^\\w|\\s]+.*")) { - return result + "Invalid order! "; - } else { - return result; - } - } + + @Override + public String execute(Order order) { + String result = super.execute(order); + if (order.getName() == null || order.getName().isEmpty() + || order.getName().matches(".*[^\\w|\\s]+.*")) { + return result + "Invalid name! "; + } else { + return result; + } + } } diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Order.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Order.java index 60bf21f8e..8a60bed9d 100644 --- a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Order.java +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Order.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.intercepting.filter; /** @@ -6,60 +28,62 @@ package com.iluwatar.intercepting.filter; */ public class Order { - private String name; - private String contactNumber; - private String address; - private String depositNumber; - private String order; - - public Order() { - } + private String name; + private String contactNumber; + private String address; + private String depositNumber; + private String order; - public Order(String name, String contactNumber, String address, String depositNumber, String order) { - this.name = name; - this.contactNumber = contactNumber; - this.address = address; - this.depositNumber = depositNumber; - this.order = order; - } - - public String getName() { - return name; - } + public Order() {} - public void setName(String name) { - this.name = name; - } + /** + * Constructor + */ + public Order(String name, String contactNumber, String address, String depositNumber, String order) { + this.name = name; + this.contactNumber = contactNumber; + this.address = address; + this.depositNumber = depositNumber; + this.order = order; + } - public String getContactNumber() { - return contactNumber; - } + public String getName() { + return name; + } - public void setContactNumber(String contactNumber) { - this.contactNumber = contactNumber; - } + public void setName(String name) { + this.name = name; + } - public String getAddress() { - return address; - } + public String getContactNumber() { + return contactNumber; + } - public void setAddress(String address) { - this.address = address; - } + public void setContactNumber(String contactNumber) { + this.contactNumber = contactNumber; + } - public String getDepositNumber() { - return depositNumber; - } + public String getAddress() { + return address; + } - public void setDepositNumber(String depositNumber) { - this.depositNumber = depositNumber; - } + public void setAddress(String address) { + this.address = address; + } - public String getOrder() { - return order; - } + public String getDepositNumber() { + return depositNumber; + } - public void setOrder(String order) { - this.order = order; - } + public void setDepositNumber(String depositNumber) { + this.depositNumber = depositNumber; + } + + public String getOrder() { + return order; + } + + public void setOrder(String order) { + this.order = order; + } } diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/OrderFilter.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/OrderFilter.java index cdeaec6e0..9bfe890f3 100644 --- a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/OrderFilter.java +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/OrderFilter.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.intercepting.filter; /** @@ -7,14 +29,14 @@ package com.iluwatar.intercepting.filter; * */ public class OrderFilter extends AbstractFilter { - - @Override - public String execute(Order order) { - String result = super.execute(order); - if (order.getOrder() == null || order.getOrder().isEmpty()) { - return result + "Invalid order! "; - } else { - return result; - } - } + + @Override + public String execute(Order order) { + String result = super.execute(order); + if (order.getOrder() == null || order.getOrder().isEmpty()) { + return result + "Invalid order! "; + } else { + return result; + } + } } diff --git a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Target.java b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Target.java index 6ca456512..e8fc693bc 100644 --- a/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Target.java +++ b/intercepting-filter/src/main/java/com/iluwatar/intercepting/filter/Target.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.intercepting.filter; import java.awt.BorderLayout; @@ -22,57 +44,61 @@ import javax.swing.table.DefaultTableModel; */ public class Target extends JFrame { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - private JTable jt; - private JScrollPane jsp; - private DefaultTableModel dtm; - private JButton del; + private JTable jt; + private JScrollPane jsp; + private DefaultTableModel dtm; + private JButton del; - public Target() { - super("Order System"); - setDefaultCloseOperation(EXIT_ON_CLOSE); - setSize(640, 480); - dtm = new DefaultTableModel(new Object[] { "Name", "Contact Number", - "Address", "Deposit Number", "Order" }, 0); - jt = new JTable(dtm); - del = new JButton("Delete"); - setup(); - } + /** + * Constructor + */ + public Target() { + super("Order System"); + setDefaultCloseOperation(EXIT_ON_CLOSE); + setSize(640, 480); + dtm = + new DefaultTableModel(new Object[] {"Name", "Contact Number", "Address", "Deposit Number", + "Order"}, 0); + jt = new JTable(dtm); + del = new JButton("Delete"); + setup(); + } - private void setup() { - setLayout(new BorderLayout()); - JPanel bot = new JPanel(); - add(jt.getTableHeader(), BorderLayout.NORTH); - bot.setLayout(new BorderLayout()); - bot.add(del, BorderLayout.EAST); - add(bot, BorderLayout.SOUTH); - jsp = new JScrollPane(jt); - jsp.setPreferredSize(new Dimension(500, 250)); - add(jsp, BorderLayout.CENTER); + private void setup() { + setLayout(new BorderLayout()); + JPanel bot = new JPanel(); + add(jt.getTableHeader(), BorderLayout.NORTH); + bot.setLayout(new BorderLayout()); + bot.add(del, BorderLayout.EAST); + add(bot, BorderLayout.SOUTH); + jsp = new JScrollPane(jt); + jsp.setPreferredSize(new Dimension(500, 250)); + add(jsp, BorderLayout.CENTER); - del.addActionListener(new DListener()); + del.addActionListener(new DListener()); - JRootPane rootPane = SwingUtilities.getRootPane(del); - rootPane.setDefaultButton(del); - setVisible(true); - } + JRootPane rootPane = SwingUtilities.getRootPane(del); + rootPane.setDefaultButton(del); + setVisible(true); + } - public void execute(String[] request) { - dtm.addRow(new Object[] { request[0], request[1], request[2], - request[3], request[4] }); - } + public void execute(String[] request) { + dtm.addRow(new Object[] {request[0], request[1], request[2], request[3], request[4]}); + } - class DListener implements ActionListener { - @Override - public void actionPerformed(ActionEvent e) { - int temp = jt.getSelectedRow(); - if (temp == -1) - return; - int temp2 = jt.getSelectedRowCount(); - for (int i = 0; i < temp2; i++) { - dtm.removeRow(temp); - } - } - } + class DListener implements ActionListener { + @Override + public void actionPerformed(ActionEvent e) { + int temp = jt.getSelectedRow(); + if (temp == -1) { + return; + } + int temp2 = jt.getSelectedRowCount(); + for (int i = 0; i < temp2; i++) { + dtm.removeRow(temp); + } + } + } } diff --git a/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/AppTest.java b/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/AppTest.java index 9d31127a2..c91fc3297 100644 --- a/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/AppTest.java +++ b/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.intercepting.filter; import org.junit.Test; -import com.iluwatar.intercepting.filter.App; - /** * * Application test. @@ -11,9 +31,9 @@ import com.iluwatar.intercepting.filter.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/FilterManagerTest.java b/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/FilterManagerTest.java new file mode 100644 index 000000000..4d7187727 --- /dev/null +++ b/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/FilterManagerTest.java @@ -0,0 +1,68 @@ +/** + * The MIT License + * Copyright (c) 2014 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.intercepting.filter; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +/** + * Date: 12/13/15 - 3:01 PM + * + * @author Jeroen Meulemeester + */ +public class FilterManagerTest { + + @Test + public void testFilterRequest() throws Exception { + final Target target = mock(Target.class); + final FilterManager filterManager = new FilterManager(); + assertEquals("RUNNING...", filterManager.filterRequest(mock(Order.class))); + verifyZeroInteractions(target); + } + + @Test + public void testAddFilter() throws Exception { + final Target target = mock(Target.class); + final FilterManager filterManager = new FilterManager(); + + verifyZeroInteractions(target); + + final Filter filter = mock(Filter.class); + when(filter.execute(any(Order.class))).thenReturn("filter"); + + filterManager.addFilter(filter); + + final Order order = mock(Order.class); + assertEquals("filter", filterManager.filterRequest(order)); + + verify(filter, times(1)).execute(any(Order.class)); + verifyZeroInteractions(target, filter, order); + } +} \ No newline at end of file diff --git a/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/FilterTest.java b/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/FilterTest.java new file mode 100644 index 000000000..c18a743d2 --- /dev/null +++ b/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/FilterTest.java @@ -0,0 +1,120 @@ +/** + * The MIT License + * Copyright (c) 2014 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.intercepting.filter; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.util.ArrayList; +import java.util.List; + +import static junit.framework.TestCase.assertSame; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +/** + * Date: 12/13/15 - 2:17 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class FilterTest { + + private static final Order PERFECT_ORDER = new Order("name", "12345678901", "addr", "dep", "order"); + private static final Order WRONG_ORDER = new Order("name", "12345678901", "addr", "dep", ""); + private static final Order WRONG_DEPOSIT = new Order("name", "12345678901", "addr", "", "order"); + private static final Order WRONG_ADDRESS = new Order("name", "12345678901", "", "dep", "order"); + private static final Order WRONG_CONTACT = new Order("name", "", "addr", "dep", "order"); + private static final Order WRONG_NAME = new Order("", "12345678901", "addr", "dep", "order"); + + @Parameters + public static List getTestData() { + final List testData = new ArrayList<>(); + testData.add(new Object[]{new NameFilter(), PERFECT_ORDER, ""}); + testData.add(new Object[]{new NameFilter(), WRONG_NAME, "Invalid name!"}); + testData.add(new Object[]{new NameFilter(), WRONG_CONTACT, ""}); + testData.add(new Object[]{new NameFilter(), WRONG_ADDRESS, ""}); + testData.add(new Object[]{new NameFilter(), WRONG_DEPOSIT, ""}); + testData.add(new Object[]{new NameFilter(), WRONG_ORDER, ""}); + + testData.add(new Object[]{new ContactFilter(), PERFECT_ORDER, ""}); + testData.add(new Object[]{new ContactFilter(), WRONG_NAME, ""}); + testData.add(new Object[]{new ContactFilter(), WRONG_CONTACT, "Invalid contact number!"}); + testData.add(new Object[]{new ContactFilter(), WRONG_ADDRESS, ""}); + testData.add(new Object[]{new ContactFilter(), WRONG_DEPOSIT, ""}); + testData.add(new Object[]{new ContactFilter(), WRONG_ORDER, ""}); + + testData.add(new Object[]{new AddressFilter(), PERFECT_ORDER, ""}); + testData.add(new Object[]{new AddressFilter(), WRONG_NAME, ""}); + testData.add(new Object[]{new AddressFilter(), WRONG_CONTACT, ""}); + testData.add(new Object[]{new AddressFilter(), WRONG_ADDRESS, "Invalid address!"}); + testData.add(new Object[]{new AddressFilter(), WRONG_DEPOSIT, ""}); + testData.add(new Object[]{new AddressFilter(), WRONG_ORDER, ""}); + + testData.add(new Object[]{new DepositFilter(), PERFECT_ORDER, ""}); + testData.add(new Object[]{new DepositFilter(), WRONG_NAME, ""}); + testData.add(new Object[]{new DepositFilter(), WRONG_CONTACT, ""}); + testData.add(new Object[]{new DepositFilter(), WRONG_ADDRESS, ""}); + testData.add(new Object[]{new DepositFilter(), WRONG_DEPOSIT, "Invalid deposit number!"}); + testData.add(new Object[]{new DepositFilter(), WRONG_ORDER, ""}); + + testData.add(new Object[]{new OrderFilter(), PERFECT_ORDER, ""}); + testData.add(new Object[]{new OrderFilter(), WRONG_NAME, ""}); + testData.add(new Object[]{new OrderFilter(), WRONG_CONTACT, ""}); + testData.add(new Object[]{new OrderFilter(), WRONG_ADDRESS, ""}); + testData.add(new Object[]{new OrderFilter(), WRONG_DEPOSIT, ""}); + testData.add(new Object[]{new OrderFilter(), WRONG_ORDER, "Invalid order!"}); + + return testData; + } + + private final Filter filter; + private final Order order; + private final String result; + + /** + * Constructor + */ + public FilterTest(Filter filter, Order order, String result) { + this.filter = filter; + this.order = order; + this.result = result; + } + + @Test + public void testExecute() throws Exception { + final String result = this.filter.execute(this.order); + assertNotNull(result); + assertEquals(this.result, result.trim()); + } + + @Test + public void testNext() throws Exception { + assertNull(this.filter.getNext()); + assertSame(this.filter, this.filter.getLast()); + } + +} diff --git a/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/OrderTest.java b/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/OrderTest.java new file mode 100644 index 000000000..5ef98b872 --- /dev/null +++ b/intercepting-filter/src/test/java/com/iluwatar/intercepting/filter/OrderTest.java @@ -0,0 +1,73 @@ +/** + * The MIT License + * Copyright (c) 2014 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.intercepting.filter; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Date: 12/13/15 - 2:57 PM + * + * @author Jeroen Meulemeester + */ +public class OrderTest { + + private static final String EXPECTED_VALUE = "test"; + + @Test + public void testSetName() throws Exception { + final Order order = new Order(); + order.setName(EXPECTED_VALUE); + assertEquals(EXPECTED_VALUE, order.getName()); + } + + @Test + public void testSetContactNumber() throws Exception { + final Order order = new Order(); + order.setContactNumber(EXPECTED_VALUE); + assertEquals(EXPECTED_VALUE, order.getContactNumber()); + } + + @Test + public void testSetAddress() throws Exception { + final Order order = new Order(); + order.setAddress(EXPECTED_VALUE); + assertEquals(EXPECTED_VALUE, order.getAddress()); + } + + @Test + public void testSetDepositNumber() throws Exception { + final Order order = new Order(); + order.setDepositNumber(EXPECTED_VALUE); + assertEquals(EXPECTED_VALUE, order.getDepositNumber()); + } + + @Test + public void testSetOrder() throws Exception { + final Order order = new Order(); + order.setOrder(EXPECTED_VALUE); + assertEquals(EXPECTED_VALUE, order.getOrder()); + } + +} diff --git a/interpreter/index.md b/interpreter/README.md similarity index 87% rename from interpreter/index.md rename to interpreter/README.md index 57b117e06..87c1c47f7 100644 --- a/interpreter/index.md +++ b/interpreter/README.md @@ -7,21 +7,24 @@ categories: Behavioral tags: - Java - Gang Of Four + - Difficulty-Intermediate --- -**Intent:** Given a language, define a representation for its grammar along +## Intent +Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language. ![alt text](./etc/interpreter_1.png "Interpreter") -**Applicability:** Use the Interpreter pattern when there is a language to +## Applicability +Use the Interpreter pattern when there is a language to interpret, and you can represent statements in the language as abstract syntax trees. The Interpreter pattern works best when * the grammar is simple. For complex grammars, the class hierarchy for the grammar becomes large and unmanageable. Tools such as parser generators are a better alternative in such cases. They can interpret expressions without building abstract syntax trees, which can save space and possibly time * efficiency is not a critical concern. The most efficient interpreters are usually not implemented by interpreting parse trees directly but by first translating them into another form. For example, regular expressions are often transformed into state machines. But even then, the translator can be implemented by the Interpreter pattern, so the pattern is still applicable -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/interpreter/pom.xml b/interpreter/pom.xml index 8a536748c..9f5dd31f3 100644 --- a/interpreter/pom.xml +++ b/interpreter/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT interpreter diff --git a/interpreter/src/main/java/com/iluwatar/interpreter/App.java b/interpreter/src/main/java/com/iluwatar/interpreter/App.java index 955563915..708f06e6f 100644 --- a/interpreter/src/main/java/com/iluwatar/interpreter/App.java +++ b/interpreter/src/main/java/com/iluwatar/interpreter/App.java @@ -1,78 +1,95 @@ +/** + * The MIT License + * Copyright (c) 2014 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.interpreter; import java.util.Stack; /** * - * The Interpreter pattern is a design pattern that specifies how to evaluate sentences - * in a language. The basic idea is to have a class for each symbol (terminal or nonterminal) - * in a specialized computer language. The syntax tree of a sentence in the language is an - * instance of the composite pattern and is used to evaluate (interpret) the sentence for a - * client. + * The Interpreter pattern is a design pattern that specifies how to evaluate sentences in a + * language. The basic idea is to have a class for each symbol (terminal or nonterminal) in a + * specialized computer language. The syntax tree of a sentence in the language is an instance of + * the composite pattern and is used to evaluate (interpret) the sentence for a client. *

- * In this example we use the Interpreter pattern to break sentences into expressions - * ({@link Expression}) that can be evaluated and as a whole form the result. + * In this example we use the Interpreter pattern to break sentences into expressions ( + * {@link Expression}) that can be evaluated and as a whole form the result. * */ public class App { - /** - * - * Program entry point. - *

- * Expressions can be evaluated using prefix, infix or postfix notations - * This sample uses postfix, where operator comes after the operands - * - * @param args command line args - * - */ - public static void main(String[] args) { - String tokenString = "4 3 2 - 1 + *"; - Stack stack = new Stack<>(); + /** + * + * Program entry point. + *

+ * Expressions can be evaluated using prefix, infix or postfix notations This sample uses postfix, + * where operator comes after the operands + * + * @param args command line args + * + */ + public static void main(String[] args) { + String tokenString = "4 3 2 - 1 + *"; + Stack stack = new Stack<>(); - String[] tokenList = tokenString.split(" "); - for (String s : tokenList) { - if (isOperator(s)) { - Expression rightExpression = stack.pop(); - Expression leftExpression = stack.pop(); - System.out - .println(String.format( - "popped from stack left: %d right: %d", - leftExpression.interpret(), - rightExpression.interpret())); - Expression operator = getOperatorInstance(s, leftExpression, - rightExpression); - System.out.println(String.format("operator: %s", operator)); - int result = operator.interpret(); - NumberExpression resultExpression = new NumberExpression(result); - stack.push(resultExpression); - System.out.println(String.format("push result to stack: %d", - resultExpression.interpret())); - } else { - Expression i = new NumberExpression(s); - stack.push(i); - System.out.println(String.format("push to stack: %d", - i.interpret())); - } - } - System.out - .println(String.format("result: %d", stack.pop().interpret())); - } + String[] tokenList = tokenString.split(" "); + for (String s : tokenList) { + if (isOperator(s)) { + Expression rightExpression = stack.pop(); + Expression leftExpression = stack.pop(); + System.out.println(String.format("popped from stack left: %d right: %d", + leftExpression.interpret(), rightExpression.interpret())); + Expression operator = getOperatorInstance(s, leftExpression, rightExpression); + System.out.println(String.format("operator: %s", operator)); + int result = operator.interpret(); + NumberExpression resultExpression = new NumberExpression(result); + stack.push(resultExpression); + System.out.println(String.format("push result to stack: %d", resultExpression.interpret())); + } else { + Expression i = new NumberExpression(s); + stack.push(i); + System.out.println(String.format("push to stack: %d", i.interpret())); + } + } + System.out.println(String.format("result: %d", stack.pop().interpret())); + } - public static boolean isOperator(String s) { - return s.equals("+") || s.equals("-") || s.equals("*"); - } + public static boolean isOperator(String s) { + return s.equals("+") || s.equals("-") || s.equals("*"); + } - public static Expression getOperatorInstance(String s, Expression left, - Expression right) { - switch (s) { - case "+": - return new PlusExpression(left, right); - case "-": - return new MinusExpression(left, right); - case "*": - return new MultiplyExpression(left, right); - } - return null; - } + /** + * Get expression for string + */ + public static Expression getOperatorInstance(String s, Expression left, Expression right) { + switch (s) { + case "+": + return new PlusExpression(left, right); + case "-": + return new MinusExpression(left, right); + case "*": + return new MultiplyExpression(left, right); + default: + return new MultiplyExpression(left, right); + } + } } diff --git a/interpreter/src/main/java/com/iluwatar/interpreter/Expression.java b/interpreter/src/main/java/com/iluwatar/interpreter/Expression.java index e70e57f7c..20cdb512a 100644 --- a/interpreter/src/main/java/com/iluwatar/interpreter/Expression.java +++ b/interpreter/src/main/java/com/iluwatar/interpreter/Expression.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.interpreter; /** @@ -7,8 +29,8 @@ package com.iluwatar.interpreter; */ public abstract class Expression { - public abstract int interpret(); + public abstract int interpret(); - @Override - public abstract String toString(); + @Override + public abstract String toString(); } diff --git a/interpreter/src/main/java/com/iluwatar/interpreter/MinusExpression.java b/interpreter/src/main/java/com/iluwatar/interpreter/MinusExpression.java index d26f977da..53748baf8 100644 --- a/interpreter/src/main/java/com/iluwatar/interpreter/MinusExpression.java +++ b/interpreter/src/main/java/com/iluwatar/interpreter/MinusExpression.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.interpreter; /** @@ -7,22 +29,22 @@ package com.iluwatar.interpreter; */ public class MinusExpression extends Expression { - private Expression leftExpression; - private Expression rightExpression; + private Expression leftExpression; + private Expression rightExpression; - public MinusExpression(Expression leftExpression, Expression rightExpression) { - this.leftExpression = leftExpression; - this.rightExpression = rightExpression; - } + public MinusExpression(Expression leftExpression, Expression rightExpression) { + this.leftExpression = leftExpression; + this.rightExpression = rightExpression; + } - @Override - public int interpret() { - return leftExpression.interpret() - rightExpression.interpret(); - } + @Override + public int interpret() { + return leftExpression.interpret() - rightExpression.interpret(); + } - @Override - public String toString() { - return "-"; - } + @Override + public String toString() { + return "-"; + } } diff --git a/interpreter/src/main/java/com/iluwatar/interpreter/MultiplyExpression.java b/interpreter/src/main/java/com/iluwatar/interpreter/MultiplyExpression.java index 9feada7ee..bd060d3c0 100644 --- a/interpreter/src/main/java/com/iluwatar/interpreter/MultiplyExpression.java +++ b/interpreter/src/main/java/com/iluwatar/interpreter/MultiplyExpression.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.interpreter; /** @@ -7,23 +29,22 @@ package com.iluwatar.interpreter; */ public class MultiplyExpression extends Expression { - private Expression leftExpression; - private Expression rightExpression; + private Expression leftExpression; + private Expression rightExpression; - public MultiplyExpression(Expression leftExpression, - Expression rightExpression) { - this.leftExpression = leftExpression; - this.rightExpression = rightExpression; - } + public MultiplyExpression(Expression leftExpression, Expression rightExpression) { + this.leftExpression = leftExpression; + this.rightExpression = rightExpression; + } - @Override - public int interpret() { - return leftExpression.interpret() * rightExpression.interpret(); - } + @Override + public int interpret() { + return leftExpression.interpret() * rightExpression.interpret(); + } - @Override - public String toString() { - return "*"; - } + @Override + public String toString() { + return "*"; + } } diff --git a/interpreter/src/main/java/com/iluwatar/interpreter/NumberExpression.java b/interpreter/src/main/java/com/iluwatar/interpreter/NumberExpression.java index 0cf6b034a..891c926bd 100644 --- a/interpreter/src/main/java/com/iluwatar/interpreter/NumberExpression.java +++ b/interpreter/src/main/java/com/iluwatar/interpreter/NumberExpression.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.interpreter; /** @@ -7,24 +29,23 @@ package com.iluwatar.interpreter; */ public class NumberExpression extends Expression { - private int number; + private int number; - public NumberExpression(int number) { - this.number = number; - } + public NumberExpression(int number) { + this.number = number; + } - public NumberExpression(String s) { - this.number = Integer.parseInt(s); - } + public NumberExpression(String s) { + this.number = Integer.parseInt(s); + } - @Override - public int interpret() { - return number; - } - - @Override - public String toString() { - return "number"; - } + @Override + public int interpret() { + return number; + } + @Override + public String toString() { + return "number"; + } } diff --git a/interpreter/src/main/java/com/iluwatar/interpreter/PlusExpression.java b/interpreter/src/main/java/com/iluwatar/interpreter/PlusExpression.java index f244fa946..066d536b1 100644 --- a/interpreter/src/main/java/com/iluwatar/interpreter/PlusExpression.java +++ b/interpreter/src/main/java/com/iluwatar/interpreter/PlusExpression.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.interpreter; /** @@ -7,22 +29,21 @@ package com.iluwatar.interpreter; */ public class PlusExpression extends Expression { - private Expression leftExpression; - private Expression rightExpression; + private Expression leftExpression; + private Expression rightExpression; - public PlusExpression(Expression leftExpression, Expression rightExpression) { - this.leftExpression = leftExpression; - this.rightExpression = rightExpression; - } + public PlusExpression(Expression leftExpression, Expression rightExpression) { + this.leftExpression = leftExpression; + this.rightExpression = rightExpression; + } - @Override - public int interpret() { - return leftExpression.interpret() + rightExpression.interpret(); - } - - @Override - public String toString() { - return "+"; - } + @Override + public int interpret() { + return leftExpression.interpret() + rightExpression.interpret(); + } + @Override + public String toString() { + return "+"; + } } diff --git a/interpreter/src/test/java/com/iluwatar/interpreter/AppTest.java b/interpreter/src/test/java/com/iluwatar/interpreter/AppTest.java index b0e486833..01b927e67 100644 --- a/interpreter/src/test/java/com/iluwatar/interpreter/AppTest.java +++ b/interpreter/src/test/java/com/iluwatar/interpreter/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.interpreter; import org.junit.Test; -import com.iluwatar.interpreter.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.interpreter.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/interpreter/src/test/java/com/iluwatar/interpreter/ExpressionTest.java b/interpreter/src/test/java/com/iluwatar/interpreter/ExpressionTest.java new file mode 100644 index 000000000..d07ee84fb --- /dev/null +++ b/interpreter/src/test/java/com/iluwatar/interpreter/ExpressionTest.java @@ -0,0 +1,133 @@ +/** + * The MIT License + * Copyright (c) 2014 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.interpreter; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiFunction; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/14/15 - 11:48 AM + * + * @author Jeroen Meulemeester + */ +public abstract class ExpressionTest { + + /** + * Generate inputs ranging from -10 to 10 for both input params and calculate the expected result + * + * @param resultCalc The function used to calculate the expected result + * @return A data set with test entries + */ + static List prepareParameters(final BiFunction resultCalc) { + final List testData = new ArrayList<>(); + for (int i = -10; i < 10; i++) { + for (int j = -10; j < 10; j++) { + testData.add(new Object[]{ + new NumberExpression(i), + new NumberExpression(j), + resultCalc.apply(i, j) + }); + } + } + return testData; + } + + /** + * The input used as first parameter during the test + */ + private final NumberExpression first; + + /** + * The input used as second parameter during the test + */ + private final NumberExpression second; + + /** + * The expected result of the calculation, taking the first and second parameter in account + */ + private final int result; + + /** + * The expected {@link E#toString()} response + */ + private final String expectedToString; + + /** + * Factory, used to create a new test object instance with the correct first and second parameter + */ + private final BiFunction factory; + + /** + * Create a new test instance with the given parameters and expected results + * + * @param first The input used as first parameter during the test + * @param second The input used as second parameter during the test + * @param result The expected result of the tested expression + * @param expectedToString The expected {@link E#toString()} response + * @param factory Factory, used to create a new test object instance + */ + ExpressionTest(final NumberExpression first, final NumberExpression second, final int result, + final String expectedToString, final BiFunction factory) { + + this.first = first; + this.second = second; + this.result = result; + this.expectedToString = expectedToString; + this.factory = factory; + } + + /** + * Get the first parameter + * + * @return The first parameter + */ + final NumberExpression getFirst() { + return this.first; + } + + /** + * Verify if the expression calculates the correct result when calling {@link E#interpret()} + */ + @Test + public void testInterpret() { + final E expression = this.factory.apply(this.first, this.second); + assertNotNull(expression); + assertEquals(this.result, expression.interpret()); + } + + /** + * Verify if the expression has the expected {@link E#toString()} value + */ + @Test + public void testToString() { + final E expression = this.factory.apply(this.first, this.second); + assertNotNull(expression); + assertEquals(expectedToString, expression.toString()); + } +} diff --git a/interpreter/src/test/java/com/iluwatar/interpreter/MinusExpressionTest.java b/interpreter/src/test/java/com/iluwatar/interpreter/MinusExpressionTest.java new file mode 100644 index 000000000..fe4ebc58f --- /dev/null +++ b/interpreter/src/test/java/com/iluwatar/interpreter/MinusExpressionTest.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 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.interpreter; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.util.List; + +/** + * Date: 12/14/15 - 12:08 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class MinusExpressionTest extends ExpressionTest { + + /** + * Create a new set of test entries with the expected result + * + * @return The list of parameters used during this test + */ + @Parameters + public static List data() { + return prepareParameters((f, s) -> f - s); + } + + /** + * Create a new test instance using the given test parameters and expected result + * + * @param first The first expression parameter + * @param second The second expression parameter + * @param result The expected result + */ + public MinusExpressionTest(final NumberExpression first, final NumberExpression second, final int result) { + super(first, second, result, "-", MinusExpression::new); + } + +} \ No newline at end of file diff --git a/interpreter/src/test/java/com/iluwatar/interpreter/MultiplyExpressionTest.java b/interpreter/src/test/java/com/iluwatar/interpreter/MultiplyExpressionTest.java new file mode 100644 index 000000000..f876dda34 --- /dev/null +++ b/interpreter/src/test/java/com/iluwatar/interpreter/MultiplyExpressionTest.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 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.interpreter; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.util.List; + +/** + * Date: 12/14/15 - 12:08 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class MultiplyExpressionTest extends ExpressionTest { + + /** + * Create a new set of test entries with the expected result + * + * @return The list of parameters used during this test + */ + @Parameters + public static List data() { + return prepareParameters((f, s) -> f * s); + } + + /** + * Create a new test instance using the given test parameters and expected result + * + * @param first The first expression parameter + * @param second The second expression parameter + * @param result The expected result + */ + public MultiplyExpressionTest(final NumberExpression first, final NumberExpression second, final int result) { + super(first, second, result, "*", MultiplyExpression::new); + } + +} \ No newline at end of file diff --git a/interpreter/src/test/java/com/iluwatar/interpreter/NumberExpressionTest.java b/interpreter/src/test/java/com/iluwatar/interpreter/NumberExpressionTest.java new file mode 100644 index 000000000..d055fcd47 --- /dev/null +++ b/interpreter/src/test/java/com/iluwatar/interpreter/NumberExpressionTest.java @@ -0,0 +1,74 @@ +/** + * The MIT License + * Copyright (c) 2014 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.interpreter; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.util.List; + +import static org.junit.Assert.assertEquals; + +/** + * Date: 12/14/15 - 12:08 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class NumberExpressionTest extends ExpressionTest { + + /** + * Create a new set of test entries with the expected result + * + * @return The list of parameters used during this test + */ + @Parameters + public static List data() { + return prepareParameters((f, s) -> f); + } + + /** + * Create a new test instance using the given test parameters and expected result + * + * @param first The first expression parameter + * @param second The second expression parameter + * @param result The expected result + */ + public NumberExpressionTest(final NumberExpression first, final NumberExpression second, final int result) { + super(first, second, result, "number", (f, s) -> f); + } + + /** + * Verify if the {@link NumberExpression#NumberExpression(String)} constructor works as expected + */ + @Test + public void testFromString() throws Exception { + final int expectedValue = getFirst().interpret(); + final String testStingValue = String.valueOf(expectedValue); + final NumberExpression numberExpression = new NumberExpression(testStingValue); + assertEquals(expectedValue, numberExpression.interpret()); + } + +} \ No newline at end of file diff --git a/interpreter/src/test/java/com/iluwatar/interpreter/PlusExpressionTest.java b/interpreter/src/test/java/com/iluwatar/interpreter/PlusExpressionTest.java new file mode 100644 index 000000000..e99d49555 --- /dev/null +++ b/interpreter/src/test/java/com/iluwatar/interpreter/PlusExpressionTest.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 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.interpreter; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import java.util.List; + +/** + * Date: 12/14/15 - 12:08 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class PlusExpressionTest extends ExpressionTest { + + /** + * Create a new set of test entries with the expected result + * + * @return The list of parameters used during this test + */ + @Parameters + public static List data() { + return prepareParameters((f, s) -> f + s); + } + + /** + * Create a new test instance using the given test parameters and expected result + * + * @param first The first expression parameter + * @param second The second expression parameter + * @param result The expected result + */ + public PlusExpressionTest(final NumberExpression first, final NumberExpression second, final int result) { + super(first, second, result, "+", PlusExpression::new); + } + +} \ No newline at end of file diff --git a/iterator/index.md b/iterator/README.md similarity index 81% rename from iterator/index.md rename to iterator/README.md index ecfdb1b2a..d6be7758d 100644 --- a/iterator/index.md +++ b/iterator/README.md @@ -10,21 +10,26 @@ tags: - Gang Of Four --- -**Intent:** Provide a way to access the elements of an aggregate object +## Also known as +Cursor + +## Intent +Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation. ![alt text](./etc/iterator_1.png "Iterator") -**Applicability:** Use the Iterator pattern +## Applicability +Use the Iterator pattern * to access an aggregate object's contents without exposing its internal representation * to support multiple traversals of aggregate objects * to provide a uniform interface for traversing different aggregate structures -**Real world examples:** +## Real world examples * [java.util.Iterator](http://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/iterator/pom.xml b/iterator/pom.xml index 22e574ba5..484030a33 100644 --- a/iterator/pom.xml +++ b/iterator/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT iterator diff --git a/iterator/src/main/java/com/iluwatar/iterator/App.java b/iterator/src/main/java/com/iluwatar/iterator/App.java index b8ecfa42c..8da0a7433 100644 --- a/iterator/src/main/java/com/iluwatar/iterator/App.java +++ b/iterator/src/main/java/com/iluwatar/iterator/App.java @@ -1,49 +1,71 @@ +/** + * The MIT License + * Copyright (c) 2014 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.iterator; /** * - * The Iterator pattern is a design pattern in which an iterator is used to - * traverse a container and access the container's elements. The Iterator pattern - * decouples algorithms from containers. + * The Iterator pattern is a design pattern in which an iterator is used to traverse a container and + * access the container's elements. The Iterator pattern decouples algorithms from containers. *

- * In this example the Iterator ({@link ItemIterator}) adds abstraction layer on - * top of a collection ({@link TreasureChest}). This way the collection can change - * its internal implementation without affecting its clients. + * In this example the Iterator ({@link ItemIterator}) adds abstraction layer on top of a collection + * ({@link TreasureChest}). This way the collection can change its internal implementation without + * affecting its clients. * */ public class App { - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { - TreasureChest chest = new TreasureChest(); + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + TreasureChest chest = new TreasureChest(); - ItemIterator ringIterator = chest.Iterator(ItemType.RING); - while (ringIterator.hasNext()) { - System.out.println(ringIterator.next()); - } + ItemIterator ringIterator = chest.iterator(ItemType.RING); + while (ringIterator.hasNext()) { + System.out.println(ringIterator.next()); + } - System.out.println("----------"); + System.out.println("----------"); - ItemIterator potionIterator = chest.Iterator(ItemType.POTION); - while (potionIterator.hasNext()) { - System.out.println(potionIterator.next()); - } + ItemIterator potionIterator = chest.iterator(ItemType.POTION); + while (potionIterator.hasNext()) { + System.out.println(potionIterator.next()); + } - System.out.println("----------"); + System.out.println("----------"); - ItemIterator weaponIterator = chest.Iterator(ItemType.WEAPON); - while (weaponIterator.hasNext()) { - System.out.println(weaponIterator.next()); - } + ItemIterator weaponIterator = chest.iterator(ItemType.WEAPON); + while (weaponIterator.hasNext()) { + System.out.println(weaponIterator.next()); + } - System.out.println("----------"); + System.out.println("----------"); - ItemIterator it = chest.Iterator(ItemType.ANY); - while (it.hasNext()) { - System.out.println(it.next()); - } - } + ItemIterator it = chest.iterator(ItemType.ANY); + while (it.hasNext()) { + System.out.println(it.next()); + } + } } diff --git a/iterator/src/main/java/com/iluwatar/iterator/Item.java b/iterator/src/main/java/com/iluwatar/iterator/Item.java index 4df167c6f..8bb540fbd 100644 --- a/iterator/src/main/java/com/iluwatar/iterator/Item.java +++ b/iterator/src/main/java/com/iluwatar/iterator/Item.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.iterator; /** @@ -7,24 +29,24 @@ package com.iluwatar.iterator; */ public class Item { - private ItemType type; - private String name; + private ItemType type; + private String name; - public Item(ItemType type, String name) { - this.setType(type); - this.name = name; - } + public Item(ItemType type, String name) { + this.setType(type); + this.name = name; + } - @Override - public String toString() { - return name; - } + @Override + public String toString() { + return name; + } - public ItemType getType() { - return type; - } + public ItemType getType() { + return type; + } - public void setType(ItemType type) { - this.type = type; - } + public final void setType(ItemType type) { + this.type = type; + } } diff --git a/iterator/src/main/java/com/iluwatar/iterator/ItemIterator.java b/iterator/src/main/java/com/iluwatar/iterator/ItemIterator.java index 91b5b62c0..fa1ff633b 100644 --- a/iterator/src/main/java/com/iluwatar/iterator/ItemIterator.java +++ b/iterator/src/main/java/com/iluwatar/iterator/ItemIterator.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.iterator; /** @@ -7,7 +29,7 @@ package com.iluwatar.iterator; */ public interface ItemIterator { - boolean hasNext(); + boolean hasNext(); - Item next(); + Item next(); } diff --git a/iterator/src/main/java/com/iluwatar/iterator/ItemType.java b/iterator/src/main/java/com/iluwatar/iterator/ItemType.java index 288590c31..0b1def25b 100644 --- a/iterator/src/main/java/com/iluwatar/iterator/ItemType.java +++ b/iterator/src/main/java/com/iluwatar/iterator/ItemType.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.iterator; /** @@ -7,6 +29,6 @@ package com.iluwatar.iterator; */ public enum ItemType { - ANY, WEAPON, RING, POTION + ANY, WEAPON, RING, POTION } diff --git a/iterator/src/main/java/com/iluwatar/iterator/TreasureChest.java b/iterator/src/main/java/com/iluwatar/iterator/TreasureChest.java index f4e1337bc..2c5d698e9 100644 --- a/iterator/src/main/java/com/iluwatar/iterator/TreasureChest.java +++ b/iterator/src/main/java/com/iluwatar/iterator/TreasureChest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.iterator; import java.util.ArrayList; @@ -10,30 +32,36 @@ import java.util.List; */ public class TreasureChest { - private List items; + private List items; - public TreasureChest() { - items = new ArrayList<>(); - items.add(new Item(ItemType.POTION, "Potion of courage")); - items.add(new Item(ItemType.RING, "Ring of shadows")); - items.add(new Item(ItemType.POTION, "Potion of wisdom")); - items.add(new Item(ItemType.POTION, "Potion of blood")); - items.add(new Item(ItemType.WEAPON, "Sword of silver +1")); - items.add(new Item(ItemType.POTION, "Potion of rust")); - items.add(new Item(ItemType.POTION, "Potion of healing")); - items.add(new Item(ItemType.RING, "Ring of armor")); - items.add(new Item(ItemType.WEAPON, "Steel halberd")); - items.add(new Item(ItemType.WEAPON, "Dagger of poison")); - } + /** + * Constructor + */ + public TreasureChest() { + items = new ArrayList<>(); + items.add(new Item(ItemType.POTION, "Potion of courage")); + items.add(new Item(ItemType.RING, "Ring of shadows")); + items.add(new Item(ItemType.POTION, "Potion of wisdom")); + items.add(new Item(ItemType.POTION, "Potion of blood")); + items.add(new Item(ItemType.WEAPON, "Sword of silver +1")); + items.add(new Item(ItemType.POTION, "Potion of rust")); + items.add(new Item(ItemType.POTION, "Potion of healing")); + items.add(new Item(ItemType.RING, "Ring of armor")); + items.add(new Item(ItemType.WEAPON, "Steel halberd")); + items.add(new Item(ItemType.WEAPON, "Dagger of poison")); + } - ItemIterator Iterator(ItemType type) { - return new TreasureChestItemIterator(this, type); - } + ItemIterator iterator(ItemType itemType) { + return new TreasureChestItemIterator(this, itemType); + } - public List getItems() { - ArrayList list = new ArrayList<>(); - list.addAll(items); - return list; - } + /** + * Get all items + */ + public List getItems() { + List list = new ArrayList<>(); + list.addAll(items); + return list; + } } diff --git a/iterator/src/main/java/com/iluwatar/iterator/TreasureChestItemIterator.java b/iterator/src/main/java/com/iluwatar/iterator/TreasureChestItemIterator.java index 1a6daef4e..7c80422c2 100644 --- a/iterator/src/main/java/com/iluwatar/iterator/TreasureChestItemIterator.java +++ b/iterator/src/main/java/com/iluwatar/iterator/TreasureChestItemIterator.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.iterator; import java.util.List; @@ -9,46 +31,48 @@ import java.util.List; */ public class TreasureChestItemIterator implements ItemIterator { - private TreasureChest chest; - private int idx; - private ItemType type; + private TreasureChest chest; + private int idx; + private ItemType type; - public TreasureChestItemIterator(TreasureChest chest, ItemType type) { - this.chest = chest; - this.type = type; - this.idx = -1; - } + /** + * Constructor + */ + public TreasureChestItemIterator(TreasureChest chest, ItemType type) { + this.chest = chest; + this.type = type; + this.idx = -1; + } - @Override - public boolean hasNext() { - return findNextIdx() != -1; - } + @Override + public boolean hasNext() { + return findNextIdx() != -1; + } - @Override - public Item next() { - idx = findNextIdx(); - if (idx != -1) { - return chest.getItems().get(idx); - } - return null; - } + @Override + public Item next() { + idx = findNextIdx(); + if (idx != -1) { + return chest.getItems().get(idx); + } + return null; + } - private int findNextIdx() { + private int findNextIdx() { - List items = chest.getItems(); - boolean found = false; - int tempIdx = idx; - while (!found) { - tempIdx++; - if (tempIdx >= items.size()) { - tempIdx = -1; - break; - } - if (type.equals(ItemType.ANY) - || items.get(tempIdx).getType().equals(type)) { - break; - } - } - return tempIdx; - } + List items = chest.getItems(); + boolean found = false; + int tempIdx = idx; + while (!found) { + tempIdx++; + if (tempIdx >= items.size()) { + tempIdx = -1; + break; + } + if (type.equals(ItemType.ANY) || items.get(tempIdx).getType().equals(type)) { + break; + } + } + return tempIdx; + } } diff --git a/iterator/src/test/java/com/iluwatar/iterator/AppTest.java b/iterator/src/test/java/com/iluwatar/iterator/AppTest.java index 1c1d65e34..7259386bb 100644 --- a/iterator/src/test/java/com/iluwatar/iterator/AppTest.java +++ b/iterator/src/test/java/com/iluwatar/iterator/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.iterator; import org.junit.Test; -import com.iluwatar.iterator.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.iterator.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/iterator/src/test/java/com/iluwatar/iterator/TreasureChestTest.java b/iterator/src/test/java/com/iluwatar/iterator/TreasureChestTest.java new file mode 100644 index 000000000..9bef322d6 --- /dev/null +++ b/iterator/src/test/java/com/iluwatar/iterator/TreasureChestTest.java @@ -0,0 +1,130 @@ +/** + * The MIT License + * Copyright (c) 2014 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.iterator; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +/** + * Date: 12/14/15 - 2:58 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class TreasureChestTest { + + /** + * Create a list of all expected items in the chest. + * + * @return The set of all expected items in the chest + */ + @Parameterized.Parameters + public static List data() { + final List parameters = new ArrayList<>(); + parameters.add(new Object[]{new Item(ItemType.POTION, "Potion of courage")}); + parameters.add(new Object[]{new Item(ItemType.RING, "Ring of shadows")}); + parameters.add(new Object[]{new Item(ItemType.POTION, "Potion of wisdom")}); + parameters.add(new Object[]{new Item(ItemType.POTION, "Potion of blood")}); + parameters.add(new Object[]{new Item(ItemType.WEAPON, "Sword of silver +1")}); + parameters.add(new Object[]{new Item(ItemType.POTION, "Potion of rust")}); + parameters.add(new Object[]{new Item(ItemType.POTION, "Potion of healing")}); + parameters.add(new Object[]{new Item(ItemType.RING, "Ring of armor")}); + parameters.add(new Object[]{new Item(ItemType.WEAPON, "Steel halberd")}); + parameters.add(new Object[]{new Item(ItemType.WEAPON, "Dagger of poison")}); + return parameters; + } + + /** + * One of the expected items in the chest + */ + private final Item expectedItem; + + /** + * Create a new test instance, test if the given expected item can be retrieved from the chest + * + * @param expectedItem One of the items that should be in the chest + */ + public TreasureChestTest(final Item expectedItem) { + this.expectedItem = expectedItem; + } + + /** + * Test if the expected item can be retrieved from the chest using the {@link ItemIterator} + */ + @Test + public void testIterator() { + final TreasureChest chest = new TreasureChest(); + final ItemIterator iterator = chest.iterator(expectedItem.getType()); + assertNotNull(iterator); + + while (iterator.hasNext()) { + final Item item = iterator.next(); + assertNotNull(item); + assertEquals(this.expectedItem.getType(), item.getType()); + + final String name = item.toString(); + assertNotNull(name); + if (this.expectedItem.toString().equals(name)) { + return; + } + } + + fail("Expected to find item [" + this.expectedItem + "] using iterator, but we didn't."); + + } + + /** + * Test if the expected item can be retrieved from the chest using the {@link + * TreasureChest#getItems()} method + */ + @Test + public void testGetItems() throws Exception { + final TreasureChest chest = new TreasureChest(); + final List items = chest.getItems(); + assertNotNull(items); + + for (final Item item : items) { + assertNotNull(item); + assertNotNull(item.getType()); + assertNotNull(item.toString()); + + final boolean sameType = this.expectedItem.getType() == item.getType(); + final boolean sameName = this.expectedItem.toString().equals(item.toString()); + if (sameType && sameName) { + return; + } + } + + fail("Expected to find item [" + this.expectedItem + "] in the item list, but we didn't."); + + } + +} \ No newline at end of file diff --git a/layers/index.md b/layers/README.md similarity index 75% rename from layers/index.md rename to layers/README.md index 37089a19c..8e8eda366 100644 --- a/layers/index.md +++ b/layers/README.md @@ -4,21 +4,25 @@ title: Layers folder: layers permalink: /patterns/layers/ categories: Architectural -tags: Java +tags: + - Java + - Difficulty-Intermediate + - Spring --- -**Intent:** Layers is an architectural style where software responsibilities are +## Intent +Layers is an architectural style where software responsibilities are divided among the different layers of the application. ![alt text](./etc/layers.png "Layers") -**Applicability:** Use the Layers architecture when +## Applicability +Use the Layers architecture when * you want clearly divide software responsibilities into differents parts of the program * you want to prevent a change from propagating throughout the application * you want to make your application more maintainable and testable -**Credits:** +## Credits * [Pattern Oriented Software Architecture Vol I-V](http://www.amazon.com/Pattern-Oriented-Software-Architecture-Volume-Patterns/dp/0471958697) - diff --git a/layers/pom.xml b/layers/pom.xml index 5d3fd778c..bf155b655 100644 --- a/layers/pom.xml +++ b/layers/pom.xml @@ -1,4 +1,28 @@ + @@ -6,7 +30,7 @@ com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT com.iluwatar.layers layers @@ -32,5 +56,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/layers/src/main/java/com/iluwatar/layers/App.java b/layers/src/main/java/com/iluwatar/layers/App.java index bac946265..f8a1648bf 100644 --- a/layers/src/main/java/com/iluwatar/layers/App.java +++ b/layers/src/main/java/com/iluwatar/layers/App.java @@ -1,35 +1,56 @@ +/** + * The MIT License + * Copyright (c) 2014 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.layers; import java.util.Arrays; /** * - * Layers is an architectural style where software responsibilities are - * divided among the different layers of the application. + * Layers is an architectural style where software responsibilities are divided among the different layers of the + * application. *

- * This example demonstrates a traditional 3-layer architecture consisting of data access - * layer, business layer and presentation layer. + * This example demonstrates a traditional 3-layer architecture consisting of data access layer, business layer and + * presentation layer. *

* The data access layer is formed of Spring Data repositories CakeDao, CakeToppingDao and - * CakeLayerDao. The repositories can be used for CRUD operations on cakes, cake toppings - * and cake layers respectively. + * CakeLayerDao. The repositories can be used for CRUD operations on cakes, cake toppings and cake layers + * respectively. *

- * The business layer is built on top of the data access layer. CakeBakingService offers - * methods to retrieve available cake toppings and cake layers and baked cakes. Also the - * service is used to create new cakes out of cake toppings and cake layers. + * The business layer is built on top of the data access layer. CakeBakingService offers methods to + * retrieve available cake toppings and cake layers and baked cakes. Also the service is used to create new cakes out of + * cake toppings and cake layers. *

- * The presentation layer is built on the business layer and in this example it simply lists - * the cakes that have been baked. + * The presentation layer is built on the business layer and in this example it simply lists the cakes that have been + * baked. *

- * We have applied so called strict layering which means that the layers can only access - * the classes directly beneath them. This leads the solution to create an additional set of - * DTOs (CakeInfo, CakeToppingInfo, CakeLayerInfo) - * to translate data between layers. In other words, CakeBakingService cannot - * return entities (Cake, CakeTopping, CakeLayer) - * directly since these reside on data access layer but instead translates these into business - * layer DTOs (CakeInfo, CakeToppingInfo, CakeLayerInfo) - * and returns them instead. This way the presentation layer does not have any knowledge of - * other layers than the business layer and thus is not affected by changes to them. + * We have applied so called strict layering which means that the layers can only access the classes directly beneath + * them. This leads the solution to create an additional set of DTOs ( CakeInfo, + * CakeToppingInfo, CakeLayerInfo) to translate data between layers. In other words, + * CakeBakingService cannot return entities ( Cake, CakeTopping, + * CakeLayer) directly since these reside on data access layer but instead translates these into business + * layer DTOs (CakeInfo, CakeToppingInfo, CakeLayerInfo) and returns them + * instead. This way the presentation layer does not have any knowledge of other layers than the business layer and thus + * is not affected by changes to them. * * @see Cake * @see CakeTopping @@ -45,52 +66,53 @@ import java.util.Arrays; */ public class App { - private static CakeBakingService cakeBakingService = new CakeBakingServiceImpl(); - - /** - * Application entry point - * @param args Command line parameters - */ - public static void main(String[] args) { - - // initialize example data - initializeData(cakeBakingService); - - // create view and render it - CakeViewImpl cakeView = new CakeViewImpl(cakeBakingService); - cakeView.render(); - } - - /** - * Initializes the example data - * @param cakeBakingService - */ - private static void initializeData(CakeBakingService cakeBakingService) { - cakeBakingService.saveNewLayer(new CakeLayerInfo("chocolate", 1200)); - cakeBakingService.saveNewLayer(new CakeLayerInfo("banana", 900)); - cakeBakingService.saveNewLayer(new CakeLayerInfo("strawberry", 950)); - cakeBakingService.saveNewLayer(new CakeLayerInfo("lemon", 950)); - cakeBakingService.saveNewLayer(new CakeLayerInfo("vanilla", 950)); - cakeBakingService.saveNewLayer(new CakeLayerInfo("strawberry", 950)); - - cakeBakingService.saveNewTopping(new CakeToppingInfo("candies", 350)); - cakeBakingService.saveNewTopping(new CakeToppingInfo("cherry", 350)); + private static CakeBakingService cakeBakingService = new CakeBakingServiceImpl(); - CakeInfo cake1 = new CakeInfo(new CakeToppingInfo("candies", 0), - Arrays.asList(new CakeLayerInfo("chocolate", 0), new CakeLayerInfo("banana", 0), - new CakeLayerInfo("strawberry", 0))); - try { - cakeBakingService.bakeNewCake(cake1); - } catch (CakeBakingException e) { - e.printStackTrace(); - } - CakeInfo cake2 = new CakeInfo(new CakeToppingInfo("cherry", 0), - Arrays.asList(new CakeLayerInfo("vanilla", 0), new CakeLayerInfo("lemon", 0), - new CakeLayerInfo("strawberry", 0))); - try { - cakeBakingService.bakeNewCake(cake2); - } catch (CakeBakingException e) { - e.printStackTrace(); - } - } + /** + * Application entry point + * + * @param args Command line parameters + */ + public static void main(String[] args) { + + // initialize example data + initializeData(cakeBakingService); + + // create view and render it + CakeViewImpl cakeView = new CakeViewImpl(cakeBakingService); + cakeView.render(); + } + + /** + * Initializes the example data + */ + private static void initializeData(CakeBakingService cakeBakingService) { + cakeBakingService.saveNewLayer(new CakeLayerInfo("chocolate", 1200)); + cakeBakingService.saveNewLayer(new CakeLayerInfo("banana", 900)); + cakeBakingService.saveNewLayer(new CakeLayerInfo("strawberry", 950)); + cakeBakingService.saveNewLayer(new CakeLayerInfo("lemon", 950)); + cakeBakingService.saveNewLayer(new CakeLayerInfo("vanilla", 950)); + cakeBakingService.saveNewLayer(new CakeLayerInfo("strawberry", 950)); + + cakeBakingService.saveNewTopping(new CakeToppingInfo("candies", 350)); + cakeBakingService.saveNewTopping(new CakeToppingInfo("cherry", 350)); + + CakeInfo cake1 = + new CakeInfo(new CakeToppingInfo("candies", 0), Arrays.asList(new CakeLayerInfo( + "chocolate", 0), new CakeLayerInfo("banana", 0), new CakeLayerInfo("strawberry", 0))); + try { + cakeBakingService.bakeNewCake(cake1); + } catch (CakeBakingException e) { + e.printStackTrace(); + } + CakeInfo cake2 = + new CakeInfo(new CakeToppingInfo("cherry", 0), Arrays.asList( + new CakeLayerInfo("vanilla", 0), new CakeLayerInfo("lemon", 0), new CakeLayerInfo( + "strawberry", 0))); + try { + cakeBakingService.bakeNewCake(cake2); + } catch (CakeBakingException e) { + e.printStackTrace(); + } + } } diff --git a/layers/src/main/java/com/iluwatar/layers/Cake.java b/layers/src/main/java/com/iluwatar/layers/Cake.java index 193ba5e3f..f6eae2ad7 100644 --- a/layers/src/main/java/com/iluwatar/layers/Cake.java +++ b/layers/src/main/java/com/iluwatar/layers/Cake.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.layers; import java.util.HashSet; @@ -19,50 +41,50 @@ import javax.persistence.OneToOne; @Entity public class Cake { - @Id - @GeneratedValue - private Long id; + @Id + @GeneratedValue + private Long id; - @OneToOne(cascade=CascadeType.REMOVE) - private CakeTopping topping; - - @OneToMany(cascade=CascadeType.REMOVE, fetch=FetchType.EAGER) - private Set layers; - - public Cake() { - setLayers(new HashSet<>()); - } + @OneToOne(cascade = CascadeType.REMOVE) + private CakeTopping topping; - public Long getId() { - return id; - } + @OneToMany(cascade = CascadeType.REMOVE, fetch = FetchType.EAGER) + private Set layers; - public void setId(Long id) { - this.id = id; - } + public Cake() { + setLayers(new HashSet<>()); + } - public CakeTopping getTopping() { - return topping; - } + public Long getId() { + return id; + } - public void setTopping(CakeTopping topping) { - this.topping = topping; - } + public void setId(Long id) { + this.id = id; + } - public Set getLayers() { - return layers; - } + public CakeTopping getTopping() { + return topping; + } - public void setLayers(Set layers) { - this.layers = layers; - } - - public void addLayer(CakeLayer layer) { - this.layers.add(layer); - } - - @Override - public String toString() { - return String.format("id=%s topping=%s layers=%s", id, topping, layers.toString()); - } + public void setTopping(CakeTopping topping) { + this.topping = topping; + } + + public Set getLayers() { + return layers; + } + + public final void setLayers(Set layers) { + this.layers = layers; + } + + public void addLayer(CakeLayer layer) { + this.layers.add(layer); + } + + @Override + public String toString() { + return String.format("id=%s topping=%s layers=%s", id, topping, layers.toString()); + } } diff --git a/layers/src/main/java/com/iluwatar/layers/CakeBakingException.java b/layers/src/main/java/com/iluwatar/layers/CakeBakingException.java index 0a44d56f9..4e79f34e1 100644 --- a/layers/src/main/java/com/iluwatar/layers/CakeBakingException.java +++ b/layers/src/main/java/com/iluwatar/layers/CakeBakingException.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.layers; /** @@ -6,13 +28,12 @@ package com.iluwatar.layers; * */ public class CakeBakingException extends Exception { - - private static final long serialVersionUID = 1L; - public CakeBakingException() { - } + private static final long serialVersionUID = 1L; - public CakeBakingException(String message) { - super(message); - } + public CakeBakingException() {} + + public CakeBakingException(String message) { + super(message); + } } diff --git a/layers/src/main/java/com/iluwatar/layers/CakeBakingService.java b/layers/src/main/java/com/iluwatar/layers/CakeBakingService.java index fec16b494..1cb5b3574 100644 --- a/layers/src/main/java/com/iluwatar/layers/CakeBakingService.java +++ b/layers/src/main/java/com/iluwatar/layers/CakeBakingService.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.layers; import java.util.List; @@ -8,41 +30,34 @@ import java.util.List; * */ public interface CakeBakingService { - - /** - * Bakes new cake according to parameters - * @param cakeInfo - * @throws CakeBakingException - */ - void bakeNewCake(CakeInfo cakeInfo) throws CakeBakingException; - - /** - * Get all cakes - * @return - */ - List getAllCakes(); - /** - * Store new cake topping - * @param toppingInfo - */ - void saveNewTopping(CakeToppingInfo toppingInfo); + /** + * Bakes new cake according to parameters + */ + void bakeNewCake(CakeInfo cakeInfo) throws CakeBakingException; - /** - * Get available cake toppings - * @return - */ - List getAvailableToppings(); - - /** - * Add new cake layer - * @param layerInfo - */ - void saveNewLayer(CakeLayerInfo layerInfo); - - /** - * Get available cake layers - * @return - */ - List getAvailableLayers(); + /** + * Get all cakes + */ + List getAllCakes(); + + /** + * Store new cake topping + */ + void saveNewTopping(CakeToppingInfo toppingInfo); + + /** + * Get available cake toppings + */ + List getAvailableToppings(); + + /** + * Add new cake layer + */ + void saveNewLayer(CakeLayerInfo layerInfo); + + /** + * Get available cake layers + */ + List getAvailableLayers(); } diff --git a/layers/src/main/java/com/iluwatar/layers/CakeBakingServiceImpl.java b/layers/src/main/java/com/iluwatar/layers/CakeBakingServiceImpl.java index 7e5e3dcff..e8518b625 100644 --- a/layers/src/main/java/com/iluwatar/layers/CakeBakingServiceImpl.java +++ b/layers/src/main/java/com/iluwatar/layers/CakeBakingServiceImpl.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.layers; import java.util.ArrayList; @@ -22,128 +44,132 @@ import org.springframework.transaction.annotation.Transactional; @Transactional public class CakeBakingServiceImpl implements CakeBakingService { - private AbstractApplicationContext context; + private AbstractApplicationContext context; - public CakeBakingServiceImpl() { - this.context = new ClassPathXmlApplicationContext("applicationContext.xml"); - } - - @Override - public void bakeNewCake(CakeInfo cakeInfo) throws CakeBakingException { - List allToppings = getAvailableToppings(); - List matchingToppings = allToppings.stream() - .filter((t) -> t.name.equals(cakeInfo.cakeToppingInfo.name)).collect(Collectors.toList()); - if (matchingToppings.isEmpty()) { - throw new CakeBakingException(String.format("Topping %s is not available", cakeInfo.cakeToppingInfo.name)); - } - List allLayers = getAvailableLayerEntities(); - Set foundLayers = new HashSet<>(); - for (CakeLayerInfo info: cakeInfo.cakeLayerInfos) { - Optional found = allLayers.stream().filter((layer) -> layer.getName().equals(info.name)).findFirst(); - if (!found.isPresent()) { - throw new CakeBakingException(String.format("Layer %s is not available", info.name)); - } else { - foundLayers.add(found.get()); - } - } - CakeToppingDao toppingBean = context.getBean(CakeToppingDao.class); - CakeTopping topping = toppingBean.findOne(matchingToppings.iterator().next().id.get()); - CakeDao cakeBean = context.getBean(CakeDao.class); - Cake cake = new Cake(); - cake.setTopping(topping); - cake.setLayers(foundLayers); - cakeBean.save(cake); - topping.setCake(cake); - toppingBean.save(topping); - CakeLayerDao layerBean = context.getBean(CakeLayerDao.class); - for (CakeLayer layer: foundLayers) { - layer.setCake(cake); - layerBean.save(layer); - } - } + public CakeBakingServiceImpl() { + this.context = new ClassPathXmlApplicationContext("applicationContext.xml"); + } - @Override - public void saveNewTopping(CakeToppingInfo toppingInfo) { - CakeToppingDao bean = context.getBean(CakeToppingDao.class); - bean.save(new CakeTopping(toppingInfo.name, toppingInfo.calories)); - } + @Override + public void bakeNewCake(CakeInfo cakeInfo) throws CakeBakingException { + List allToppings = getAvailableToppingEntities(); + List matchingToppings = + allToppings.stream().filter(t -> t.getName().equals(cakeInfo.cakeToppingInfo.name)) + .collect(Collectors.toList()); + if (matchingToppings.isEmpty()) { + throw new CakeBakingException(String.format("Topping %s is not available", + cakeInfo.cakeToppingInfo.name)); + } + List allLayers = getAvailableLayerEntities(); + Set foundLayers = new HashSet<>(); + for (CakeLayerInfo info : cakeInfo.cakeLayerInfos) { + Optional found = + allLayers.stream().filter(layer -> layer.getName().equals(info.name)).findFirst(); + if (!found.isPresent()) { + throw new CakeBakingException(String.format("Layer %s is not available", info.name)); + } else { + foundLayers.add(found.get()); + } + } + CakeToppingDao toppingBean = context.getBean(CakeToppingDao.class); + CakeTopping topping = toppingBean.findOne(matchingToppings.iterator().next().getId()); + CakeDao cakeBean = context.getBean(CakeDao.class); + Cake cake = new Cake(); + cake.setTopping(topping); + cake.setLayers(foundLayers); + cakeBean.save(cake); + topping.setCake(cake); + toppingBean.save(topping); + CakeLayerDao layerBean = context.getBean(CakeLayerDao.class); + for (CakeLayer layer : foundLayers) { + layer.setCake(cake); + layerBean.save(layer); + } + } - @Override - public void saveNewLayer(CakeLayerInfo layerInfo) { - CakeLayerDao bean = context.getBean(CakeLayerDao.class); - bean.save(new CakeLayer(layerInfo.name, layerInfo.calories)); - } + @Override + public void saveNewTopping(CakeToppingInfo toppingInfo) { + CakeToppingDao bean = context.getBean(CakeToppingDao.class); + bean.save(new CakeTopping(toppingInfo.name, toppingInfo.calories)); + } - private List getAvailableToppingEntities() { - CakeToppingDao bean = context.getBean(CakeToppingDao.class); - List result = new ArrayList<>(); - Iterator iterator = bean.findAll().iterator(); - while (iterator.hasNext()) { - CakeTopping topping = iterator.next(); - if (topping.getCake() == null) { - result.add(topping); - } - } - return result; - } - - @Override - public List getAvailableToppings() { - CakeToppingDao bean = context.getBean(CakeToppingDao.class); - List result = new ArrayList<>(); - Iterator iterator = bean.findAll().iterator(); - while (iterator.hasNext()) { - CakeTopping next = iterator.next(); - if (next.getCake() == null) { - result.add(new CakeToppingInfo(next.getId(), next.getName(), next.getCalories())); - } - } - return result; - } + @Override + public void saveNewLayer(CakeLayerInfo layerInfo) { + CakeLayerDao bean = context.getBean(CakeLayerDao.class); + bean.save(new CakeLayer(layerInfo.name, layerInfo.calories)); + } - private List getAvailableLayerEntities() { - CakeLayerDao bean = context.getBean(CakeLayerDao.class); - List result = new ArrayList<>(); - Iterator iterator = bean.findAll().iterator(); - while (iterator.hasNext()) { - CakeLayer next = iterator.next(); - if (next.getCake() == null) { - result.add(next); - } - } - return result; - } - - @Override - public List getAvailableLayers() { - CakeLayerDao bean = context.getBean(CakeLayerDao.class); - List result = new ArrayList<>(); - Iterator iterator = bean.findAll().iterator(); - while (iterator.hasNext()) { - CakeLayer next = iterator.next(); - if (next.getCake() == null) { - result.add(new CakeLayerInfo(next.getId(), next.getName(), next.getCalories())); - } - } - return result; - } + private List getAvailableToppingEntities() { + CakeToppingDao bean = context.getBean(CakeToppingDao.class); + List result = new ArrayList<>(); + Iterator iterator = bean.findAll().iterator(); + while (iterator.hasNext()) { + CakeTopping topping = iterator.next(); + if (topping.getCake() == null) { + result.add(topping); + } + } + return result; + } - @Override - public List getAllCakes() { - CakeDao cakeBean = context.getBean(CakeDao.class); - List result = new ArrayList<>(); - Iterator iterator = cakeBean.findAll().iterator(); - while (iterator.hasNext()) { - Cake cake = iterator.next(); - CakeToppingInfo cakeToppingInfo = new CakeToppingInfo(cake.getTopping().getId(), - cake.getTopping().getName(), cake.getTopping().getCalories()); - ArrayList cakeLayerInfos = new ArrayList(); - for (CakeLayer layer: cake.getLayers()) { - cakeLayerInfos.add(new CakeLayerInfo(layer.getId(), layer.getName(), layer.getCalories())); - } - CakeInfo cakeInfo = new CakeInfo(cake.getId(), cakeToppingInfo, cakeLayerInfos); - result.add(cakeInfo); - } - return result; - } + @Override + public List getAvailableToppings() { + CakeToppingDao bean = context.getBean(CakeToppingDao.class); + List result = new ArrayList<>(); + Iterator iterator = bean.findAll().iterator(); + while (iterator.hasNext()) { + CakeTopping next = iterator.next(); + if (next.getCake() == null) { + result.add(new CakeToppingInfo(next.getId(), next.getName(), next.getCalories())); + } + } + return result; + } + + private List getAvailableLayerEntities() { + CakeLayerDao bean = context.getBean(CakeLayerDao.class); + List result = new ArrayList<>(); + Iterator iterator = bean.findAll().iterator(); + while (iterator.hasNext()) { + CakeLayer next = iterator.next(); + if (next.getCake() == null) { + result.add(next); + } + } + return result; + } + + @Override + public List getAvailableLayers() { + CakeLayerDao bean = context.getBean(CakeLayerDao.class); + List result = new ArrayList<>(); + Iterator iterator = bean.findAll().iterator(); + while (iterator.hasNext()) { + CakeLayer next = iterator.next(); + if (next.getCake() == null) { + result.add(new CakeLayerInfo(next.getId(), next.getName(), next.getCalories())); + } + } + return result; + } + + @Override + public List getAllCakes() { + CakeDao cakeBean = context.getBean(CakeDao.class); + List result = new ArrayList<>(); + Iterator iterator = cakeBean.findAll().iterator(); + while (iterator.hasNext()) { + Cake cake = iterator.next(); + CakeToppingInfo cakeToppingInfo = + new CakeToppingInfo(cake.getTopping().getId(), cake.getTopping().getName(), cake + .getTopping().getCalories()); + ArrayList cakeLayerInfos = new ArrayList<>(); + for (CakeLayer layer : cake.getLayers()) { + cakeLayerInfos.add(new CakeLayerInfo(layer.getId(), layer.getName(), layer.getCalories())); + } + CakeInfo cakeInfo = new CakeInfo(cake.getId(), cakeToppingInfo, cakeLayerInfos); + result.add(cakeInfo); + } + return result; + } } diff --git a/layers/src/main/java/com/iluwatar/layers/CakeDao.java b/layers/src/main/java/com/iluwatar/layers/CakeDao.java index 075e75d31..15107b030 100644 --- a/layers/src/main/java/com/iluwatar/layers/CakeDao.java +++ b/layers/src/main/java/com/iluwatar/layers/CakeDao.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.layers; import org.springframework.data.repository.CrudRepository; diff --git a/layers/src/main/java/com/iluwatar/layers/CakeInfo.java b/layers/src/main/java/com/iluwatar/layers/CakeInfo.java index 335ce5f4f..e71da5d30 100644 --- a/layers/src/main/java/com/iluwatar/layers/CakeInfo.java +++ b/layers/src/main/java/com/iluwatar/layers/CakeInfo.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.layers; import java.util.List; @@ -10,31 +32,40 @@ import java.util.Optional; */ public class CakeInfo { - public final Optional id; - public final CakeToppingInfo cakeToppingInfo; - public final List cakeLayerInfos; + public final Optional id; + public final CakeToppingInfo cakeToppingInfo; + public final List cakeLayerInfos; - public CakeInfo(Long id, CakeToppingInfo cakeToppingInfo, List cakeLayerInfos) { - this.id = Optional.of(id); - this.cakeToppingInfo = cakeToppingInfo; - this.cakeLayerInfos = cakeLayerInfos; - } - - public CakeInfo(CakeToppingInfo cakeToppingInfo, List cakeLayerInfos) { - this.id = Optional.empty(); - this.cakeToppingInfo = cakeToppingInfo; - this.cakeLayerInfos = cakeLayerInfos; - } - - public int calculateTotalCalories() { - int total = cakeToppingInfo != null ? cakeToppingInfo.calories : 0; - total += cakeLayerInfos.stream().mapToInt(c -> c.calories).sum(); - return total; - } - - @Override - public String toString() { - return String.format("CakeInfo id=%d topping=%s layers=%s totalCalories=%d", id.get(), cakeToppingInfo, - cakeLayerInfos, calculateTotalCalories()); - } + /** + * Constructor + */ + public CakeInfo(Long id, CakeToppingInfo cakeToppingInfo, List cakeLayerInfos) { + this.id = Optional.of(id); + this.cakeToppingInfo = cakeToppingInfo; + this.cakeLayerInfos = cakeLayerInfos; + } + + /** + * Constructor + */ + public CakeInfo(CakeToppingInfo cakeToppingInfo, List cakeLayerInfos) { + this.id = Optional.empty(); + this.cakeToppingInfo = cakeToppingInfo; + this.cakeLayerInfos = cakeLayerInfos; + } + + /** + * Calculate calories + */ + public int calculateTotalCalories() { + int total = cakeToppingInfo != null ? cakeToppingInfo.calories : 0; + total += cakeLayerInfos.stream().mapToInt(c -> c.calories).sum(); + return total; + } + + @Override + public String toString() { + return String.format("CakeInfo id=%d topping=%s layers=%s totalCalories=%d", id.orElse(-1L), + cakeToppingInfo, cakeLayerInfos, calculateTotalCalories()); + } } diff --git a/layers/src/main/java/com/iluwatar/layers/CakeLayer.java b/layers/src/main/java/com/iluwatar/layers/CakeLayer.java index 2f8649c18..50632f1b0 100644 --- a/layers/src/main/java/com/iluwatar/layers/CakeLayer.java +++ b/layers/src/main/java/com/iluwatar/layers/CakeLayer.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.layers; import javax.persistence.CascadeType; @@ -14,59 +36,58 @@ import javax.persistence.ManyToOne; @Entity public class CakeLayer { - @Id - @GeneratedValue - private Long id; + @Id + @GeneratedValue + private Long id; - private String name; + private String name; - private int calories; - - @ManyToOne(cascade = CascadeType.ALL) - private Cake cake; + private int calories; - public CakeLayer() { - } + @ManyToOne(cascade = CascadeType.ALL) + private Cake cake; - public CakeLayer(String name, int calories) { - this.setName(name); - this.setCalories(calories); - } + public CakeLayer() {} - public Long getId() { - return id; - } + public CakeLayer(String name, int calories) { + this.setName(name); + this.setCalories(calories); + } - public void setId(Long id) { - this.id = id; - } + public Long getId() { + return id; + } - public String getName() { - return name; - } + public void setId(Long id) { + this.id = id; + } - public void setName(String name) { - this.name = name; - } + public String getName() { + return name; + } - public int getCalories() { - return calories; - } + public final void setName(String name) { + this.name = name; + } - public void setCalories(int calories) { - this.calories = calories; - } - - @Override - public String toString() { - return String.format("id=%s name=%s calories=%d", id, name, calories); - } + public int getCalories() { + return calories; + } - public Cake getCake() { - return cake; - } + public final void setCalories(int calories) { + this.calories = calories; + } - public void setCake(Cake cake) { - this.cake = cake; - } + @Override + public String toString() { + return String.format("id=%s name=%s calories=%d", id, name, calories); + } + + public Cake getCake() { + return cake; + } + + public void setCake(Cake cake) { + this.cake = cake; + } } diff --git a/layers/src/main/java/com/iluwatar/layers/CakeLayerDao.java b/layers/src/main/java/com/iluwatar/layers/CakeLayerDao.java index 9e1d035a8..2eea55eaa 100644 --- a/layers/src/main/java/com/iluwatar/layers/CakeLayerDao.java +++ b/layers/src/main/java/com/iluwatar/layers/CakeLayerDao.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.layers; import org.springframework.data.repository.CrudRepository; diff --git a/layers/src/main/java/com/iluwatar/layers/CakeLayerInfo.java b/layers/src/main/java/com/iluwatar/layers/CakeLayerInfo.java index 9aa7ff7f6..7f327cac3 100644 --- a/layers/src/main/java/com/iluwatar/layers/CakeLayerInfo.java +++ b/layers/src/main/java/com/iluwatar/layers/CakeLayerInfo.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.layers; import java.util.Optional; @@ -9,24 +31,30 @@ import java.util.Optional; */ public class CakeLayerInfo { - public final Optional id; - public final String name; - public final int calories; + public final Optional id; + public final String name; + public final int calories; - public CakeLayerInfo(Long id, String name, int calories) { - this.id = Optional.of(id); - this.name = name; - this.calories = calories; - } - - public CakeLayerInfo(String name, int calories) { - this.id = Optional.empty(); - this.name = name; - this.calories = calories; - } - - @Override - public String toString() { - return String.format("CakeLayerInfo id=%d name=%s calories=%d", id.get(), name, calories); - } + /** + * Constructor + */ + public CakeLayerInfo(Long id, String name, int calories) { + this.id = Optional.of(id); + this.name = name; + this.calories = calories; + } + + /** + * Constructor + */ + public CakeLayerInfo(String name, int calories) { + this.id = Optional.empty(); + this.name = name; + this.calories = calories; + } + + @Override + public String toString() { + return String.format("CakeLayerInfo id=%d name=%s calories=%d", id.orElse(-1L), name, calories); + } } diff --git a/layers/src/main/java/com/iluwatar/layers/CakeTopping.java b/layers/src/main/java/com/iluwatar/layers/CakeTopping.java index f0e30997a..7db86d0f6 100644 --- a/layers/src/main/java/com/iluwatar/layers/CakeTopping.java +++ b/layers/src/main/java/com/iluwatar/layers/CakeTopping.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.layers; import javax.persistence.CascadeType; @@ -14,59 +36,58 @@ import javax.persistence.OneToOne; @Entity public class CakeTopping { - @Id - @GeneratedValue - private Long id; - - private String name; - - private int calories; - - @OneToOne(cascade = CascadeType.ALL) - private Cake cake; - - public CakeTopping() { - } - - public CakeTopping(String name, int calories) { - this.setName(name); - this.setCalories(calories); - } + @Id + @GeneratedValue + private Long id; - public Long getId() { - return id; - } + private String name; - public void setId(Long id) { - this.id = id; - } + private int calories; - public String getName() { - return name; - } + @OneToOne(cascade = CascadeType.ALL) + private Cake cake; - public void setName(String name) { - this.name = name; - } + public CakeTopping() {} - public int getCalories() { - return calories; - } + public CakeTopping(String name, int calories) { + this.setName(name); + this.setCalories(calories); + } - public void setCalories(int calories) { - this.calories = calories; - } - - @Override - public String toString() { - return String.format("id=%s name=%s calories=%d", name, calories); - } + public Long getId() { + return id; + } - public Cake getCake() { - return cake; - } + public void setId(Long id) { + this.id = id; + } - public void setCake(Cake cake) { - this.cake = cake; - } + public String getName() { + return name; + } + + public final void setName(String name) { + this.name = name; + } + + public final int getCalories() { + return calories; + } + + public void setCalories(int calories) { + this.calories = calories; + } + + @Override + public String toString() { + return String.format("id=%s name=%s calories=%d", id, name, calories); + } + + public Cake getCake() { + return cake; + } + + public void setCake(Cake cake) { + this.cake = cake; + } } diff --git a/layers/src/main/java/com/iluwatar/layers/CakeToppingDao.java b/layers/src/main/java/com/iluwatar/layers/CakeToppingDao.java index 3ddcf53ec..7a7b32aa7 100644 --- a/layers/src/main/java/com/iluwatar/layers/CakeToppingDao.java +++ b/layers/src/main/java/com/iluwatar/layers/CakeToppingDao.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.layers; import org.springframework.data.repository.CrudRepository; diff --git a/layers/src/main/java/com/iluwatar/layers/CakeToppingInfo.java b/layers/src/main/java/com/iluwatar/layers/CakeToppingInfo.java index 152b0ff85..9acb0526e 100644 --- a/layers/src/main/java/com/iluwatar/layers/CakeToppingInfo.java +++ b/layers/src/main/java/com/iluwatar/layers/CakeToppingInfo.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.layers; import java.util.Optional; @@ -9,24 +31,30 @@ import java.util.Optional; */ public class CakeToppingInfo { - public final Optional id; - public final String name; - public final int calories; + public final Optional id; + public final String name; + public final int calories; - public CakeToppingInfo(Long id, String name, int calories) { - this.id = Optional.of(id); - this.name = name; - this.calories = calories; - } - - public CakeToppingInfo(String name, int calories) { - this.id = Optional.empty(); - this.name = name; - this.calories = calories; - } - - @Override - public String toString() { - return String.format("CakeToppingInfo id=%d name=%s calories=%d", id.get(), name, calories); - } + /** + * Constructor + */ + public CakeToppingInfo(Long id, String name, int calories) { + this.id = Optional.of(id); + this.name = name; + this.calories = calories; + } + + /** + * Constructor + */ + public CakeToppingInfo(String name, int calories) { + this.id = Optional.empty(); + this.name = name; + this.calories = calories; + } + + @Override + public String toString() { + return String.format("CakeToppingInfo id=%d name=%s calories=%d", id.orElse(-1L), name, calories); + } } diff --git a/layers/src/main/java/com/iluwatar/layers/CakeViewImpl.java b/layers/src/main/java/com/iluwatar/layers/CakeViewImpl.java index 5fed15c3a..bc489e16e 100644 --- a/layers/src/main/java/com/iluwatar/layers/CakeViewImpl.java +++ b/layers/src/main/java/com/iluwatar/layers/CakeViewImpl.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.layers; /** @@ -7,13 +29,13 @@ package com.iluwatar.layers; */ public class CakeViewImpl implements View { - private CakeBakingService cakeBakingService; + private CakeBakingService cakeBakingService; - public CakeViewImpl(CakeBakingService cakeBakingService) { - this.cakeBakingService = cakeBakingService; - } - - public void render() { - cakeBakingService.getAllCakes().stream().forEach((cake) -> System.out.println(cake)); - } + public CakeViewImpl(CakeBakingService cakeBakingService) { + this.cakeBakingService = cakeBakingService; + } + + public void render() { + cakeBakingService.getAllCakes().stream().forEach(cake -> System.out.println(cake)); + } } diff --git a/layers/src/main/java/com/iluwatar/layers/View.java b/layers/src/main/java/com/iluwatar/layers/View.java index 123d4ecbf..fa4f307ca 100644 --- a/layers/src/main/java/com/iluwatar/layers/View.java +++ b/layers/src/main/java/com/iluwatar/layers/View.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.layers; /** @@ -7,6 +29,6 @@ package com.iluwatar.layers; */ public interface View { - void render(); - + void render(); + } diff --git a/layers/src/main/resources/META-INF/persistence.xml b/layers/src/main/resources/META-INF/persistence.xml index d94d8582b..32afb9fd4 100644 --- a/layers/src/main/resources/META-INF/persistence.xml +++ b/layers/src/main/resources/META-INF/persistence.xml @@ -1,8 +1,32 @@ - + + + + \ No newline at end of file diff --git a/layers/src/main/resources/applicationContext.xml b/layers/src/main/resources/applicationContext.xml index 0c908ad2e..e1ddc7427 100644 --- a/layers/src/main/resources/applicationContext.xml +++ b/layers/src/main/resources/applicationContext.xml @@ -1,42 +1,63 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layers/src/test/java/com/iluwatar/layers/AppTest.java b/layers/src/test/java/com/iluwatar/layers/AppTest.java index e55ab4f84..5a5374def 100644 --- a/layers/src/test/java/com/iluwatar/layers/AppTest.java +++ b/layers/src/test/java/com/iluwatar/layers/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.layers; import org.junit.Test; -import com.iluwatar.layers.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.layers.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/layers/src/test/java/com/iluwatar/layers/CakeBakingExceptionTest.java b/layers/src/test/java/com/iluwatar/layers/CakeBakingExceptionTest.java new file mode 100644 index 000000000..1cca98472 --- /dev/null +++ b/layers/src/test/java/com/iluwatar/layers/CakeBakingExceptionTest.java @@ -0,0 +1,52 @@ +/** + * The MIT License + * Copyright (c) 2014 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.layers; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +/** + * Date: 12/15/15 - 7:57 PM + * + * @author Jeroen Meulemeester + */ +public class CakeBakingExceptionTest { + + @Test + public void testConstructor() throws Exception { + final CakeBakingException exception = new CakeBakingException(); + assertNull(exception.getMessage()); + assertNull(exception.getCause()); + } + + @Test + public void testConstructorWithMessage() throws Exception { + final String expectedMessage = "message"; + final CakeBakingException exception = new CakeBakingException(expectedMessage); + assertEquals(expectedMessage, exception.getMessage()); + assertNull(exception.getCause()); + } + +} diff --git a/layers/src/test/java/com/iluwatar/layers/CakeBakingServiceImplTest.java b/layers/src/test/java/com/iluwatar/layers/CakeBakingServiceImplTest.java new file mode 100644 index 000000000..e1c539cf2 --- /dev/null +++ b/layers/src/test/java/com/iluwatar/layers/CakeBakingServiceImplTest.java @@ -0,0 +1,181 @@ +/** + * The MIT License + * Copyright (c) 2014 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.layers; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Date: 12/15/15 - 9:55 PM + * + * @author Jeroen Meulemeester + */ +public class CakeBakingServiceImplTest { + + @Test + public void testLayers() throws CakeBakingException { + final CakeBakingServiceImpl service = new CakeBakingServiceImpl(); + + final List initialLayers = service.getAvailableLayers(); + assertNotNull(initialLayers); + assertTrue(initialLayers.isEmpty()); + + service.saveNewLayer(new CakeLayerInfo("Layer1", 1000)); + service.saveNewLayer(new CakeLayerInfo("Layer2", 2000)); + + final List availableLayers = service.getAvailableLayers(); + assertNotNull(availableLayers); + assertEquals(2, availableLayers.size()); + for (final CakeLayerInfo layer : availableLayers) { + assertNotNull(layer.id); + assertNotNull(layer.name); + assertNotNull(layer.toString()); + assertTrue(layer.calories > 0); + } + + } + + @Test + public void testToppings() throws CakeBakingException { + final CakeBakingServiceImpl service = new CakeBakingServiceImpl(); + + final List initialToppings = service.getAvailableToppings(); + assertNotNull(initialToppings); + assertTrue(initialToppings.isEmpty()); + + service.saveNewTopping(new CakeToppingInfo("Topping1", 1000)); + service.saveNewTopping(new CakeToppingInfo("Topping2", 2000)); + + final List availableToppings = service.getAvailableToppings(); + assertNotNull(availableToppings); + assertEquals(2, availableToppings.size()); + for (final CakeToppingInfo topping : availableToppings) { + assertNotNull(topping.id); + assertNotNull(topping.name); + assertNotNull(topping.toString()); + assertTrue(topping.calories > 0); + } + + } + + @Test + public void testBakeCakes() throws CakeBakingException { + final CakeBakingServiceImpl service = new CakeBakingServiceImpl(); + + final List initialCakes = service.getAllCakes(); + assertNotNull(initialCakes); + assertTrue(initialCakes.isEmpty()); + + final CakeToppingInfo topping1 = new CakeToppingInfo("Topping1", 1000); + final CakeToppingInfo topping2 = new CakeToppingInfo("Topping2", 2000); + service.saveNewTopping(topping1); + service.saveNewTopping(topping2); + + final CakeLayerInfo layer1 = new CakeLayerInfo("Layer1", 1000); + final CakeLayerInfo layer2 = new CakeLayerInfo("Layer2", 2000); + final CakeLayerInfo layer3 = new CakeLayerInfo("Layer3", 2000); + service.saveNewLayer(layer1); + service.saveNewLayer(layer2); + service.saveNewLayer(layer3); + + service.bakeNewCake(new CakeInfo(topping1, Arrays.asList(layer1, layer2))); + service.bakeNewCake(new CakeInfo(topping2, Collections.singletonList(layer3))); + + final List allCakes = service.getAllCakes(); + assertNotNull(allCakes); + assertEquals(2, allCakes.size()); + for (final CakeInfo cakeInfo : allCakes) { + assertNotNull(cakeInfo.id); + assertNotNull(cakeInfo.cakeToppingInfo); + assertNotNull(cakeInfo.cakeLayerInfos); + assertNotNull(cakeInfo.toString()); + assertFalse(cakeInfo.cakeLayerInfos.isEmpty()); + assertTrue(cakeInfo.calculateTotalCalories() > 0); + } + + } + + @Test(expected = CakeBakingException.class) + public void testBakeCakeMissingTopping() throws CakeBakingException { + final CakeBakingServiceImpl service = new CakeBakingServiceImpl(); + + final CakeLayerInfo layer1 = new CakeLayerInfo("Layer1", 1000); + final CakeLayerInfo layer2 = new CakeLayerInfo("Layer2", 2000); + service.saveNewLayer(layer1); + service.saveNewLayer(layer2); + + final CakeToppingInfo missingTopping = new CakeToppingInfo("Topping1", 1000); + service.bakeNewCake(new CakeInfo(missingTopping, Arrays.asList(layer1, layer2))); + } + + @Test(expected = CakeBakingException.class) + public void testBakeCakeMissingLayer() throws CakeBakingException { + final CakeBakingServiceImpl service = new CakeBakingServiceImpl(); + + final List initialCakes = service.getAllCakes(); + assertNotNull(initialCakes); + assertTrue(initialCakes.isEmpty()); + + final CakeToppingInfo topping1 = new CakeToppingInfo("Topping1", 1000); + service.saveNewTopping(topping1); + + final CakeLayerInfo layer1 = new CakeLayerInfo("Layer1", 1000); + service.saveNewLayer(layer1); + + final CakeLayerInfo missingLayer = new CakeLayerInfo("Layer2", 2000); + service.bakeNewCake(new CakeInfo(topping1, Arrays.asList(layer1, missingLayer))); + + } + + @Test(expected = CakeBakingException.class) + public void testBakeCakesUsedLayer() throws CakeBakingException { + final CakeBakingServiceImpl service = new CakeBakingServiceImpl(); + + final List initialCakes = service.getAllCakes(); + assertNotNull(initialCakes); + assertTrue(initialCakes.isEmpty()); + + final CakeToppingInfo topping1 = new CakeToppingInfo("Topping1", 1000); + final CakeToppingInfo topping2 = new CakeToppingInfo("Topping2", 2000); + service.saveNewTopping(topping1); + service.saveNewTopping(topping2); + + final CakeLayerInfo layer1 = new CakeLayerInfo("Layer1", 1000); + final CakeLayerInfo layer2 = new CakeLayerInfo("Layer2", 2000); + service.saveNewLayer(layer1); + service.saveNewLayer(layer2); + + service.bakeNewCake(new CakeInfo(topping1, Arrays.asList(layer1, layer2))); + service.bakeNewCake(new CakeInfo(topping2, Collections.singletonList(layer2))); + + } + +} diff --git a/layers/src/test/java/com/iluwatar/layers/CakeTest.java b/layers/src/test/java/com/iluwatar/layers/CakeTest.java new file mode 100644 index 000000000..6cd50106c --- /dev/null +++ b/layers/src/test/java/com/iluwatar/layers/CakeTest.java @@ -0,0 +1,119 @@ +/** + * The MIT License + * Copyright (c) 2014 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.layers; + +import org.junit.Test; + +import java.util.HashSet; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +/** + * Date: 12/15/15 - 8:02 PM + * + * @author Jeroen Meulemeester + */ +public class CakeTest { + + @Test + public void testSetId() { + final Cake cake = new Cake(); + assertNull(cake.getId()); + + final Long expectedId = Long.valueOf(1234L); + cake.setId(expectedId); + assertEquals(expectedId, cake.getId()); + } + + @Test + public void testSetTopping() { + final Cake cake = new Cake(); + assertNull(cake.getTopping()); + + final CakeTopping expectedTopping = new CakeTopping("DummyTopping", 1000); + cake.setTopping(expectedTopping); + assertEquals(expectedTopping, cake.getTopping()); + } + + @Test + public void testSetLayers() { + final Cake cake = new Cake(); + assertNotNull(cake.getLayers()); + assertTrue(cake.getLayers().isEmpty()); + + final Set expectedLayers = new HashSet<>(); + expectedLayers.add(new CakeLayer("layer1", 1000)); + expectedLayers.add(new CakeLayer("layer2", 2000)); + expectedLayers.add(new CakeLayer("layer3", 3000)); + + cake.setLayers(expectedLayers); + assertEquals(expectedLayers, cake.getLayers()); + } + + @Test + public void testAddLayer() { + final Cake cake = new Cake(); + assertNotNull(cake.getLayers()); + assertTrue(cake.getLayers().isEmpty()); + + final Set initialLayers = new HashSet<>(); + initialLayers.add(new CakeLayer("layer1", 1000)); + initialLayers.add(new CakeLayer("layer2", 2000)); + + cake.setLayers(initialLayers); + assertEquals(initialLayers, cake.getLayers()); + + final CakeLayer newLayer = new CakeLayer("layer3", 3000); + cake.addLayer(newLayer); + + final Set expectedLayers = new HashSet<>(); + expectedLayers.addAll(initialLayers); + expectedLayers.addAll(initialLayers); + expectedLayers.add(newLayer); + assertEquals(expectedLayers, cake.getLayers()); + } + + @Test + public void testToString() { + final CakeTopping topping = new CakeTopping("topping", 20); + topping.setId(2345L); + + final CakeLayer layer = new CakeLayer("layer", 100); + layer.setId(3456L); + + final Cake cake = new Cake(); + cake.setId(1234L); + cake.setTopping(topping); + cake.addLayer(layer); + + final String expected = "id=1234 topping=id=2345 name=topping calories=20 " + + "layers=[id=3456 name=layer calories=100]"; + assertEquals(expected, cake.toString()); + + } + +} diff --git a/layers/src/test/java/com/iluwatar/layers/CakeViewImplTest.java b/layers/src/test/java/com/iluwatar/layers/CakeViewImplTest.java new file mode 100644 index 000000000..a69aa23bb --- /dev/null +++ b/layers/src/test/java/com/iluwatar/layers/CakeViewImplTest.java @@ -0,0 +1,66 @@ +/** + * The MIT License + * Copyright (c) 2014 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.layers; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.Mockito.*; + +/** + * Date: 12/15/15 - 10:04 PM + * + * @author Jeroen Meulemeester + */ +public class CakeViewImplTest extends StdOutTest { + + /** + * Verify if the cake view renders the expected result + */ + @Test + public void testRender() { + + final List layers = new ArrayList<>(); + layers.add(new CakeLayerInfo("layer1", 1000)); + layers.add(new CakeLayerInfo("layer2", 2000)); + layers.add(new CakeLayerInfo("layer3", 3000)); + + final List cakes = new ArrayList<>(); + final CakeInfo cake = new CakeInfo(new CakeToppingInfo("topping", 1000), layers); + cakes.add(cake); + + final CakeBakingService bakingService = mock(CakeBakingService.class); + when(bakingService.getAllCakes()).thenReturn(cakes); + + final CakeViewImpl cakeView = new CakeViewImpl(bakingService); + + verifyZeroInteractions(getStdOutMock()); + + cakeView.render(); + verify(getStdOutMock(), times(1)).println(cake); + + } + +} diff --git a/layers/src/test/java/com/iluwatar/layers/StdOutTest.java b/layers/src/test/java/com/iluwatar/layers/StdOutTest.java new file mode 100644 index 000000000..3c94b178c --- /dev/null +++ b/layers/src/test/java/com/iluwatar/layers/StdOutTest.java @@ -0,0 +1,76 @@ +/** + * The MIT License + * Copyright (c) 2014 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.layers; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since the actions of the views don't have + * any influence on any other accessible objects, except for writing to std-out using {@link + * System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/lazy-loading/index.md b/lazy-loading/README.md similarity index 61% rename from lazy-loading/index.md rename to lazy-loading/README.md index 700892af0..d40061293 100644 --- a/lazy-loading/index.md +++ b/lazy-loading/README.md @@ -4,20 +4,30 @@ title: Lazy Loading folder: lazy-loading permalink: /patterns/lazy-loading/ categories: Other -tags: Java +tags: + - Java + - Difficulty-Beginner + - Idiom + - Performance --- -**Intent:** Lazy loading is a design pattern commonly used to defer +## Intent +Lazy loading is a design pattern commonly used to defer initialization of an object until the point at which it is needed. It can contribute to efficiency in the program's operation if properly and appropriately used. ![alt text](./etc/lazy-loading.png "Lazy Loading") -**Applicability:** Use the Lazy Loading idiom when +## Applicability +Use the Lazy Loading idiom when * eager loading is expensive or the object to be loaded might not be needed at all -**Real world examples:** +## Real world examples * JPA annotations @OneToOne, @OneToMany, @ManyToOne, @ManyToMany and fetch = FetchType.LAZY + +## Credits + +* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) diff --git a/lazy-loading/pom.xml b/lazy-loading/pom.xml index 61da2f3b5..fe6d44c78 100644 --- a/lazy-loading/pom.xml +++ b/lazy-loading/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT lazy-loading diff --git a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/App.java b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/App.java index db09c781b..7a658a8c6 100644 --- a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/App.java +++ b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/App.java @@ -1,37 +1,58 @@ +/** + * The MIT License + * Copyright (c) 2014 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.lazy.loading; /** * * Lazy loading idiom defers object creation until needed. *

- * This example shows different implementations of the pattern - * with increasing sophistication. + * This example shows different implementations of the pattern with increasing sophistication. *

* Additional information and lazy loading flavours are described in * http://martinfowler.com/eaaCatalog/lazyLoad.html * */ -public class App -{ - /** - * Program entry point - * @param args command line args - */ - public static void main( String[] args ) { - - // Simple lazy loader - not thread safe - HolderNaive holderNaive = new HolderNaive(); - Heavy heavy = holderNaive.getHeavy(); - System.out.println("heavy=" + heavy); - - // Thread safe lazy loader, but with heavy synchronization on each access - HolderThreadSafe holderThreadSafe = new HolderThreadSafe(); - Heavy another = holderThreadSafe.getHeavy(); - System.out.println("another=" + another); - - // The most efficient lazy loader utilizing Java 8 features - Java8Holder java8Holder = new Java8Holder(); - Heavy next = java8Holder.getHeavy(); - System.out.println("next=" + next); - } +public class App { + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + + // Simple lazy loader - not thread safe + HolderNaive holderNaive = new HolderNaive(); + Heavy heavy = holderNaive.getHeavy(); + System.out.println("heavy=" + heavy); + + // Thread safe lazy loader, but with heavy synchronization on each access + HolderThreadSafe holderThreadSafe = new HolderThreadSafe(); + Heavy another = holderThreadSafe.getHeavy(); + System.out.println("another=" + another); + + // The most efficient lazy loader utilizing Java 8 features + Java8Holder java8Holder = new Java8Holder(); + Heavy next = java8Holder.getHeavy(); + System.out.println("next=" + next); + } } diff --git a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/Heavy.java b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/Heavy.java index c2715434f..57e8e263e 100644 --- a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/Heavy.java +++ b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/Heavy.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.lazy.loading; /** @@ -7,13 +29,16 @@ package com.iluwatar.lazy.loading; */ public class Heavy { - public Heavy() { - System.out.println("Creating Heavy ..."); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - System.out.println("... Heavy created"); - } + /** + * Constructor + */ + public Heavy() { + System.out.println("Creating Heavy ..."); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("... Heavy created"); + } } diff --git a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/HolderNaive.java b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/HolderNaive.java index 1421d4b23..75c65d1b9 100644 --- a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/HolderNaive.java +++ b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/HolderNaive.java @@ -1,23 +1,50 @@ +/** + * The MIT License + * Copyright (c) 2014 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.lazy.loading; /** * - * Simple implementation of the lazy loading idiom. - * However, this is not thread safe. + * Simple implementation of the lazy loading idiom. However, this is not thread safe. * */ public class HolderNaive { - - private Heavy heavy; - - public HolderNaive() { - System.out.println("HolderNaive created"); - } - public Heavy getHeavy() { - if (heavy == null) { - heavy = new Heavy(); - } - return heavy; - } + private Heavy heavy; + + /** + * Constructor + */ + public HolderNaive() { + System.out.println("HolderNaive created"); + } + + /** + * Get heavy object + */ + public Heavy getHeavy() { + if (heavy == null) { + heavy = new Heavy(); + } + return heavy; + } } diff --git a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/HolderThreadSafe.java b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/HolderThreadSafe.java index cc039d9f0..c6e0fcfd9 100644 --- a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/HolderThreadSafe.java +++ b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/HolderThreadSafe.java @@ -1,24 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014 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.lazy.loading; /** * - * Same as HolderNaive but with added synchronization. - * This implementation is thread safe, but each {@link #getHeavy()} - * call costs additional synchronization overhead. + * Same as HolderNaive but with added synchronization. This implementation is thread safe, but each + * {@link #getHeavy()} call costs additional synchronization overhead. * */ public class HolderThreadSafe { - - private Heavy heavy; - - public HolderThreadSafe() { - System.out.println("HolderThreadSafe created"); - } - public synchronized Heavy getHeavy() { - if (heavy == null) { - heavy = new Heavy(); - } - return heavy; - } + private Heavy heavy; + + /** + * Constructor + */ + public HolderThreadSafe() { + System.out.println("HolderThreadSafe created"); + } + + /** + * Get heavy object + */ + public synchronized Heavy getHeavy() { + if (heavy == null) { + heavy = new Heavy(); + } + return heavy; + } } diff --git a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/Java8Holder.java b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/Java8Holder.java index a03aae352..e4ce394cc 100644 --- a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/Java8Holder.java +++ b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/Java8Holder.java @@ -1,34 +1,59 @@ +/** + * The MIT License + * Copyright (c) 2014 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.lazy.loading; import java.util.function.Supplier; /** * - * This lazy loader is thread safe and more efficient than {@link HolderThreadSafe}. - * It utilizes Java 8 functional interface {@link Supplier} as {@link Heavy} factory. + * This lazy loader is thread safe and more efficient than {@link HolderThreadSafe}. It utilizes + * Java 8 functional interface {@link Supplier} as {@link Heavy} factory. * */ public class Java8Holder { - - private Supplier heavy = () -> createAndCacheHeavy(); - - public Java8Holder() { - System.out.println("Java8Holder created"); - } - public Heavy getHeavy() { - return heavy.get(); - } - - private synchronized Heavy createAndCacheHeavy() { - class HeavyFactory implements Supplier { - private final Heavy heavyInstance = new Heavy(); - @Override - public Heavy get() { return heavyInstance; } - } - if (!HeavyFactory.class.isInstance(heavy)) { - heavy = new HeavyFactory(); - } - return heavy.get(); - } + private Supplier heavy = () -> createAndCacheHeavy(); + + public Java8Holder() { + System.out.println("Java8Holder created"); + } + + public Heavy getHeavy() { + return heavy.get(); + } + + private synchronized Heavy createAndCacheHeavy() { + class HeavyFactory implements Supplier { + private final Heavy heavyInstance = new Heavy(); + + @Override + public Heavy get() { + return heavyInstance; + } + } + if (!HeavyFactory.class.isInstance(heavy)) { + heavy = new HeavyFactory(); + } + return heavy.get(); + } } diff --git a/lazy-loading/src/test/java/com/iluwatar/lazy/loading/AbstractHolderTest.java b/lazy-loading/src/test/java/com/iluwatar/lazy/loading/AbstractHolderTest.java new file mode 100644 index 000000000..3118847d9 --- /dev/null +++ b/lazy-loading/src/test/java/com/iluwatar/lazy/loading/AbstractHolderTest.java @@ -0,0 +1,63 @@ +/** + * The MIT License + * Copyright (c) 2014 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.lazy.loading; + +import org.junit.Test; + +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertSame; +import static junit.framework.TestCase.assertNull; + +/** + * Date: 12/19/15 - 11:58 AM + * + * @author Jeroen Meulemeester + */ +public abstract class AbstractHolderTest { + + /** + * Get the internal state of the holder value + * + * @return The internal value + */ + abstract Heavy getInternalHeavyValue() throws Exception; + + /** + * Request a lazy loaded {@link Heavy} object from the holder. + * + * @return The lazy loaded {@link Heavy} object + */ + abstract Heavy getHeavy() throws Exception; + + /** + * This test shows that the heavy field is not instantiated until the method getHeavy is called + */ + @Test(timeout = 3000) + public void testGetHeavy() throws Exception { + assertNull(getInternalHeavyValue()); + assertNotNull(getHeavy()); + assertNotNull(getInternalHeavyValue()); + assertSame(getHeavy(), getInternalHeavyValue()); + } + +} diff --git a/lazy-loading/src/test/java/com/iluwatar/lazy/loading/AppTest.java b/lazy-loading/src/test/java/com/iluwatar/lazy/loading/AppTest.java index 0ef7e8ae2..36cdedb6a 100644 --- a/lazy-loading/src/test/java/com/iluwatar/lazy/loading/AppTest.java +++ b/lazy-loading/src/test/java/com/iluwatar/lazy/loading/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.lazy.loading; import org.junit.Test; -import com.iluwatar.lazy.loading.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.lazy.loading.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/lazy-loading/src/test/java/com/iluwatar/lazy/loading/HolderNaiveTest.java b/lazy-loading/src/test/java/com/iluwatar/lazy/loading/HolderNaiveTest.java new file mode 100644 index 000000000..a9c1b6afb --- /dev/null +++ b/lazy-loading/src/test/java/com/iluwatar/lazy/loading/HolderNaiveTest.java @@ -0,0 +1,48 @@ +/** + * The MIT License + * Copyright (c) 2014 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.lazy.loading; + +import java.lang.reflect.Field; + +/** + * Date: 12/19/15 - 12:05 PM + * + * @author Jeroen Meulemeester + */ +public class HolderNaiveTest extends AbstractHolderTest { + + private final HolderNaive holder = new HolderNaive(); + + @Override + Heavy getInternalHeavyValue() throws Exception { + final Field holderField = HolderNaive.class.getDeclaredField("heavy"); + holderField.setAccessible(true); + return (Heavy) holderField.get(this.holder); + } + + @Override + Heavy getHeavy() { + return holder.getHeavy(); + } + +} \ No newline at end of file diff --git a/lazy-loading/src/test/java/com/iluwatar/lazy/loading/HolderThreadSafeTest.java b/lazy-loading/src/test/java/com/iluwatar/lazy/loading/HolderThreadSafeTest.java index f27ffc6a9..3803a482e 100644 --- a/lazy-loading/src/test/java/com/iluwatar/lazy/loading/HolderThreadSafeTest.java +++ b/lazy-loading/src/test/java/com/iluwatar/lazy/loading/HolderThreadSafeTest.java @@ -1,42 +1,48 @@ +/** + * The MIT License + * Copyright (c) 2014 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.lazy.loading; -import org.junit.Test; - import java.lang.reflect.Field; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - /** - * Using reflection this test shows that the heavy field is not instantiated until the method getHeavy is called + * Date: 12/19/15 - 12:19 PM * - * Created by jones on 11/10/2015. + * @author Jeroen Meulemeester */ -public class HolderThreadSafeTest { +public class HolderThreadSafeTest extends AbstractHolderTest { - @Test - public void test() throws IllegalAccessException { - HolderThreadSafe hts = new HolderThreadSafe(); + private final HolderThreadSafe holder = new HolderThreadSafe(); - {//first call is null - Field[] ff = HolderThreadSafe.class.getDeclaredFields(); - for (Field f: ff) { - f.setAccessible(true); - } + @Override + Heavy getInternalHeavyValue() throws Exception { + final Field holderField = HolderThreadSafe.class.getDeclaredField("heavy"); + holderField.setAccessible(true); + return (Heavy) holderField.get(this.holder); + } - assertNull(ff[0].get(hts)); - } + @Override + Heavy getHeavy() throws Exception { + return this.holder.getHeavy(); + } - // now it is lazily loaded - hts.getHeavy(); - - {//now it is not null - call via reflection so that the test is the same before and after - Field[] ff = HolderThreadSafe.class.getDeclaredFields(); - for (Field f: ff) { - f.setAccessible(true); - } - - assertNotNull(ff[0].get(hts)); - } - } -} +} \ No newline at end of file diff --git a/lazy-loading/src/test/java/com/iluwatar/lazy/loading/Java8HolderTest.java b/lazy-loading/src/test/java/com/iluwatar/lazy/loading/Java8HolderTest.java new file mode 100644 index 000000000..08a3995f9 --- /dev/null +++ b/lazy-loading/src/test/java/com/iluwatar/lazy/loading/Java8HolderTest.java @@ -0,0 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 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.lazy.loading; + +import java.lang.reflect.Field; +import java.util.function.Supplier; + +/** + * Date: 12/19/15 - 12:27 PM + * + * @author Jeroen Meulemeester + */ +public class Java8HolderTest extends AbstractHolderTest { + + private final Java8Holder holder = new Java8Holder(); + + + @Override + Heavy getInternalHeavyValue() throws Exception { + final Field holderField = Java8Holder.class.getDeclaredField("heavy"); + holderField.setAccessible(true); + + final Supplier supplier = (Supplier) holderField.get(this.holder); + final Class supplierClass = supplier.getClass(); + + // This is a little fishy, but I don't know another way to test this: + // The lazy holder is at first a lambda, but gets replaced with a new supplier after loading ... + if (supplierClass.isLocalClass()) { + final Field instanceField = supplierClass.getDeclaredField("heavyInstance"); + instanceField.setAccessible(true); + return (Heavy) instanceField.get(supplier); + } else { + return null; + } + } + + @Override + Heavy getHeavy() throws Exception { + return holder.getHeavy(); + } + +} \ No newline at end of file diff --git a/mediator/index.md b/mediator/README.md similarity index 86% rename from mediator/index.md rename to mediator/README.md index cb4ce7fb1..c7a0478c8 100644 --- a/mediator/index.md +++ b/mediator/README.md @@ -10,18 +10,20 @@ tags: - Difficulty-Intermediate --- -**Intent:** Define an object that encapsulates how a set of objects interact. +## Intent +Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently. ![alt text](./etc/mediator_1.png "Mediator") -**Applicability:** Use the Mediator pattern when +## Applicability +Use the Mediator pattern when * a set of objects communicate in well-defined but complex ways. The resulting interdependencies are unstructured and difficult to understand * reusing an object is difficult because it refers to and communicates with many other objects * a behavior that's distributed between several classes should be customizable without a lot of subclassing -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/mediator/pom.xml b/mediator/pom.xml index 1e325bb8f..a6cdd028e 100644 --- a/mediator/pom.xml +++ b/mediator/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT mediator @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/mediator/src/main/java/com/iluwatar/mediator/Action.java b/mediator/src/main/java/com/iluwatar/mediator/Action.java index a5b466342..b2dc25ed3 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/Action.java +++ b/mediator/src/main/java/com/iluwatar/mediator/Action.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mediator; /** @@ -7,25 +29,23 @@ package com.iluwatar.mediator; */ public enum Action { - HUNT("hunted a rabbit", "arrives for dinner"), - TALE("tells a tale", "comes to listen"), - GOLD("found gold", "takes his share of the gold"), - ENEMY("spotted enemies", "runs for cover"), - NONE("", ""); + HUNT("hunted a rabbit", "arrives for dinner"), 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 description; + private String title; + private String description; - Action(String title, String description) { - this.title = title; - this.description = description; - } + Action(String title, String description) { + this.title = title; + this.description = description; + } - public String getDescription() { - return description; - } + public String getDescription() { + return description; + } - public String toString() { - return title; - } + public String toString() { + return title; + } } diff --git a/mediator/src/main/java/com/iluwatar/mediator/App.java b/mediator/src/main/java/com/iluwatar/mediator/App.java index 9648ac608..57aec35d2 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/App.java +++ b/mediator/src/main/java/com/iluwatar/mediator/App.java @@ -1,55 +1,76 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mediator; /** * - * 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 running behavior. + * 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 + * running behavior. *

- * 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 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 - * difficult to change the program, since any change may affect code in several - * other classes. + * 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 + * 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 difficult to change the program, since any change may affect code in several other + * classes. *

- * 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. + * 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. *

- * 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. + * 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 { - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { - - // create party and members - Party party = new PartyImpl(); - Hobbit hobbit = new Hobbit(); - Wizard wizard = new Wizard(); - Rogue rogue = new Rogue(); - Hunter hunter = new Hunter(); + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { - // add party members - party.addMember(hobbit); - party.addMember(wizard); - party.addMember(rogue); - party.addMember(hunter); + // create party and members + Party party = new PartyImpl(); + Hobbit hobbit = new Hobbit(); + Wizard wizard = new Wizard(); + Rogue rogue = new Rogue(); + Hunter hunter = new Hunter(); - // perform actions -> the other party members - // are notified by the party - hobbit.act(Action.ENEMY); - wizard.act(Action.TALE); - rogue.act(Action.GOLD); - hunter.act(Action.HUNT); - } + // add party members + party.addMember(hobbit); + party.addMember(wizard); + party.addMember(rogue); + party.addMember(hunter); + + // perform actions -> the other party members + // are notified by the party + hobbit.act(Action.ENEMY); + wizard.act(Action.TALE); + rogue.act(Action.GOLD); + hunter.act(Action.HUNT); + } } diff --git a/mediator/src/main/java/com/iluwatar/mediator/Hobbit.java b/mediator/src/main/java/com/iluwatar/mediator/Hobbit.java index 5981a34a8..41ed5f5bd 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/Hobbit.java +++ b/mediator/src/main/java/com/iluwatar/mediator/Hobbit.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mediator; /** @@ -7,9 +29,9 @@ package com.iluwatar.mediator; */ public class Hobbit extends PartyMemberBase { - @Override - public String toString() { - return "Hobbit"; - } + @Override + public String toString() { + return "Hobbit"; + } } diff --git a/mediator/src/main/java/com/iluwatar/mediator/Hunter.java b/mediator/src/main/java/com/iluwatar/mediator/Hunter.java index 1487c836e..0ff2a550e 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/Hunter.java +++ b/mediator/src/main/java/com/iluwatar/mediator/Hunter.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mediator; /** @@ -7,9 +29,8 @@ package com.iluwatar.mediator; */ public class Hunter extends PartyMemberBase { - @Override - public String toString() { - return "Hunter"; - } - + @Override + public String toString() { + return "Hunter"; + } } diff --git a/mediator/src/main/java/com/iluwatar/mediator/Party.java b/mediator/src/main/java/com/iluwatar/mediator/Party.java index 9328cdaa2..c3c2f1666 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/Party.java +++ b/mediator/src/main/java/com/iluwatar/mediator/Party.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mediator; /** @@ -7,8 +29,8 @@ package com.iluwatar.mediator; */ public interface Party { - void addMember(PartyMember member); + void addMember(PartyMember member); - void act(PartyMember actor, Action action); + void act(PartyMember actor, Action action); } diff --git a/mediator/src/main/java/com/iluwatar/mediator/PartyImpl.java b/mediator/src/main/java/com/iluwatar/mediator/PartyImpl.java index 5548f5163..f45c2869a 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/PartyImpl.java +++ b/mediator/src/main/java/com/iluwatar/mediator/PartyImpl.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mediator; import java.util.ArrayList; @@ -10,24 +32,24 @@ import java.util.List; */ public class PartyImpl implements Party { - private final List members; + private final List members; - public PartyImpl() { - members = new ArrayList<>(); - } + public PartyImpl() { + members = new ArrayList<>(); + } - @Override - public void act(PartyMember actor, Action action) { - for (PartyMember member : members) { - if (member != actor) { - member.partyAction(action); - } - } - } + @Override + public void act(PartyMember actor, Action action) { + for (PartyMember member : members) { + if (!member.equals(actor)) { + member.partyAction(action); + } + } + } - @Override - public void addMember(PartyMember member) { - members.add(member); - member.joinedParty(this); - } + @Override + public void addMember(PartyMember member) { + members.add(member); + member.joinedParty(this); + } } diff --git a/mediator/src/main/java/com/iluwatar/mediator/PartyMember.java b/mediator/src/main/java/com/iluwatar/mediator/PartyMember.java index fffe6dd39..1406ca889 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/PartyMember.java +++ b/mediator/src/main/java/com/iluwatar/mediator/PartyMember.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mediator; /** @@ -7,9 +29,9 @@ package com.iluwatar.mediator; */ public interface PartyMember { - void joinedParty(Party party); + void joinedParty(Party party); - void partyAction(Action action); + void partyAction(Action action); - void act(Action action); + void act(Action action); } diff --git a/mediator/src/main/java/com/iluwatar/mediator/PartyMemberBase.java b/mediator/src/main/java/com/iluwatar/mediator/PartyMemberBase.java index 7a151c355..7d3ca3dbf 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/PartyMemberBase.java +++ b/mediator/src/main/java/com/iluwatar/mediator/PartyMemberBase.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mediator; /** @@ -7,28 +29,28 @@ package com.iluwatar.mediator; */ public abstract class PartyMemberBase implements PartyMember { - protected Party party; + protected Party party; - @Override - public void joinedParty(Party party) { - System.out.println(this + " joins the party"); - this.party = party; - } + @Override + public void joinedParty(Party party) { + System.out.println(this + " joins the party"); + this.party = party; + } - @Override - public void partyAction(Action action) { - System.out.println(this + " " + action.getDescription()); - } + @Override + public void partyAction(Action action) { + System.out.println(this + " " + action.getDescription()); + } - @Override - public void act(Action action) { - if (party != null) { - System.out.println(this + " " + action.toString()); - party.act(this, action); - } - } + @Override + public void act(Action action) { + if (party != null) { + System.out.println(this + " " + action.toString()); + party.act(this, action); + } + } - @Override - public abstract String toString(); + @Override + public abstract String toString(); } diff --git a/mediator/src/main/java/com/iluwatar/mediator/Rogue.java b/mediator/src/main/java/com/iluwatar/mediator/Rogue.java index 69974066f..b15d81d51 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/Rogue.java +++ b/mediator/src/main/java/com/iluwatar/mediator/Rogue.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mediator; /** @@ -7,9 +29,9 @@ package com.iluwatar.mediator; */ public class Rogue extends PartyMemberBase { - @Override - public String toString() { - return "Rogue"; - } + @Override + public String toString() { + return "Rogue"; + } } diff --git a/mediator/src/main/java/com/iluwatar/mediator/Wizard.java b/mediator/src/main/java/com/iluwatar/mediator/Wizard.java index d4d2594d7..6f9d4433b 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/Wizard.java +++ b/mediator/src/main/java/com/iluwatar/mediator/Wizard.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mediator; /** @@ -7,9 +29,9 @@ package com.iluwatar.mediator; */ public class Wizard extends PartyMemberBase { - @Override - public String toString() { - return "Wizard"; - } + @Override + public String toString() { + return "Wizard"; + } } diff --git a/mediator/src/test/java/com/iluwatar/mediator/AppTest.java b/mediator/src/test/java/com/iluwatar/mediator/AppTest.java index 2b614b003..7691e11b0 100644 --- a/mediator/src/test/java/com/iluwatar/mediator/AppTest.java +++ b/mediator/src/test/java/com/iluwatar/mediator/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mediator; import org.junit.Test; -import com.iluwatar.mediator.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.mediator.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/mediator/src/test/java/com/iluwatar/mediator/PartyImplTest.java b/mediator/src/test/java/com/iluwatar/mediator/PartyImplTest.java new file mode 100644 index 000000000..7e9181fd7 --- /dev/null +++ b/mediator/src/test/java/com/iluwatar/mediator/PartyImplTest.java @@ -0,0 +1,63 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mediator; + +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/19/15 - 10:00 PM + * + * @author Jeroen Meulemeester + */ +public class PartyImplTest { + + /** + * Verify if a member is notified when it's joining a party. Generate an action and see if the + * other member gets it. Also check members don't get their own actions. + */ + @Test + public void testPartyAction() { + final PartyMember partyMember1 = mock(PartyMember.class); + final PartyMember partyMember2 = mock(PartyMember.class); + + final PartyImpl party = new PartyImpl(); + party.addMember(partyMember1); + party.addMember(partyMember2); + + verify(partyMember1).joinedParty(party); + verify(partyMember2).joinedParty(party); + + party.act(partyMember1, Action.GOLD); + verifyZeroInteractions(partyMember1); + verify(partyMember2).partyAction(Action.GOLD); + + verifyNoMoreInteractions(partyMember1, partyMember2); + + } + +} diff --git a/mediator/src/test/java/com/iluwatar/mediator/PartyMemberTest.java b/mediator/src/test/java/com/iluwatar/mediator/PartyMemberTest.java new file mode 100644 index 000000000..0bf43f9cd --- /dev/null +++ b/mediator/src/test/java/com/iluwatar/mediator/PartyMemberTest.java @@ -0,0 +1,150 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mediator; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.PrintStream; +import java.util.Arrays; +import java.util.Collection; +import java.util.function.Supplier; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/19/15 - 10:13 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class PartyMemberTest { + + @Parameterized.Parameters + public static Collection[]> data() { + return Arrays.asList( + new Supplier[]{Hobbit::new}, + new Supplier[]{Hunter::new}, + new Supplier[]{Rogue::new}, + new Supplier[]{Wizard::new} + ); + } + + /** + * The mocked standard out {@link PrintStream}, required since some actions on a {@link + * PartyMember} have any influence on any other accessible objects, except for writing to std-out + * using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * The factory, used to create a new instance of the tested party member + */ + private final Supplier memberSupplier; + + /** + * Create a new test instance, using the given {@link PartyMember} factory + * + * @param memberSupplier The party member factory + */ + public PartyMemberTest(final Supplier memberSupplier) { + this.memberSupplier = memberSupplier; + } + + /** + * Verify if a party action triggers the correct output to the std-Out + */ + @Test + public void testPartyAction() { + final PartyMember member = this.memberSupplier.get(); + + for (final Action action : Action.values()) { + member.partyAction(action); + verify(this.stdOutMock).println(member.toString() + " " + action.getDescription()); + } + + verifyNoMoreInteractions(this.stdOutMock); + } + + /** + * Verify if a member action triggers the expected interactions with the party class + */ + @Test + public void testAct() { + final PartyMember member = this.memberSupplier.get(); + + member.act(Action.GOLD); + verifyZeroInteractions(this.stdOutMock); + + final Party party = mock(Party.class); + member.joinedParty(party); + verify(this.stdOutMock).println(member.toString() + " joins the party"); + + for (final Action action : Action.values()) { + member.act(action); + verify(this.stdOutMock).println(member.toString() + " " + action.toString()); + verify(party).act(member, action); + } + + verifyNoMoreInteractions(party, this.stdOutMock); + } + + /** + * Verify if {@link PartyMember#toString()} generate the expected output + */ + @Test + public void testToString() throws Exception { + final PartyMember member = this.memberSupplier.get(); + final Class memberClass = member.getClass(); + assertEquals(memberClass.getSimpleName(), member.toString()); + } + +} diff --git a/memento/index.md b/memento/README.md similarity index 78% rename from memento/index.md rename to memento/README.md index 056af8b26..463b5fec0 100644 --- a/memento/index.md +++ b/memento/README.md @@ -7,22 +7,28 @@ categories: Behavioral tags: - Java - Gang Of Four + - Difficulty-Intermediate --- -**Intent:** Without violating encapsulation, capture and externalize an +## Also known as +Token + +## Intent +Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later. ![alt text](./etc/memento.png "Memento") -**Applicability:** Use the Memento pattern when +## Applicability +Use the Memento pattern when * a snapshot of an object's state must be saved so that it can be restored to that state later, and * a direct interface to obtaining the state would expose implementation details and break the object's encapsulation -**Real world examples:** +## Real world examples * [java.util.Date](http://docs.oracle.com/javase/8/docs/api/java/util/Date.html) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/memento/pom.xml b/memento/pom.xml index 98d1d4a9f..a320f186c 100644 --- a/memento/pom.xml +++ b/memento/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT memento diff --git a/memento/src/main/java/com/iluwatar/memento/App.java b/memento/src/main/java/com/iluwatar/memento/App.java index 71d0ed466..e1ee349b1 100644 --- a/memento/src/main/java/com/iluwatar/memento/App.java +++ b/memento/src/main/java/com/iluwatar/memento/App.java @@ -1,49 +1,73 @@ +/** + * The MIT License + * Copyright (c) 2014 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.memento; import java.util.Stack; /** * - * The Memento pattern is a software design pattern that provides the ability to restore - * an object to its previous state (undo via rollback). + * The Memento pattern is a software design pattern that provides the ability to restore an object + * to its previous state (undo via rollback). *

- * The Memento pattern is implemented with three objects: the originator, a caretaker and - * a memento. The originator is some object that has an internal state. The caretaker is - * going to do something to the originator, but wants to be able to undo the change. The - * caretaker first asks the originator for a memento object. Then it does whatever operation - * (or sequence of operations) it was going to do. To roll back to the state before the - * operations, it returns the memento object to the originator. The memento object itself - * is an opaque object (one which the caretaker cannot, or should not, change). When using - * this pattern, care should be taken if the originator may change other objects or - * resources - the memento pattern operates on a single object. + * The Memento pattern is implemented with three objects: the originator, a caretaker and a memento. + * The originator is some object that has an internal state. The caretaker is going to do something + * to the originator, but wants to be able to undo the change. The caretaker first asks the + * originator for a memento object. Then it does whatever operation (or sequence of operations) it + * was going to do. To roll back to the state before the operations, it returns the memento object + * to the originator. The memento object itself is an opaque object (one which the caretaker cannot, + * or should not, change). When using this pattern, care should be taken if the originator may + * change other objects or resources - the memento pattern operates on a single object. *

- * 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 the state. + * 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 + * the state. * */ public class App { - public static void main(String[] args) { - Stack states = new Stack<>(); + /** + * Program entry point + */ + public static void main(String[] args) { + Stack states = new Stack<>(); - Star star = new Star(StarType.SUN, 10000000, 500000); - System.out.println(star); - states.add(star.getMemento()); - star.timePasses(); - System.out.println(star); - states.add(star.getMemento()); - star.timePasses(); - System.out.println(star); - states.add(star.getMemento()); - star.timePasses(); - System.out.println(star); - states.add(star.getMemento()); - star.timePasses(); - System.out.println(star); - while (states.size() > 0) { - star.setMemento(states.pop()); - System.out.println(star); - } - } + Star star = new Star(StarType.SUN, 10000000, 500000); + System.out.println(star); + states.add(star.getMemento()); + star.timePasses(); + System.out.println(star); + states.add(star.getMemento()); + star.timePasses(); + System.out.println(star); + states.add(star.getMemento()); + star.timePasses(); + System.out.println(star); + states.add(star.getMemento()); + star.timePasses(); + System.out.println(star); + while (states.size() > 0) { + star.setMemento(states.pop()); + System.out.println(star); + } + } } diff --git a/memento/src/main/java/com/iluwatar/memento/Star.java b/memento/src/main/java/com/iluwatar/memento/Star.java index 43b5c5c3c..d2df823a0 100644 --- a/memento/src/main/java/com/iluwatar/memento/Star.java +++ b/memento/src/main/java/com/iluwatar/memento/Star.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.memento; /** @@ -7,99 +29,104 @@ package com.iluwatar.memento; */ public class Star { - private StarType type; - private int ageYears; - private int massTons; + private StarType type; + private int ageYears; + private int massTons; - public Star(StarType startType, int startAge, int startMass) { - this.type = startType; - this.ageYears = startAge; - this.massTons = startMass; - } + /** + * Constructor + */ + public Star(StarType startType, int startAge, int startMass) { + this.type = startType; + this.ageYears = startAge; + this.massTons = startMass; + } - public void timePasses() { - ageYears *= 2; - massTons *= 8; - switch (type) { - case RED_GIANT: - type = StarType.WHITE_DWARF; - break; - case SUN: - type = StarType.RED_GIANT; - break; - case SUPERNOVA: - type = StarType.DEAD; - break; - case WHITE_DWARF: - type = StarType.SUPERNOVA; - break; - case DEAD: - ageYears *= 2; - massTons = 0; - break; - default: - break; - } - } + /** + * Makes time pass for the star + */ + public void timePasses() { + ageYears *= 2; + massTons *= 8; + switch (type) { + case RED_GIANT: + type = StarType.WHITE_DWARF; + break; + case SUN: + type = StarType.RED_GIANT; + break; + case SUPERNOVA: + type = StarType.DEAD; + break; + case WHITE_DWARF: + type = StarType.SUPERNOVA; + break; + case DEAD: + ageYears *= 2; + massTons = 0; + break; + default: + break; + } + } - StarMemento getMemento() { + StarMemento getMemento() { - StarMementoInternal state = new StarMementoInternal(); - state.setAgeYears(ageYears); - state.setMassTons(massTons); - state.setType(type); - return state; + StarMementoInternal state = new StarMementoInternal(); + state.setAgeYears(ageYears); + state.setMassTons(massTons); + state.setType(type); + return state; - } + } - void setMemento(StarMemento memento) { + void setMemento(StarMemento memento) { - StarMementoInternal state = (StarMementoInternal) memento; - this.type = state.getType(); - this.ageYears = state.getAgeYears(); - this.massTons = state.getMassTons(); + StarMementoInternal state = (StarMementoInternal) memento; + this.type = state.getType(); + this.ageYears = state.getAgeYears(); + this.massTons = state.getMassTons(); - } + } - @Override - public String toString() { - return String.format("%s age: %d years mass: %d tons", type.toString(), - ageYears, massTons); - } - - /** - * - * StarMemento implementation - * - */ - private static class StarMementoInternal implements StarMemento { + @Override + public String toString() { + return String.format("%s age: %d years mass: %d tons", type.toString(), ageYears, massTons); + } - private StarType type; - private int ageYears; - private int massTons; + /** + * + * StarMemento implementation + * + */ + private static class StarMementoInternal implements StarMemento { - public StarType getType() { - return type; - } + private StarType type; + private int ageYears; + private int massTons; - public void setType(StarType type) { - this.type = type; - } + public StarType getType() { + return type; + } - public int getAgeYears() { - return ageYears; - } + public void setType(StarType type) { + this.type = type; + } - public void setAgeYears(int ageYears) { - this.ageYears = ageYears; - } + public int getAgeYears() { + return ageYears; + } - public int getMassTons() { - return massTons; - } + public void setAgeYears(int ageYears) { + this.ageYears = ageYears; + } - public void setMassTons(int massTons) { - this.massTons = massTons; - } - } + public int getMassTons() { + return massTons; + } + + public void setMassTons(int massTons) { + this.massTons = massTons; + } + } } diff --git a/memento/src/main/java/com/iluwatar/memento/StarMemento.java b/memento/src/main/java/com/iluwatar/memento/StarMemento.java index a60f1f058..5f66b2939 100644 --- a/memento/src/main/java/com/iluwatar/memento/StarMemento.java +++ b/memento/src/main/java/com/iluwatar/memento/StarMemento.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.memento; /** diff --git a/memento/src/main/java/com/iluwatar/memento/StarType.java b/memento/src/main/java/com/iluwatar/memento/StarType.java index bae853097..ffd2f1adb 100644 --- a/memento/src/main/java/com/iluwatar/memento/StarType.java +++ b/memento/src/main/java/com/iluwatar/memento/StarType.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.memento; /** @@ -7,16 +29,17 @@ package com.iluwatar.memento; */ public enum StarType { - SUN("sun"), RED_GIANT("red giant"), WHITE_DWARF("white dwarf"), SUPERNOVA("supernova"), DEAD("dead star"), UNDEFINED(""); + SUN("sun"), RED_GIANT("red giant"), WHITE_DWARF("white dwarf"), SUPERNOVA("supernova"), DEAD( + "dead star"), UNDEFINED(""); - private String title; + private String title; - StarType(String title) { - this.title = title; - } + StarType(String title) { + this.title = title; + } - @Override - public String toString() { - return title; - } + @Override + public String toString() { + return title; + } } diff --git a/memento/src/test/java/com/iluwatar/memento/AppTest.java b/memento/src/test/java/com/iluwatar/memento/AppTest.java index 84afd5945..cfdce9471 100644 --- a/memento/src/test/java/com/iluwatar/memento/AppTest.java +++ b/memento/src/test/java/com/iluwatar/memento/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.memento; import org.junit.Test; -import com.iluwatar.memento.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.memento.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/memento/src/test/java/com/iluwatar/memento/StarTest.java b/memento/src/test/java/com/iluwatar/memento/StarTest.java new file mode 100644 index 000000000..8f81fcfa7 --- /dev/null +++ b/memento/src/test/java/com/iluwatar/memento/StarTest.java @@ -0,0 +1,97 @@ +/** + * The MIT License + * Copyright (c) 2014 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.memento; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Date: 12/20/15 - 10:08 AM + * + * @author Jeroen Meulemeester + */ +public class StarTest { + + /** + * Verify the stages of a dying sun, without going back in time + */ + @Test + public void testTimePasses() { + final Star star = new Star(StarType.SUN, 1, 2); + assertEquals("sun age: 1 years mass: 2 tons", star.toString()); + + star.timePasses(); + assertEquals("red giant age: 2 years mass: 16 tons", star.toString()); + + star.timePasses(); + assertEquals("white dwarf age: 4 years mass: 128 tons", star.toString()); + + star.timePasses(); + assertEquals("supernova age: 8 years mass: 1024 tons", star.toString()); + + star.timePasses(); + assertEquals("dead star age: 16 years mass: 8192 tons", star.toString()); + + star.timePasses(); + assertEquals("dead star age: 64 years mass: 0 tons", star.toString()); + + star.timePasses(); + assertEquals("dead star age: 256 years mass: 0 tons", star.toString()); + } + + /** + * Verify some stage of a dying sun, but go back in time to test the memento + */ + @Test + public void testSetMemento() { + final Star star = new Star(StarType.SUN, 1, 2); + final StarMemento firstMemento = star.getMemento(); + assertEquals("sun age: 1 years mass: 2 tons", star.toString()); + + star.timePasses(); + final StarMemento secondMemento = star.getMemento(); + assertEquals("red giant age: 2 years mass: 16 tons", star.toString()); + + star.timePasses(); + final StarMemento thirdMemento = star.getMemento(); + assertEquals("white dwarf age: 4 years mass: 128 tons", star.toString()); + + star.timePasses(); + assertEquals("supernova age: 8 years mass: 1024 tons", star.toString()); + + star.setMemento(thirdMemento); + assertEquals("white dwarf age: 4 years mass: 128 tons", star.toString()); + + star.timePasses(); + assertEquals("supernova age: 8 years mass: 1024 tons", star.toString()); + + star.setMemento(secondMemento); + assertEquals("red giant age: 2 years mass: 16 tons", star.toString()); + + star.setMemento(firstMemento); + assertEquals("sun age: 1 years mass: 2 tons", star.toString()); + + } + +} diff --git a/message-channel/index.md b/message-channel/README.md similarity index 61% rename from message-channel/index.md rename to message-channel/README.md index b5fb1761a..8aebd0157 100644 --- a/message-channel/index.md +++ b/message-channel/README.md @@ -4,18 +4,23 @@ title: Message Channel folder: message-channel permalink: /patterns/message-channel/ categories: Integration -tags: Java +tags: + - Java + - EIP + - Apache Camel™ --- -**Intent:** When two applications communicate using a messaging system they do it by using logical addresses +## Intent +When two applications communicate using a messaging system they do it by using logical addresses of the system, so called Message Channels. ![alt text](./etc/message-channel.png "Message Channel") -**Applicability:** Use the Message Channel pattern when +## Applicability +Use the Message Channel pattern when * two or more applications need to communicate using a messaging system -**Real world examples:** +## Real world examples -* [akka-camel](http://doc.akka.io/docs/akka/snapshot/scala/camel.html) +* [akka-camel](http://doc.akka.io/docs/akka/snapshot/scala/camel.html) \ No newline at end of file diff --git a/message-channel/pom.xml b/message-channel/pom.xml index ee7d54c95..cbeba7a75 100644 --- a/message-channel/pom.xml +++ b/message-channel/pom.xml @@ -1,4 +1,28 @@ + @@ -6,7 +30,7 @@ com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT message-channel diff --git a/message-channel/src/main/java/com/iluwatar/message/channel/App.java b/message-channel/src/main/java/com/iluwatar/message/channel/App.java index f9a334a8a..dab04bd37 100644 --- a/message-channel/src/main/java/com/iluwatar/message/channel/App.java +++ b/message-channel/src/main/java/com/iluwatar/message/channel/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.message.channel; import org.apache.camel.CamelContext; @@ -6,48 +28,45 @@ import org.apache.camel.impl.DefaultCamelContext; /** * - * When two applications communicate with each other using a messaging system - * they first need to establish a communication channel that will carry the - * data. Message Channel decouples Message producers and consumers. + * When two applications communicate with each other using a messaging system they first need to + * establish a communication channel that will carry the data. Message Channel decouples Message + * producers and consumers. *

- * The sending application doesn't necessarily know what particular application - * will end up retrieving it, but it can be assured that the application that - * retrieves the information is interested in that information. This is because - * the messaging system has different Message Channels for different types of - * information the applications want to communicate. When an application sends - * information, it doesn't randomly add the information to any channel available; - * it adds it to a channel whose specific purpose is to communicate that sort of - * information. Likewise, an application that wants to receive particular information - * doesn't pull info off some random channel; it selects what channel to get information - * from based on what type of information it wants. + * The sending application doesn't necessarily know what particular application will end up + * retrieving it, but it can be assured that the application that retrieves the information is + * interested in that information. This is because the messaging system has different Message + * Channels for different types of information the applications want to communicate. When an + * application sends information, it doesn't randomly add the information to any channel available; + * it adds it to a channel whose specific purpose is to communicate that sort of information. + * Likewise, an application that wants to receive particular information doesn't pull info off some + * random channel; it selects what channel to get information from based on what type of information + * it wants. *

- * In this example we use Apache Camel to establish two different Message Channels. The first - * one reads from standard input and delivers messages to Direct endpoint. The second Message - * Channel is established from the Direct component to console output. No actual messages are sent, - * only the established routes are printed to standard output. + * In this example we use Apache Camel to establish two different Message Channels. The first one + * reads from standard input and delivers messages to Direct endpoint. The second Message Channel is + * established from the Direct component to console output. No actual messages are sent, only the + * established routes are printed to standard output. * */ public class App { - /** - * Program entry point - * @param args command line args - * @throws Exception - */ - public static void main(String[] args) throws Exception { - CamelContext context = new DefaultCamelContext(); - - context.addRoutes(new RouteBuilder() { + /** + * Program entry point + */ + public static void main(String[] args) throws Exception { + CamelContext context = new DefaultCamelContext(); - @Override - public void configure() throws Exception { - from("stream:in").to("direct:greetings"); - from("direct:greetings").to("stream:out"); - } - }); - - context.start(); - context.getRoutes().stream().forEach((r) -> System.out.println(r)); - context.stop(); - } + context.addRoutes(new RouteBuilder() { + + @Override + public void configure() throws Exception { + from("stream:in").to("direct:greetings"); + from("direct:greetings").to("stream:out"); + } + }); + + context.start(); + context.getRoutes().stream().forEach(r -> System.out.println(r)); + context.stop(); + } } diff --git a/message-channel/src/test/java/com/iluwatar/message/channel/AppTest.java b/message-channel/src/test/java/com/iluwatar/message/channel/AppTest.java index fa3af1161..ace457c16 100644 --- a/message-channel/src/test/java/com/iluwatar/message/channel/AppTest.java +++ b/message-channel/src/test/java/com/iluwatar/message/channel/AppTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.message.channel; import org.junit.Test; @@ -8,10 +30,10 @@ import org.junit.Test; * */ public class AppTest { - - @Test - public void test() throws Exception { - String[] args = {}; - App.main(args); - } + + @Test + public void test() throws Exception { + String[] args = {}; + App.main(args); + } } diff --git a/model-view-controller/index.md b/model-view-controller/README.md similarity index 81% rename from model-view-controller/index.md rename to model-view-controller/README.md index 1ba1089c0..bc96f7ab1 100644 --- a/model-view-controller/index.md +++ b/model-view-controller/README.md @@ -4,21 +4,25 @@ title: Model-View-Controller folder: model-view-controller permalink: /patterns/model-view-controller/ categories: Presentation Tier -tags: Java +tags: + - Java + - Difficulty-Intermediate --- -**Intent:** Separate the user interface into three interconnected components: +## Intent +Separate the user interface into three interconnected components: the model, the view and the controller. Let the model manage the data, the view display the data and the controller mediate updating the data and redrawing the display. ![alt text](./etc/model-view-controller.png "Model-View-Controller") -**Applicability:** Use the Model-View-Controller pattern when +## Applicability +Use the Model-View-Controller pattern when * you want to clearly separate the domain data from its user interface representation -**Credits:** +## Credits * [Trygve Reenskaug - Model-view-controller](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) * [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) diff --git a/model-view-controller/pom.xml b/model-view-controller/pom.xml index 8c9c07809..3c01f6e8d 100644 --- a/model-view-controller/pom.xml +++ b/model-view-controller/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT model-view-controller @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/App.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/App.java index 0ba34b5d4..4a3d2b41e 100644 --- a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/App.java +++ b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.model.view.controller; /** @@ -5,34 +27,37 @@ package com.iluwatar.model.view.controller; * 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. *

- * 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. + * 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. *

- * 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. + * 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 { - - /** - * Program entry point - * @param args command line args - */ - public static void main( String[] args ) { - // create model, view and controller - GiantModel giant = new GiantModel(Health.HEALTHY, Fatigue.ALERT, Nourishment.SATURATED); - GiantView view = new GiantView(); - GiantController controller = new GiantController(giant, view); - // initial display - controller.updateView(); - // controller receives some interactions that affect the giant - controller.setHealth(Health.WOUNDED); - controller.setNourishment(Nourishment.HUNGRY); - controller.setFatigue(Fatigue.TIRED); - // redisplay - controller.updateView(); - } + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + // create model, view and controller + GiantModel giant = new GiantModel(Health.HEALTHY, Fatigue.ALERT, Nourishment.SATURATED); + GiantView view = new GiantView(); + GiantController controller = new GiantController(giant, view); + // initial display + controller.updateView(); + // controller receives some interactions that affect the giant + controller.setHealth(Health.WOUNDED); + controller.setNourishment(Nourishment.HUNGRY); + controller.setFatigue(Fatigue.TIRED); + // redisplay + controller.updateView(); + } } diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Fatigue.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Fatigue.java index d19df6a8c..38d3030e0 100644 --- a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Fatigue.java +++ b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Fatigue.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.model.view.controller; /** @@ -7,16 +29,16 @@ package com.iluwatar.model.view.controller; */ public enum Fatigue { - ALERT("alert"), TIRED("tired"), SLEEPING("sleeping"); - - private String title; - - Fatigue(String title) { - this.title = title; - } + ALERT("alert"), TIRED("tired"), SLEEPING("sleeping"); - @Override - public String toString() { - return title; - } + private String title; + + Fatigue(String title) { + this.title = title; + } + + @Override + public String toString() { + return title; + } } diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantController.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantController.java index acda727d9..e291e682c 100644 --- a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantController.java +++ b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantController.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.model.view.controller; /** @@ -7,39 +29,39 @@ package com.iluwatar.model.view.controller; */ public class GiantController { - private GiantModel giant; - private GiantView view; + private GiantModel giant; + private GiantView view; - public GiantController(GiantModel giant, GiantView view) { - this.giant = giant; - this.view = view; - } - - public Health getHealth() { - return giant.getHealth(); - } + public GiantController(GiantModel giant, GiantView view) { + this.giant = giant; + this.view = view; + } - public void setHealth(Health health) { - this.giant.setHealth(health); - } + public Health getHealth() { + return giant.getHealth(); + } - public Fatigue getFatigue() { - return giant.getFatigue(); - } + public void setHealth(Health health) { + this.giant.setHealth(health); + } - public void setFatigue(Fatigue fatigue) { - this.giant.setFatigue(fatigue); - } + public Fatigue getFatigue() { + return giant.getFatigue(); + } - public Nourishment getNourishment() { - return giant.getNourishment(); - } + public void setFatigue(Fatigue fatigue) { + this.giant.setFatigue(fatigue); + } - public void setNourishment(Nourishment nourishment) { - this.giant.setNourishment(nourishment); - } - - public void updateView() { - this.view.displayGiant(giant); - } + public Nourishment getNourishment() { + return giant.getNourishment(); + } + + public void setNourishment(Nourishment nourishment) { + this.giant.setNourishment(nourishment); + } + + public void updateView() { + this.view.displayGiant(giant); + } } diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantModel.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantModel.java index 97c298768..9b9303de7 100644 --- a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantModel.java +++ b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantModel.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.model.view.controller; /** @@ -6,43 +28,43 @@ package com.iluwatar.model.view.controller; * */ public class GiantModel { - - private Health health; - private Fatigue fatigue; - private Nourishment nourishment; - GiantModel(Health health, Fatigue fatigue, Nourishment nourishment) { - this.health = health; - this.fatigue = fatigue; - this.nourishment = nourishment; - } + private Health health; + private Fatigue fatigue; + private Nourishment nourishment; - public Health getHealth() { - return health; - } + GiantModel(Health health, Fatigue fatigue, Nourishment nourishment) { + this.health = health; + this.fatigue = fatigue; + this.nourishment = nourishment; + } - public void setHealth(Health health) { - this.health = health; - } + public Health getHealth() { + return health; + } - public Fatigue getFatigue() { - return fatigue; - } + public void setHealth(Health health) { + this.health = health; + } - public void setFatigue(Fatigue fatigue) { - this.fatigue = fatigue; - } + public Fatigue getFatigue() { + return fatigue; + } - public Nourishment getNourishment() { - return nourishment; - } + public void setFatigue(Fatigue fatigue) { + this.fatigue = fatigue; + } - public void setNourishment(Nourishment nourishment) { - this.nourishment = nourishment; - } - - @Override - public String toString() { - return String.format("The giant looks %s, %s and %s.", health, fatigue, nourishment); - } + public Nourishment getNourishment() { + return nourishment; + } + + public void setNourishment(Nourishment nourishment) { + this.nourishment = nourishment; + } + + @Override + public String toString() { + return String.format("The giant looks %s, %s and %s.", health, fatigue, nourishment); + } } diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantView.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantView.java index 1cf5e20a6..dd4361487 100644 --- a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantView.java +++ b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantView.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.model.view.controller; /** @@ -7,7 +29,7 @@ package com.iluwatar.model.view.controller; */ public class GiantView { - public void displayGiant(GiantModel giant) { - System.out.println(giant); - } + public void displayGiant(GiantModel giant) { + System.out.println(giant); + } } diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Health.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Health.java index feaec81d7..4efe0100b 100644 --- a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Health.java +++ b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Health.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.model.view.controller; /** @@ -6,17 +28,17 @@ package com.iluwatar.model.view.controller; * */ public enum Health { - - HEALTHY("healthy"), WOUNDED("wounded"), DEAD("dead"); - - private String title; - - Health(String title) { - this.title = title; - } - @Override - public String toString() { - return title; - } + HEALTHY("healthy"), WOUNDED("wounded"), DEAD("dead"); + + private String title; + + Health(String title) { + this.title = title; + } + + @Override + public String toString() { + return title; + } } diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Nourishment.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Nourishment.java index 7bab931b6..e2d41ccf8 100644 --- a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Nourishment.java +++ b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/Nourishment.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.model.view.controller; /** @@ -7,16 +29,16 @@ package com.iluwatar.model.view.controller; */ public enum Nourishment { - SATURATED("saturated"), HUNGRY("hungry"), STARVING("starving"); - - private String title; - - Nourishment(String title) { - this.title = title; - } + SATURATED("saturated"), HUNGRY("hungry"), STARVING("starving"); - @Override - public String toString() { - return title; - } + private String title; + + Nourishment(String title) { + this.title = title; + } + + @Override + public String toString() { + return title; + } } diff --git a/model-view-controller/src/test/java/com/iluwatar/model/view/controller/AppTest.java b/model-view-controller/src/test/java/com/iluwatar/model/view/controller/AppTest.java index 4bb31f6e6..aad850d69 100644 --- a/model-view-controller/src/test/java/com/iluwatar/model/view/controller/AppTest.java +++ b/model-view-controller/src/test/java/com/iluwatar/model/view/controller/AppTest.java @@ -1,19 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 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.model.view.controller; import org.junit.Test; -import com.iluwatar.model.view.controller.App; - /** * * Application test * */ public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } + + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/model-view-controller/src/test/java/com/iluwatar/model/view/controller/GiantControllerTest.java b/model-view-controller/src/test/java/com/iluwatar/model/view/controller/GiantControllerTest.java new file mode 100644 index 000000000..7e0532d30 --- /dev/null +++ b/model-view-controller/src/test/java/com/iluwatar/model/view/controller/GiantControllerTest.java @@ -0,0 +1,122 @@ +/** + * The MIT License + * Copyright (c) 2014 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.model.view.controller; + +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/20/15 - 2:19 PM + * + * @author Jeroen Meulemeester + */ +public class GiantControllerTest { + + /** + * Verify if the controller passes the health level through to the model and vice versa + */ + @Test + public void testSetHealth() { + final GiantModel model = mock(GiantModel.class); + final GiantView view = mock(GiantView.class); + final GiantController controller = new GiantController(model, view); + + verifyZeroInteractions(model, view); + + for (final Health health : Health.values()) { + controller.setHealth(health); + verify(model).setHealth(health); + verifyZeroInteractions(view); + } + + controller.getHealth(); + verify(model).getHealth(); + + verifyNoMoreInteractions(model, view); + } + + /** + * Verify if the controller passes the fatigue level through to the model and vice versa + */ + @Test + public void testSetFatigue() { + final GiantModel model = mock(GiantModel.class); + final GiantView view = mock(GiantView.class); + final GiantController controller = new GiantController(model, view); + + verifyZeroInteractions(model, view); + + for (final Fatigue fatigue : Fatigue.values()) { + controller.setFatigue(fatigue); + verify(model).setFatigue(fatigue); + verifyZeroInteractions(view); + } + + controller.getFatigue(); + verify(model).getFatigue(); + + verifyNoMoreInteractions(model, view); + } + + /** + * Verify if the controller passes the nourishment level through to the model and vice versa + */ + @Test + public void testSetNourishment() { + final GiantModel model = mock(GiantModel.class); + final GiantView view = mock(GiantView.class); + final GiantController controller = new GiantController(model, view); + + verifyZeroInteractions(model, view); + + for (final Nourishment nourishment : Nourishment.values()) { + controller.setNourishment(nourishment); + verify(model).setNourishment(nourishment); + verifyZeroInteractions(view); + } + + controller.getNourishment(); + verify(model).getNourishment(); + + verifyNoMoreInteractions(model, view); + } + + @Test + public void testUpdateView() { + final GiantModel model = mock(GiantModel.class); + final GiantView view = mock(GiantView.class); + final GiantController controller = new GiantController(model, view); + + verifyZeroInteractions(model, view); + + controller.updateView(); + verify(view).displayGiant(model); + + verifyNoMoreInteractions(model, view); + } + +} \ No newline at end of file diff --git a/model-view-controller/src/test/java/com/iluwatar/model/view/controller/GiantModelTest.java b/model-view-controller/src/test/java/com/iluwatar/model/view/controller/GiantModelTest.java new file mode 100644 index 000000000..8e322fc59 --- /dev/null +++ b/model-view-controller/src/test/java/com/iluwatar/model/view/controller/GiantModelTest.java @@ -0,0 +1,78 @@ +/** + * The MIT License + * Copyright (c) 2014 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.model.view.controller; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Date: 12/20/15 - 2:10 PM + * + * @author Jeroen Meulemeester + */ +public class GiantModelTest { + + /** + * Verify if the health value is set properly though the constructor and setter + */ + @Test + public void testSetHealth() { + final GiantModel model = new GiantModel(Health.HEALTHY, Fatigue.ALERT, Nourishment.SATURATED); + assertEquals(Health.HEALTHY, model.getHealth()); + for (final Health health : Health.values()) { + model.setHealth(health); + assertEquals(health, model.getHealth()); + assertEquals("The giant looks " + health.toString() + ", alert and saturated.", model.toString()); + } + } + + /** + * Verify if the fatigue level is set properly though the constructor and setter + */ + @Test + public void testSetFatigue() { + final GiantModel model = new GiantModel(Health.HEALTHY, Fatigue.ALERT, Nourishment.SATURATED); + assertEquals(Fatigue.ALERT, model.getFatigue()); + for (final Fatigue fatigue : Fatigue.values()) { + model.setFatigue(fatigue); + assertEquals(fatigue, model.getFatigue()); + assertEquals("The giant looks healthy, " + fatigue.toString() + " and saturated.", model.toString()); + } + } + + /** + * Verify if the nourishment level is set properly though the constructor and setter + */ + @Test + public void testSetNourishment() { + final GiantModel model = new GiantModel(Health.HEALTHY, Fatigue.ALERT, Nourishment.SATURATED); + assertEquals(Nourishment.SATURATED, model.getNourishment()); + for (final Nourishment nourishment : Nourishment.values()) { + model.setNourishment(nourishment); + assertEquals(nourishment, model.getNourishment()); + assertEquals("The giant looks healthy, alert and " + nourishment.toString() + ".", model.toString()); + } + } + +} diff --git a/model-view-controller/src/test/java/com/iluwatar/model/view/controller/GiantViewTest.java b/model-view-controller/src/test/java/com/iluwatar/model/view/controller/GiantViewTest.java new file mode 100644 index 000000000..89d503d4e --- /dev/null +++ b/model-view-controller/src/test/java/com/iluwatar/model/view/controller/GiantViewTest.java @@ -0,0 +1,86 @@ +/** + * The MIT License + * Copyright (c) 2014 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.model.view.controller; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/20/15 - 2:04 PM + * + * @author Jeroen Meulemeester + */ +public class GiantViewTest { + + /** + * The mocked standard out {@link PrintStream}, required since the actions of the views don't have + * any influence on any other accessible objects, except for writing to std-out using {@link + * System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Verify if the {@link GiantView} does what it has to do: Print the {@link GiantModel} to the + * standard out stream, nothing more, nothing less. + */ + @Test + public void testDisplayGiant() { + final GiantView view = new GiantView(); + + final GiantModel model = mock(GiantModel.class); + view.displayGiant(model); + + verify(this.stdOutMock).println(model); + verifyNoMoreInteractions(model, this.stdOutMock); + + } + +} \ No newline at end of file diff --git a/model-view-presenter/index.md b/model-view-presenter/README.md similarity index 70% rename from model-view-presenter/index.md rename to model-view-presenter/README.md index b51268013..a3b921ce4 100644 --- a/model-view-presenter/index.md +++ b/model-view-presenter/README.md @@ -4,20 +4,24 @@ title: Model-View-Presenter folder: model-view-presenter permalink: /patterns/model-view-presenter/ categories: Presentation Tier -tags: Java +tags: + - Java + - Difficulty-Intermediate --- -**Intent:** Apply a "Separation of Concerns" principle in a way that allows +## Intent +Apply a "Separation of Concerns" principle in a way that allows developers to build and test user interfaces. ![alt text](./etc/model-view-presenter_1.png "Model-View-Presenter") -**Applicability:** Use the Model-View-Presenter in any of the following +## Applicability +Use the Model-View-Presenter in any of the following situations * when you want to improve the "Separation of Concerns" principle in presentation logic * when a user interface development and testing is necessary. -**Real world examples:** +## Real world examples * [MVP4J](https://github.com/amineoualialami/mvp4j) diff --git a/model-view-presenter/etc/data/test.txt b/model-view-presenter/etc/data/test.txt index 997ae361a..85aa49612 100644 --- a/model-view-presenter/etc/data/test.txt +++ b/model-view-presenter/etc/data/test.txt @@ -1,2 +1,25 @@ +==== + The MIT License + Copyright (c) 2014 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. +==== + Test line 1 Test line 2 \ No newline at end of file diff --git a/model-view-presenter/pom.xml b/model-view-presenter/pom.xml index 4f2d320ba..0aad02b27 100644 --- a/model-view-presenter/pom.xml +++ b/model-view-presenter/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT model-view-presenter model-view-presenter diff --git a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/App.java b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/App.java index ac6ccf091..2c8985252 100644 --- a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/App.java +++ b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/App.java @@ -1,31 +1,54 @@ +/** + * The MIT License + * Copyright (c) 2014 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.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 + * "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). *

- * 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. + * 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. *

- * Finally, please notice the wiring between the Presenter and the View - * and between the Presenter and the Model. + * Finally, please notice the wiring between the Presenter and the View and between the Presenter + * and the Model. * */ public class App { - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { - FileLoader loader = new FileLoader(); - FileSelectorJFrame jFrame = new FileSelectorJFrame(); - FileSelectorPresenter presenter = new FileSelectorPresenter(jFrame); - presenter.setLoader(loader); - presenter.start(); - } + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + FileLoader loader = new FileLoader(); + FileSelectorJFrame jFrame = new FileSelectorJFrame(); + FileSelectorPresenter presenter = new FileSelectorPresenter(jFrame); + presenter.setLoader(loader); + presenter.start(); + } } diff --git a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileLoader.java b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileLoader.java index 96d843f83..b9e36fd00 100644 --- a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileLoader.java +++ b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileLoader.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.model.view.presenter; import java.io.BufferedReader; @@ -5,76 +27,74 @@ import java.io.File; import java.io.FileReader; /** - * Every instance of this class represents the Model component in the - * Model-View-Presenter architectural pattern. + * Every instance of this class represents the Model component in the Model-View-Presenter + * architectural pattern. *

* It is responsible for reading and loading the contents of a given file. */ public class FileLoader { - /** - * Indicates if the file is loaded or not. - */ - private boolean loaded = false; + /** + * Indicates if the file is loaded or not. + */ + private boolean loaded; - /** - * The name of the file that we want to load. - */ - private String fileName; + /** + * The name of the file that we want to load. + */ + private String fileName; - /** - * Loads the data of the file specified. - */ - public String loadData() { - try { - BufferedReader br = new BufferedReader(new FileReader(new File( - this.fileName))); - StringBuilder sb = new StringBuilder(); - String line; + /** + * Loads the data of the file specified. + */ + public String loadData() { + try { + BufferedReader br = new BufferedReader(new FileReader(new File(this.fileName))); + StringBuilder sb = new StringBuilder(); + String line; - while ((line = br.readLine()) != null) { - sb.append(line).append('\n'); - } + while ((line = br.readLine()) != null) { + sb.append(line).append('\n'); + } - this.loaded = true; - br.close(); + this.loaded = true; + br.close(); - return sb.toString(); - } + return sb.toString(); + } catch (Exception e) { + e.printStackTrace(); + } - catch (Exception e) { - e.printStackTrace(); - } + return null; + } - return null; - } + /** + * Sets the path of the file to be loaded, to the given value. + * + * @param fileName The path of the file to be loaded. + */ + public void setFileName(String fileName) { + this.fileName = fileName; + } - /** - * Sets the path of the file to be loaded, to the given value. - * @param fileName The path of the file to be loaded. - */ - public void setFileName(String fileName) { - this.fileName = fileName; - } + /** + * @return fileName The path of the file to be loaded. + */ + public String getFileName() { + return this.fileName; + } - /** - * @return fileName The path of the file to be loaded. - */ - public String getFileName() { - return this.fileName; - } + /** + * @return True, if the file given exists, false otherwise. + */ + public boolean fileExists() { + return new File(this.fileName).exists(); + } - /** - * @return True, if the file given exists, false otherwise. - */ - public boolean fileExists() { - return new File(this.fileName).exists(); - } - - /** - * @return True, if the file is loaded, false otherwise. - */ - public boolean isLoaded() { - return this.loaded; - } + /** + * @return True, if the file is loaded, false otherwise. + */ + public boolean isLoaded() { + return this.loaded; + } } diff --git a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorJFrame.java b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorJFrame.java index f4d24f59f..9cf883086 100644 --- a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorJFrame.java +++ b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorJFrame.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.model.view.presenter; import java.awt.Color; @@ -14,187 +36,183 @@ import javax.swing.JTextArea; import javax.swing.JTextField; /** - * This class is the GUI implementation of the View component in the - * Model-View-Presenter pattern. + * This class is the GUI implementation of the View component in the Model-View-Presenter pattern. */ -public class FileSelectorJFrame extends JFrame implements FileSelectorView, - ActionListener { +public class FileSelectorJFrame extends JFrame implements FileSelectorView, ActionListener { - /** - * Default serial version ID. - */ - private static final long serialVersionUID = 1L; + /** + * Default serial version ID. + */ + private static final long serialVersionUID = 1L; - /** - * The "OK" button for loading the file. - */ - private JButton OK; + /** + * The "OK" button for loading the file. + */ + private JButton ok; - /** - * The cancel button. - */ - private JButton cancel; + /** + * The cancel button. + */ + private JButton cancel; - /** - * The information label. - */ - private JLabel info; + /** + * The information label. + */ + private JLabel info; - /** - * The contents label. - */ - private JLabel contents; + /** + * The contents label. + */ + private JLabel contents; - /** - * The text field for giving the name of the file that we want to open. - */ - private JTextField input; + /** + * The text field for giving the name of the file that we want to open. + */ + private JTextField input; - /** - * A text area that will keep the contents of the file opened. - */ - private JTextArea area; + /** + * A text area that will keep the contents of the file opened. + */ + private JTextArea area; - /** - * The panel that will hold our widgets. - */ - private JPanel panel; + /** + * The panel that will hold our widgets. + */ + private JPanel panel; - /** - * The Presenter component that the frame will interact with - */ - private FileSelectorPresenter presenter; + /** + * The Presenter component that the frame will interact with + */ + private FileSelectorPresenter presenter; - /** - * The name of the file that we want to read it's contents. - */ - private String fileName; + /** + * The name of the file that we want to read it's contents. + */ + private String fileName; - /** - * Constructor. - */ - public FileSelectorJFrame() { - super("File Loader"); - this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - this.setLayout(null); - this.setBounds(100, 100, 500, 200); + /** + * Constructor. + */ + public FileSelectorJFrame() { + super("File Loader"); + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + this.setLayout(null); + this.setBounds(100, 100, 500, 200); - /* - * Add the panel. - */ - this.panel = new JPanel(); - panel.setLayout(null); - this.add(panel); - panel.setBounds(0, 0, 500, 200); - panel.setBackground(Color.LIGHT_GRAY); + /* + * Add the panel. + */ + this.panel = new JPanel(); + panel.setLayout(null); + this.add(panel); + panel.setBounds(0, 0, 500, 200); + panel.setBackground(Color.LIGHT_GRAY); - /* - * Add the info label. - */ - this.info = new JLabel("File Name :"); - this.panel.add(info); - info.setBounds(30, 10, 100, 30); + /* + * Add the info label. + */ + this.info = new JLabel("File Name :"); + this.panel.add(info); + info.setBounds(30, 10, 100, 30); - /* - * Add the contents label. - */ - this.contents = new JLabel("File contents :"); - this.panel.add(contents); - this.contents.setBounds(30, 100, 120, 30); + /* + * Add the contents label. + */ + this.contents = new JLabel("File contents :"); + this.panel.add(contents); + this.contents.setBounds(30, 100, 120, 30); - /* - * Add the text field. - */ - this.input = new JTextField(100); - this.panel.add(input); - this.input.setBounds(150, 15, 200, 20); + /* + * Add the text field. + */ + this.input = new JTextField(100); + this.panel.add(input); + this.input.setBounds(150, 15, 200, 20); - /* - * Add the text area. - */ - this.area = new JTextArea(100, 100); - JScrollPane pane = new JScrollPane(area); - pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); - this.panel.add(pane); - this.area.setEditable(false); - pane.setBounds(150, 100, 250, 80); + /* + * Add the text area. + */ + this.area = new JTextArea(100, 100); + JScrollPane pane = new JScrollPane(area); + pane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); + this.panel.add(pane); + this.area.setEditable(false); + pane.setBounds(150, 100, 250, 80); - /* - * Add the OK button. - */ - this.OK = new JButton("OK"); - this.panel.add(OK); - this.OK.setBounds(250, 50, 100, 25); - this.OK.addActionListener(this); + /* + * Add the OK button. + */ + this.ok = new JButton("OK"); + this.panel.add(ok); + this.ok.setBounds(250, 50, 100, 25); + this.ok.addActionListener(this); - /* - * Add the cancel button. - */ - this.cancel = new JButton("Cancel"); - this.panel.add(this.cancel); - this.cancel.setBounds(380, 50, 100, 25); - this.cancel.addActionListener(this); + /* + * Add the cancel button. + */ + this.cancel = new JButton("Cancel"); + this.panel.add(this.cancel); + this.cancel.setBounds(380, 50, 100, 25); + this.cancel.addActionListener(this); - this.presenter = null; - this.fileName = null; - } + this.presenter = null; + this.fileName = null; + } - @Override - public void actionPerformed(ActionEvent e) { - if (e.getSource() == this.OK) { - this.fileName = this.input.getText(); - presenter.fileNameChanged(); - presenter.confirmed(); - } + @Override + public void actionPerformed(ActionEvent e) { + if (this.ok.equals(e.getSource())) { + this.fileName = this.input.getText(); + presenter.fileNameChanged(); + presenter.confirmed(); + } else if (this.cancel.equals(e.getSource())) { + presenter.cancelled(); + } + } - else if (e.getSource() == this.cancel) { - presenter.cancelled(); - } - } + @Override + public void open() { + this.setVisible(true); + } - @Override - public void open() { - this.setVisible(true); - } + @Override + public void close() { + this.dispose(); + } - @Override - public void close() { - this.dispose(); - } + @Override + public boolean isOpened() { + return this.isVisible(); + } - @Override - public boolean isOpened() { - return this.isVisible(); - } + @Override + public void setPresenter(FileSelectorPresenter presenter) { + this.presenter = presenter; + } - @Override - public void setPresenter(FileSelectorPresenter presenter) { - this.presenter = presenter; - } + @Override + public FileSelectorPresenter getPresenter() { + return this.presenter; + } - @Override - public FileSelectorPresenter getPresenter() { - return this.presenter; - } + @Override + public void setFileName(String name) { + this.fileName = name; + } - @Override - public void setFileName(String name) { - this.fileName = name; - } + @Override + public String getFileName() { + return this.fileName; + } - @Override - public String getFileName() { - return this.fileName; - } + @Override + public void showMessage(String message) { + JOptionPane.showMessageDialog(null, message); + } - @Override - public void showMessage(String message) { - JOptionPane.showMessageDialog(null, message); - } - - @Override - public void displayData(String data) { - this.area.setText(data); - } + @Override + public void displayData(String data) { + this.area.setText(data); + } } diff --git a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorPresenter.java b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorPresenter.java index 7119d60bf..54f9a91c6 100644 --- a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorPresenter.java +++ b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorPresenter.java @@ -1,75 +1,99 @@ +/** + * The MIT License + * Copyright (c) 2014 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.model.view.presenter; /** - * Every instance of this class represents the Presenter component in the - * Model-View-Presenter architectural pattern. + * Every instance of this class represents the Presenter component in the Model-View-Presenter + * architectural pattern. *

- * It is responsible for reacting to the user's actions and update the View - * component. + * It is responsible for reacting to the user's actions and update the View component. */ public class FileSelectorPresenter { - /** - * The View component that the presenter interacts with. - */ - private FileSelectorView view; + /** + * The View component that the presenter interacts with. + */ + private FileSelectorView view; - /** - * The Model component that the presenter interacts with. - */ - private FileLoader loader; + /** + * The Model component that the presenter interacts with. + */ + private FileLoader loader; - /** - * Constructor - * @param view The view component that the presenter will interact with. - */ - public FileSelectorPresenter(FileSelectorView view) { - this.view = view; - } + /** + * Constructor + * + * @param view The view component that the presenter will interact with. + */ + public FileSelectorPresenter(FileSelectorView view) { + this.view = view; + } - /** - * Sets the {@link FileLoader} object, to the value given as parameter. - * @param loader The new {@link FileLoader} object(the Model component). - */ - public void setLoader(FileLoader loader) { - this.loader = loader; - } + /** + * Sets the {@link FileLoader} object, to the value given as parameter. + * + * @param loader The new {@link FileLoader} object(the Model component). + */ + public void setLoader(FileLoader loader) { + this.loader = loader; + } - /** - * Starts the presenter. - */ - public void start() { - view.setPresenter(this); - view.open(); - } + /** + * Starts the presenter. + */ + public void start() { + view.setPresenter(this); + view.open(); + } - /** - * An "event" that fires when the name of the file to be loaded changes. - */ - public void fileNameChanged() { - loader.setFileName(view.getFileName()); - } + /** + * An "event" that fires when the name of the file to be loaded changes. + */ + public void fileNameChanged() { + loader.setFileName(view.getFileName()); + } - public void confirmed() { - if (loader.getFileName() == null || loader.getFileName().equals("")) { - view.showMessage("Please give the name of the file first!"); - return; - } + /** + * Ok button handler + */ + public void confirmed() { + if (loader.getFileName() == null || loader.getFileName().equals("")) { + view.showMessage("Please give the name of the file first!"); + return; + } - if (loader.fileExists()) { - String data = loader.loadData(); - view.displayData(data); - } + if (loader.fileExists()) { + String data = loader.loadData(); + view.displayData(data); + } else { + view.showMessage("The file specified does not exist."); + } + } - else { - view.showMessage("The file specified does not exist."); - } - } - - /** - * Cancels the file loading process. - */ - public void cancelled() { - view.close(); - } + /** + * Cancels the file loading process. + */ + public void cancelled() { + view.close(); + } } diff --git a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorStub.java b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorStub.java index d0cec4c40..b9ce73878 100644 --- a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorStub.java +++ b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorStub.java @@ -1,109 +1,131 @@ +/** + * The MIT License + * Copyright (c) 2014 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.model.view.presenter; /** - * Every instance of this class represents the Stub component in the - * Model-View-Presenter architectural pattern. + * Every instance of this class represents the Stub component in the Model-View-Presenter + * architectural pattern. *

- * The stub implements the View interface and it is useful when we want the test - * the reaction to user events, such as mouse clicks. + * The stub implements the View interface and it is useful when we want the test the reaction to + * user events, such as mouse clicks. *

- * Since we can not test the GUI directly, the MVP pattern provides this - * functionality through the View's dummy implementation, the Stub. + * Since we can not test the GUI directly, the MVP pattern provides this functionality through the + * View's dummy implementation, the Stub. */ public class FileSelectorStub implements FileSelectorView { - /** - * Indicates whether or not the view is opened. - */ - private boolean opened; + /** + * Indicates whether or not the view is opened. + */ + private boolean opened; - /** - * The presenter Component. - */ - private FileSelectorPresenter presenter; + /** + * The presenter Component. + */ + private FileSelectorPresenter presenter; - /** - * The current name of the file. - */ - private String name; + /** + * The current name of the file. + */ + private String name; - /** - * Indicates the number of messages that were "displayed" to the user. - */ - private int numOfMessageSent; + /** + * Indicates the number of messages that were "displayed" to the user. + */ + private int numOfMessageSent; - /** - * Indicates if the data of the file where displayed or not. - */ - private boolean dataDisplayed; + /** + * Indicates if the data of the file where displayed or not. + */ + private boolean dataDisplayed; - /** - * Constructor - */ - public FileSelectorStub() { - this.opened = false; - this.presenter = null; - this.name = ""; - this.numOfMessageSent = 0; - this.dataDisplayed = false; - } + /** + * Constructor + */ + public FileSelectorStub() { + this.opened = false; + this.presenter = null; + this.name = ""; + this.numOfMessageSent = 0; + this.dataDisplayed = false; + } - @Override - public void open() { - this.opened = true; - } + @Override + public void open() { + this.opened = true; + } - @Override - public void setPresenter(FileSelectorPresenter presenter) { - this.presenter = presenter; - } + @Override + public void setPresenter(FileSelectorPresenter presenter) { + this.presenter = presenter; + } - @Override - public boolean isOpened() { - return this.opened; - } + @Override + public boolean isOpened() { + return this.opened; + } - @Override - public FileSelectorPresenter getPresenter() { - return this.presenter; - } + @Override + public FileSelectorPresenter getPresenter() { + return this.presenter; + } - @Override - public String getFileName() { - return this.name; - } + @Override + public String getFileName() { + return this.name; + } - @Override - public void setFileName(String name) { - this.name = name; - } + @Override + public void setFileName(String name) { + this.name = name; + } - @Override - public void showMessage(String message) { - this.numOfMessageSent++; - } + @Override + public void showMessage(String message) { + this.numOfMessageSent++; + } - @Override - public void close() { - this.opened = false; - } + @Override + public void close() { + this.opened = false; + } - @Override - public void displayData(String data) { - this.dataDisplayed = true; - } + @Override + public void displayData(String data) { + this.dataDisplayed = true; + } - /** - * Returns the number of messages that were displayed to the user. - */ - public int getMessagesSent() { - return this.numOfMessageSent; - } + /** + * Returns the number of messages that were displayed to the user. + */ + public int getMessagesSent() { + return this.numOfMessageSent; + } - /** - * @return True if the data where displayed, false otherwise. - */ - public boolean dataDisplayed() { - return this.dataDisplayed; - } + /** + * @return True if the data where displayed, false otherwise. + */ + public boolean dataDisplayed() { + return this.dataDisplayed; + } } diff --git a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorView.java b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorView.java index 8cd265f9b..94edcf7d0 100644 --- a/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorView.java +++ b/model-view-presenter/src/main/java/com/iluwatar/model/view/presenter/FileSelectorView.java @@ -1,57 +1,83 @@ +/** + * The MIT License + * Copyright (c) 2014 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.model.view.presenter; /** - * This interface represents the View component in the Model-View-Presenter - * pattern. It can be implemented by either the GUI components, or by the Stub. + * This interface represents the View component in the Model-View-Presenter pattern. It can be + * implemented by either the GUI components, or by the Stub. */ public interface FileSelectorView { - /** - * Opens the view. - */ - public void open(); + /** + * Opens the view. + */ + void open(); - /** - * Closes the view. - */ - public void close(); + /** + * Closes the view. + */ + void close(); - /** - * @return True, if the view is opened, false otherwise. - */ - public boolean isOpened(); + /** + * @return True, if the view is opened, false otherwise. + */ + boolean isOpened(); - /** - * Sets the presenter component, to the one given as parameter. - * @param presenter The new presenter component. - */ - public void setPresenter(FileSelectorPresenter presenter); + /** + * Sets the presenter component, to the one given as parameter. + * + * @param presenter The new presenter component. + */ + void setPresenter(FileSelectorPresenter presenter); - /** - * @return The presenter Component. - */ - public FileSelectorPresenter getPresenter(); + /** + * @return The presenter Component. + */ + FileSelectorPresenter getPresenter(); - /** - * Sets the file's name, to the value given as parameter. - * @param name The new name of the file. - */ - public void setFileName(String name); + /** + * Sets the file's name, to the value given as parameter. + * + * @param name The new name of the file. + */ + void setFileName(String name); - /** - * @return The name of the file. - */ - public String getFileName(); + /** + * @return The name of the file. + */ + String getFileName(); - /** - * Displays a message to the users. - * @param message The message to be displayed. - */ - public void showMessage(String message); + /** + * Displays a message to the users. + * + * @param message The message to be displayed. + */ + void showMessage(String message); - /** - * Displays the data to the view. - * @param data The data to be written. - */ - public void displayData(String data); + /** + * Displays the data to the view. + * + * @param data The data to be written. + */ + void displayData(String data); } diff --git a/model-view-presenter/src/test/java/com/iluwatar/model/view/presenter/AppTest.java b/model-view-presenter/src/test/java/com/iluwatar/model/view/presenter/AppTest.java new file mode 100644 index 000000000..4e329e3bc --- /dev/null +++ b/model-view-presenter/src/test/java/com/iluwatar/model/view/presenter/AppTest.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 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.model.view.presenter; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } + +} diff --git a/model-view-presenter/src/test/java/com/iluwatar/model/view/presenter/FileLoaderTest.java b/model-view-presenter/src/test/java/com/iluwatar/model/view/presenter/FileLoaderTest.java new file mode 100644 index 000000000..eed01b835 --- /dev/null +++ b/model-view-presenter/src/test/java/com/iluwatar/model/view/presenter/FileLoaderTest.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 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.model.view.presenter; + +import org.junit.Test; + +import static org.junit.Assert.assertNull; + +/** + * Date: 12/21/15 - 12:12 PM + * + * @author Jeroen Meulemeester + */ +public class FileLoaderTest { + + @Test + public void testLoadData() throws Exception { + final FileLoader fileLoader = new FileLoader(); + fileLoader.setFileName("non-existing-file"); + assertNull(fileLoader.loadData()); + } + +} \ No newline at end of file diff --git a/model-view-presenter/src/test/java/com/iluwatar/model/view/presenter/FileSelectorPresenterTest.java b/model-view-presenter/src/test/java/com/iluwatar/model/view/presenter/FileSelectorPresenterTest.java index 119448e9c..42afdb2b8 100644 --- a/model-view-presenter/src/test/java/com/iluwatar/model/view/presenter/FileSelectorPresenterTest.java +++ b/model-view-presenter/src/test/java/com/iluwatar/model/view/presenter/FileSelectorPresenterTest.java @@ -1,125 +1,145 @@ +/** + * The MIT License + * Copyright (c) 2014 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.model.view.presenter; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import org.junit.Before; import org.junit.Test; -import com.iluwatar.model.view.presenter.FileLoader; -import com.iluwatar.model.view.presenter.FileSelectorPresenter; -import com.iluwatar.model.view.presenter.FileSelectorStub; - /** - * This test case is responsible for testing our application by taking advantage - * of the Model-View-Controller architectural pattern. + * This test case is responsible for testing our application by taking advantage of the + * Model-View-Controller architectural pattern. */ public class FileSelectorPresenterTest { - /** - * The Presenter component. - */ - private FileSelectorPresenter presenter; + /** + * The Presenter component. + */ + private FileSelectorPresenter presenter; - /** - * The View component, implemented this time as a Stub!!! - */ - private FileSelectorStub stub; + /** + * The View component, implemented this time as a Stub!!! + */ + private FileSelectorStub stub; - /** - * The Model component. - */ - private FileLoader loader; + /** + * The Model component. + */ + private FileLoader loader; - /** - * Initializes the components of the test case. - */ - @Before - public void setUp() { - this.stub = new FileSelectorStub(); - this.loader = new FileLoader(); - presenter = new FileSelectorPresenter(this.stub); - presenter.setLoader(loader); - } + /** + * Initializes the components of the test case. + */ + @Before + public void setUp() { + this.stub = new FileSelectorStub(); + this.loader = new FileLoader(); + presenter = new FileSelectorPresenter(this.stub); + presenter.setLoader(loader); + } - /** - * Tests if the Presenter was successfully connected with the View. - */ - @Test - public void wiring() { - presenter.start(); + /** + * Tests if the Presenter was successfully connected with the View. + */ + @Test + public void wiring() { + presenter.start(); - assertNotNull(stub.getPresenter()); - assertTrue(stub.isOpened()); - } + assertNotNull(stub.getPresenter()); + assertTrue(stub.isOpened()); + } - /** - * Tests if the name of the file changes. - */ - @Test - public void updateFileNameToLoader() { - String EXPECTED_FILE = "Stamatis"; - stub.setFileName(EXPECTED_FILE); + /** + * Tests if the name of the file changes. + */ + @Test + public void updateFileNameToLoader() { + String expectedFile = "Stamatis"; + stub.setFileName(expectedFile); - presenter.start(); - presenter.fileNameChanged(); + presenter.start(); + presenter.fileNameChanged(); - assertEquals(EXPECTED_FILE, loader.getFileName()); - } + assertEquals(expectedFile, loader.getFileName()); + } - /** - * Tests if we receive a confirmation when we attempt to open a file that - * it's name is null or an empty string. - */ - @Test - public void fileConfirmationWhenNameIsNull() { - stub.setFileName(null); + /** + * Tests if we receive a confirmation when we attempt to open a file that it's name is null or an + * empty string. + */ + @Test + public void fileConfirmationWhenNameIsNull() { + stub.setFileName(null); - presenter.start(); - presenter.fileNameChanged(); - presenter.confirmed(); + presenter.start(); + presenter.fileNameChanged(); + presenter.confirmed(); - assertFalse(loader.isLoaded()); - assertEquals(1, stub.getMessagesSent()); - } + assertFalse(loader.isLoaded()); + assertEquals(1, stub.getMessagesSent()); + } - /** - * Tests if we receive a confirmation when we attempt to open a file that it - * doesn't exist. - */ - @Test - public void fileConfirmationWhenFileDoesNotExist() { - stub.setFileName("RandomName.txt"); + /** + * Tests if we receive a confirmation when we attempt to open a file that it doesn't exist. + */ + @Test + public void fileConfirmationWhenFileDoesNotExist() { + stub.setFileName("RandomName.txt"); - presenter.start(); - presenter.fileNameChanged(); - presenter.confirmed(); + presenter.start(); + presenter.fileNameChanged(); + presenter.confirmed(); - assertFalse(loader.isLoaded()); - assertEquals(1, stub.getMessagesSent()); - } + assertFalse(loader.isLoaded()); + assertEquals(1, stub.getMessagesSent()); + } - /** - * Tests if we can open the file, when it exists. - */ - @Test - public void fileConfirmationWhenFileExists() { - stub.setFileName("etc/data/test.txt"); - presenter.start(); - presenter.fileNameChanged(); - presenter.confirmed(); + /** + * Tests if we can open the file, when it exists. + */ + @Test + public void fileConfirmationWhenFileExists() { + stub.setFileName("etc/data/test.txt"); + presenter.start(); + presenter.fileNameChanged(); + presenter.confirmed(); - assertTrue(loader.isLoaded()); - assertTrue(stub.dataDisplayed()); - } + assertTrue(loader.isLoaded()); + assertTrue(stub.dataDisplayed()); + } - /** - * Tests if the view closes after cancellation. - */ - @Test - public void cancellation() { - presenter.start(); - presenter.cancelled(); + /** + * Tests if the view closes after cancellation. + */ + @Test + public void cancellation() { + presenter.start(); + presenter.cancelled(); - assertFalse(stub.isOpened()); - } + assertFalse(stub.isOpened()); + } } diff --git a/monad/README.md b/monad/README.md new file mode 100644 index 000000000..41edd3d92 --- /dev/null +++ b/monad/README.md @@ -0,0 +1,35 @@ +--- +layout: pattern +title: Monad +folder: monad +permalink: /patterns/monad/ +categories: Other +tags: + - Java + - Difficulty-Expert + - Functional +--- + +## Intent + +Monad pattern based on monad from linear algebra represents the way of chaining operations +together step by step. Binding functions can be described as passing one's output to another's input +basing on the 'same type' contract. Formally, monad consists of a type constructor M and two +operations: +bind - that takes monadic object and a function from plain object to monadic value and returns monadic value +return - that takes plain type object and returns this object wrapped in a monadic value. + +![alt text](./etc/monad.png "Monad") + +## Applicability + +Use the Monad in any of the following situations + +* when you want to chain operations easily +* when you want to apply each function regardless of the result of any of them + +## Credits + +* [Design Pattern Reloaded by Remi Forax](https://youtu.be/-k2X7guaArU) +* [Brian Beckman: Don't fear the Monad](https://channel9.msdn.com/Shows/Going+Deep/Brian-Beckman-Dont-fear-the-Monads) +* [Monad on Wikipedia](https://en.wikipedia.org/wiki/Monad_(functional_programming)) \ No newline at end of file diff --git a/monad/etc/monad.png b/monad/etc/monad.png new file mode 100644 index 000000000..f82e7a3e4 Binary files /dev/null and b/monad/etc/monad.png differ diff --git a/monad/etc/monad.ucls b/monad/etc/monad.ucls new file mode 100644 index 000000000..9c98df7c6 --- /dev/null +++ b/monad/etc/monad.ucls @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/monad/pom.xml b/monad/pom.xml new file mode 100644 index 000000000..7f128272d --- /dev/null +++ b/monad/pom.xml @@ -0,0 +1,43 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + monad + + + junit + junit + test + + + + diff --git a/monad/src/main/java/com/iluwatar/monad/App.java b/monad/src/main/java/com/iluwatar/monad/App.java new file mode 100644 index 000000000..7b28fdcf8 --- /dev/null +++ b/monad/src/main/java/com/iluwatar/monad/App.java @@ -0,0 +1,57 @@ +/** + * The MIT License + * Copyright (c) 2014 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.monad; + +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * The Monad pattern defines a monad structure, that enables chaining operations + * in pipelines and processing data step by step. + * Formally, monad consists of a type constructor M and two operations: + *
bind - that takes monadic object and a function from plain object to the + * monadic value and returns monadic value. + *
return - that takes plain type object and returns this object wrapped in a monadic value. + *

+ * In the given example, the Monad pattern is represented as a {@link Validator} that takes an instance + * of a plain object with {@link Validator#of(Object)} + * and validates it {@link Validator#validate(Function, Predicate, String)} against given predicates. + *

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 { + + /** + * Program entry point. + * + * @param args @param args command line args + */ + public static void main(String[] args) { + User user = new User("user", 24, Sex.FEMALE, "foobar.com"); + System.out.println(Validator.of(user).validate(User::getName, Objects::nonNull, "name is null") + .validate(User::getName, name -> !name.isEmpty(), "name is empty") + .validate(User::getEmail, email -> !email.contains("@"), "email doesn't containt '@'") + .validate(User::getAge, age -> age > 20 && age < 30, "age isn't between...").get().toString()); + } +} diff --git a/monad/src/main/java/com/iluwatar/monad/Sex.java b/monad/src/main/java/com/iluwatar/monad/Sex.java new file mode 100644 index 000000000..b5d094d4b --- /dev/null +++ b/monad/src/main/java/com/iluwatar/monad/Sex.java @@ -0,0 +1,27 @@ +/** + * The MIT License + * Copyright (c) 2014 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.monad; + +public enum Sex { + MALE, FEMALE +} diff --git a/monad/src/main/java/com/iluwatar/monad/User.java b/monad/src/main/java/com/iluwatar/monad/User.java new file mode 100644 index 000000000..471094526 --- /dev/null +++ b/monad/src/main/java/com/iluwatar/monad/User.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 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.monad; + +public class User { + + private String name; + private int age; + private Sex sex; + private String email; + + /** + * @param name - name + * @param age - age + * @param sex - sex + * @param email - email address + */ + public User(String name, int age, Sex sex, String email) { + this.name = name; + this.age = age; + this.sex = sex; + this.email = email; + } + + public String getName() { + return name; + } + + public int getAge() { + return age; + } + + public Sex getSex() { + return sex; + } + + public String getEmail() { + return email; + } +} diff --git a/monad/src/main/java/com/iluwatar/monad/Validator.java b/monad/src/main/java/com/iluwatar/monad/Validator.java new file mode 100644 index 000000000..cc4f36020 --- /dev/null +++ b/monad/src/main/java/com/iluwatar/monad/Validator.java @@ -0,0 +1,113 @@ +/** + * The MIT License + * Copyright (c) 2014 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.monad; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * Class representing Monad design pattern. Monad is a way of chaining operations on the + * given object together step by step. In Validator each step results in either success or + * failure indicator, giving a way of receiving each of them easily and finally getting + * validated object or list of exceptions. + * + * @param Placeholder for an object. + */ +public class Validator { + /** + * Object that is validated + */ + private final T t; + + /** + * List of exception thrown during validation. + */ + private final List exceptions = new ArrayList<>(); + + /** + * Creates a monadic value of given object. + * @param t object to be validated + */ + private Validator(T t) { + this.t = t; + } + + /** + * Creates validator against given object + * + * @param t object to be validated + * @param object's type + * @return new instance of a validator + */ + public static Validator of(T t) { + return new Validator<>(Objects.requireNonNull(t)); + } + + /** + * @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 + * @return this + */ + public Validator validate(Predicate validation, String message) { + if (!validation.test(t)) { + exceptions.add(new IllegalStateException(message)); + } + return this; + } + + /** + * Extension for the {@link Validator#validate(Function, Predicate, String)} method, + * dedicated for objects, that need to be projected before requested validation. + * + * @param projection function that gets an objects, and returns projection representing + * element to be validated. + * @param validation see {@link Validator#validate(Function, Predicate, String)} + * @param message see {@link Validator#validate(Function, Predicate, String)} + * @param see {@link Validator#validate(Function, Predicate, String)} + * @return this + */ + public Validator validate(Function projection, Predicate validation, + String message) { + return validate(projection.andThen(validation::test)::apply, message); + } + + /** + * Receives validated object or throws exception when invalid. + * + * @return object that was validated + * @throws IllegalStateException when any validation step results with failure + */ + public T get() throws IllegalStateException { + if (exceptions.isEmpty()) { + return t; + } + IllegalStateException e = new IllegalStateException(); + exceptions.forEach(e::addSuppressed); + throw e; + } +} diff --git a/monad/src/test/java/com/iluwatar/monad/AppTest.java b/monad/src/test/java/com/iluwatar/monad/AppTest.java new file mode 100644 index 000000000..78440b468 --- /dev/null +++ b/monad/src/test/java/com/iluwatar/monad/AppTest.java @@ -0,0 +1,35 @@ +/** + * The MIT License + * Copyright (c) 2014 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.monad; + +import org.junit.Test; + +public class AppTest { + + @Test + public void testMain() { + String[] args = {}; + App.main(args); + } + +} diff --git a/monad/src/test/java/com/iluwatar/monad/MonadTest.java b/monad/src/test/java/com/iluwatar/monad/MonadTest.java new file mode 100644 index 000000000..4ada7191d --- /dev/null +++ b/monad/src/test/java/com/iluwatar/monad/MonadTest.java @@ -0,0 +1,64 @@ +/** + * The MIT License + * Copyright (c) 2014 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.monad; + + +import junit.framework.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.util.Objects; + +public class MonadTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void testForInvalidName() { + thrown.expect(IllegalStateException.class); + User tom = new User(null, 21, Sex.MALE, "tom@foo.bar"); + Validator.of(tom).validate(User::getName, Objects::nonNull, "name cannot be null").get(); + } + + @Test + public void testForInvalidAge() { + thrown.expect(IllegalStateException.class); + User john = new User("John", 17, Sex.MALE, "john@qwe.bar"); + Validator.of(john).validate(User::getName, Objects::nonNull, "name cannot be null") + .validate(User::getAge, age -> age > 21, "user is underaged") + .get(); + } + + @Test + public void testForValid() { + User sarah = new User("Sarah", 42, Sex.FEMALE, "sarah@det.org"); + User validated = Validator.of(sarah).validate(User::getName, Objects::nonNull, "name cannot be null") + .validate(User::getAge, age -> age > 21, "user is underaged") + .validate(User::getSex, sex -> sex == Sex.FEMALE, "user is not female") + .validate(User::getEmail, email -> email.contains("@"), "email does not contain @ sign") + .get(); + Assert.assertSame(validated, sarah); + } +} diff --git a/monostate/index.md b/monostate/README.md similarity index 73% rename from monostate/index.md rename to monostate/README.md index 2b88f131e..3576dc659 100644 --- a/monostate/index.md +++ b/monostate/README.md @@ -4,25 +4,32 @@ title: MonoState folder: monostate permalink: /patterns/monostate/ categories: Creational -tags: Java +tags: + - Java + - Difficulty-Beginner --- -**Intent:** Enforces a behaviour like sharing the same state amongst all instances. +## Also known as +Borg + +## Intent +Enforces a behaviour like sharing the same state amongst all instances. ![alt text](./etc/monostate.png "MonoState") -**Applicability:** Use the Monostate pattern when +## Applicability +Use the Monostate pattern when * The same state must be shared across all instances of a class. * Typically this pattern might be used everywhere a Singleton might be used. Singleton usage however is not transparent, Monostate usage is. * Monostate has one major advantage over singleton. The subclasses might decorate the shared state as they wish and hence can provide dynamically different behaviour than the base class. -**Typical Use Case:** +## Typical Use Case * the logging class * managing a connection to a database * file manager -**Real world examples:** +## Real world examples Yet to see this. diff --git a/monostate/pom.xml b/monostate/pom.xml index 480a4967c..64ee52abe 100644 --- a/monostate/pom.xml +++ b/monostate/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT monostate @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/monostate/src/main/java/com/iluwatar/monostate/App.java b/monostate/src/main/java/com/iluwatar/monostate/App.java index 0daad5b67..bfb51fe91 100644 --- a/monostate/src/main/java/com/iluwatar/monostate/App.java +++ b/monostate/src/main/java/com/iluwatar/monostate/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.monostate; @@ -28,8 +50,8 @@ public class App { public static void main(String[] args) { LoadBalancer loadBalancer1 = new LoadBalancer(); LoadBalancer loadBalancer2 = new LoadBalancer(); - loadBalancer1.serverequest(new Request("Hello")); - loadBalancer2.serverequest(new Request("Hello World")); + loadBalancer1.serverRequest(new Request("Hello")); + loadBalancer2.serverRequest(new Request("Hello World")); } } diff --git a/monostate/src/main/java/com/iluwatar/monostate/LoadBalancer.java b/monostate/src/main/java/com/iluwatar/monostate/LoadBalancer.java index 7bc0043e8..613f0e105 100644 --- a/monostate/src/main/java/com/iluwatar/monostate/LoadBalancer.java +++ b/monostate/src/main/java/com/iluwatar/monostate/LoadBalancer.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.monostate; import java.util.ArrayList; @@ -13,8 +35,8 @@ import java.util.List; public class LoadBalancer { private static List servers = new ArrayList<>(); - private static int id = 0; - private static int lastServedId = 0; + private static int id; + private static int lastServedId; static { servers.add(new Server("localhost", 8081, ++id)); @@ -24,6 +46,9 @@ public class LoadBalancer { servers.add(new Server("localhost", 8084, ++id)); } + /** + * Add new server + */ public final void addServer(Server server) { synchronized (servers) { servers.add(server); @@ -39,14 +64,17 @@ public class LoadBalancer { return lastServedId; } - public void serverequest(Request request) { + /** + * Handle request + */ + public void serverRequest(Request request) { if (lastServedId >= servers.size()) { lastServedId = 0; } Server server = servers.get(lastServedId++); server.serve(request); } - + } diff --git a/monostate/src/main/java/com/iluwatar/monostate/Request.java b/monostate/src/main/java/com/iluwatar/monostate/Request.java index ee1f31d85..9cdb4d7e4 100644 --- a/monostate/src/main/java/com/iluwatar/monostate/Request.java +++ b/monostate/src/main/java/com/iluwatar/monostate/Request.java @@ -1,8 +1,30 @@ +/** + * The MIT License + * Copyright (c) 2014 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.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. * */ diff --git a/monostate/src/main/java/com/iluwatar/monostate/Server.java b/monostate/src/main/java/com/iluwatar/monostate/Server.java index ce4e0222d..bf700a57a 100644 --- a/monostate/src/main/java/com/iluwatar/monostate/Server.java +++ b/monostate/src/main/java/com/iluwatar/monostate/Server.java @@ -1,9 +1,31 @@ +/** + * The MIT License + * Copyright (c) 2014 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.monostate; /** * - * The Server class. Each Server sits behind a LoadBalancer which delegates the call to the - * servers in a simplistic Round Robin fashion. + * The Server class. Each Server sits behind a LoadBalancer which delegates the call to the servers + * in a simplistic Round Robin fashion. * */ public class Server { @@ -11,6 +33,9 @@ public class Server { public final int port; public final int id; + /** + * Constructor + */ public Server(String host, int port, int id) { this.host = host; this.port = port; @@ -25,7 +50,8 @@ public class Server { return port; } - public final void serve(Request request) { - System.out.println("Server ID " + id + " associated to host : " + getHost() + " and Port " + getPort() +" Processed request with value " + request.value); + public void serve(Request request) { + System.out.println("Server ID " + id + " associated to host : " + getHost() + " and Port " + + getPort() + " Processed request with value " + request.value); } } diff --git a/monostate/src/test/java/com/iluwatar/monostate/AppTest.java b/monostate/src/test/java/com/iluwatar/monostate/AppTest.java index c5f1f7e92..6b5f11a39 100644 --- a/monostate/src/test/java/com/iluwatar/monostate/AppTest.java +++ b/monostate/src/test/java/com/iluwatar/monostate/AppTest.java @@ -1,26 +1,35 @@ +/** + * The MIT License + * Copyright (c) 2014 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.monostate; -import org.junit.Assert; import org.junit.Test; public class AppTest { - @Test - public void testSameStateAmonstAllInstances() { - LoadBalancer balancer = new LoadBalancer(); - LoadBalancer balancer2 = new LoadBalancer(); - balancer.addServer(new Server("localhost", 8085, 6)); - // Both should have the same number of servers. - Assert.assertTrue(balancer.getNoOfServers() == balancer2.getNoOfServers()); - // Both Should have the same LastServedId - Assert.assertTrue(balancer.getLastServedId() == balancer2.getLastServedId()); - } - @Test public void testMain() { String[] args = {}; App.main(args); - Assert.assertTrue(LoadBalancer.getLastServedId() == 2); } } diff --git a/monostate/src/test/java/com/iluwatar/monostate/LoadBalancerTest.java b/monostate/src/test/java/com/iluwatar/monostate/LoadBalancerTest.java new file mode 100644 index 000000000..70369e8a0 --- /dev/null +++ b/monostate/src/test/java/com/iluwatar/monostate/LoadBalancerTest.java @@ -0,0 +1,71 @@ +/** + * The MIT License + * Copyright (c) 2014 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.monostate; + +import org.junit.Assert; +import org.junit.Test; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.*; + +/** + * Date: 12/21/15 - 12:26 PM + * + * @author Jeroen Meulemeester + */ +public class LoadBalancerTest { + + @Test + public void testSameStateAmongstAllInstances() { + final LoadBalancer firstBalancer = new LoadBalancer(); + final LoadBalancer secondBalancer = new LoadBalancer(); + firstBalancer.addServer(new Server("localhost", 8085, 6)); + // Both should have the same number of servers. + Assert.assertTrue(firstBalancer.getNoOfServers() == secondBalancer.getNoOfServers()); + // Both Should have the same LastServedId + Assert.assertTrue(firstBalancer.getLastServedId() == secondBalancer.getLastServedId()); + } + + @Test + public void testServe() { + final Server server = mock(Server.class); + when(server.getHost()).thenReturn("testhost"); + when(server.getPort()).thenReturn(1234); + doNothing().when(server).serve(any(Request.class)); + + final LoadBalancer loadBalancer = new LoadBalancer(); + loadBalancer.addServer(server); + + verifyZeroInteractions(server); + + final Request request = new Request("test"); + for (int i = 0; i < loadBalancer.getNoOfServers() * 2; i++) { + loadBalancer.serverRequest(request); + } + + verify(server, times(2)).serve(request); + verifyNoMoreInteractions(server); + + } + +} diff --git a/multiton/index.md b/multiton/README.md similarity index 62% rename from multiton/index.md rename to multiton/README.md index 617bedb6a..0462ff0ec 100644 --- a/multiton/index.md +++ b/multiton/README.md @@ -4,14 +4,21 @@ title: Multiton folder: multiton permalink: /patterns/multiton/ categories: Creational -tags: Java +tags: + - Java + - Difficulty-Beginner --- -**Intent:** Ensure a class only has limited number of instances, and provide a +## Also known as +Registry + +## Intent +Ensure a class only has limited number of instances, and provide a global point of access to them. ![alt text](./etc/multiton.png "Multiton") -**Applicability:** Use the Multiton pattern when +## Applicability +Use the Multiton pattern when * there must be specific number of instances of a class, and they must be accessible to clients from a well-known access point diff --git a/multiton/pom.xml b/multiton/pom.xml index 9735bc8ea..0b835ed4d 100644 --- a/multiton/pom.xml +++ b/multiton/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT multiton diff --git a/multiton/src/main/java/com/iluwatar/multiton/App.java b/multiton/src/main/java/com/iluwatar/multiton/App.java index 9f2c5da78..1ffd57a34 100644 --- a/multiton/src/main/java/com/iluwatar/multiton/App.java +++ b/multiton/src/main/java/com/iluwatar/multiton/App.java @@ -1,32 +1,54 @@ +/** + * The MIT License + * Copyright (c) 2014 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.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 the Multiton by passing an enumeration as parameter. + * 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 + * the Multiton by passing an enumeration as parameter. *

- * In this 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. + * In this 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. * */ public class App { - - /** - * Program entry point - * @param args command line args - */ - public static void main( String[] args ) { - System.out.println("KHAMUL=" + Nazgul.getInstance(NazgulName.KHAMUL)); - System.out.println("MURAZOR=" + Nazgul.getInstance(NazgulName.MURAZOR)); - System.out.println("DWAR=" + Nazgul.getInstance(NazgulName.DWAR)); - System.out.println("JI_INDUR=" + Nazgul.getInstance(NazgulName.JI_INDUR)); - System.out.println("AKHORAHIL=" + Nazgul.getInstance(NazgulName.AKHORAHIL)); - System.out.println("HOARMURATH=" + Nazgul.getInstance(NazgulName.HOARMURATH)); - System.out.println("ADUNAPHEL=" + Nazgul.getInstance(NazgulName.ADUNAPHEL)); - System.out.println("REN=" + Nazgul.getInstance(NazgulName.REN)); - System.out.println("UVATHA=" + Nazgul.getInstance(NazgulName.UVATHA)); - } + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + System.out.println("KHAMUL=" + Nazgul.getInstance(NazgulName.KHAMUL)); + System.out.println("MURAZOR=" + Nazgul.getInstance(NazgulName.MURAZOR)); + System.out.println("DWAR=" + Nazgul.getInstance(NazgulName.DWAR)); + System.out.println("JI_INDUR=" + Nazgul.getInstance(NazgulName.JI_INDUR)); + System.out.println("AKHORAHIL=" + Nazgul.getInstance(NazgulName.AKHORAHIL)); + System.out.println("HOARMURATH=" + Nazgul.getInstance(NazgulName.HOARMURATH)); + System.out.println("ADUNAPHEL=" + Nazgul.getInstance(NazgulName.ADUNAPHEL)); + System.out.println("REN=" + Nazgul.getInstance(NazgulName.REN)); + System.out.println("UVATHA=" + Nazgul.getInstance(NazgulName.UVATHA)); + } } diff --git a/multiton/src/main/java/com/iluwatar/multiton/Nazgul.java b/multiton/src/main/java/com/iluwatar/multiton/Nazgul.java index 833923f75..5a1dbede6 100644 --- a/multiton/src/main/java/com/iluwatar/multiton/Nazgul.java +++ b/multiton/src/main/java/com/iluwatar/multiton/Nazgul.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.multiton; import java.util.Map; @@ -5,38 +27,37 @@ 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 class Nazgul { +public final class Nazgul { - private static Map nazguls; - - private NazgulName name; + private static Map nazguls; - static { - nazguls = new ConcurrentHashMap<>(); - nazguls.put(NazgulName.KHAMUL, new Nazgul(NazgulName.KHAMUL)); - nazguls.put(NazgulName.MURAZOR, new Nazgul(NazgulName.MURAZOR)); - nazguls.put(NazgulName.DWAR, new Nazgul(NazgulName.DWAR)); - nazguls.put(NazgulName.JI_INDUR, new Nazgul(NazgulName.JI_INDUR)); - nazguls.put(NazgulName.AKHORAHIL, new Nazgul(NazgulName.AKHORAHIL)); - nazguls.put(NazgulName.HOARMURATH, new Nazgul(NazgulName.HOARMURATH)); - nazguls.put(NazgulName.ADUNAPHEL, new Nazgul(NazgulName.ADUNAPHEL)); - nazguls.put(NazgulName.REN, new Nazgul(NazgulName.REN)); - nazguls.put(NazgulName.UVATHA, new Nazgul(NazgulName.UVATHA)); - } - - private Nazgul(NazgulName name) { - this.name = name; - } + private NazgulName name; - public static Nazgul getInstance(NazgulName name) { - return nazguls.get(name); - } - - public NazgulName getName() { - return name; - } + static { + nazguls = new ConcurrentHashMap<>(); + nazguls.put(NazgulName.KHAMUL, new Nazgul(NazgulName.KHAMUL)); + nazguls.put(NazgulName.MURAZOR, new Nazgul(NazgulName.MURAZOR)); + nazguls.put(NazgulName.DWAR, new Nazgul(NazgulName.DWAR)); + nazguls.put(NazgulName.JI_INDUR, new Nazgul(NazgulName.JI_INDUR)); + nazguls.put(NazgulName.AKHORAHIL, new Nazgul(NazgulName.AKHORAHIL)); + nazguls.put(NazgulName.HOARMURATH, new Nazgul(NazgulName.HOARMURATH)); + nazguls.put(NazgulName.ADUNAPHEL, new Nazgul(NazgulName.ADUNAPHEL)); + nazguls.put(NazgulName.REN, new Nazgul(NazgulName.REN)); + nazguls.put(NazgulName.UVATHA, new Nazgul(NazgulName.UVATHA)); + } + + private Nazgul(NazgulName name) { + this.name = name; + } + + public static Nazgul getInstance(NazgulName name) { + return nazguls.get(name); + } + + public NazgulName getName() { + return name; + } } diff --git a/multiton/src/main/java/com/iluwatar/multiton/NazgulName.java b/multiton/src/main/java/com/iluwatar/multiton/NazgulName.java index cef1e43a9..693dfc235 100644 --- a/multiton/src/main/java/com/iluwatar/multiton/NazgulName.java +++ b/multiton/src/main/java/com/iluwatar/multiton/NazgulName.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.multiton; /** @@ -7,6 +29,6 @@ package com.iluwatar.multiton; */ public enum NazgulName { - KHAMUL, MURAZOR, DWAR, JI_INDUR, AKHORAHIL, HOARMURATH, ADUNAPHEL, REN, UVATHA; - + KHAMUL, MURAZOR, DWAR, JI_INDUR, AKHORAHIL, HOARMURATH, ADUNAPHEL, REN, UVATHA; + } diff --git a/multiton/src/test/java/com/iluwatar/multiton/AppTest.java b/multiton/src/test/java/com/iluwatar/multiton/AppTest.java index 439f08e24..2d927b190 100644 --- a/multiton/src/test/java/com/iluwatar/multiton/AppTest.java +++ b/multiton/src/test/java/com/iluwatar/multiton/AppTest.java @@ -1,19 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 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.multiton; import org.junit.Test; -import com.iluwatar.multiton.App; - /** * * Application test * */ public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } + + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/multiton/src/test/java/com/iluwatar/multiton/NazgulTest.java b/multiton/src/test/java/com/iluwatar/multiton/NazgulTest.java new file mode 100644 index 000000000..4aae5d87d --- /dev/null +++ b/multiton/src/test/java/com/iluwatar/multiton/NazgulTest.java @@ -0,0 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014 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.multiton; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; + +/** + * Date: 12/22/15 - 22:28 AM + * + * @author Jeroen Meulemeester + */ +public class NazgulTest { + + /** + * Verify if {@link Nazgul#getInstance(NazgulName)} returns the correct Nazgul multiton instance + */ + @Test + public void testGetInstance() { + for (final NazgulName name : NazgulName.values()) { + final Nazgul nazgul = Nazgul.getInstance(name); + assertNotNull(nazgul); + assertSame(nazgul, Nazgul.getInstance(name)); + assertEquals(name, nazgul.getName()); + } + } + +} diff --git a/mute-idiom/README.md b/mute-idiom/README.md new file mode 100644 index 000000000..bb674b648 --- /dev/null +++ b/mute-idiom/README.md @@ -0,0 +1,28 @@ +--- +layout: pattern +title: Mute Idiom +folder: mute-idiom +permalink: /patterns/mute-idiom/ +categories: Other +tags: + - Java + - Difficulty-Beginner + - Idiom +--- + +## Intent +Provide a template to suppress any exceptions that either are declared but cannot occur or should only be logged; +while executing some business logic. The template removes the need to write repeated `try-catch` blocks. + + +![alt text](./etc/mute-idiom.png "Mute Idiom") + +## Applicability +Use this idiom when + +* an API declares some exception but can never throw that exception eg. ByteArrayOutputStream bulk write method. +* you need to suppress some exception just by logging it, such as closing a resource. + +## Credits + +* [JOOQ: Mute Design Pattern](http://blog.jooq.org/2016/02/18/the-mute-design-pattern/) diff --git a/mute-idiom/etc/mute-idiom.png b/mute-idiom/etc/mute-idiom.png new file mode 100644 index 000000000..203bdafc4 Binary files /dev/null and b/mute-idiom/etc/mute-idiom.png differ diff --git a/mute-idiom/etc/mute-idiom.ucls b/mute-idiom/etc/mute-idiom.ucls new file mode 100644 index 000000000..7b4d2f916 --- /dev/null +++ b/mute-idiom/etc/mute-idiom.ucls @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/mute-idiom/pom.xml b/mute-idiom/pom.xml new file mode 100644 index 000000000..c138c5ed4 --- /dev/null +++ b/mute-idiom/pom.xml @@ -0,0 +1,34 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + mute-idiom + + + junit + junit + test + + + diff --git a/mute-idiom/src/main/java/com/iluwatar/mute/App.java b/mute-idiom/src/main/java/com/iluwatar/mute/App.java new file mode 100644 index 000000000..8a2aca41e --- /dev/null +++ b/mute-idiom/src/main/java/com/iluwatar/mute/App.java @@ -0,0 +1,103 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mute; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.sql.SQLException; + +/** + * Mute pattern is utilized when we need to suppress an exception due to an API flaw or in + * situation when all we can do to handle the exception is to log it. + * This pattern should not be used everywhere. It is very important to logically handle the + * exceptions in a system, but some situations like the ones described above require this pattern, + * so that we don't need to repeat + *

+ * 
+ *   try {
+ *     // code that may throwing exception we need to ignore or may never be thrown
+ *   } catch (Exception ex) {
+ *     // ignore by logging or throw error if unexpected exception occurs
+ *   }
+ * 
+ * 
every time we need to ignore an exception. + * + */ +public class App { + + /** + * Program entry point. + * + * @param args command line args. + * @throws Exception if any exception occurs + */ + public static void main(String[] args) throws Exception { + + useOfLoggedMute(); + + useOfMute(); + } + + /* + * Typically used when the API declares some exception but cannot do so. Usually a + * signature mistake.In this example out is not supposed to throw exception as it is a + * ByteArrayOutputStream. So we utilize mute, which will throw AssertionError if unexpected + * exception occurs. + */ + private static void useOfMute() { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Mute.mute(() -> out.write("Hello".getBytes())); + } + + private static void useOfLoggedMute() throws SQLException { + Resource resource = null; + try { + resource = acquireResource(); + utilizeResource(resource); + } finally { + closeResource(resource); + } + } + + /* + * All we can do while failed close of a resource is to log it. + */ + private static void closeResource(Resource resource) { + Mute.loggedMute(() -> resource.close()); + } + + private static void utilizeResource(Resource resource) throws SQLException { + System.out.println("Utilizing acquired resource: " + resource); + } + + private static Resource acquireResource() throws SQLException { + return new Resource() { + + @Override + public void close() throws IOException { + throw new IOException("Error in closing resource: " + this); + } + }; + } +} diff --git a/mute-idiom/src/main/java/com/iluwatar/mute/CheckedRunnable.java b/mute-idiom/src/main/java/com/iluwatar/mute/CheckedRunnable.java new file mode 100644 index 000000000..d1440636f --- /dev/null +++ b/mute-idiom/src/main/java/com/iluwatar/mute/CheckedRunnable.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mute; + +/** + * A runnable which may throw exception on execution. + * + */ +@FunctionalInterface +public interface CheckedRunnable { + /** + * Same as {@link Runnable#run()} with a possibility of exception in execution. + * @throws Exception if any exception occurs. + */ + void run() throws Exception; +} diff --git a/mute-idiom/src/main/java/com/iluwatar/mute/Mute.java b/mute-idiom/src/main/java/com/iluwatar/mute/Mute.java new file mode 100644 index 000000000..64169a8f5 --- /dev/null +++ b/mute-idiom/src/main/java/com/iluwatar/mute/Mute.java @@ -0,0 +1,68 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mute; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * A utility class that allows you to utilize mute idiom. + */ +public final class Mute { + + // The constructor is never meant to be called. + private Mute() {} + + /** + * Executes the runnable and throws the exception occurred within a {@link AssertionError}. + * This method should be utilized to mute the operations that are guaranteed not to throw an exception. + * For instance {@link ByteArrayOutputStream#write(byte[])} declares in it's signature that it can throw + * an {@link IOException}, but in reality it cannot. This is because the bulk write method is not overridden + * in {@link ByteArrayOutputStream}. + * + * @param runnable a runnable that should never throw an exception on execution. + */ + public static void mute(CheckedRunnable runnable) { + try { + runnable.run(); + } catch (Exception e) { + throw new AssertionError(e); + } + } + + /** + * Executes the runnable and logs the exception occurred on {@link System#err}. + * This method should be utilized to mute the operations about which most you can do is log. + * For instance while closing a connection to database, or cleaning up a resource, + * all you can do is log the exception occurred. + * + * @param runnable a runnable that may throw an exception on execution. + */ + public static void loggedMute(CheckedRunnable runnable) { + try { + runnable.run(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/mute-idiom/src/main/java/com/iluwatar/mute/Resource.java b/mute-idiom/src/main/java/com/iluwatar/mute/Resource.java new file mode 100644 index 000000000..6970d06bc --- /dev/null +++ b/mute-idiom/src/main/java/com/iluwatar/mute/Resource.java @@ -0,0 +1,35 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mute; + +import java.io.Closeable; + +/** + * Represents any resource that the application might acquire and that must be closed + * after it is utilized. Example of such resources can be a database connection, open + * files, sockets. + */ +public interface Resource extends Closeable { + +} diff --git a/mute-idiom/src/test/java/com/iluwatar/mute/AppTest.java b/mute-idiom/src/test/java/com/iluwatar/mute/AppTest.java new file mode 100644 index 000000000..8075d9c85 --- /dev/null +++ b/mute-idiom/src/test/java/com/iluwatar/mute/AppTest.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mute; + +import org.junit.Test; + +/** + * Tests that Mute idiom example runs without errors. + * + */ +public class AppTest { + + @Test + public void test() throws Exception { + App.main(null); + } +} diff --git a/mute-idiom/src/test/java/com/iluwatar/mute/MuteTest.java b/mute-idiom/src/test/java/com/iluwatar/mute/MuteTest.java new file mode 100644 index 000000000..58cbfe893 --- /dev/null +++ b/mute-idiom/src/test/java/com/iluwatar/mute/MuteTest.java @@ -0,0 +1,78 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mute; + +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.PrintStream; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class MuteTest { + + private static final String MESSAGE = "should not occur"; + + @Rule public ExpectedException exception = ExpectedException.none(); + + @Test + public void muteShouldRunTheCheckedRunnableAndNotThrowAnyExceptionIfCheckedRunnableDoesNotThrowAnyException() { + Mute.mute(() -> methodNotThrowingAnyException()); + } + + @Test + public void muteShouldRethrowUnexpectedExceptionAsAssertionError() throws Exception { + exception.expect(AssertionError.class); + exception.expectMessage(MESSAGE); + + Mute.mute(() -> methodThrowingException()); + } + + @Test + public void loggedMuteShouldRunTheCheckedRunnableAndNotThrowAnyExceptionIfCheckedRunnableDoesNotThrowAnyException() { + Mute.loggedMute(() -> methodNotThrowingAnyException()); + } + + @Test + public void loggedMuteShouldLogExceptionTraceBeforeSwallowingIt() throws IOException { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + System.setErr(new PrintStream(stream)); + + Mute.loggedMute(() -> methodThrowingException()); + + assertTrue(new String(stream.toByteArray()).contains(MESSAGE)); + } + + + private void methodNotThrowingAnyException() { + System.out.println("Executed successfully"); + } + + private void methodThrowingException() throws Exception { + throw new Exception(MESSAGE); + } +} diff --git a/mutex/README.md b/mutex/README.md new file mode 100644 index 000000000..84755872f --- /dev/null +++ b/mutex/README.md @@ -0,0 +1,30 @@ +--- +layout: pattern +title: Mutex +folder: mutex +permalink: /patterns/mutex/ +categories: Concurrency +tags: + - Java + - Difficulty-Intermediate +--- + +## Also known as +Mutual Exclusion Lock +Binary Semaphore + +## Intent +Create a lock which only allows a single thread to access a resource at any one instant. + +![alt text](./etc/mutex.png "Mutex") + +## Applicability +Use a Mutex when + +* you need to prevent two threads accessing a critical section at the same time +* concurrent access to a resource could lead to a race condition + +## Credits + +* [Lock (computer science)] (http://en.wikipedia.org/wiki/Lock_(computer_science)) +* [Semaphores] (http://tutorials.jenkov.com/java-concurrency/semaphores.html) diff --git a/mutex/etc/mutex.png b/mutex/etc/mutex.png new file mode 100644 index 000000000..3b7c966f8 Binary files /dev/null and b/mutex/etc/mutex.png differ diff --git a/mutex/pom.xml b/mutex/pom.xml new file mode 100644 index 000000000..852006cd4 --- /dev/null +++ b/mutex/pom.xml @@ -0,0 +1,42 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + mutex + + + junit + junit + test + + + diff --git a/mutex/src/main/java/com/iluwatar/mutex/App.java b/mutex/src/main/java/com/iluwatar/mutex/App.java new file mode 100644 index 000000000..f6494bd5c --- /dev/null +++ b/mutex/src/main/java/com/iluwatar/mutex/App.java @@ -0,0 +1,50 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mutex; + +/** + * A Mutex prevents multiple threads from accessing a resource simultaneously. + *

+ * In this example we have two thieves who are taking beans from a jar. + * Only one thief can take a bean at a time. This is ensured by a Mutex lock + * which must be acquired in order to access the jar. Each thief attempts to + * acquire the lock, take a bean and then release the lock. If the lock has + * already been acquired, the thief will be prevented from continuing (blocked) + * until the lock has been released. The thieves stop taking beans once there + * are no beans left to take. + */ +public class App { + + /** + * main method + */ + public static void main(String[] args) { + Mutex mutex = new Mutex(); + Jar jar = new Jar(1000, mutex); + Thief peter = new Thief("Peter", jar); + Thief john = new Thief("John", jar); + peter.start(); + john.start(); + } + +} diff --git a/mutex/src/main/java/com/iluwatar/mutex/Jar.java b/mutex/src/main/java/com/iluwatar/mutex/Jar.java new file mode 100644 index 000000000..4fe0a4637 --- /dev/null +++ b/mutex/src/main/java/com/iluwatar/mutex/Jar.java @@ -0,0 +1,67 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mutex; + +/** + * A Jar has a resource of beans which can only be accessed by a single Thief + * (thread) at any one time. A Mutex lock is used to prevent more than one Thief + * taking a bean simultaneously. + */ +public class Jar { + + /** + * The lock which must be acquired to access the beans resource. + */ + private final Lock lock; + + /** + * The resource within the jar. + */ + private int beans; + + public Jar(int beans, Lock lock) { + this.beans = beans; + this.lock = lock; + } + + /** + * Method for a thief to take a bean. + */ + public boolean takeBean() { + boolean success = false; + try { + lock.acquire(); + success = beans > 0; + if (success) { + beans = beans - 1; + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + lock.release(); + } + + return success; + } + +} diff --git a/mutex/src/main/java/com/iluwatar/mutex/Lock.java b/mutex/src/main/java/com/iluwatar/mutex/Lock.java new file mode 100644 index 000000000..fef67010e --- /dev/null +++ b/mutex/src/main/java/com/iluwatar/mutex/Lock.java @@ -0,0 +1,34 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mutex; + +/** + * Lock is an interface for a lock which can be acquired and released. + */ +public interface Lock { + + void acquire() throws InterruptedException; + + void release(); + +} diff --git a/mutex/src/main/java/com/iluwatar/mutex/Mutex.java b/mutex/src/main/java/com/iluwatar/mutex/Mutex.java new file mode 100644 index 000000000..8e08534cd --- /dev/null +++ b/mutex/src/main/java/com/iluwatar/mutex/Mutex.java @@ -0,0 +1,67 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mutex; + +/** + * Mutex is an implementation of a mutual exclusion lock. + */ +public class Mutex implements Lock { + + /** + * The current owner of the lock. + */ + private Object owner; + + /** + * Returns the current owner of the Mutex, or null if available + */ + public Object getOwner() { + return owner; + } + + /** + * Method called by a thread to acquire the lock. If the lock has already + * been acquired this will wait until the lock has been released to + * re-attempt the acquire. + */ + @Override + public synchronized void acquire() throws InterruptedException { + while (owner != null) { + wait(); + } + + owner = Thread.currentThread(); + } + + /** + * Method called by a thread to release the lock. + */ + @Override + public synchronized void release() { + if (Thread.currentThread() == owner) { + owner = null; + notify(); + } + } + +} diff --git a/mutex/src/main/java/com/iluwatar/mutex/Thief.java b/mutex/src/main/java/com/iluwatar/mutex/Thief.java new file mode 100644 index 000000000..d2225876c --- /dev/null +++ b/mutex/src/main/java/com/iluwatar/mutex/Thief.java @@ -0,0 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mutex; + +/** + * Thief is a class which continually tries to acquire a jar and take a bean + * from it. When the jar is empty the thief stops. + */ +public class Thief extends Thread { + + /** + * The name of the thief. + */ + private final String name; + + /** + * The jar + */ + private final Jar jar; + + public Thief(String name, Jar jar) { + this.name = name; + this.jar = jar; + } + + /** + * In the run method the thief repeatedly tries to take a bean until none + * are left. + */ + @Override + public void run() { + int beans = 0; + + while (jar.takeBean()) { + beans = beans + 1; + System.out.println(name + " took a bean."); + } + + System.out.println(name + " took " + beans + " beans."); + } + +} diff --git a/mutex/src/test/java/com/iluwatar/mutex/AppTest.java b/mutex/src/test/java/com/iluwatar/mutex/AppTest.java new file mode 100644 index 000000000..f3a123519 --- /dev/null +++ b/mutex/src/test/java/com/iluwatar/mutex/AppTest.java @@ -0,0 +1,34 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mutex; + +import org.junit.Test; +import java.io.IOException; + +public class AppTest{ + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } +} \ No newline at end of file diff --git a/mutex/src/test/java/com/iluwatar/mutex/JarTest.java b/mutex/src/test/java/com/iluwatar/mutex/JarTest.java new file mode 100644 index 000000000..97101466a --- /dev/null +++ b/mutex/src/test/java/com/iluwatar/mutex/JarTest.java @@ -0,0 +1,43 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mutex; + +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * Test case for taking beans from a Jar + */ +public class JarTest { + + @Test + public void testTakeBeans() { + Mutex mutex = new Mutex(); + Jar jar = new Jar(10, mutex); + for (int i = 0; i < 10; i++) { + assertTrue(jar.takeBean()); + } + assertFalse(jar.takeBean()); + } + +} \ No newline at end of file diff --git a/mutex/src/test/java/com/iluwatar/mutex/MutexTest.java b/mutex/src/test/java/com/iluwatar/mutex/MutexTest.java new file mode 100644 index 000000000..8b3e82cb4 --- /dev/null +++ b/mutex/src/test/java/com/iluwatar/mutex/MutexTest.java @@ -0,0 +1,47 @@ +/** + * The MIT License + * Copyright (c) 2014 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.mutex; + +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * Test case for acquiring and releasing a Mutex + */ +public class MutexTest { + + @Test + public void acquireReleaseTest() { + Mutex mutex = new Mutex(); + assertNull(mutex.getOwner()); + try { + mutex.acquire(); + assertEquals(mutex.getOwner(), Thread.currentThread()); + } catch (InterruptedException e) { + fail(e.toString()); + } + mutex.release(); + assertNull(mutex.getOwner()); + } + +} \ No newline at end of file diff --git a/naked-objects/index.md b/naked-objects/README.md similarity index 63% rename from naked-objects/index.md rename to naked-objects/README.md index 805cea810..66e6ac2b0 100644 --- a/naked-objects/index.md +++ b/naked-objects/README.md @@ -4,25 +4,29 @@ title: Naked Objects folder: naked-objects permalink: /patterns/naked-objects/ categories: Architectural -tags: Java +tags: + - Java + - Difficulty-Expert --- -**Intent:** The Naked Objects architectural pattern is well suited for rapid +## Intent +The Naked Objects architectural pattern is well suited for rapid prototyping. Using the pattern, you only need to write the domain objects, everything else is autogenerated by the framework. ![alt text](./etc/naked-objects.png "Naked Objects") -**Applicability:** Use the Naked Objects pattern when +## Applicability +Use the Naked Objects pattern when * you are prototyping and need fast development cycle * an autogenerated user interface is good enough * you want to automatically publish the domain as REST services -**Real world examples:** +## Real world examples * [Apache Isis](https://isis.apache.org/) -**Credits:** +## Credits -* [Richard Pawson - Naked Objects](http://downloads.nakedobjects.net/resources/Pawson%20thesis.pdf) +* [Richard Pawson - Naked Objects](https://isis.apache.org/resources/thesis/Pawson-Naked-Objects-thesis.pdf) diff --git a/naked-objects/dom/pom.xml b/naked-objects/dom/pom.xml index b0efdb435..119416694 100644 --- a/naked-objects/dom/pom.xml +++ b/naked-objects/dom/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.7.0 + 1.13.0-SNAPSHOT naked-objects-dom diff --git a/naked-objects/dom/src/main/java/domainapp/dom/app/homepage/HomePageService.java b/naked-objects/dom/src/main/java/domainapp/dom/app/homepage/HomePageService.java index 641c39ae7..162cd3bb4 100644 --- a/naked-objects/dom/src/main/java/domainapp/dom/app/homepage/HomePageService.java +++ b/naked-objects/dom/src/main/java/domainapp/dom/app/homepage/HomePageService.java @@ -1,20 +1,16 @@ /* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. */ package domainapp.dom.app.homepage; @@ -25,27 +21,24 @@ import org.apache.isis.applib.annotation.HomePage; import org.apache.isis.applib.annotation.NatureOfService; import org.apache.isis.applib.annotation.SemanticsOf; -@DomainService( - nature = NatureOfService.VIEW_CONTRIBUTIONS_ONLY // trick to suppress the actions from the top-level menu -) +@DomainService(nature = NatureOfService.VIEW_CONTRIBUTIONS_ONLY) public class HomePageService { - //region > homePage (action) + // endregion - @Action( - semantics = SemanticsOf.SAFE - ) - @HomePage - public HomePageViewModel homePage() { - return container.injectServicesInto(new HomePageViewModel()); - } + // region > injected services - //endregion + @javax.inject.Inject + DomainObjectContainer container; - //region > injected services + // endregion - @javax.inject.Inject - DomainObjectContainer container; + // region > homePage (action) + + @Action(semantics = SemanticsOf.SAFE) + @HomePage + public HomePageViewModel homePage() { + return container.injectServicesInto(new HomePageViewModel()); + } - //endregion } diff --git a/naked-objects/dom/src/main/java/domainapp/dom/app/homepage/HomePageViewModel.java b/naked-objects/dom/src/main/java/domainapp/dom/app/homepage/HomePageViewModel.java index 83015d057..f367a39fd 100644 --- a/naked-objects/dom/src/main/java/domainapp/dom/app/homepage/HomePageViewModel.java +++ b/naked-objects/dom/src/main/java/domainapp/dom/app/homepage/HomePageViewModel.java @@ -1,20 +1,16 @@ /* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. */ package domainapp.dom.app.homepage; @@ -28,23 +24,26 @@ import domainapp.dom.modules.simple.SimpleObjects; @ViewModel public class HomePageViewModel { - //region > title - public String title() { - return getObjects().size() + " objects"; - } - //endregion + // endregion - //region > object (collection) - @org.apache.isis.applib.annotation.HomePage - public List getObjects() { - return simpleObjects.listAll(); - } - //endregion + // region > injected services - //region > injected services + @javax.inject.Inject + SimpleObjects simpleObjects; - @javax.inject.Inject - SimpleObjects simpleObjects; + // endregion + + // region > title + public String title() { + return getObjects().size() + " objects"; + } + + // endregion + + // region > object (collection) + @org.apache.isis.applib.annotation.HomePage + public List getObjects() { + return simpleObjects.listAll(); + } - //endregion } diff --git a/naked-objects/dom/src/main/java/domainapp/dom/modules/simple/SimpleObject.java b/naked-objects/dom/src/main/java/domainapp/dom/modules/simple/SimpleObject.java index a4fba6dda..bbbd54b00 100644 --- a/naked-objects/dom/src/main/java/domainapp/dom/modules/simple/SimpleObject.java +++ b/naked-objects/dom/src/main/java/domainapp/dom/modules/simple/SimpleObject.java @@ -1,20 +1,16 @@ /* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. */ package domainapp.dom.modules.simple; @@ -37,113 +33,94 @@ import org.apache.isis.applib.services.eventbus.ActionDomainEvent; import org.apache.isis.applib.services.i18n.TranslatableString; import org.apache.isis.applib.util.ObjectContracts; -@javax.jdo.annotations.PersistenceCapable( - identityType=IdentityType.DATASTORE, - schema = "simple", - table = "SimpleObject" -) +@javax.jdo.annotations.PersistenceCapable(identityType = IdentityType.DATASTORE, schema = "simple", + table = "SimpleObject") @javax.jdo.annotations.DatastoreIdentity( - strategy=javax.jdo.annotations.IdGeneratorStrategy.IDENTITY, - column="id") -@javax.jdo.annotations.Version( - strategy=VersionStrategy.VERSION_NUMBER, - column="version") + strategy = javax.jdo.annotations.IdGeneratorStrategy.IDENTITY, column = "id") +@javax.jdo.annotations.Version(strategy = VersionStrategy.VERSION_NUMBER, column = "version") @javax.jdo.annotations.Queries({ - @javax.jdo.annotations.Query( - name = "find", language = "JDOQL", - value = "SELECT " - + "FROM domainapp.dom.modules.simple.SimpleObject "), - @javax.jdo.annotations.Query( - name = "findByName", language = "JDOQL", - value = "SELECT " - + "FROM domainapp.dom.modules.simple.SimpleObject " - + "WHERE name.indexOf(:name) >= 0 ") -}) -@javax.jdo.annotations.Unique(name="SimpleObject_name_UNQ", members = {"name"}) + @javax.jdo.annotations.Query(name = "find", language = "JDOQL", value = "SELECT " + + "FROM domainapp.dom.modules.simple.SimpleObject "), + @javax.jdo.annotations.Query(name = "findByName", language = "JDOQL", value = "SELECT " + + "FROM domainapp.dom.modules.simple.SimpleObject " + "WHERE name.indexOf(:name) >= 0 ")}) +@javax.jdo.annotations.Unique(name = "SimpleObject_name_UNQ", members = {"name"}) @DomainObject -@DomainObjectLayout( - bookmarking = BookmarkPolicy.AS_ROOT, - cssClassFa = "fa-flag" -) +@DomainObjectLayout(bookmarking = BookmarkPolicy.AS_ROOT, cssClassFa = "fa-flag") public class SimpleObject implements Comparable { + // endregion - //region > identificatiom - public TranslatableString title() { - return TranslatableString.tr("Object: {name}", "name", getName()); + // region > name (property) + + private String name; + + // region > identificatiom + public TranslatableString title() { + return TranslatableString.tr("Object: {name}", "name", getName()); + } + + @javax.jdo.annotations.Column(allowsNull = "false", length = 40) + @Title(sequence = "1") + @Property(editing = Editing.DISABLED) + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + // endregion + + // region > updateName (action) + + public static class UpdateNameDomainEvent extends ActionDomainEvent { + public UpdateNameDomainEvent(final SimpleObject source, final Identifier identifier, + final Object... arguments) { + super(source, identifier, arguments); } - //endregion + } - //region > name (property) + @Action(domainEvent = UpdateNameDomainEvent.class) + public SimpleObject updateName( + @Parameter(maxLength = 40) @ParameterLayout(named = "New name") final String name) { + setName(name); + return this; + } - private String name; + public String default0UpdateName() { + return getName(); + } - @javax.jdo.annotations.Column(allowsNull="false", length = 40) - @Title(sequence="1") - @Property( - editing = Editing.DISABLED - ) - public String getName() { - return name; - } + public TranslatableString validateUpdateName(final String name) { + return name.contains("!") ? TranslatableString.tr("Exclamation mark is not allowed") : null; + } - public void setName(final String name) { - this.name = name; - } + // endregion - // endregion + // region > version (derived property) + public Long getVersionSequence() { + return (Long) JDOHelper.getVersion(this); + } - //region > updateName (action) + // endregion - public static class UpdateNameDomainEvent extends ActionDomainEvent { - public UpdateNameDomainEvent(final SimpleObject source, final Identifier identifier, final Object... arguments) { - super(source, identifier, arguments); - } - } + // region > compareTo - @Action( - domainEvent = UpdateNameDomainEvent.class - ) - public SimpleObject updateName( - @Parameter(maxLength = 40) - @ParameterLayout(named = "New name") - final String name) { - setName(name); - return this; - } + @Override + public int compareTo(final SimpleObject other) { + return ObjectContracts.compare(this, other, "name"); + } - public String default0UpdateName() { - return getName(); - } + // endregion - public TranslatableString validateUpdateName(final String name) { - return name.contains("!")? TranslatableString.tr("Exclamation mark is not allowed"): null; - } + // region > injected services - //endregion + @javax.inject.Inject + @SuppressWarnings("unused") + private DomainObjectContainer container; - //region > version (derived property) - public Long getVersionSequence() { - return (Long) JDOHelper.getVersion(this); - } - //endregion - - //region > compareTo - - @Override - public int compareTo(final SimpleObject other) { - return ObjectContracts.compare(this, other, "name"); - } - - //endregion - - //region > injected services - - @javax.inject.Inject - @SuppressWarnings("unused") - private DomainObjectContainer container; - - //endregion + // endregion } diff --git a/naked-objects/dom/src/main/java/domainapp/dom/modules/simple/SimpleObjects.java b/naked-objects/dom/src/main/java/domainapp/dom/modules/simple/SimpleObjects.java index 0634dd16a..5ebad0159 100644 --- a/naked-objects/dom/src/main/java/domainapp/dom/modules/simple/SimpleObjects.java +++ b/naked-objects/dom/src/main/java/domainapp/dom/modules/simple/SimpleObjects.java @@ -1,20 +1,16 @@ /* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. */ package domainapp.dom.modules.simple; @@ -37,71 +33,60 @@ import org.apache.isis.applib.services.i18n.TranslatableString; @DomainService(repositoryFor = SimpleObject.class) @DomainServiceLayout(menuOrder = "10") public class SimpleObjects { + // endregion - //region > title - public TranslatableString title() { - return TranslatableString.tr("Simple Objects"); + // region > injected services + + @javax.inject.Inject + DomainObjectContainer container; + + // endregion + + // region > title + public TranslatableString title() { + return TranslatableString.tr("Simple Objects"); + } + + // endregion + + // region > listAll (action) + @Action(semantics = SemanticsOf.SAFE) + @ActionLayout(bookmarking = BookmarkPolicy.AS_ROOT) + @MemberOrder(sequence = "1") + public List listAll() { + return container.allInstances(SimpleObject.class); + } + + // endregion + + // region > findByName (action) + @Action(semantics = SemanticsOf.SAFE) + @ActionLayout(bookmarking = BookmarkPolicy.AS_ROOT) + @MemberOrder(sequence = "2") + public List findByName(@ParameterLayout(named = "Name") final String name) { + return container.allMatches(new QueryDefault<>(SimpleObject.class, "findByName", "name", name)); + } + + // endregion + + // region > create (action) + public static class CreateDomainEvent extends ActionDomainEvent { + public CreateDomainEvent(final SimpleObjects source, final Identifier identifier, + final Object... arguments) { + super(source, identifier, arguments); } - //endregion + } - //region > listAll (action) - @Action( - semantics = SemanticsOf.SAFE - ) - @ActionLayout( - bookmarking = BookmarkPolicy.AS_ROOT - ) - @MemberOrder(sequence = "1") - public List listAll() { - return container.allInstances(SimpleObject.class); - } - //endregion + /** + * Create simple object + */ + @Action(domainEvent = CreateDomainEvent.class) + @MemberOrder(sequence = "3") + public SimpleObject create(@ParameterLayout(named = "Name") final String name) { + final SimpleObject obj = container.newTransientInstance(SimpleObject.class); + obj.setName(name); + container.persistIfNotAlready(obj); + return obj; + } - //region > findByName (action) - @Action( - semantics = SemanticsOf.SAFE - ) - @ActionLayout( - bookmarking = BookmarkPolicy.AS_ROOT - ) - @MemberOrder(sequence = "2") - public List findByName( - @ParameterLayout(named="Name") - final String name - ) { - return container.allMatches( - new QueryDefault<>( - SimpleObject.class, - "findByName", - "name", name)); - } - //endregion - - //region > create (action) - public static class CreateDomainEvent extends ActionDomainEvent { - public CreateDomainEvent(final SimpleObjects source, final Identifier identifier, final Object... arguments) { - super(source, identifier, arguments); - } - } - - @Action( - domainEvent = CreateDomainEvent.class - ) - @MemberOrder(sequence = "3") - public SimpleObject create( - final @ParameterLayout(named="Name") String name) { - final SimpleObject obj = container.newTransientInstance(SimpleObject.class); - obj.setName(name); - container.persistIfNotAlready(obj); - return obj; - } - - //endregion - - //region > injected services - - @javax.inject.Inject - DomainObjectContainer container; - - //endregion } diff --git a/naked-objects/dom/src/test/java/domainapp/dom/modules/simple/SimpleObjectTest.java b/naked-objects/dom/src/test/java/domainapp/dom/modules/simple/SimpleObjectTest.java index e29b3f246..fc62239c2 100644 --- a/naked-objects/dom/src/test/java/domainapp/dom/modules/simple/SimpleObjectTest.java +++ b/naked-objects/dom/src/test/java/domainapp/dom/modules/simple/SimpleObjectTest.java @@ -1,18 +1,16 @@ /** - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. */ package domainapp.dom.modules.simple; @@ -23,27 +21,27 @@ import static org.assertj.core.api.Assertions.assertThat; public class SimpleObjectTest { - SimpleObject simpleObject; + SimpleObject simpleObject; - @Before - public void setUp() throws Exception { - simpleObject = new SimpleObject(); - } - - public static class Name extends SimpleObjectTest { - - @Test - public void happyCase() throws Exception { - // given - String name = "Foobar"; - assertThat(simpleObject.getName()).isNull(); - - // when - simpleObject.setName(name); - - // then - assertThat(simpleObject.getName()).isEqualTo(name); - } + @Before + public void setUp() throws Exception { + simpleObject = new SimpleObject(); + } + + public static class Name extends SimpleObjectTest { + + @Test + public void happyCase() throws Exception { + // given + String name = "Foobar"; + assertThat(simpleObject.getName()).isNull(); + + // when + simpleObject.setName(name); + + // then + assertThat(simpleObject.getName()).isEqualTo(name); } + } } diff --git a/naked-objects/dom/src/test/java/domainapp/dom/modules/simple/SimpleObjectsTest.java b/naked-objects/dom/src/test/java/domainapp/dom/modules/simple/SimpleObjectsTest.java index a41d25ad9..47cad61b8 100644 --- a/naked-objects/dom/src/test/java/domainapp/dom/modules/simple/SimpleObjectsTest.java +++ b/naked-objects/dom/src/test/java/domainapp/dom/modules/simple/SimpleObjectsTest.java @@ -1,18 +1,16 @@ /** - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. */ package domainapp.dom.modules.simple; @@ -35,70 +33,70 @@ import static org.assertj.core.api.Assertions.assertThat; public class SimpleObjectsTest { - @Rule - public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES); + @Rule + public JUnitRuleMockery2 context = JUnitRuleMockery2.createFor(Mode.INTERFACES_AND_CLASSES); - @Mock - DomainObjectContainer mockContainer; - - SimpleObjects simpleObjects; + @Mock + DomainObjectContainer mockContainer; - @Before - public void setUp() throws Exception { - simpleObjects = new SimpleObjects(); - simpleObjects.container = mockContainer; - } + SimpleObjects simpleObjects; - public static class Create extends SimpleObjectsTest { + @Before + public void setUp() throws Exception { + simpleObjects = new SimpleObjects(); + simpleObjects.container = mockContainer; + } - @Test - public void happyCase() throws Exception { + public static class Create extends SimpleObjectsTest { - // given - final SimpleObject simpleObject = new SimpleObject(); + @Test + public void happyCase() throws Exception { - final Sequence seq = context.sequence("create"); - context.checking(new Expectations() { - { - oneOf(mockContainer).newTransientInstance(SimpleObject.class); - inSequence(seq); - will(returnValue(simpleObject)); + // given + final SimpleObject simpleObject = new SimpleObject(); - oneOf(mockContainer).persistIfNotAlready(simpleObject); - inSequence(seq); - } - }); + final Sequence seq = context.sequence("create"); + context.checking(new Expectations() { + { + oneOf(mockContainer).newTransientInstance(SimpleObject.class); + inSequence(seq); + will(returnValue(simpleObject)); - // when - final SimpleObject obj = simpleObjects.create("Foobar"); - - // then - assertThat(obj).isEqualTo(simpleObject); - assertThat(obj.getName()).isEqualTo("Foobar"); + oneOf(mockContainer).persistIfNotAlready(simpleObject); + inSequence(seq); } + }); + // when + final SimpleObject obj = simpleObjects.create("Foobar"); + + // then + assertThat(obj).isEqualTo(simpleObject); + assertThat(obj.getName()).isEqualTo("Foobar"); } - public static class ListAll extends SimpleObjectsTest { + } - @Test - public void happyCase() throws Exception { + public static class ListAll extends SimpleObjectsTest { - // given - final List all = Lists.newArrayList(); + @Test + public void happyCase() throws Exception { - context.checking(new Expectations() { - { - oneOf(mockContainer).allInstances(SimpleObject.class); - will(returnValue(all)); - } - }); + // given + final List all = Lists.newArrayList(); - // when - final List list = simpleObjects.listAll(); - - // then - assertThat(list).isEqualTo(all); + context.checking(new Expectations() { + { + oneOf(mockContainer).allInstances(SimpleObject.class); + will(returnValue(all)); } + }); + + // when + final List list = simpleObjects.listAll(); + + // then + assertThat(list).isEqualTo(all); } + } } diff --git a/naked-objects/fixture/pom.xml b/naked-objects/fixture/pom.xml index bbed2fe1b..46b281468 100644 --- a/naked-objects/fixture/pom.xml +++ b/naked-objects/fixture/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.7.0 + 1.13.0-SNAPSHOT naked-objects-fixture diff --git a/naked-objects/fixture/src/main/java/domainapp/fixture/DomainAppFixturesProvider.java b/naked-objects/fixture/src/main/java/domainapp/fixture/DomainAppFixturesProvider.java index ab6b6b4be..ccc11f2b8 100644 --- a/naked-objects/fixture/src/main/java/domainapp/fixture/DomainAppFixturesProvider.java +++ b/naked-objects/fixture/src/main/java/domainapp/fixture/DomainAppFixturesProvider.java @@ -1,20 +1,16 @@ /* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. */ package domainapp.fixture; @@ -31,14 +27,12 @@ import domainapp.fixture.scenarios.RecreateSimpleObjects; */ @DomainService(nature = NatureOfService.DOMAIN) public class DomainAppFixturesProvider implements FixtureScriptsSpecificationProvider { - @Override - public FixtureScriptsSpecification getSpecification() { - return FixtureScriptsSpecification - .builder(DomainAppFixturesProvider.class) - .with(FixtureScripts.MultipleExecutionStrategy.EXECUTE) - .withRunScriptDefault(RecreateSimpleObjects.class) - .withRunScriptDropDown(FixtureScriptsSpecification.DropDownPolicy.CHOICES) - .withRecreate(RecreateSimpleObjects.class) - .build(); - } + @Override + public FixtureScriptsSpecification getSpecification() { + return FixtureScriptsSpecification.builder(DomainAppFixturesProvider.class) + .with(FixtureScripts.MultipleExecutionStrategy.EXECUTE) + .withRunScriptDefault(RecreateSimpleObjects.class) + .withRunScriptDropDown(FixtureScriptsSpecification.DropDownPolicy.CHOICES) + .withRecreate(RecreateSimpleObjects.class).build(); + } } diff --git a/naked-objects/fixture/src/main/java/domainapp/fixture/modules/simple/SimpleObjectCreate.java b/naked-objects/fixture/src/main/java/domainapp/fixture/modules/simple/SimpleObjectCreate.java index 926217d09..f0617fea2 100644 --- a/naked-objects/fixture/src/main/java/domainapp/fixture/modules/simple/SimpleObjectCreate.java +++ b/naked-objects/fixture/src/main/java/domainapp/fixture/modules/simple/SimpleObjectCreate.java @@ -1,20 +1,16 @@ /* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. */ package domainapp.fixture.modules.simple; @@ -26,46 +22,48 @@ import domainapp.dom.modules.simple.SimpleObjects; public class SimpleObjectCreate extends FixtureScript { - //region > name (input) - private String name; - /** - * Name of the object (required) - */ - public String getName() { - return name; - } - - public SimpleObjectCreate setName(final String name) { - this.name = name; - return this; - } - //endregion + // endregion - //region > simpleObject (output) - private SimpleObject simpleObject; + // region > simpleObject (output) + private SimpleObject simpleObject; - /** - * The created simple object (output). - * @return - */ - public SimpleObject getSimpleObject() { - return simpleObject; - } - //endregion + @javax.inject.Inject + private SimpleObjects simpleObjects; - @Override - protected void execute(final ExecutionContext ec) { + // region > name (input) + private String name; - String name = checkParam("name", ec, String.class); + /** + * Name of the object (required) + */ + public String getName() { + return name; + } - this.simpleObject = wrap(simpleObjects).create(name); + public SimpleObjectCreate setName(final String name) { + this.name = name; + return this; + } + + /** + * The created simple object (output). + */ + public SimpleObject getSimpleObject() { + return simpleObject; + } - // also make available to UI - ec.addResult(this, simpleObject); - } + // endregion - @javax.inject.Inject - private SimpleObjects simpleObjects; + @Override + protected void execute(final ExecutionContext ec) { + String paramName = checkParam("name", ec, String.class); + + this.simpleObject = wrap(simpleObjects).create(paramName); + + // also make available to UI + ec.addResult(this, simpleObject); + } + } diff --git a/naked-objects/fixture/src/main/java/domainapp/fixture/modules/simple/SimpleObjectsTearDown.java b/naked-objects/fixture/src/main/java/domainapp/fixture/modules/simple/SimpleObjectsTearDown.java index cc06eb4ac..7000bf4c0 100644 --- a/naked-objects/fixture/src/main/java/domainapp/fixture/modules/simple/SimpleObjectsTearDown.java +++ b/naked-objects/fixture/src/main/java/domainapp/fixture/modules/simple/SimpleObjectsTearDown.java @@ -1,20 +1,16 @@ /* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. */ package domainapp.fixture.modules.simple; @@ -24,13 +20,12 @@ import org.apache.isis.applib.services.jdosupport.IsisJdoSupport; public class SimpleObjectsTearDown extends FixtureScript { - @Override - protected void execute(ExecutionContext executionContext) { - isisJdoSupport.executeUpdate("delete from \"simple\".\"SimpleObject\""); - } - - - @javax.inject.Inject - private IsisJdoSupport isisJdoSupport; + @javax.inject.Inject + private IsisJdoSupport isisJdoSupport; + @Override + protected void execute(ExecutionContext executionContext) { + isisJdoSupport.executeUpdate("delete from \"simple\".\"SimpleObject\""); + } + } diff --git a/naked-objects/fixture/src/main/java/domainapp/fixture/scenarios/RecreateSimpleObjects.java b/naked-objects/fixture/src/main/java/domainapp/fixture/scenarios/RecreateSimpleObjects.java index 072769e29..62ad0405a 100644 --- a/naked-objects/fixture/src/main/java/domainapp/fixture/scenarios/RecreateSimpleObjects.java +++ b/naked-objects/fixture/src/main/java/domainapp/fixture/scenarios/RecreateSimpleObjects.java @@ -1,20 +1,16 @@ /* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. */ package domainapp.fixture.scenarios; @@ -33,60 +29,63 @@ import domainapp.fixture.modules.simple.SimpleObjectsTearDown; public class RecreateSimpleObjects extends FixtureScript { - public final List NAMES = Collections.unmodifiableList(Arrays.asList( - "Foo", "Bar", "Baz", "Frodo", "Froyo", "Fizz", "Bip", "Bop", "Bang", "Boo")); + public final List names = Collections.unmodifiableList(Arrays.asList("Foo", "Bar", "Baz", + "Frodo", "Froyo", "Fizz", "Bip", "Bop", "Bang", "Boo")); - public RecreateSimpleObjects() { - withDiscoverability(Discoverability.DISCOVERABLE); + // region > number (optional input) + private Integer number; + + // endregion + + // region > simpleObjects (output) + private final List simpleObjects = Lists.newArrayList(); + + public RecreateSimpleObjects() { + withDiscoverability(Discoverability.DISCOVERABLE); + } + + /** + * The number of objects to create, up to 10; optional, defaults to 3. + */ + public Integer getNumber() { + return number; + } + + public RecreateSimpleObjects setNumber(final Integer number) { + this.number = number; + return this; + } + + /** + * The simpleobjects created by this fixture (output). + */ + public List getSimpleObjects() { + return simpleObjects; + } + + // endregion + + @Override + protected void execute(final ExecutionContext ec) { + + // defaults + final int paramNumber = defaultParam("number", ec, 3); + + // validate + if (paramNumber < 0 || paramNumber > names.size()) { + throw new IllegalArgumentException(String.format("number must be in range [0,%d)", + names.size())); } - //region > number (optional input) - private Integer number; + // + // execute + // + ec.executeChild(this, new SimpleObjectsTearDown()); - /** - * The number of objects to create, up to 10; optional, defaults to 3. - */ - public Integer getNumber() { - return number; - } - - public RecreateSimpleObjects setNumber(final Integer number) { - this.number = number; - return this; - } - //endregion - - //region > simpleObjects (output) - private final List simpleObjects = Lists.newArrayList(); - - /** - * The simpleobjects created by this fixture (output). - */ - public List getSimpleObjects() { - return simpleObjects; - } - //endregion - - @Override - protected void execute(final ExecutionContext ec) { - - // defaults - final int number = defaultParam("number", ec, 3); - - // validate - if(number < 0 || number > NAMES.size()) { - throw new IllegalArgumentException(String.format("number must be in range [0,%d)", NAMES.size())); - } - - // - // execute - // - ec.executeChild(this, new SimpleObjectsTearDown()); - - for (int i = 0; i < number; i++) { - final SimpleObjectCreate fs = new SimpleObjectCreate().setName(NAMES.get(i)); - ec.executeChild(this, fs.getName(), fs); - simpleObjects.add(fs.getSimpleObject()); - } + for (int i = 0; i < paramNumber; i++) { + final SimpleObjectCreate fs = new SimpleObjectCreate().setName(names.get(i)); + ec.executeChild(this, fs.getName(), fs); + simpleObjects.add(fs.getSimpleObject()); } + } } diff --git a/naked-objects/integtests/pom.xml b/naked-objects/integtests/pom.xml index 221b072e5..dccaf64a3 100644 --- a/naked-objects/integtests/pom.xml +++ b/naked-objects/integtests/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.7.0 + 1.13.0-SNAPSHOT naked-objects-integtests diff --git a/naked-objects/integtests/src/test/java/domainapp/integtests/bootstrap/SimpleAppSystemInitializer.java b/naked-objects/integtests/src/test/java/domainapp/integtests/bootstrap/SimpleAppSystemInitializer.java index 28e9d3786..b7c76d0ed 100644 --- a/naked-objects/integtests/src/test/java/domainapp/integtests/bootstrap/SimpleAppSystemInitializer.java +++ b/naked-objects/integtests/src/test/java/domainapp/integtests/bootstrap/SimpleAppSystemInitializer.java @@ -1,20 +1,16 @@ /* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. */ package domainapp.integtests.bootstrap; @@ -23,32 +19,39 @@ import org.apache.isis.core.integtestsupport.IsisSystemForTest; import org.apache.isis.objectstore.jdo.datanucleus.DataNucleusPersistenceMechanismInstaller; import org.apache.isis.objectstore.jdo.datanucleus.IsisConfigurationForJdoIntegTests; -public class SimpleAppSystemInitializer { +public final class SimpleAppSystemInitializer { - public static void initIsft() { - IsisSystemForTest isft = IsisSystemForTest.getElseNull(); - if(isft == null) { - isft = new SimpleAppSystemBuilder().build().setUpSystem(); - IsisSystemForTest.set(isft); - } + private SimpleAppSystemInitializer() { + } + + /** + * Init test system + */ + public static void initIsft() { + IsisSystemForTest isft = IsisSystemForTest.getElseNull(); + if (isft == null) { + isft = new SimpleAppSystemBuilder().build().setUpSystem(); + IsisSystemForTest.set(isft); + } + } + + private static class SimpleAppSystemBuilder extends IsisSystemForTest.Builder { + + public SimpleAppSystemBuilder() { + withLoggingAt(org.apache.log4j.Level.INFO); + with(testConfiguration()); + with(new DataNucleusPersistenceMechanismInstaller()); + + // services annotated with @DomainService + withServicesIn("domainapp"); } - private static class SimpleAppSystemBuilder extends IsisSystemForTest.Builder { + private static IsisConfiguration testConfiguration() { + final IsisConfigurationForJdoIntegTests testConfiguration = + new IsisConfigurationForJdoIntegTests(); - public SimpleAppSystemBuilder() { - withLoggingAt(org.apache.log4j.Level.INFO); - with(testConfiguration()); - with(new DataNucleusPersistenceMechanismInstaller()); - - // services annotated with @DomainService - withServicesIn( "domainapp" ); - } - - private static IsisConfiguration testConfiguration() { - final IsisConfigurationForJdoIntegTests testConfiguration = new IsisConfigurationForJdoIntegTests(); - - testConfiguration.addRegisterEntitiesPackagePrefix("domainapp.dom.modules"); - return testConfiguration; - } + testConfiguration.addRegisterEntitiesPackagePrefix("domainapp.dom.modules"); + return testConfiguration; } + } } diff --git a/naked-objects/integtests/src/test/java/domainapp/integtests/specglue/BootstrappingGlue.java b/naked-objects/integtests/src/test/java/domainapp/integtests/specglue/BootstrappingGlue.java index b175d4744..190e1f5bb 100644 --- a/naked-objects/integtests/src/test/java/domainapp/integtests/specglue/BootstrappingGlue.java +++ b/naked-objects/integtests/src/test/java/domainapp/integtests/specglue/BootstrappingGlue.java @@ -1,18 +1,16 @@ /** -O * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * O * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. */ package domainapp.integtests.specglue; @@ -25,17 +23,17 @@ import domainapp.integtests.bootstrap.SimpleAppSystemInitializer; public class BootstrappingGlue extends CukeGlueAbstract { - @Before(value={"@integration"}, order=100) - public void beforeScenarioIntegrationScope() { - org.apache.log4j.PropertyConfigurator.configure("logging.properties"); - SimpleAppSystemInitializer.initIsft(); - - before(ScenarioExecutionScope.INTEGRATION); - } + @Before(value = {"@integration"}, order = 100) + public void beforeScenarioIntegrationScope() { + org.apache.log4j.PropertyConfigurator.configure("logging.properties"); + SimpleAppSystemInitializer.initIsft(); - @After - public void afterScenario(cucumber.api.Scenario sc) { - assertMocksSatisfied(); - after(sc); - } + before(ScenarioExecutionScope.INTEGRATION); + } + + @After + public void afterScenario(cucumber.api.Scenario sc) { + assertMocksSatisfied(); + after(sc); + } } diff --git a/naked-objects/integtests/src/test/java/domainapp/integtests/specglue/CatalogOfFixturesGlue.java b/naked-objects/integtests/src/test/java/domainapp/integtests/specglue/CatalogOfFixturesGlue.java index a2d5c8985..2fcb7cca7 100644 --- a/naked-objects/integtests/src/test/java/domainapp/integtests/specglue/CatalogOfFixturesGlue.java +++ b/naked-objects/integtests/src/test/java/domainapp/integtests/specglue/CatalogOfFixturesGlue.java @@ -1,18 +1,16 @@ /** - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. */ package domainapp.integtests.specglue; @@ -23,9 +21,8 @@ import domainapp.fixture.scenarios.RecreateSimpleObjects; public class CatalogOfFixturesGlue extends CukeGlueAbstract { - @Before(value={"@integration", "@SimpleObjectsFixture"}, order=20000) - public void integrationFixtures() throws Throwable { - scenarioExecution().install(new RecreateSimpleObjects()); - } - + @Before(value = {"@integration", "@SimpleObjectsFixture"}, order = 20000) + public void integrationFixtures() throws Throwable { + scenarioExecution().install(new RecreateSimpleObjects()); + } } diff --git a/naked-objects/integtests/src/test/java/domainapp/integtests/specglue/modules/simple/SimpleObjectGlue.java b/naked-objects/integtests/src/test/java/domainapp/integtests/specglue/modules/simple/SimpleObjectGlue.java index 63d96bd53..2ea375b4a 100644 --- a/naked-objects/integtests/src/test/java/domainapp/integtests/specglue/modules/simple/SimpleObjectGlue.java +++ b/naked-objects/integtests/src/test/java/domainapp/integtests/specglue/modules/simple/SimpleObjectGlue.java @@ -1,18 +1,16 @@ /** - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. */ package domainapp.integtests.specglue.modules.simple; @@ -30,21 +28,21 @@ import static org.junit.Assert.assertThat; public class SimpleObjectGlue extends CukeGlueAbstract { - @Given("^there are.* (\\d+) simple objects$") - public void there_are_N_simple_objects(int n) throws Throwable { - try { - final List findAll = service(SimpleObjects.class).listAll(); - assertThat(findAll.size(), is(n)); - putVar("list", "all", findAll); - - } finally { - assertMocksSatisfied(); - } + @Given("^there are.* (\\d+) simple objects$") + public void thereAreNumSimpleObjects(int n) throws Throwable { + try { + final List findAll = service(SimpleObjects.class).listAll(); + assertThat(findAll.size(), is(n)); + putVar("list", "all", findAll); + + } finally { + assertMocksSatisfied(); } - - @When("^I create a new simple object$") - public void I_create_a_new_simple_object() throws Throwable { - service(SimpleObjects.class).create(UUID.randomUUID().toString()); - } - + } + + @When("^I create a new simple object$") + public void createNewSimpleObject() throws Throwable { + service(SimpleObjects.class).create(UUID.randomUUID().toString()); + } + } diff --git a/naked-objects/integtests/src/test/java/domainapp/integtests/specs/RunSpecs.java b/naked-objects/integtests/src/test/java/domainapp/integtests/specs/RunSpecs.java index 910b5a826..8a842a0f3 100644 --- a/naked-objects/integtests/src/test/java/domainapp/integtests/specs/RunSpecs.java +++ b/naked-objects/integtests/src/test/java/domainapp/integtests/specs/RunSpecs.java @@ -1,18 +1,16 @@ /** - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. */ package domainapp.integtests.specs; @@ -23,17 +21,12 @@ import cucumber.api.junit.Cucumber; /** - * Runs scenarios in all .feature files (this package and any subpackages). + * Runs scenarios in all .feature files (this package and any subpackages). */ @RunWith(Cucumber.class) -@CucumberOptions( - format = { - "html:target/cucumber-html-report" - ,"json:target/cucumber.json" - }, - glue={"classpath:domainapp.integtests.specglue"}, - strict = true, - tags = { "~@backlog", "~@ignore" }) +@CucumberOptions(format = {"html:target/cucumber-html-report", "json:target/cucumber.json"}, + glue = {"classpath:domainapp.integtests.specglue"}, strict = true, tags = {"~@backlog", + "~@ignore"}) public class RunSpecs { - // intentionally empty + // intentionally empty } diff --git a/naked-objects/integtests/src/test/java/domainapp/integtests/tests/SimpleAppIntegTest.java b/naked-objects/integtests/src/test/java/domainapp/integtests/tests/SimpleAppIntegTest.java index 3ceef4e63..7a7ad91b2 100644 --- a/naked-objects/integtests/src/test/java/domainapp/integtests/tests/SimpleAppIntegTest.java +++ b/naked-objects/integtests/src/test/java/domainapp/integtests/tests/SimpleAppIntegTest.java @@ -27,13 +27,13 @@ import domainapp.integtests.bootstrap.SimpleAppSystemInitializer; public abstract class SimpleAppIntegTest extends IntegrationTestAbstract { - @BeforeClass - public static void initClass() { - org.apache.log4j.PropertyConfigurator.configure("logging.properties"); - SimpleAppSystemInitializer.initIsft(); + @BeforeClass + public static void initClass() { + org.apache.log4j.PropertyConfigurator.configure("logging.properties"); + SimpleAppSystemInitializer.initIsft(); - // instantiating will install onto ThreadLocal - new ScenarioExecutionForIntegration(); - } + // instantiating will install onto ThreadLocal + new ScenarioExecutionForIntegration(); + } } diff --git a/naked-objects/integtests/src/test/java/domainapp/integtests/tests/modules/simple/SimpleObjectIntegTest.java b/naked-objects/integtests/src/test/java/domainapp/integtests/tests/modules/simple/SimpleObjectIntegTest.java index 610136bb8..872aff7a3 100644 --- a/naked-objects/integtests/src/test/java/domainapp/integtests/tests/modules/simple/SimpleObjectIntegTest.java +++ b/naked-objects/integtests/src/test/java/domainapp/integtests/tests/modules/simple/SimpleObjectIntegTest.java @@ -35,87 +35,86 @@ import static org.assertj.core.api.Assertions.assertThat; public class SimpleObjectIntegTest extends SimpleAppIntegTest { + @Inject + FixtureScripts fixtureScripts; + + RecreateSimpleObjects fs; + SimpleObject simpleObjectPojo; + SimpleObject simpleObjectWrapped; + + @Before + public void setUp() throws Exception { + // given + fs = new RecreateSimpleObjects().setNumber(1); + fixtureScripts.runFixtureScript(fs, null); + + simpleObjectPojo = fs.getSimpleObjects().get(0); + + assertThat(simpleObjectPojo).isNotNull(); + simpleObjectWrapped = wrap(simpleObjectPojo); + } + + public static class Name extends SimpleObjectIntegTest { + + @Test + public void accessible() throws Exception { + // when + final String name = simpleObjectWrapped.getName(); + // then + assertThat(name).isEqualTo(fs.names.get(0)); + } + + @Test + public void cannotBeUpdatedDirectly() throws Exception { + + // expect + expectedExceptions.expect(DisabledException.class); + + // when + simpleObjectWrapped.setName("new name"); + } + } + + public static class UpdateName extends SimpleObjectIntegTest { + + @Test + public void happyCase() throws Exception { + + // when + simpleObjectWrapped.updateName("new name"); + + // then + assertThat(simpleObjectWrapped.getName()).isEqualTo("new name"); + } + + @Test + public void failsValidation() throws Exception { + + // expect + expectedExceptions.expect(InvalidException.class); + expectedExceptions.expectMessage("Exclamation mark is not allowed"); + + // when + simpleObjectWrapped.updateName("new name!"); + } + } + + public static class Title extends SimpleObjectIntegTest { + @Inject - FixtureScripts fixtureScripts; + DomainObjectContainer container; - RecreateSimpleObjects fs; - SimpleObject simpleObjectPojo; - SimpleObject simpleObjectWrapped; + @Test + public void interpolatesName() throws Exception { - @Before - public void setUp() throws Exception { - // given - fs = new RecreateSimpleObjects().setNumber(1); - fixtureScripts.runFixtureScript(fs, null); + // given + final String name = simpleObjectWrapped.getName(); - simpleObjectPojo = fs.getSimpleObjects().get(0); + // when + final String title = container.titleOf(simpleObjectWrapped); - assertThat(simpleObjectPojo).isNotNull(); - simpleObjectWrapped = wrap(simpleObjectPojo); - } - - public static class Name extends SimpleObjectIntegTest { - - @Test - public void accessible() throws Exception { - // when - final String name = simpleObjectWrapped.getName(); - // then - assertThat(name).isEqualTo(fs.NAMES.get(0)); - } - - @Test - public void cannotBeUpdatedDirectly() throws Exception { - - // expect - expectedExceptions.expect(DisabledException.class); - - // when - simpleObjectWrapped.setName("new name"); - } - } - - public static class UpdateName extends SimpleObjectIntegTest { - - @Test - public void happyCase() throws Exception { - - // when - simpleObjectWrapped.updateName("new name"); - - // then - assertThat(simpleObjectWrapped.getName()).isEqualTo("new name"); - } - - @Test - public void failsValidation() throws Exception { - - // expect - expectedExceptions.expect(InvalidException.class); - expectedExceptions.expectMessage("Exclamation mark is not allowed"); - - // when - simpleObjectWrapped.updateName("new name!"); - } - } - - - public static class Title extends SimpleObjectIntegTest { - - @Inject - DomainObjectContainer container; - - @Test - public void interpolatesName() throws Exception { - - // given - final String name = simpleObjectWrapped.getName(); - - // when - final String title = container.titleOf(simpleObjectWrapped); - - // then - assertThat(title).isEqualTo("Object: " + name); - } + // then + assertThat(title).isEqualTo("Object: " + name); } + } } \ No newline at end of file diff --git a/naked-objects/integtests/src/test/java/domainapp/integtests/tests/modules/simple/SimpleObjectsIntegTest.java b/naked-objects/integtests/src/test/java/domainapp/integtests/tests/modules/simple/SimpleObjectsIntegTest.java index fd3b0ff46..332213542 100644 --- a/naked-objects/integtests/src/test/java/domainapp/integtests/tests/modules/simple/SimpleObjectsIntegTest.java +++ b/naked-objects/integtests/src/test/java/domainapp/integtests/tests/modules/simple/SimpleObjectsIntegTest.java @@ -42,102 +42,102 @@ import static org.assertj.core.api.Assertions.assertThat; public class SimpleObjectsIntegTest extends SimpleAppIntegTest { - @Inject - FixtureScripts fixtureScripts; - @Inject - SimpleObjects simpleObjects; + @Inject + FixtureScripts fixtureScripts; + @Inject + SimpleObjects simpleObjects; - public static class ListAll extends SimpleObjectsIntegTest { + public static class ListAll extends SimpleObjectsIntegTest { - @Test - public void happyCase() throws Exception { + @Test + public void happyCase() throws Exception { - // given - RecreateSimpleObjects fs = new RecreateSimpleObjects(); - fixtureScripts.runFixtureScript(fs, null); - nextTransaction(); + // given + RecreateSimpleObjects fs = new RecreateSimpleObjects(); + fixtureScripts.runFixtureScript(fs, null); + nextTransaction(); - // when - final List all = wrap(simpleObjects).listAll(); + // when + final List all = wrap(simpleObjects).listAll(); - // then - assertThat(all).hasSize(fs.getSimpleObjects().size()); + // then + assertThat(all).hasSize(fs.getSimpleObjects().size()); - SimpleObject simpleObject = wrap(all.get(0)); - assertThat(simpleObject.getName()).isEqualTo(fs.getSimpleObjects().get(0).getName()); - } - - @Test - public void whenNone() throws Exception { - - // given - FixtureScript fs = new SimpleObjectsTearDown(); - fixtureScripts.runFixtureScript(fs, null); - nextTransaction(); - - // when - final List all = wrap(simpleObjects).listAll(); - - // then - assertThat(all).hasSize(0); - } + SimpleObject simpleObject = wrap(all.get(0)); + assertThat(simpleObject.getName()).isEqualTo(fs.getSimpleObjects().get(0).getName()); } - public static class Create extends SimpleObjectsIntegTest { + @Test + public void whenNone() throws Exception { - @Test - public void happyCase() throws Exception { + // given + FixtureScript fs = new SimpleObjectsTearDown(); + fixtureScripts.runFixtureScript(fs, null); + nextTransaction(); - // given - FixtureScript fs = new SimpleObjectsTearDown(); - fixtureScripts.runFixtureScript(fs, null); - nextTransaction(); + // when + final List all = wrap(simpleObjects).listAll(); - // when - wrap(simpleObjects).create("Faz"); - - // then - final List all = wrap(simpleObjects).listAll(); - assertThat(all).hasSize(1); - } - - @Test - public void whenAlreadyExists() throws Exception { - - // given - FixtureScript fs = new SimpleObjectsTearDown(); - fixtureScripts.runFixtureScript(fs, null); - nextTransaction(); - wrap(simpleObjects).create("Faz"); - nextTransaction(); - - // then - expectedExceptions.expectCause(causalChainContains(SQLIntegrityConstraintViolationException.class)); - - // when - wrap(simpleObjects).create("Faz"); - nextTransaction(); - } - - private static Matcher causalChainContains(final Class cls) { - return new TypeSafeMatcher() { - @Override - protected boolean matchesSafely(Throwable item) { - final List causalChain = Throwables.getCausalChain(item); - for (Throwable throwable : causalChain) { - if(cls.isAssignableFrom(throwable.getClass())){ - return true; - } - } - return false; - } - - @Override - public void describeTo(Description description) { - description.appendText("exception with causal chain containing " + cls.getSimpleName()); - } - }; - } + // then + assertThat(all).hasSize(0); } + } + + public static class Create extends SimpleObjectsIntegTest { + + @Test + public void happyCase() throws Exception { + + // given + FixtureScript fs = new SimpleObjectsTearDown(); + fixtureScripts.runFixtureScript(fs, null); + nextTransaction(); + + // when + wrap(simpleObjects).create("Faz"); + + // then + final List all = wrap(simpleObjects).listAll(); + assertThat(all).hasSize(1); + } + + @Test + public void whenAlreadyExists() throws Exception { + + // given + FixtureScript fs = new SimpleObjectsTearDown(); + fixtureScripts.runFixtureScript(fs, null); + nextTransaction(); + wrap(simpleObjects).create("Faz"); + nextTransaction(); + + // then + expectedExceptions.expectCause(causalChainContains(SQLIntegrityConstraintViolationException.class)); + + // when + wrap(simpleObjects).create("Faz"); + nextTransaction(); + } + + private static Matcher causalChainContains(final Class cls) { + return new TypeSafeMatcher() { + @Override + protected boolean matchesSafely(Throwable item) { + final List causalChain = Throwables.getCausalChain(item); + for (Throwable throwable : causalChain) { + if (cls.isAssignableFrom(throwable.getClass())) { + return true; + } + } + return false; + } + + @Override + public void describeTo(Description description) { + description.appendText("exception with causal chain containing " + cls.getSimpleName()); + } + }; + } + } } \ No newline at end of file diff --git a/naked-objects/pom.xml b/naked-objects/pom.xml index 067d4a5a8..b3e48dcb6 100644 --- a/naked-objects/pom.xml +++ b/naked-objects/pom.xml @@ -15,7 +15,7 @@ java-design-patterns com.iluwatar - 1.7.0 + 1.13.0-SNAPSHOT naked-objects @@ -350,17 +350,17 @@ ${project.groupId} naked-objects-dom - 1.7.0 + 1.13.0-SNAPSHOT ${project.groupId} naked-objects-fixture - 1.7.0 + 1.13.0-SNAPSHOT ${project.groupId} naked-objects-webapp - 1.7.0 + 1.13.0-SNAPSHOT diff --git a/naked-objects/webapp/ide/intellij/launch/README.txt b/naked-objects/webapp/ide/intellij/launch/README.txt index 5f8e5ab8a..d33eaceb4 100644 --- a/naked-objects/webapp/ide/intellij/launch/README.txt +++ b/naked-objects/webapp/ide/intellij/launch/README.txt @@ -1,2 +1,25 @@ +==== + The MIT License + Copyright (c) 2014 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. +==== + Copy into workspace\.idea\runConfigurations directory, and adjust file paths for Maven tasks. diff --git a/naked-objects/webapp/ide/intellij/launch/SimpleApp_PROTOTYPE.xml b/naked-objects/webapp/ide/intellij/launch/SimpleApp_PROTOTYPE.xml index 918ea3540..e01c95512 100644 --- a/naked-objects/webapp/ide/intellij/launch/SimpleApp_PROTOTYPE.xml +++ b/naked-objects/webapp/ide/intellij/launch/SimpleApp_PROTOTYPE.xml @@ -1,3 +1,27 @@ + diff --git a/naked-objects/webapp/ide/intellij/launch/SimpleApp__enhance_only_.xml b/naked-objects/webapp/ide/intellij/launch/SimpleApp__enhance_only_.xml index 31993b500..fcd68f002 100644 --- a/naked-objects/webapp/ide/intellij/launch/SimpleApp__enhance_only_.xml +++ b/naked-objects/webapp/ide/intellij/launch/SimpleApp__enhance_only_.xml @@ -1,3 +1,27 @@ + s diff --git a/naked-objects/webapp/pom.xml b/naked-objects/webapp/pom.xml index bc7d7c299..762438aca 100644 --- a/naked-objects/webapp/pom.xml +++ b/naked-objects/webapp/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.7.0 + 1.13.0-SNAPSHOT naked-objects-webapp diff --git a/naked-objects/webapp/src/main/java/domainapp/webapp/SimpleApplication.java b/naked-objects/webapp/src/main/java/domainapp/webapp/SimpleApplication.java index 57d1e0ba1..89d316d20 100644 --- a/naked-objects/webapp/src/main/java/domainapp/webapp/SimpleApplication.java +++ b/naked-objects/webapp/src/main/java/domainapp/webapp/SimpleApplication.java @@ -1,20 +1,16 @@ /* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license + * agreements. See the NOTICE file distributed with this work for additional information regarding + * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. You may obtain a + * copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. */ package domainapp.webapp; @@ -53,6 +49,7 @@ import de.agilecoders.wicket.themes.markup.html.bootswatch.BootswatchThemeProvid * *

* See: + * *

  * <filter>
  *   <filter-name>wicket</filter-name>
@@ -67,87 +64,95 @@ import de.agilecoders.wicket.themes.markup.html.bootswatch.BootswatchThemeProvid
  */
 public class SimpleApplication extends IsisWicketApplication {
 
-    private static final long serialVersionUID = 1L;
+  private static final long serialVersionUID = 1L;
 
-    /**
-     * uncomment for a (slightly hacky) way of allowing logins using query args, eg:
-     * 
-     * ?user=sven&pass=pass
-     * 
-     * 

- * for demos only, obvious. - */ - private final static boolean DEMO_MODE_USING_CREDENTIALS_AS_QUERYARGS = false; + /** + * uncomment for a (slightly hacky) way of allowing logins using query args, eg: + * + * ?user=sven&pass=pass + * + *

+ * for demos only, obvious. + */ + private static final boolean DEMO_MODE_USING_CREDENTIALS_AS_QUERYARGS = false; - @Override - protected void init() { - super.init(); + @Override + protected void init() { + super.init(); - IBootstrapSettings settings = Bootstrap.getSettings(); - settings.setThemeProvider(new BootswatchThemeProvider(BootswatchTheme.Flatly)); + IBootstrapSettings settings = Bootstrap.getSettings(); + settings.setThemeProvider(new BootswatchThemeProvider(BootswatchTheme.Flatly)); + } + + @Override + public Session newSession(final Request request, final Response response) { + if (!DEMO_MODE_USING_CREDENTIALS_AS_QUERYARGS) { + return super.newSession(request, response); } - @Override - public Session newSession(final Request request, final Response response) { - if(!DEMO_MODE_USING_CREDENTIALS_AS_QUERYARGS) { - return super.newSession(request, response); - } - - // else demo mode - final AuthenticatedWebSessionForIsis s = (AuthenticatedWebSessionForIsis) super.newSession(request, response); - IRequestParameters requestParameters = request.getRequestParameters(); - final org.apache.wicket.util.string.StringValue user = requestParameters.getParameterValue("user"); - final org.apache.wicket.util.string.StringValue password = requestParameters.getParameterValue("pass"); - s.signIn(user.toString(), password.toString()); - return s; + // else demo mode + final AuthenticatedWebSessionForIsis s = + (AuthenticatedWebSessionForIsis) super.newSession(request, response); + IRequestParameters requestParameters = request.getRequestParameters(); + final org.apache.wicket.util.string.StringValue user = + requestParameters.getParameterValue("user"); + final org.apache.wicket.util.string.StringValue password = + requestParameters.getParameterValue("pass"); + s.signIn(user.toString(), password.toString()); + return s; + } + + @Override + public WebRequest newWebRequest(HttpServletRequest servletRequest, String filterPath) { + if (!DEMO_MODE_USING_CREDENTIALS_AS_QUERYARGS) { + return super.newWebRequest(servletRequest, filterPath); } - @Override - public WebRequest newWebRequest(HttpServletRequest servletRequest, String filterPath) { - if(!DEMO_MODE_USING_CREDENTIALS_AS_QUERYARGS) { - return super.newWebRequest(servletRequest, filterPath); - } - - // else demo mode - try { - String uname = servletRequest.getParameter("user"); - if (uname != null) { - servletRequest.getSession().invalidate(); - } - } catch (Exception e) { - } - WebRequest request = super.newWebRequest(servletRequest, filterPath); - return request; + // else demo mode + try { + String uname = servletRequest.getParameter("user"); + if (uname != null) { + servletRequest.getSession().invalidate(); + } + } catch (Exception e) { + System.out.println(e); } - - @Override - protected Module newIsisWicketModule() { - final Module isisDefaults = super.newIsisWicketModule(); - - final Module overrides = new AbstractModule() { - @Override - protected void configure() { - bind(String.class).annotatedWith(Names.named("applicationName")).toInstance("Simple App"); - bind(String.class).annotatedWith(Names.named("applicationCss")).toInstance("css/application.css"); - bind(String.class).annotatedWith(Names.named("applicationJs")).toInstance("scripts/application.js"); - bind(String.class).annotatedWith(Names.named("welcomeMessage")).toInstance(readLines(getClass(), "welcome.html")); - bind(String.class).annotatedWith(Names.named("aboutMessage")).toInstance("Simple App"); - bind(InputStream.class).annotatedWith(Names.named("metaInfManifest")).toProvider(Providers.of(getServletContext().getResourceAsStream("/META-INF/MANIFEST.MF"))); - } - }; + return super.newWebRequest(servletRequest, filterPath); + } - return Modules.override(isisDefaults).with(overrides); - } + @Override + protected Module newIsisWicketModule() { + final Module isisDefaults = super.newIsisWicketModule(); - private static String readLines(final Class contextClass, final String resourceName) { - try { - List readLines = Resources.readLines(Resources.getResource(contextClass, resourceName), Charset.defaultCharset()); - final String aboutText = Joiner.on("\n").join(readLines); - return aboutText; - } catch (IOException e) { - return "This is a simple app"; - } + final Module overrides = new AbstractModule() { + @Override + protected void configure() { + bind(String.class).annotatedWith(Names.named("applicationName")).toInstance("Simple App"); + bind(String.class).annotatedWith(Names.named("applicationCss")).toInstance( + "css/application.css"); + bind(String.class).annotatedWith(Names.named("applicationJs")).toInstance( + "scripts/application.js"); + bind(String.class).annotatedWith(Names.named("welcomeMessage")).toInstance( + readLines(getClass(), "welcome.html")); + bind(String.class).annotatedWith(Names.named("aboutMessage")).toInstance("Simple App"); + bind(InputStream.class).annotatedWith(Names.named("metaInfManifest")).toProvider( + Providers.of(getServletContext().getResourceAsStream("/META-INF/MANIFEST.MF"))); + } + }; + + return Modules.override(isisDefaults).with(overrides); + } + + private static String readLines(final Class contextClass, final String resourceName) { + try { + List readLines = + Resources.readLines(Resources.getResource(contextClass, resourceName), + Charset.defaultCharset()); + return Joiner.on("\n").join(readLines); + } catch (IOException e) { + return "This is a simple app"; } + } } diff --git a/naked-objects/webapp/src/main/webapp/about/index.html b/naked-objects/webapp/src/main/webapp/about/index.html index 070651ace..bfcc52017 100644 --- a/naked-objects/webapp/src/main/webapp/about/index.html +++ b/naked-objects/webapp/src/main/webapp/about/index.html @@ -1,3 +1,27 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT null-object @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/null-object/src/main/java/com/iluwatar/nullobject/App.java b/null-object/src/main/java/com/iluwatar/nullobject/App.java index 9cc4a14da..90a9c12c8 100644 --- a/null-object/src/main/java/com/iluwatar/nullobject/App.java +++ b/null-object/src/main/java/com/iluwatar/nullobject/App.java @@ -1,36 +1,50 @@ +/** + * The MIT License + * Copyright (c) 2014 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.nullobject; /** * - * Null Object pattern replaces null values with neutral objects. - * Many times this simplifies algorithms since no extra null checks - * are needed. + * Null Object pattern replaces null values with neutral objects. Many times this simplifies + * algorithms since no extra null checks are needed. *

- * In this example we build a binary tree where the nodes are either - * normal or Null Objects. No null values are used in the tree making - * the traversal easy. + * In this example we build a binary tree where the nodes are either normal or Null Objects. No null + * values are used in the tree making the traversal easy. * */ -public class App -{ - /** - * Program entry point - * @param args command line args - */ - public static void main( String[] args ) { - - Node root = new NodeImpl("1", - new NodeImpl("11", - new NodeImpl("111", - NullNode.getInstance(), - NullNode.getInstance()), - NullNode.getInstance()), - new NodeImpl("12", - NullNode.getInstance(), - new NodeImpl("122", - NullNode.getInstance(), - NullNode.getInstance()))); +public class App { + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { - root.walk(); - } + Node root = + new NodeImpl("1", new NodeImpl("11", new NodeImpl("111", NullNode.getInstance(), + NullNode.getInstance()), NullNode.getInstance()), new NodeImpl("12", + NullNode.getInstance(), new NodeImpl("122", NullNode.getInstance(), + NullNode.getInstance()))); + + root.walk(); + } } diff --git a/null-object/src/main/java/com/iluwatar/nullobject/Node.java b/null-object/src/main/java/com/iluwatar/nullobject/Node.java index 3d52087c9..36363736c 100644 --- a/null-object/src/main/java/com/iluwatar/nullobject/Node.java +++ b/null-object/src/main/java/com/iluwatar/nullobject/Node.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.nullobject; /** @@ -7,9 +29,13 @@ package com.iluwatar.nullobject; */ public interface Node { - String getName(); - int getTreeSize(); - Node getLeft(); - Node getRight(); - void walk(); + String getName(); + + int getTreeSize(); + + Node getLeft(); + + Node getRight(); + + void walk(); } diff --git a/null-object/src/main/java/com/iluwatar/nullobject/NodeImpl.java b/null-object/src/main/java/com/iluwatar/nullobject/NodeImpl.java index fc9c9c9e3..46787ac96 100644 --- a/null-object/src/main/java/com/iluwatar/nullobject/NodeImpl.java +++ b/null-object/src/main/java/com/iluwatar/nullobject/NodeImpl.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.nullobject; /** @@ -7,44 +29,47 @@ package com.iluwatar.nullobject; */ public class NodeImpl implements Node { - private final String name; - private final Node left; - private final Node right; - - public NodeImpl(String name, Node left, Node right) { - this.name = name; - this.left = left; - this.right = right; - } - - @Override - public int getTreeSize() { - return 1 + left.getTreeSize() + right.getTreeSize(); - } + private final String name; + private final Node left; + private final Node right; - @Override - public Node getLeft() { - return left; - } + /** + * Constructor + */ + public NodeImpl(String name, Node left, Node right) { + this.name = name; + this.left = left; + this.right = right; + } - @Override - public Node getRight() { - return right; - } + @Override + public int getTreeSize() { + return 1 + left.getTreeSize() + right.getTreeSize(); + } - @Override - public String getName() { - return name; - } + @Override + public Node getLeft() { + return left; + } - @Override - public void walk() { - System.out.println(name); - if (left.getTreeSize() > 0) { - left.walk(); - } - if (right.getTreeSize() > 0) { - right.walk(); - } - } + @Override + public Node getRight() { + return right; + } + + @Override + public String getName() { + return name; + } + + @Override + public void walk() { + System.out.println(name); + if (left.getTreeSize() > 0) { + left.walk(); + } + if (right.getTreeSize() > 0) { + right.walk(); + } + } } diff --git a/null-object/src/main/java/com/iluwatar/nullobject/NullNode.java b/null-object/src/main/java/com/iluwatar/nullobject/NullNode.java index 4a0f4cd2b..d4ea0e429 100644 --- a/null-object/src/main/java/com/iluwatar/nullobject/NullNode.java +++ b/null-object/src/main/java/com/iluwatar/nullobject/NullNode.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.nullobject; /** @@ -7,38 +29,38 @@ package com.iluwatar.nullobject; * Implemented as Singleton, since all the NullNodes are the same. * */ -public class NullNode implements Node { +public final class NullNode implements Node { - private static NullNode instance = new NullNode(); - - private NullNode() { - } - - public static NullNode getInstance() { - return instance; - } - - @Override - public int getTreeSize() { - return 0; - } + private static NullNode instance = new NullNode(); - @Override - public Node getLeft() { - return null; - } + private NullNode() {} - @Override - public Node getRight() { - return null; - } + public static NullNode getInstance() { + return instance; + } - @Override - public String getName() { - return null; - } + @Override + public int getTreeSize() { + return 0; + } - @Override - public void walk() { - } + @Override + public Node getLeft() { + return null; + } + + @Override + public Node getRight() { + return null; + } + + @Override + public String getName() { + return null; + } + + @Override + public void walk() { + // Do nothing + } } diff --git a/null-object/src/test/java/com/iluwatar/nullobject/AppTest.java b/null-object/src/test/java/com/iluwatar/nullobject/AppTest.java index 7ddf2cabc..170f7b977 100644 --- a/null-object/src/test/java/com/iluwatar/nullobject/AppTest.java +++ b/null-object/src/test/java/com/iluwatar/nullobject/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.nullobject; import org.junit.Test; -import com.iluwatar.nullobject.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.nullobject.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/null-object/src/test/java/com/iluwatar/nullobject/NullNodeTest.java b/null-object/src/test/java/com/iluwatar/nullobject/NullNodeTest.java new file mode 100644 index 000000000..1375b8949 --- /dev/null +++ b/null-object/src/test/java/com/iluwatar/nullobject/NullNodeTest.java @@ -0,0 +1,65 @@ +/** + * The MIT License + * Copyright (c) 2014 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.nullobject; + +import org.junit.Test; +import org.mockito.Mockito; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; + +/** + * Date: 12/26/15 - 11:47 PM + * + * @author Jeroen Meulemeester + */ +public class NullNodeTest extends StdOutTest { + + /** + * Verify if {@link NullNode#getInstance()} actually returns the same object instance + */ + @Test + public void testGetInstance() { + final NullNode instance = NullNode.getInstance(); + assertNotNull(instance); + assertSame(instance, NullNode.getInstance()); + } + + @Test + public void testFields() { + final NullNode node = NullNode.getInstance(); + assertEquals(0, node.getTreeSize()); + assertNull(node.getName()); + assertNull(node.getLeft()); + assertNull(node.getRight()); + } + + @Test + public void testWalk() throws Exception { + NullNode.getInstance().walk(); + Mockito.verifyZeroInteractions(getStdOutMock()); + } + +} \ No newline at end of file diff --git a/null-object/src/test/java/com/iluwatar/nullobject/StdOutTest.java b/null-object/src/test/java/com/iluwatar/nullobject/StdOutTest.java new file mode 100644 index 000000000..0c0122132 --- /dev/null +++ b/null-object/src/test/java/com/iluwatar/nullobject/StdOutTest.java @@ -0,0 +1,76 @@ +/** + * The MIT License + * Copyright (c) 2014 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.nullobject; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since walking through the tree has no + * influence on any other accessible object, except for writing to std-out using {@link + * System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/null-object/src/test/java/com/iluwatar/nullobject/TreeTest.java b/null-object/src/test/java/com/iluwatar/nullobject/TreeTest.java new file mode 100644 index 000000000..6c77cd236 --- /dev/null +++ b/null-object/src/test/java/com/iluwatar/nullobject/TreeTest.java @@ -0,0 +1,123 @@ +/** + * The MIT License + * Copyright (c) 2014 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.nullobject; + +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mockito; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; + +/** + * Date: 12/26/15 - 11:44 PM + * + * @author Jeroen Meulemeester + */ +public class TreeTest extends StdOutTest { + + /** + * During the tests, the same tree structure will be used, shown below. End points will be + * terminated with the {@link NullNode} instance. + * + *

+   * root
+   * ├── level1_a
+   * │   ├── level2_a
+   * │   │   ├── level3_a
+   * │   │   └── level3_b
+   * │   └── level2_b
+   * └── level1_b
+   * 
+ */ + private static final Node TREE_ROOT; + + static { + final NodeImpl level1B = new NodeImpl("level1_b", NullNode.getInstance(), NullNode.getInstance()); + final NodeImpl level2B = new NodeImpl("level2_b", NullNode.getInstance(), NullNode.getInstance()); + final NodeImpl level3A = new NodeImpl("level3_a", NullNode.getInstance(), NullNode.getInstance()); + final NodeImpl level3B = new NodeImpl("level3_b", NullNode.getInstance(), NullNode.getInstance()); + final NodeImpl level2A = new NodeImpl("level2_a", level3A, level3B); + final NodeImpl level1A = new NodeImpl("level1_a", level2A, level2B); + TREE_ROOT = new NodeImpl("root", level1A, level1B); + } + + /** + * Verify the number of items in the tree. The root has 6 children so we expect a {@link + * Node#getTreeSize()} of 7 {@link Node}s in total. + */ + @Test + public void testTreeSize() { + assertEquals(7, TREE_ROOT.getTreeSize()); + } + + /** + * Walk through the tree and verify if every item is handled + */ + @Test + public void testWalk() { + TREE_ROOT.walk(); + + final InOrder inOrder = Mockito.inOrder(getStdOutMock()); + inOrder.verify(getStdOutMock()).println("root"); + inOrder.verify(getStdOutMock()).println("level1_a"); + inOrder.verify(getStdOutMock()).println("level2_a"); + inOrder.verify(getStdOutMock()).println("level3_a"); + inOrder.verify(getStdOutMock()).println("level3_b"); + inOrder.verify(getStdOutMock()).println("level2_b"); + inOrder.verify(getStdOutMock()).println("level1_b"); + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testGetLeft() throws Exception { + final Node level1 = TREE_ROOT.getLeft(); + assertNotNull(level1); + assertEquals("level1_a", level1.getName()); + assertEquals(5, level1.getTreeSize()); + + final Node level2 = level1.getLeft(); + assertNotNull(level2); + assertEquals("level2_a", level2.getName()); + assertEquals(3, level2.getTreeSize()); + + final Node level3 = level2.getLeft(); + assertNotNull(level3); + assertEquals("level3_a", level3.getName()); + assertEquals(1, level3.getTreeSize()); + assertSame(NullNode.getInstance(), level3.getRight()); + assertSame(NullNode.getInstance(), level3.getLeft()); + } + + @Test + public void testGetRight() throws Exception { + final Node level1 = TREE_ROOT.getRight(); + assertNotNull(level1); + assertEquals("level1_b", level1.getName()); + assertEquals(1, level1.getTreeSize()); + assertSame(NullNode.getInstance(), level1.getRight()); + assertSame(NullNode.getInstance(), level1.getLeft()); + } + +} diff --git a/object-pool/index.md b/object-pool/README.md similarity index 73% rename from object-pool/index.md rename to object-pool/README.md index 276d9a1f6..cf36d9880 100644 --- a/object-pool/index.md +++ b/object-pool/README.md @@ -4,17 +4,22 @@ title: Object Pool folder: object-pool permalink: /patterns/object-pool/ categories: Creational -tags: Java +tags: + - Java + - Difficulty-Beginner + - Performance --- -**Intent:** When objects are expensive to create and they are needed only for +## Intent +When objects are expensive to create and they are needed only for short periods of time it is advantageous to utilize the Object Pool pattern. The Object Pool provides a cache for instantiated objects tracking which ones are in use and which are available. ![alt text](./etc/object-pool.png "Object Pool") -**Applicability:** Use the Object Pool pattern when +## Applicability +Use the Object Pool pattern when * the objects are expensive to create (allocation cost) * you need a large number of short-lived objects (memory fragmentation) diff --git a/object-pool/pom.xml b/object-pool/pom.xml index 528e3db16..666242d1d 100644 --- a/object-pool/pom.xml +++ b/object-pool/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT object-pool diff --git a/object-pool/src/main/java/com/iluwatar/object/pool/App.java b/object-pool/src/main/java/com/iluwatar/object/pool/App.java index c1893a774..66b3e3f90 100644 --- a/object-pool/src/main/java/com/iluwatar/object/pool/App.java +++ b/object-pool/src/main/java/com/iluwatar/object/pool/App.java @@ -1,49 +1,74 @@ +/** + * The MIT License + * Copyright (c) 2014 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.object.pool; /** * - * When it is necessary to work with a large number of objects that are particularly expensive to instantiate - * and each object is only needed for a short period of time, the performance of an entire application may be - * adversely affected. An object pool design pattern may be deemed desirable in cases such as these. + * When it is necessary to work with a large number of objects that are particularly expensive to + * instantiate and each object is only needed for a short period of time, the performance of an + * entire application may be adversely affected. An object pool design pattern may be deemed + * desirable in cases such as these. *

- * The object pool design pattern creates a set of objects that may be reused. When a new object is needed, it - * is requested from the pool. If a previously prepared object is available it is returned immediately, avoiding - * the instantiation cost. If no objects are present in the pool, a new item is created and returned. When the - * object has been used and is no longer needed, it is returned to the pool, allowing it to be used again in the - * future without repeating the computationally expensive instantiation process. It is important to note that - * once an object has been used and returned, existing references will become invalid. + * The object pool design pattern creates a set of objects that may be reused. When a new object is + * needed, it is requested from the pool. If a previously prepared object is available it is + * returned immediately, avoiding the instantiation cost. If no objects are present in the pool, a + * new item is created and returned. When the object has been used and is no longer needed, it is + * returned to the pool, allowing it to be used again in the future without repeating the + * computationally expensive instantiation process. It is important to note that once an object has + * been used and returned, existing references will become invalid. *

- * In this example we have created {@link OliphauntPool} inheriting from generic {@link ObjectPool}. {@link Oliphaunt}s can be checked - * out from the pool and later returned to it. The pool tracks created instances and their status (available, - * inUse). + * In this example we have created {@link OliphauntPool} inheriting from generic {@link ObjectPool}. + * {@link Oliphaunt}s can be checked out from the pool and later returned to it. The pool tracks + * created instances and their status (available, inUse). * */ public class App { - - /** - * Program entry point - * @param args command line args - */ - public static void main( String[] args ) { - OliphauntPool pool = new OliphauntPool(); - System.out.println(pool); - Oliphaunt oliphaunt1 = pool.checkOut(); - System.out.println("Checked out " + oliphaunt1); - System.out.println(pool); - Oliphaunt oliphaunt2 = pool.checkOut(); - System.out.println("Checked out " + oliphaunt2); - Oliphaunt oliphaunt3 = pool.checkOut(); - System.out.println("Checked out " + oliphaunt3); - System.out.println(pool); - System.out.println("Checking in " + oliphaunt1); - pool.checkIn(oliphaunt1); - System.out.println("Checking in " + oliphaunt2); - pool.checkIn(oliphaunt2); - System.out.println(pool); - Oliphaunt oliphaunt4 = pool.checkOut(); - System.out.println("Checked out " + oliphaunt4); - Oliphaunt oliphaunt5 = pool.checkOut(); - System.out.println("Checked out " + oliphaunt5); - System.out.println(pool); - } + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + OliphauntPool pool = new OliphauntPool(); + System.out.println(pool); + Oliphaunt oliphaunt1 = pool.checkOut(); + System.out.println("Checked out " + oliphaunt1); + System.out.println(pool); + Oliphaunt oliphaunt2 = pool.checkOut(); + System.out.println("Checked out " + oliphaunt2); + Oliphaunt oliphaunt3 = pool.checkOut(); + System.out.println("Checked out " + oliphaunt3); + System.out.println(pool); + System.out.println("Checking in " + oliphaunt1); + pool.checkIn(oliphaunt1); + System.out.println("Checking in " + oliphaunt2); + pool.checkIn(oliphaunt2); + System.out.println(pool); + Oliphaunt oliphaunt4 = pool.checkOut(); + System.out.println("Checked out " + oliphaunt4); + Oliphaunt oliphaunt5 = pool.checkOut(); + System.out.println("Checked out " + oliphaunt5); + System.out.println(pool); + } } diff --git a/object-pool/src/main/java/com/iluwatar/object/pool/ObjectPool.java b/object-pool/src/main/java/com/iluwatar/object/pool/ObjectPool.java index 8b582630b..3fcbd2c41 100644 --- a/object-pool/src/main/java/com/iluwatar/object/pool/ObjectPool.java +++ b/object-pool/src/main/java/com/iluwatar/object/pool/ObjectPool.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.object.pool; import java.util.HashSet; @@ -5,33 +27,34 @@ import java.util.HashSet; /** * * Generic object pool - * - * @param */ public abstract class ObjectPool { - private HashSet available = new HashSet<>(); - private HashSet inUse = new HashSet<>(); - - protected abstract T create(); - - public synchronized T checkOut() { - if (available.size() <= 0) { - available.add(create()); - } - T instance = available.iterator().next(); - available.remove(instance); - inUse.add(instance); - return instance; - } - - public synchronized void checkIn(T instance) { - inUse.remove(instance); - available.add(instance); - } - - @Override - public String toString() { - return String.format("Pool available=%d inUse=%d", available.size(), inUse.size()); - } + private HashSet available = new HashSet<>(); + private HashSet inUse = new HashSet<>(); + + protected abstract T create(); + + /** + * Checkout object from pool + */ + public synchronized T checkOut() { + if (available.size() <= 0) { + available.add(create()); + } + T instance = available.iterator().next(); + available.remove(instance); + inUse.add(instance); + return instance; + } + + public synchronized void checkIn(T instance) { + inUse.remove(instance); + available.add(instance); + } + + @Override + public String toString() { + return String.format("Pool available=%d inUse=%d", available.size(), inUse.size()); + } } diff --git a/object-pool/src/main/java/com/iluwatar/object/pool/Oliphaunt.java b/object-pool/src/main/java/com/iluwatar/object/pool/Oliphaunt.java index 4b32e0ba1..46c1a03c9 100644 --- a/object-pool/src/main/java/com/iluwatar/object/pool/Oliphaunt.java +++ b/object-pool/src/main/java/com/iluwatar/object/pool/Oliphaunt.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.object.pool; /** @@ -6,26 +28,29 @@ package com.iluwatar.object.pool; * */ public class Oliphaunt { - - private static int counter = 1; - - private final int id; - - public Oliphaunt() { - id = counter++; - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - public int getId() { - return id; - } - - @Override - public String toString() { - return String.format("Oliphaunt id=%d", id); - } + + private static int counter = 1; + + private final int id; + + /** + * Constructor + */ + public Oliphaunt() { + id = counter++; + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + public int getId() { + return id; + } + + @Override + public String toString() { + return String.format("Oliphaunt id=%d", id); + } } diff --git a/object-pool/src/main/java/com/iluwatar/object/pool/OliphauntPool.java b/object-pool/src/main/java/com/iluwatar/object/pool/OliphauntPool.java index a0cfc0d54..17b998a0e 100644 --- a/object-pool/src/main/java/com/iluwatar/object/pool/OliphauntPool.java +++ b/object-pool/src/main/java/com/iluwatar/object/pool/OliphauntPool.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.object.pool; /** @@ -7,8 +29,8 @@ package com.iluwatar.object.pool; */ public class OliphauntPool extends ObjectPool { - @Override - protected Oliphaunt create() { - return new Oliphaunt(); - } + @Override + protected Oliphaunt create() { + return new Oliphaunt(); + } } diff --git a/object-pool/src/test/java/com/iluwatar/object/pool/AppTest.java b/object-pool/src/test/java/com/iluwatar/object/pool/AppTest.java index 4114590ec..45aa1b253 100644 --- a/object-pool/src/test/java/com/iluwatar/object/pool/AppTest.java +++ b/object-pool/src/test/java/com/iluwatar/object/pool/AppTest.java @@ -1,19 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 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.object.pool; import org.junit.Test; -import com.iluwatar.object.pool.App; - /** * * Application test * */ public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } + + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/object-pool/src/test/java/com/iluwatar/object/pool/OliphauntPoolTest.java b/object-pool/src/test/java/com/iluwatar/object/pool/OliphauntPoolTest.java new file mode 100644 index 000000000..f855e18c4 --- /dev/null +++ b/object-pool/src/test/java/com/iluwatar/object/pool/OliphauntPoolTest.java @@ -0,0 +1,121 @@ +/** + * The MIT License + * Copyright (c) 2014 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.object.pool; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +/** + * Date: 12/27/15 - 1:05 AM + * + * @author Jeroen Meulemeester + */ +public class OliphauntPoolTest { + + /** + * Use the same object 100 times subsequently. This should not take much time since the heavy + * object instantiation is done only once. Verify if we get the same object each time. + */ + @Test(timeout = 5000) + public void testSubsequentCheckinCheckout() { + final OliphauntPool pool = new OliphauntPool(); + assertEquals(pool.toString(), "Pool available=0 inUse=0"); + + final Oliphaunt expectedOliphaunt = pool.checkOut(); + assertEquals(pool.toString(), "Pool available=0 inUse=1"); + + pool.checkIn(expectedOliphaunt); + assertEquals(pool.toString(), "Pool available=1 inUse=0"); + + for (int i = 0; i < 100; i++) { + final Oliphaunt oliphaunt = pool.checkOut(); + assertEquals(pool.toString(), "Pool available=0 inUse=1"); + assertSame(expectedOliphaunt, oliphaunt); + assertEquals(expectedOliphaunt.getId(), oliphaunt.getId()); + assertEquals(expectedOliphaunt.toString(), oliphaunt.toString()); + + pool.checkIn(oliphaunt); + assertEquals(pool.toString(), "Pool available=1 inUse=0"); + } + + } + + /** + * Use the same object 100 times subsequently. This should not take much time since the heavy + * object instantiation is done only once. Verify if we get the same object each time. + */ + @Test(timeout = 5000) + public void testConcurrentCheckinCheckout() { + final OliphauntPool pool = new OliphauntPool(); + assertEquals(pool.toString(), "Pool available=0 inUse=0"); + + final Oliphaunt firstOliphaunt = pool.checkOut(); + assertEquals(pool.toString(), "Pool available=0 inUse=1"); + + final Oliphaunt secondOliphaunt = pool.checkOut(); + assertEquals(pool.toString(), "Pool available=0 inUse=2"); + + assertNotSame(firstOliphaunt, secondOliphaunt); + assertEquals(firstOliphaunt.getId() + 1, secondOliphaunt.getId()); + + // After checking in the second, we should get the same when checking out a new oliphaunt ... + pool.checkIn(secondOliphaunt); + assertEquals(pool.toString(), "Pool available=1 inUse=1"); + + final Oliphaunt oliphaunt3 = pool.checkOut(); + assertEquals(pool.toString(), "Pool available=0 inUse=2"); + assertSame(secondOliphaunt, oliphaunt3); + + // ... and the same applies for the first one + pool.checkIn(firstOliphaunt); + assertEquals(pool.toString(), "Pool available=1 inUse=1"); + + final Oliphaunt oliphaunt4 = pool.checkOut(); + assertEquals(pool.toString(), "Pool available=0 inUse=2"); + assertSame(firstOliphaunt, oliphaunt4); + + // When both oliphaunt return to the pool, we should still get the same instances + pool.checkIn(firstOliphaunt); + assertEquals(pool.toString(), "Pool available=1 inUse=1"); + + pool.checkIn(secondOliphaunt); + assertEquals(pool.toString(), "Pool available=2 inUse=0"); + + // The order of the returned instances is not determined, so just put them in a list + // and verify if both expected instances are in there. + final List oliphaunts = Arrays.asList(pool.checkOut(), pool.checkOut()); + assertEquals(pool.toString(), "Pool available=0 inUse=2"); + assertTrue(oliphaunts.contains(firstOliphaunt)); + assertTrue(oliphaunts.contains(secondOliphaunt)); + + } + + +} \ No newline at end of file diff --git a/observer/index.md b/observer/README.md similarity index 73% rename from observer/index.md rename to observer/README.md index fedb4cb8c..6fbe3cdab 100644 --- a/observer/index.md +++ b/observer/README.md @@ -8,28 +8,35 @@ tags: - Java - Difficulty-Beginner - Gang Of Four + - Reactive --- -**Intent:** Define a one-to-many dependency between objects so that when one +## Also known as +Dependents, Publish-Subscribe + +## Intent +Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. ![alt text](./etc/observer_1.png "Observer") -**Applicability:** Use the Observer pattern in any of the following situations +## Applicability +Use the Observer pattern in any of the following situations * when an abstraction has two aspects, one dependent on the other. Encapsulating these aspects in separate objects lets you vary and reuse them independently * when a change to one object requires changing others, and you don't know how many objects need to be changed * when an object should be able to notify other objects without making assumptions about who these objects are. In other words, you don't want these objects tightly coupled -**Typical Use Case:** +## Typical Use Case * changing in one object leads to a change in other objects -**Real world examples:** +## Real world examples * [java.util.Observer](http://docs.oracle.com/javase/8/docs/api/java/util/Observer.html) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) +* [Java Generics and Collections](http://www.amazon.com/Java-Generics-Collections-Maurice-Naftalin/dp/0596527756/) diff --git a/observer/pom.xml b/observer/pom.xml index 023d93bea..847a7ea89 100644 --- a/observer/pom.xml +++ b/observer/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT observer @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/observer/src/main/java/com/iluwatar/observer/App.java b/observer/src/main/java/com/iluwatar/observer/App.java index 5f03a9e2b..dcced00d4 100644 --- a/observer/src/main/java/com/iluwatar/observer/App.java +++ b/observer/src/main/java/com/iluwatar/observer/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.observer; import com.iluwatar.observer.generic.GHobbits; diff --git a/observer/src/main/java/com/iluwatar/observer/Hobbits.java b/observer/src/main/java/com/iluwatar/observer/Hobbits.java index 02baaec83..ed9636bd6 100644 --- a/observer/src/main/java/com/iluwatar/observer/Hobbits.java +++ b/observer/src/main/java/com/iluwatar/observer/Hobbits.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.observer; /** diff --git a/observer/src/main/java/com/iluwatar/observer/Orcs.java b/observer/src/main/java/com/iluwatar/observer/Orcs.java index 09ca65211..ce9c09944 100644 --- a/observer/src/main/java/com/iluwatar/observer/Orcs.java +++ b/observer/src/main/java/com/iluwatar/observer/Orcs.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.observer; /** diff --git a/observer/src/main/java/com/iluwatar/observer/Weather.java b/observer/src/main/java/com/iluwatar/observer/Weather.java index 634953945..f6aad3881 100644 --- a/observer/src/main/java/com/iluwatar/observer/Weather.java +++ b/observer/src/main/java/com/iluwatar/observer/Weather.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.observer; import java.util.ArrayList; @@ -27,6 +49,9 @@ public class Weather { observers.remove(obs); } + /** + * Makes time pass for weather + */ public void timePasses() { WeatherType[] enumValues = WeatherType.values(); currentWeather = enumValues[(currentWeather.ordinal() + 1) % enumValues.length]; diff --git a/observer/src/main/java/com/iluwatar/observer/WeatherObserver.java b/observer/src/main/java/com/iluwatar/observer/WeatherObserver.java index 1293214cd..afbf2a303 100644 --- a/observer/src/main/java/com/iluwatar/observer/WeatherObserver.java +++ b/observer/src/main/java/com/iluwatar/observer/WeatherObserver.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.observer; /** diff --git a/observer/src/main/java/com/iluwatar/observer/WeatherType.java b/observer/src/main/java/com/iluwatar/observer/WeatherType.java index c808368cf..b88452160 100644 --- a/observer/src/main/java/com/iluwatar/observer/WeatherType.java +++ b/observer/src/main/java/com/iluwatar/observer/WeatherType.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.observer; /** diff --git a/observer/src/main/java/com/iluwatar/observer/generic/GHobbits.java b/observer/src/main/java/com/iluwatar/observer/generic/GHobbits.java index 5dca0e779..da84b9aab 100644 --- a/observer/src/main/java/com/iluwatar/observer/generic/GHobbits.java +++ b/observer/src/main/java/com/iluwatar/observer/generic/GHobbits.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.observer.generic; import com.iluwatar.observer.WeatherType; diff --git a/observer/src/main/java/com/iluwatar/observer/generic/GOrcs.java b/observer/src/main/java/com/iluwatar/observer/generic/GOrcs.java index b279a78c1..9f41aa6cc 100644 --- a/observer/src/main/java/com/iluwatar/observer/generic/GOrcs.java +++ b/observer/src/main/java/com/iluwatar/observer/generic/GOrcs.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.observer.generic; import com.iluwatar.observer.WeatherType; diff --git a/observer/src/main/java/com/iluwatar/observer/generic/GWeather.java b/observer/src/main/java/com/iluwatar/observer/generic/GWeather.java index 9d1c6ed07..137000760 100644 --- a/observer/src/main/java/com/iluwatar/observer/generic/GWeather.java +++ b/observer/src/main/java/com/iluwatar/observer/generic/GWeather.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.observer.generic; import com.iluwatar.observer.WeatherType; @@ -15,6 +37,9 @@ public class GWeather extends Observable { currentWeather = WeatherType.SUNNY; } + /** + * Makes time pass for weather + */ public void timePasses() { WeatherType[] enumValues = WeatherType.values(); currentWeather = enumValues[(currentWeather.ordinal() + 1) % enumValues.length]; diff --git a/observer/src/main/java/com/iluwatar/observer/generic/Observable.java b/observer/src/main/java/com/iluwatar/observer/generic/Observable.java index f1ad2dca6..a9a6bf80d 100644 --- a/observer/src/main/java/com/iluwatar/observer/generic/Observable.java +++ b/observer/src/main/java/com/iluwatar/observer/generic/Observable.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.observer.generic; import java.util.List; @@ -22,6 +44,13 @@ public abstract class Observable, O extends Observ this.observers.add(observer); } + public void removeObserver(O observer) { + this.observers.remove(observer); + } + + /** + * Notify observers + */ @SuppressWarnings("unchecked") public void notifyObservers(A argument) { for (O observer : observers) { diff --git a/observer/src/main/java/com/iluwatar/observer/generic/Observer.java b/observer/src/main/java/com/iluwatar/observer/generic/Observer.java index b01955419..170f14ce8 100644 --- a/observer/src/main/java/com/iluwatar/observer/generic/Observer.java +++ b/observer/src/main/java/com/iluwatar/observer/generic/Observer.java @@ -1,12 +1,30 @@ +/** + * The MIT License + * Copyright (c) 2014 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.observer.generic; /** * * Observer - * - * @param - * @param - * @param */ public interface Observer, O extends Observer, A> { diff --git a/observer/src/main/java/com/iluwatar/observer/generic/Race.java b/observer/src/main/java/com/iluwatar/observer/generic/Race.java index ddc3337cb..08ec22945 100644 --- a/observer/src/main/java/com/iluwatar/observer/generic/Race.java +++ b/observer/src/main/java/com/iluwatar/observer/generic/Race.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.observer.generic; import com.iluwatar.observer.WeatherType; diff --git a/observer/src/test/java/com/iluwatar/observer/AppTest.java b/observer/src/test/java/com/iluwatar/observer/AppTest.java index 65976626d..237726463 100644 --- a/observer/src/test/java/com/iluwatar/observer/AppTest.java +++ b/observer/src/test/java/com/iluwatar/observer/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.observer; import org.junit.Test; -import com.iluwatar.observer.App; - /** * * Application test diff --git a/observer/src/test/java/com/iluwatar/observer/HobbitsTest.java b/observer/src/test/java/com/iluwatar/observer/HobbitsTest.java new file mode 100644 index 000000000..8b670c56b --- /dev/null +++ b/observer/src/test/java/com/iluwatar/observer/HobbitsTest.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 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.observer; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Date: 12/27/15 - 12:07 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class HobbitsTest extends WeatherObserverTest { + + @Parameterized.Parameters + public static Collection data() { + final List testData = new ArrayList<>(); + testData.add(new Object[]{WeatherType.SUNNY, "The happy hobbits bade in the warm sun."}); + testData.add(new Object[]{WeatherType.RAINY, "The hobbits look for cover from the rain."}); + testData.add(new Object[]{WeatherType.WINDY, "The hobbits hold their hats tightly in the windy weather."}); + testData.add(new Object[]{WeatherType.COLD, "The hobbits are shivering in the cold weather."}); + return testData; + } + + /** + * Create a new test with the given weather and expected response + * + * @param weather The weather that should be unleashed on the observer + * @param response The expected response from the observer + */ + public HobbitsTest(final WeatherType weather, final String response) { + super(weather, response, Hobbits::new); + } + +} diff --git a/observer/src/test/java/com/iluwatar/observer/OrcsTest.java b/observer/src/test/java/com/iluwatar/observer/OrcsTest.java new file mode 100644 index 000000000..a7997eaa7 --- /dev/null +++ b/observer/src/test/java/com/iluwatar/observer/OrcsTest.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 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.observer; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Date: 12/27/15 - 12:07 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class OrcsTest extends WeatherObserverTest { + + @Parameterized.Parameters + public static Collection data() { + final List testData = new ArrayList<>(); + testData.add(new Object[]{WeatherType.SUNNY, "The sun hurts the orcs' eyes."}); + testData.add(new Object[]{WeatherType.RAINY, "The orcs are dripping wet."}); + testData.add(new Object[]{WeatherType.WINDY, "The orc smell almost vanishes in the wind."}); + testData.add(new Object[]{WeatherType.COLD, "The orcs are freezing cold."}); + return testData; + } + + /** + * Create a new test with the given weather and expected response + * + * @param weather The weather that should be unleashed on the observer + * @param response The expected response from the observer + */ + public OrcsTest(final WeatherType weather, final String response) { + super(weather, response, Orcs::new); + } + +} diff --git a/observer/src/test/java/com/iluwatar/observer/StdOutTest.java b/observer/src/test/java/com/iluwatar/observer/StdOutTest.java new file mode 100644 index 000000000..afd870ae4 --- /dev/null +++ b/observer/src/test/java/com/iluwatar/observer/StdOutTest.java @@ -0,0 +1,76 @@ +/** + * The MIT License + * Copyright (c) 2014 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.observer; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/27/15 - 12:16 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since changes in the weather doesn't has + * any influence on any other accessible objects, except for writing to std-out using {@link + * System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + protected final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/observer/src/test/java/com/iluwatar/observer/WeatherObserverTest.java b/observer/src/test/java/com/iluwatar/observer/WeatherObserverTest.java new file mode 100644 index 000000000..a06c19952 --- /dev/null +++ b/observer/src/test/java/com/iluwatar/observer/WeatherObserverTest.java @@ -0,0 +1,81 @@ +/** + * The MIT License + * Copyright (c) 2014 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.observer; + +import org.junit.Test; + +import java.util.function.Supplier; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/27/15 - 11:44 AM + * + * @author Jeroen Meulemeester + */ +public abstract class WeatherObserverTest extends StdOutTest { + + /** + * The observer instance factory + */ + private final Supplier factory; + + /** + * The weather type currently tested + */ + private final WeatherType weather; + + /** + * The expected response from the observer + */ + private final String response; + + /** + * Create a new test instance using the given parameters + * + * @param weather The weather currently being tested + * @param response The expected response from the observer + * @param factory The factory, used to create an instance of the tested observer + */ + WeatherObserverTest(final WeatherType weather, final String response, final Supplier factory) { + this.weather = weather; + this.response = response; + this.factory = factory; + } + + /** + * Verify if the weather has the expected influence on the observer + */ + @Test + public void testObserver() { + final O observer = this.factory.get(); + verifyZeroInteractions(getStdOutMock()); + + observer.update(this.weather); + verify(getStdOutMock()).println(this.response); + verifyNoMoreInteractions(getStdOutMock()); + } + +} \ No newline at end of file diff --git a/observer/src/test/java/com/iluwatar/observer/WeatherTest.java b/observer/src/test/java/com/iluwatar/observer/WeatherTest.java new file mode 100644 index 000000000..7a38e7137 --- /dev/null +++ b/observer/src/test/java/com/iluwatar/observer/WeatherTest.java @@ -0,0 +1,83 @@ +/** + * The MIT License + * Copyright (c) 2014 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.observer; + +import org.junit.Test; +import org.mockito.InOrder; + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/27/15 - 11:08 AM + * + * @author Jeroen Meulemeester + */ +public class WeatherTest extends StdOutTest { + + /** + * Add a {@link WeatherObserver}, verify if it gets notified of a weather change, remove the + * observer again and verify that there are no more notifications. + */ + @Test + public void testAddRemoveObserver() { + final WeatherObserver observer = mock(WeatherObserver.class); + + final Weather weather = new Weather(); + weather.addObserver(observer); + verifyZeroInteractions(observer); + + weather.timePasses(); + verify(getStdOutMock()).println("The weather changed to rainy."); + verify(observer).update(WeatherType.RAINY); + + weather.removeObserver(observer); + weather.timePasses(); + verify(getStdOutMock()).println("The weather changed to windy."); + + verifyNoMoreInteractions(observer, getStdOutMock()); + } + + /** + * Verify if the weather passes in the order of the {@link WeatherType}s + */ + @Test + public void testTimePasses() { + final WeatherObserver observer = mock(WeatherObserver.class); + final Weather weather = new Weather(); + weather.addObserver(observer); + + final InOrder inOrder = inOrder(observer, getStdOutMock()); + final WeatherType[] weatherTypes = WeatherType.values(); + for (int i = 1; i < 20; i++) { + weather.timePasses(); + inOrder.verify(observer).update(weatherTypes[i % weatherTypes.length]); + } + + verifyNoMoreInteractions(observer); + } + +} \ No newline at end of file diff --git a/observer/src/test/java/com/iluwatar/observer/generic/GHobbitsTest.java b/observer/src/test/java/com/iluwatar/observer/generic/GHobbitsTest.java new file mode 100644 index 000000000..7668daee6 --- /dev/null +++ b/observer/src/test/java/com/iluwatar/observer/generic/GHobbitsTest.java @@ -0,0 +1,64 @@ +/** + * The MIT License + * Copyright (c) 2014 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.observer.generic; + +import com.iluwatar.observer.Hobbits; +import com.iluwatar.observer.WeatherObserverTest; +import com.iluwatar.observer.WeatherType; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Date: 12/27/15 - 12:07 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class GHobbitsTest extends ObserverTest { + + @Parameterized.Parameters + public static Collection data() { + final List testData = new ArrayList<>(); + testData.add(new Object[]{WeatherType.SUNNY, "The happy hobbits bade in the warm sun."}); + testData.add(new Object[]{WeatherType.RAINY, "The hobbits look for cover from the rain."}); + testData.add(new Object[]{WeatherType.WINDY, "The hobbits hold their hats tightly in the windy weather."}); + testData.add(new Object[]{WeatherType.COLD, "The hobbits are shivering in the cold weather."}); + return testData; + } + + /** + * Create a new test with the given weather and expected response + * + * @param weather The weather that should be unleashed on the observer + * @param response The expected response from the observer + */ + public GHobbitsTest(final WeatherType weather, final String response) { + super(weather, response, GHobbits::new); + } + +} diff --git a/observer/src/test/java/com/iluwatar/observer/generic/GWeatherTest.java b/observer/src/test/java/com/iluwatar/observer/generic/GWeatherTest.java new file mode 100644 index 000000000..758dbca8c --- /dev/null +++ b/observer/src/test/java/com/iluwatar/observer/generic/GWeatherTest.java @@ -0,0 +1,87 @@ +/** + * The MIT License + * Copyright (c) 2014 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.observer.generic; + +import com.iluwatar.observer.StdOutTest; +import com.iluwatar.observer.WeatherObserver; +import com.iluwatar.observer.WeatherType; + +import org.junit.Test; +import org.mockito.InOrder; + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/27/15 - 11:08 AM + * + * @author Jeroen Meulemeester + */ +public class GWeatherTest extends StdOutTest { + + /** + * Add a {@link WeatherObserver}, verify if it gets notified of a weather change, remove the + * observer again and verify that there are no more notifications. + */ + @Test + public void testAddRemoveObserver() { + final Race observer = mock(Race.class); + + final GWeather weather = new GWeather(); + weather.addObserver(observer); + verifyZeroInteractions(observer); + + weather.timePasses(); + verify(getStdOutMock()).println("The weather changed to rainy."); + verify(observer).update(weather, WeatherType.RAINY); + + weather.removeObserver(observer); + weather.timePasses(); + verify(getStdOutMock()).println("The weather changed to windy."); + + verifyNoMoreInteractions(observer, getStdOutMock()); + } + + /** + * Verify if the weather passes in the order of the {@link WeatherType}s + */ + @Test + public void testTimePasses() { + final Race observer = mock(Race.class); + final GWeather weather = new GWeather(); + weather.addObserver(observer); + + final InOrder inOrder = inOrder(observer, getStdOutMock()); + final WeatherType[] weatherTypes = WeatherType.values(); + for (int i = 1; i < 20; i++) { + weather.timePasses(); + inOrder.verify(observer).update(weather, weatherTypes[i % weatherTypes.length]); + } + + verifyNoMoreInteractions(observer); + } + +} \ No newline at end of file diff --git a/observer/src/test/java/com/iluwatar/observer/generic/ObserverTest.java b/observer/src/test/java/com/iluwatar/observer/generic/ObserverTest.java new file mode 100644 index 000000000..33d8daaf5 --- /dev/null +++ b/observer/src/test/java/com/iluwatar/observer/generic/ObserverTest.java @@ -0,0 +1,84 @@ +/** + * The MIT License + * Copyright (c) 2014 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.observer.generic; + +import com.iluwatar.observer.StdOutTest; +import com.iluwatar.observer.WeatherType; + +import org.junit.Test; + +import java.util.function.Supplier; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/27/15 - 11:44 AM + * + * @author Jeroen Meulemeester + */ +public abstract class ObserverTest extends StdOutTest { + + /** + * The observer instance factory + */ + private final Supplier factory; + + /** + * The weather type currently tested + */ + private final WeatherType weather; + + /** + * The expected response from the observer + */ + private final String response; + + /** + * Create a new test instance using the given parameters + * + * @param weather The weather currently being tested + * @param response The expected response from the observer + * @param factory The factory, used to create an instance of the tested observer + */ + ObserverTest(final WeatherType weather, final String response, final Supplier factory) { + this.weather = weather; + this.response = response; + this.factory = factory; + } + + /** + * Verify if the weather has the expected influence on the observer + */ + @Test + public void testObserver() { + final O observer = this.factory.get(); + verifyZeroInteractions(getStdOutMock()); + + observer.update(null, this.weather); + verify(getStdOutMock()).println(this.response); + verifyNoMoreInteractions(getStdOutMock()); + } + +} \ No newline at end of file diff --git a/observer/src/test/java/com/iluwatar/observer/generic/OrcsTest.java b/observer/src/test/java/com/iluwatar/observer/generic/OrcsTest.java new file mode 100644 index 000000000..9ac1bddea --- /dev/null +++ b/observer/src/test/java/com/iluwatar/observer/generic/OrcsTest.java @@ -0,0 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 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.observer.generic; + +import com.iluwatar.observer.WeatherType; + +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Date: 12/27/15 - 12:07 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class OrcsTest extends ObserverTest { + + @Parameterized.Parameters + public static Collection data() { + final List testData = new ArrayList<>(); + testData.add(new Object[]{WeatherType.SUNNY, "The sun hurts the orcs' eyes."}); + testData.add(new Object[]{WeatherType.RAINY, "The orcs are dripping wet."}); + testData.add(new Object[]{WeatherType.WINDY, "The orc smell almost vanishes in the wind."}); + testData.add(new Object[]{WeatherType.COLD, "The orcs are freezing cold."}); + return testData; + } + + /** + * Create a new test with the given weather and expected response + * + * @param weather The weather that should be unleashed on the observer + * @param response The expected response from the observer + */ + public OrcsTest(final WeatherType weather, final String response) { + super(weather, response, GOrcs::new); + } + +} diff --git a/page-object/README.md b/page-object/README.md new file mode 100644 index 000000000..b4f8246f1 --- /dev/null +++ b/page-object/README.md @@ -0,0 +1,31 @@ +--- +layout: pattern +title: Page Object +folder: page-object +permalink: /patterns/page-object/ +categories: Testing +tags: +- Java +- Difficulty-Intermediate +--- + +## Intent + +Page Object encapsulates the UI, hiding the underlying UI widgetry of an application (commonly a web application) and providing an application-specific API to allow the manipulation of UI components required for tests. In doing so, it allows the test class itself to focus on the test logic instead. + + +![alt text](./etc/page-object.png "Page Object") + + +## Applicability + +Use the Page Object pattern when + +* You are writing automated tests for your web application and you want to separate the UI manipulation required for the tests from the actual test logic. +* Make your tests less brittle, and more readable and robust + +## Credits + +* [Martin Fowler - PageObject](http://martinfowler.com/bliki/PageObject.html) +* [Selenium - Page Objects](https://github.com/SeleniumHQ/selenium/wiki/PageObjects) + diff --git a/page-object/etc/page-object.png b/page-object/etc/page-object.png new file mode 100644 index 000000000..4240b438e Binary files /dev/null and b/page-object/etc/page-object.png differ diff --git a/page-object/etc/page-object.ucls b/page-object/etc/page-object.ucls new file mode 100644 index 000000000..466c542fd --- /dev/null +++ b/page-object/etc/page-object.ucls @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/page-object/pom.xml b/page-object/pom.xml new file mode 100644 index 000000000..e6f888b05 --- /dev/null +++ b/page-object/pom.xml @@ -0,0 +1,46 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + page-object + + + junit + junit + test + + + net.sourceforge.htmlunit + htmlunit + + + diff --git a/page-object/src/main/java/com/iluwatar/pageobject/App.java b/page-object/src/main/java/com/iluwatar/pageobject/App.java new file mode 100644 index 000000000..235eedd25 --- /dev/null +++ b/page-object/src/main/java/com/iluwatar/pageobject/App.java @@ -0,0 +1,89 @@ +/** + * The MIT License + * Copyright (c) 2014 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.pageobject; + +import java.awt.Desktop; +import java.io.File; +import java.io.IOException; + +/** + * Page Object pattern wraps an UI component with an application specific API allowing you to + * manipulate the UI elements without having to dig around with the underlying UI technology used. This is + * especially useful for testing as it means your tests will be less brittle. Your tests can concentrate on + * the actual test cases where as the manipulation of the UI can be left to the internals of the page object + * itself. + * + *

+ * Due to this reason, it has become very popular within the test automation community. + * In particular, it is very common in that the page object is used to represent the html pages of a + * web application that is under test. This web application is referred to as AUT (Application Under Test). + * A web browser automation tool/framework like Selenium for instance, is then used to drive the automating + * of the browser navigation and user actions journeys through this web application. Your test class would + * therefore only be responsible for particular test cases and page object would be used by the test class + * for UI manipulation required for the tests. + * + *

+ * In this implementation rather than using Selenium, the HtmlUnit library is used as a replacement to + * represent the specific html elements and to drive the browser. The purpose of this example is just to + * provide a simple version that showcase the intentions of this pattern and how this pattern is used + * in order to understand it. + */ +public final class App { + + private App() { + } + + /** + * Application entry point + * + *

+ * The application under development is a web application. Normally you would probably have a + * backend that is probably implemented in an object-oriented language (e.g. Java) that serves + * the frontend which comprises of a series of HTML, CSS, JS etc... + * + *

+ * For illustrations purposes only, a very simple static html app is used here. This main method + * just fires up this simple web app in a default browser. + * + * @param args arguments + */ + public static void main(String[] args) { + + try { + File applicationFile = new File(App.class.getClassLoader().getResource("sample-ui/login.html").getPath()); + + // should work for unix like OS (mac, unix etc...) + if (Desktop.isDesktopSupported()) { + Desktop.getDesktop().open(applicationFile); + + } else { + // java Desktop not supported - above unlikely to work for Windows so try following instead... + Runtime.getRuntime().exec("cmd.exe start " + applicationFile); + } + + } catch (IOException ex) { + ex.printStackTrace(); + } + + } +} diff --git a/page-object/src/main/resources/sample-ui/album-list.html b/page-object/src/main/resources/sample-ui/album-list.html new file mode 100644 index 000000000..e02e65cf3 --- /dev/null +++ b/page-object/src/main/resources/sample-ui/album-list.html @@ -0,0 +1,60 @@ + + + + + + Album List + + + + +

+

My Album Viewer

+
+ +
+
+ + + + + + + + + + + + + + + +
Album TitleAlbum YearAlbum RatingNumber of SongsArtist
212011A11Adele
+
+
+ + + \ No newline at end of file diff --git a/page-object/src/main/resources/sample-ui/album-page.html b/page-object/src/main/resources/sample-ui/album-page.html new file mode 100644 index 000000000..5bf843be2 --- /dev/null +++ b/page-object/src/main/resources/sample-ui/album-page.html @@ -0,0 +1,74 @@ + + + + + + Album Page + + + +
+

21

+
+ +
+
+
+ + + + + + + + + + + + + + + + + + + +
Title:
Artist:
Year: + +
Rating:
Number of Songs:
+
+
+
+ + + \ No newline at end of file diff --git a/page-object/src/main/resources/sample-ui/css/album-list.css b/page-object/src/main/resources/sample-ui/css/album-list.css new file mode 100644 index 000000000..61fd0af81 --- /dev/null +++ b/page-object/src/main/resources/sample-ui/css/album-list.css @@ -0,0 +1,68 @@ +/** + * The MIT License + * Copyright (c) 2014 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. + */ + +table { + font-size: 16px; + border-collapse: collapse; +} + +th { + background-color: #FFFFFF; + border: 1px solid black; + color: black; + width: 150px; + height: 20px; +} + +td { + border: 1px solid black; + background-color: white; +} + +th, td { + padding: 15px; + text-align: left; +} \ No newline at end of file diff --git a/page-object/src/main/resources/sample-ui/css/style.css b/page-object/src/main/resources/sample-ui/css/style.css new file mode 100644 index 000000000..691bba77f --- /dev/null +++ b/page-object/src/main/resources/sample-ui/css/style.css @@ -0,0 +1,49 @@ +/** + * The MIT License + * Copyright (c) 2014 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. + */ + +body { + font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; +} \ No newline at end of file diff --git a/page-object/src/main/resources/sample-ui/login.html b/page-object/src/main/resources/sample-ui/login.html new file mode 100644 index 000000000..1d7786057 --- /dev/null +++ b/page-object/src/main/resources/sample-ui/login.html @@ -0,0 +1,48 @@ + + + + + + Login + + + +
+

Login

+
+ +
+
+ + + + +
Username:
Password:
+ +
+
+ + \ No newline at end of file diff --git a/page-object/src/test/java/com/iluwatar/pageobject/AlbumListPageTest.java b/page-object/src/test/java/com/iluwatar/pageobject/AlbumListPageTest.java new file mode 100644 index 000000000..b97410036 --- /dev/null +++ b/page-object/src/test/java/com/iluwatar/pageobject/AlbumListPageTest.java @@ -0,0 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014 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.pageobject; + + +import com.gargoylesoftware.htmlunit.WebClient; +import com.iluwatar.pageobject.pages.AlbumListPage; +import com.iluwatar.pageobject.pages.AlbumPage; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + + +public class AlbumListPageTest { + + private AlbumListPage albumListPage = new AlbumListPage(new WebClient()); + + @Before + public void setUp() { + albumListPage.navigateToPage(); + } + + @Test + public void testSelectAlbum() { + AlbumPage albumPage = albumListPage.selectAlbum("21"); + albumPage.navigateToPage(); + assertTrue(albumPage.isAt()); + } + +} diff --git a/page-object/src/test/java/com/iluwatar/pageobject/AlbumPageTest.java b/page-object/src/test/java/com/iluwatar/pageobject/AlbumPageTest.java new file mode 100644 index 000000000..01461cc8d --- /dev/null +++ b/page-object/src/test/java/com/iluwatar/pageobject/AlbumPageTest.java @@ -0,0 +1,64 @@ +/** + * The MIT License + * Copyright (c) 2014 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.pageobject; + +import com.gargoylesoftware.htmlunit.WebClient; +import com.iluwatar.pageobject.pages.AlbumListPage; +import com.iluwatar.pageobject.pages.AlbumPage; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class AlbumPageTest { + + private AlbumPage albumPage = new AlbumPage(new WebClient()); + + @Before + public void setUp() { + albumPage.navigateToPage(); + } + + @Test + public void testSaveAlbum() { + + AlbumPage albumPageAfterChanges = albumPage + .changeAlbumTitle("25") + .changeArtist("Adele Laurie Blue Adkins") + .changeAlbumYear(2015) + .changeAlbumRating("B") + .changeNumberOfSongs(20) + .saveChanges(); + + assertTrue(albumPageAfterChanges.isAt()); + + } + + @Test + public void testCancelChanges() { + AlbumListPage albumListPage = albumPage.cancelChanges(); + albumListPage.navigateToPage(); + assertTrue(albumListPage.isAt()); + } + +} diff --git a/page-object/src/test/java/com/iluwatar/pageobject/LoginPageTest.java b/page-object/src/test/java/com/iluwatar/pageobject/LoginPageTest.java new file mode 100644 index 000000000..659df0629 --- /dev/null +++ b/page-object/src/test/java/com/iluwatar/pageobject/LoginPageTest.java @@ -0,0 +1,52 @@ +/** + * The MIT License + * Copyright (c) 2014 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.pageobject; + +import com.gargoylesoftware.htmlunit.WebClient; +import com.iluwatar.pageobject.pages.AlbumListPage; +import com.iluwatar.pageobject.pages.LoginPage; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +public class LoginPageTest { + + private LoginPage loginPage = new LoginPage(new WebClient()); + + @Before + public void setUp() { + loginPage.navigateToPage(); + } + + @Test + public void testLogin() { + AlbumListPage albumListPage = loginPage + .enterUsername("admin") + .enterPassword("password") + .login(); + albumListPage.navigateToPage(); + assertTrue(albumListPage.isAt()); + } + +} diff --git a/page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumListPage.java b/page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumListPage.java new file mode 100644 index 000000000..fb322ff53 --- /dev/null +++ b/page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumListPage.java @@ -0,0 +1,96 @@ +/** + * The MIT License + * Copyright (c) 2014 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.pageobject.pages; + +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.HtmlAnchor; +import com.gargoylesoftware.htmlunit.html.HtmlPage; + +import java.io.IOException; +import java.util.List; + +/** + * Page Object encapsulating the Album List page (album-list.html) + */ +public class AlbumListPage extends Page { + + private static final String ALBUM_LIST_HTML_FILE = "album-list.html"; + private static final String PAGE_URL = "file:" + AUT_PATH + ALBUM_LIST_HTML_FILE; + + private HtmlPage page; + + + /** + * Constructor + */ + public AlbumListPage(WebClient webClient) { + super(webClient); + } + + + /** + * Navigates to the Album List Page + * + * @return {@link AlbumListPage} + */ + public AlbumListPage navigateToPage() { + try { + page = this.webClient.getPage(PAGE_URL); + } catch (IOException e) { + e.printStackTrace(); + } + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isAt() { + return "Album List".equals(page.getTitleText()); + } + + /** + * Selects an album by the given album title + * + * @param albumTitle the title of the album to click + * @return the album page + */ + public AlbumPage selectAlbum(String albumTitle) { + // uses XPath to find list of html anchor tags with the class album in it + List albumLinks = (List) page.getByXPath("//tr[@class='album']//a"); + for (HtmlAnchor anchor : albumLinks) { + if (anchor.getTextContent().equals(albumTitle)) { + try { + anchor.click(); + return new AlbumPage(webClient); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + throw new IllegalArgumentException("No links with the album title: " + albumTitle); + } + + +} diff --git a/page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumPage.java b/page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumPage.java new file mode 100644 index 000000000..4dffb93dd --- /dev/null +++ b/page-object/src/test/java/com/iluwatar/pageobject/pages/AlbumPage.java @@ -0,0 +1,174 @@ +/** + * The MIT License + * Copyright (c) 2014 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.pageobject.pages; + +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.HtmlNumberInput; +import com.gargoylesoftware.htmlunit.html.HtmlOption; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlSelect; +import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; +import com.gargoylesoftware.htmlunit.html.HtmlTextInput; + +import java.io.IOException; + +/** + * Page Object encapsulating the Album Page (album-page.html) + */ +public class AlbumPage extends Page { + + private static final String ALBUM_PAGE_HTML_FILE = "album-page.html"; + private static final String PAGE_URL = "file:" + AUT_PATH + ALBUM_PAGE_HTML_FILE; + + private HtmlPage page; + + + /** + * Constructor + */ + public AlbumPage(WebClient webClient) { + super(webClient); + } + + + /** + * Navigates to the album page + * + * @return {@link AlbumPage} + */ + public AlbumPage navigateToPage() { + try { + page = this.webClient.getPage(PAGE_URL); + } catch (IOException e) { + e.printStackTrace(); + } + return this; + } + + + /** + * {@inheritDoc} + */ + @Override + public boolean isAt() { + return "Album Page".equals(page.getTitleText()); + } + + + /** + * Sets the album title input text field + * + * @param albumTitle the new album title value to set + * @return {@link AlbumPage} + */ + public AlbumPage changeAlbumTitle(String albumTitle) { + HtmlTextInput albumTitleInputTextField = (HtmlTextInput) page.getElementById("albumTitle"); + albumTitleInputTextField.setText(albumTitle); + return this; + } + + + /** + * Sets the artist input text field + * + * @param artist the new artist value to set + * @return {@link AlbumPage} + */ + public AlbumPage changeArtist(String artist) { + HtmlTextInput artistInputTextField = (HtmlTextInput) page.getElementById("albumArtist"); + artistInputTextField.setText(artist); + return this; + } + + + /** + * Selects the select's option value based on the year value given + * + * @param year the new year value to set + * @return {@link AlbumPage} + */ + public AlbumPage changeAlbumYear(int year) { + HtmlSelect albumYearSelectOption = (HtmlSelect) page.getElementById("albumYear"); + HtmlOption yearOption = albumYearSelectOption.getOptionByValue(Integer.toString(year)); + albumYearSelectOption.setSelectedAttribute(yearOption, true); + return this; + } + + + /** + * Sets the album rating input text field + * + * @param albumRating the new album rating value to set + * @return {@link AlbumPage} + */ + public AlbumPage changeAlbumRating(String albumRating) { + HtmlTextInput albumRatingInputTextField = (HtmlTextInput) page.getElementById("albumRating"); + albumRatingInputTextField.setText(albumRating); + return this; + } + + /** + * Sets the number of songs number input field + * + * @param numberOfSongs the new number of songs value to be set + * @return {@link AlbumPage} + */ + public AlbumPage changeNumberOfSongs(int numberOfSongs) { + HtmlNumberInput numberOfSongsNumberField = (HtmlNumberInput) page.getElementById("numberOfSongs"); + numberOfSongsNumberField.setText(Integer.toString(numberOfSongs)); + return this; + } + + + /** + * Cancel changes made by clicking the cancel button + * + * @return {@link AlbumListPage} + */ + public AlbumListPage cancelChanges() { + HtmlSubmitInput cancelButton = (HtmlSubmitInput) page.getElementById("cancelButton"); + try { + cancelButton.click(); + } catch (IOException e) { + e.printStackTrace(); + } + return new AlbumListPage(webClient); + } + + + /** + * Saves changes made by clicking the save button + * + * @return {@link AlbumPage} + */ + public AlbumPage saveChanges() { + HtmlSubmitInput saveButton = (HtmlSubmitInput) page.getElementById("saveButton"); + try { + saveButton.click(); + } catch (IOException e) { + e.printStackTrace(); + } + return this; + } + +} diff --git a/page-object/src/test/java/com/iluwatar/pageobject/pages/LoginPage.java b/page-object/src/test/java/com/iluwatar/pageobject/pages/LoginPage.java new file mode 100644 index 000000000..d4bd0850c --- /dev/null +++ b/page-object/src/test/java/com/iluwatar/pageobject/pages/LoginPage.java @@ -0,0 +1,116 @@ +/** + * The MIT License + * Copyright (c) 2014 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.pageobject.pages; + +import com.gargoylesoftware.htmlunit.WebClient; +import com.gargoylesoftware.htmlunit.html.HtmlPage; +import com.gargoylesoftware.htmlunit.html.HtmlPasswordInput; +import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput; +import com.gargoylesoftware.htmlunit.html.HtmlTextInput; +import java.io.IOException; + +/** + * Page Object encapsulating the Login Page (login.html) + */ +public class LoginPage extends Page { + + private static final String LOGIN_PAGE_HTML_FILE = "login.html"; + private static final String PAGE_URL = "file:" + AUT_PATH + LOGIN_PAGE_HTML_FILE; + + private HtmlPage page; + + /** + * Constructor + * + * @param webClient {@link WebClient} + */ + public LoginPage(WebClient webClient) { + super(webClient); + } + + /** + * Navigates to the Login page + * + * @return {@link LoginPage} + */ + public LoginPage navigateToPage() { + try { + page = this.webClient.getPage(PAGE_URL); + } catch (IOException e) { + e.printStackTrace(); + } + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean isAt() { + return "Login".equals(page.getTitleText()); + } + + + /** + * Enters the username into the username input text field + * + * @param username the username to enter + * @return {@link LoginPage} + */ + public LoginPage enterUsername(String username) { + HtmlTextInput usernameInputTextField = (HtmlTextInput) page.getElementById("username"); + usernameInputTextField.setText(username); + return this; + } + + + /** + * Enters the password into the password input password field + * + * @param password the password to enter + * @return {@link LoginPage} + */ + public LoginPage enterPassword(String password) { + HtmlPasswordInput passwordInputPasswordField = (HtmlPasswordInput) page.getElementById("password"); + passwordInputPasswordField.setText(password); + return this; + } + + + /** + * Clicking on the login button to 'login' + * + * @return {@link AlbumListPage} + * - this is the page that user gets navigated to once successfully logged in + */ + public AlbumListPage login() { + HtmlSubmitInput loginButton = (HtmlSubmitInput) page.getElementById("loginButton"); + try { + loginButton.click(); + } catch (IOException e) { + e.printStackTrace(); + } + return new AlbumListPage(webClient); + } + +} diff --git a/page-object/src/test/java/com/iluwatar/pageobject/pages/Page.java b/page-object/src/test/java/com/iluwatar/pageobject/pages/Page.java new file mode 100644 index 000000000..b0b328e7c --- /dev/null +++ b/page-object/src/test/java/com/iluwatar/pageobject/pages/Page.java @@ -0,0 +1,57 @@ +/** + * The MIT License + * Copyright (c) 2014 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.pageobject.pages; + +import com.gargoylesoftware.htmlunit.WebClient; + +/** + * Encapsulation for a generic 'Page' + */ +public abstract class Page { + + /** + * Application Under Test path + * This directory location is where html web pages are located + */ + public static final String AUT_PATH = "src/main/resources/sample-ui/"; + + protected final WebClient webClient; + + /** + * Constructor + * + * @param webClient {@link WebClient} + */ + public Page(WebClient webClient) { + this.webClient = webClient; + } + + /** + * Checks that the current page is actually the page this page object represents + * + * @return true if so, otherwise false + */ + public abstract boolean isAt(); + + +} diff --git a/poison-pill/index.md b/poison-pill/README.md similarity index 66% rename from poison-pill/index.md rename to poison-pill/README.md index cd60e1a68..0815b376e 100644 --- a/poison-pill/index.md +++ b/poison-pill/README.md @@ -4,18 +4,23 @@ title: Poison Pill folder: poison-pill permalink: /patterns/poison-pill/ categories: Other -tags: Java +tags: + - Java + - Difficulty-Intermediate + - Reactive --- -**Intent:** Poison Pill is known predefined data item that allows to provide +## Intent +Poison Pill is known predefined data item that allows to provide graceful shutdown for separate distributed consumption process. ![alt text](./etc/poison-pill.png "Poison Pill") -**Applicability:** Use the Poison Pill idiom when +## Applicability +Use the Poison Pill idiom when * need to send signal from one thread/process to another to terminate -**Real world examples:** +## Real world examples * [akka.actor.PoisonPill](http://doc.akka.io/docs/akka/2.1.4/java/untyped-actors.html) diff --git a/poison-pill/pom.xml b/poison-pill/pom.xml index 06d9f34de..9717f5e13 100644 --- a/poison-pill/pom.xml +++ b/poison-pill/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT poison-pill @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/poison-pill/src/main/java/com/iluwatar/poison/pill/App.java b/poison-pill/src/main/java/com/iluwatar/poison/pill/App.java index 91d30fddd..88be9b05f 100644 --- a/poison-pill/src/main/java/com/iluwatar/poison/pill/App.java +++ b/poison-pill/src/main/java/com/iluwatar/poison/pill/App.java @@ -1,43 +1,67 @@ +/** + * The MIT License + * Copyright (c) 2014 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.poison.pill; /** - * One of the possible approaches to terminate Producer-Consumer pattern is using the Poison Pill idiom. - * If you use Poison Pill as the termination signal then Producer is responsible to notify Consumer that - * the exchange is over and reject any further messages. The Consumer receiving Poison Pill will stop - * reading messages from the queue. You must also ensure that the Poison Pill will be the last message - * that will be read from the queue (if you have prioritized queue then this can be tricky). + * One of the possible approaches to terminate Producer-Consumer pattern is using the Poison Pill + * idiom. If you use Poison Pill as the termination signal then Producer is responsible to notify + * Consumer that the exchange is over and reject any further messages. The Consumer receiving Poison + * Pill will stop reading messages from the queue. You must also ensure that the Poison Pill will be + * the last message that will be read from the queue (if you have prioritized queue then this can be + * tricky). *

- * In simple cases the Poison Pill can be just a null-reference, but holding a unique separate shared - * object-marker (with name "Poison" or "Poison Pill") is more clear and self describing. + * In simple cases the Poison Pill can be just a null-reference, but holding a unique separate + * shared object-marker (with name "Poison" or "Poison Pill") is more clear and self describing. * */ public class App { - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { - MessageQueue queue = new SimpleMessageQueue(10000); + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + MessageQueue queue = new SimpleMessageQueue(10000); - final Producer producer = new Producer("PRODUCER_1", queue); - final Consumer consumer = new Consumer("CONSUMER_1", queue); + final Producer producer = new Producer("PRODUCER_1", queue); + final Consumer consumer = new Consumer("CONSUMER_1", queue); - new Thread() { - @Override - public void run() { - consumer.consume(); - } - }.start(); + new Thread() { + @Override + public void run() { + consumer.consume(); + } + }.start(); - new Thread() { - @Override - public void run() { - producer.send("hand shake"); - producer.send("some very important information"); - producer.send("bye!"); - producer.stop(); - } - }.start(); - } + new Thread() { + @Override + public void run() { + producer.send("hand shake"); + producer.send("some very important information"); + producer.send("bye!"); + producer.stop(); + } + }.start(); + } } diff --git a/poison-pill/src/main/java/com/iluwatar/poison/pill/Consumer.java b/poison-pill/src/main/java/com/iluwatar/poison/pill/Consumer.java index 3bb9dcc56..c811225e1 100644 --- a/poison-pill/src/main/java/com/iluwatar/poison/pill/Consumer.java +++ b/poison-pill/src/main/java/com/iluwatar/poison/pill/Consumer.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.poison.pill; import com.iluwatar.poison.pill.Message.Headers; @@ -7,32 +29,36 @@ import com.iluwatar.poison.pill.Message.Headers; */ public class Consumer { - private final MQSubscribePoint queue; - private final String name; + private final MqSubscribePoint queue; + private final String name; - public Consumer(String name, MQSubscribePoint queue) { - this.name = name; - this.queue = queue; - } + public Consumer(String name, MqSubscribePoint queue) { + this.name = name; + this.queue = queue; + } - public void consume() { - while (true) { - Message msg; - try { - msg = queue.take(); - if (msg == Message.POISON_PILL) { - System.out.println(String.format("Consumer %s receive request to terminate.", name)); - break; - } - } catch (InterruptedException e) { - // allow thread to exit - System.err.println(e); - return; - } + /** + * Consume message + */ + public void consume() { + while (true) { + Message msg; + try { + msg = queue.take(); + if (Message.POISON_PILL.equals(msg)) { + System.out.println(String.format("Consumer %s receive request to terminate.", name)); + break; + } + } catch (InterruptedException e) { + // allow thread to exit + System.err.println(e); + return; + } - String sender = msg.getHeader(Headers.SENDER); - String body = msg.getBody(); - System.out.println(String.format("Message [%s] from [%s] received by [%s]", body, sender, name)); - } - } + String sender = msg.getHeader(Headers.SENDER); + String body = msg.getBody(); + System.out.println(String.format("Message [%s] from [%s] received by [%s]", body, sender, + name)); + } + } } diff --git a/poison-pill/src/main/java/com/iluwatar/poison/pill/MQPublishPoint.java b/poison-pill/src/main/java/com/iluwatar/poison/pill/MQPublishPoint.java deleted file mode 100644 index 9c72242be..000000000 --- a/poison-pill/src/main/java/com/iluwatar/poison/pill/MQPublishPoint.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.iluwatar.poison.pill; - -/** - * Endpoint to publish {@link Message} to queue - */ -public interface MQPublishPoint { - - public void put(Message msg) throws InterruptedException; -} diff --git a/poison-pill/src/main/java/com/iluwatar/poison/pill/MQSubscribePoint.java b/poison-pill/src/main/java/com/iluwatar/poison/pill/MQSubscribePoint.java deleted file mode 100644 index f689835b6..000000000 --- a/poison-pill/src/main/java/com/iluwatar/poison/pill/MQSubscribePoint.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.iluwatar.poison.pill; - -/** - * Endpoint to retrieve {@link Message} from queue - */ -public interface MQSubscribePoint { - - public Message take() throws InterruptedException; -} diff --git a/poison-pill/src/main/java/com/iluwatar/poison/pill/Message.java b/poison-pill/src/main/java/com/iluwatar/poison/pill/Message.java index 8e167790f..c2f6b57bd 100644 --- a/poison-pill/src/main/java/com/iluwatar/poison/pill/Message.java +++ b/poison-pill/src/main/java/com/iluwatar/poison/pill/Message.java @@ -1,53 +1,79 @@ +/** + * The MIT License + * Copyright (c) 2014 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.poison.pill; import java.util.Map; /** - * Interface that implements the Message pattern and represents an inbound or outbound - * message as part of an {@link Producer}-{@link Consumer} exchange. + * Interface that implements the Message pattern and represents an inbound or outbound message as + * part of an {@link Producer}-{@link Consumer} exchange. */ public interface Message { - public static final Message POISON_PILL = new Message() { + Message POISON_PILL = new Message() { - @Override - public void addHeader(Headers header, String value) { - throw poison(); - } + @Override + public void addHeader(Headers header, String value) { + throw poison(); + } - @Override - public String getHeader(Headers header) { - throw poison(); - } + @Override + public String getHeader(Headers header) { + throw poison(); + } - @Override - public Map getHeaders() { - throw poison(); - } + @Override + public Map getHeaders() { + throw poison(); + } - @Override - public void setBody(String body) { - throw poison(); - } + @Override + public void setBody(String body) { + throw poison(); + } - @Override - public String getBody() { - throw poison(); - } + @Override + public String getBody() { + throw poison(); + } - private RuntimeException poison() { - return new UnsupportedOperationException("Poison"); - } + private RuntimeException poison() { + return new UnsupportedOperationException("Poison"); + } - }; + }; - public enum Headers { - DATE, SENDER - } + public enum Headers { + DATE, SENDER + } - public void addHeader(Headers header, String value); - public String getHeader(Headers header); - public Map getHeaders(); - public void setBody(String body); - public String getBody(); + void addHeader(Headers header, String value); + + String getHeader(Headers header); + + Map getHeaders(); + + void setBody(String body); + + String getBody(); } diff --git a/poison-pill/src/main/java/com/iluwatar/poison/pill/MessageQueue.java b/poison-pill/src/main/java/com/iluwatar/poison/pill/MessageQueue.java index 99231ab51..1f774aeff 100644 --- a/poison-pill/src/main/java/com/iluwatar/poison/pill/MessageQueue.java +++ b/poison-pill/src/main/java/com/iluwatar/poison/pill/MessageQueue.java @@ -1,8 +1,30 @@ +/** + * The MIT License + * Copyright (c) 2014 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.poison.pill; /** * Represents abstraction of channel (or pipe) that bounds {@link Producer} and {@link Consumer} */ -public interface MessageQueue extends MQPublishPoint, MQSubscribePoint { +public interface MessageQueue extends MqPublishPoint, MqSubscribePoint { } diff --git a/poison-pill/src/main/java/com/iluwatar/poison/pill/MqPublishPoint.java b/poison-pill/src/main/java/com/iluwatar/poison/pill/MqPublishPoint.java new file mode 100644 index 000000000..68dabf39c --- /dev/null +++ b/poison-pill/src/main/java/com/iluwatar/poison/pill/MqPublishPoint.java @@ -0,0 +1,31 @@ +/** + * The MIT License + * Copyright (c) 2014 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.poison.pill; + +/** + * Endpoint to publish {@link Message} to queue + */ +public interface MqPublishPoint { + + void put(Message msg) throws InterruptedException; +} diff --git a/poison-pill/src/main/java/com/iluwatar/poison/pill/MqSubscribePoint.java b/poison-pill/src/main/java/com/iluwatar/poison/pill/MqSubscribePoint.java new file mode 100644 index 000000000..8bc58226e --- /dev/null +++ b/poison-pill/src/main/java/com/iluwatar/poison/pill/MqSubscribePoint.java @@ -0,0 +1,31 @@ +/** + * The MIT License + * Copyright (c) 2014 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.poison.pill; + +/** + * Endpoint to retrieve {@link Message} from queue + */ +public interface MqSubscribePoint { + + Message take() throws InterruptedException; +} diff --git a/poison-pill/src/main/java/com/iluwatar/poison/pill/Producer.java b/poison-pill/src/main/java/com/iluwatar/poison/pill/Producer.java index 56d086204..15dcad40e 100644 --- a/poison-pill/src/main/java/com/iluwatar/poison/pill/Producer.java +++ b/poison-pill/src/main/java/com/iluwatar/poison/pill/Producer.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.poison.pill; import java.util.Date; @@ -5,44 +27,55 @@ import java.util.Date; import com.iluwatar.poison.pill.Message.Headers; /** - * Class responsible for producing unit of work that can be expressed as message and submitted to queue + * Class responsible for producing unit of work that can be expressed as message and submitted to + * queue */ public class Producer { - private final MQPublishPoint queue; - private final String name; - private boolean isStopped; + private final MqPublishPoint queue; + private final String name; + private boolean isStopped; - public Producer(String name, MQPublishPoint queue) { - this.name = name; - this.queue = queue; - this.isStopped = false; - } + /** + * Constructor + */ + public Producer(String name, MqPublishPoint queue) { + this.name = name; + this.queue = queue; + this.isStopped = false; + } - public void send(String body) { - if (isStopped) { - throw new IllegalStateException(String.format("Producer %s was stopped and fail to deliver requested message [%s].", body, name)); - } - Message msg = new SimpleMessage(); - msg.addHeader(Headers.DATE, new Date().toString()); - msg.addHeader(Headers.SENDER, name); - msg.setBody(body); + /** + * Send message to queue + */ + public void send(String body) { + if (isStopped) { + throw new IllegalStateException(String.format( + "Producer %s was stopped and fail to deliver requested message [%s].", body, name)); + } + Message msg = new SimpleMessage(); + msg.addHeader(Headers.DATE, new Date().toString()); + msg.addHeader(Headers.SENDER, name); + msg.setBody(body); - try { - queue.put(msg); - } catch (InterruptedException e) { - // allow thread to exit - System.err.println(e); - } - } + try { + queue.put(msg); + } catch (InterruptedException e) { + // allow thread to exit + System.err.println(e); + } + } - public void stop() { - isStopped = true; - try { - queue.put(Message.POISON_PILL); - } catch (InterruptedException e) { - // allow thread to exit - System.err.println(e); - } - } + /** + * Stop system by sending poison pill + */ + public void stop() { + isStopped = true; + try { + queue.put(Message.POISON_PILL); + } catch (InterruptedException e) { + // allow thread to exit + System.err.println(e); + } + } } diff --git a/poison-pill/src/main/java/com/iluwatar/poison/pill/SimpleMessage.java b/poison-pill/src/main/java/com/iluwatar/poison/pill/SimpleMessage.java index 61a8664f5..3002449fd 100644 --- a/poison-pill/src/main/java/com/iluwatar/poison/pill/SimpleMessage.java +++ b/poison-pill/src/main/java/com/iluwatar/poison/pill/SimpleMessage.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.poison.pill; import java.util.Collections; @@ -9,31 +31,31 @@ import java.util.Map; */ public class SimpleMessage implements Message { - private Map headers = new HashMap<>(); - private String body; + private Map headers = new HashMap<>(); + private String body; - @Override - public void addHeader(Headers header, String value) { - headers.put(header, value); - } + @Override + public void addHeader(Headers header, String value) { + headers.put(header, value); + } - @Override - public String getHeader(Headers header) { - return headers.get(header); - } + @Override + public String getHeader(Headers header) { + return headers.get(header); + } - @Override - public Map getHeaders() { - return Collections.unmodifiableMap(headers); - } + @Override + public Map getHeaders() { + return Collections.unmodifiableMap(headers); + } - @Override - public void setBody(String body) { - this.body = body; - } + @Override + public void setBody(String body) { + this.body = body; + } - @Override - public String getBody() { - return body; - } + @Override + public String getBody() { + return body; + } } diff --git a/poison-pill/src/main/java/com/iluwatar/poison/pill/SimpleMessageQueue.java b/poison-pill/src/main/java/com/iluwatar/poison/pill/SimpleMessageQueue.java index 316ed33e5..6f45742e9 100644 --- a/poison-pill/src/main/java/com/iluwatar/poison/pill/SimpleMessageQueue.java +++ b/poison-pill/src/main/java/com/iluwatar/poison/pill/SimpleMessageQueue.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.poison.pill; import java.util.concurrent.ArrayBlockingQueue; @@ -8,20 +30,19 @@ import java.util.concurrent.BlockingQueue; */ public class SimpleMessageQueue implements MessageQueue { - private final BlockingQueue queue; + private final BlockingQueue queue; - public SimpleMessageQueue(int bound) { - queue = new ArrayBlockingQueue(bound); - } + public SimpleMessageQueue(int bound) { + queue = new ArrayBlockingQueue<>(bound); + } - @Override - public void put(Message msg) throws InterruptedException { - queue.put(msg); - } - - @Override - public Message take() throws InterruptedException { - return queue.take(); - } + @Override + public void put(Message msg) throws InterruptedException { + queue.put(msg); + } + @Override + public Message take() throws InterruptedException { + return queue.take(); + } } diff --git a/poison-pill/src/test/java/com/iluwatar/poison/pill/AppTest.java b/poison-pill/src/test/java/com/iluwatar/poison/pill/AppTest.java index 0730e5b10..5d1494004 100644 --- a/poison-pill/src/test/java/com/iluwatar/poison/pill/AppTest.java +++ b/poison-pill/src/test/java/com/iluwatar/poison/pill/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.poison.pill; import org.junit.Test; -import com.iluwatar.poison.pill.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.poison.pill.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/poison-pill/src/test/java/com/iluwatar/poison/pill/ConsumerTest.java b/poison-pill/src/test/java/com/iluwatar/poison/pill/ConsumerTest.java new file mode 100644 index 000000000..01a5dfaa6 --- /dev/null +++ b/poison-pill/src/test/java/com/iluwatar/poison/pill/ConsumerTest.java @@ -0,0 +1,78 @@ +/** + * The MIT License + * Copyright (c) 2014 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.poison.pill; + +import org.junit.Test; +import org.mockito.InOrder; + +import java.time.LocalDateTime; + +import static org.mockito.Mockito.inOrder; + +/** + * Date: 12/27/15 - 9:45 PM + * + * @author Jeroen Meulemeester + */ +public class ConsumerTest extends StdOutTest { + + @Test + public void testConsume() throws Exception { + final Message[] messages = new Message[]{ + createMessage("you", "Hello!"), + createMessage("me", "Hi!"), + Message.POISON_PILL, + createMessage("late_for_the_party", "Hello? Anyone here?"), + }; + + final MessageQueue queue = new SimpleMessageQueue(messages.length); + for (final Message message : messages) { + queue.put(message); + } + + new Consumer("NSA", queue).consume(); + + final InOrder inOrder = inOrder(getStdOutMock()); + inOrder.verify(getStdOutMock()).println("Message [Hello!] from [you] received by [NSA]"); + inOrder.verify(getStdOutMock()).println("Message [Hi!] from [me] received by [NSA]"); + inOrder.verify(getStdOutMock()).println("Consumer NSA receive request to terminate."); + inOrder.verifyNoMoreInteractions(); + + } + + /** + * Create a new message from the given sender with the given message body + * + * @param sender The sender's name + * @param message The message body + * @return The message instance + */ + private static Message createMessage(final String sender, final String message) { + final SimpleMessage msg = new SimpleMessage(); + msg.addHeader(Message.Headers.SENDER, sender); + msg.addHeader(Message.Headers.DATE, LocalDateTime.now().toString()); + msg.setBody(message); + return msg; + } + +} \ No newline at end of file diff --git a/poison-pill/src/test/java/com/iluwatar/poison/pill/PoisonMessageTest.java b/poison-pill/src/test/java/com/iluwatar/poison/pill/PoisonMessageTest.java new file mode 100644 index 000000000..4593c0822 --- /dev/null +++ b/poison-pill/src/test/java/com/iluwatar/poison/pill/PoisonMessageTest.java @@ -0,0 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 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.poison.pill; + +import org.junit.Test; + +import static com.iluwatar.poison.pill.Message.Headers; +import static com.iluwatar.poison.pill.Message.POISON_PILL; + +/** + * Date: 12/27/15 - 10:30 PM + * + * @author Jeroen Meulemeester + */ +public class PoisonMessageTest { + + @Test(expected = UnsupportedOperationException.class) + public void testAddHeader() throws Exception { + POISON_PILL.addHeader(Headers.SENDER, "sender"); + } + + @Test(expected = UnsupportedOperationException.class) + public void testGetHeader() throws Exception { + POISON_PILL.getHeader(Headers.SENDER); + } + + @Test(expected = UnsupportedOperationException.class) + public void testGetHeaders() throws Exception { + POISON_PILL.getHeaders(); + } + + @Test(expected = UnsupportedOperationException.class) + public void testSetBody() throws Exception { + POISON_PILL.setBody("Test message."); + } + + @Test(expected = UnsupportedOperationException.class) + public void testGetBody() throws Exception { + POISON_PILL.getBody(); + } + +} diff --git a/poison-pill/src/test/java/com/iluwatar/poison/pill/ProducerTest.java b/poison-pill/src/test/java/com/iluwatar/poison/pill/ProducerTest.java new file mode 100644 index 000000000..eec79edd1 --- /dev/null +++ b/poison-pill/src/test/java/com/iluwatar/poison/pill/ProducerTest.java @@ -0,0 +1,86 @@ +/** + * The MIT License + * Copyright (c) 2014 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.poison.pill; + +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/27/15 - 10:32 PM + * + * @author Jeroen Meulemeester + */ +public class ProducerTest { + + @Test + public void testSend() throws Exception { + final MqPublishPoint publishPoint = mock(MqPublishPoint.class); + final Producer producer = new Producer("producer", publishPoint); + verifyZeroInteractions(publishPoint); + + producer.send("Hello!"); + + final ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class); + verify(publishPoint).put(messageCaptor.capture()); + + final Message message = messageCaptor.getValue(); + assertNotNull(message); + assertEquals("producer", message.getHeader(Message.Headers.SENDER)); + assertNotNull(message.getHeader(Message.Headers.DATE)); + assertEquals("Hello!", message.getBody()); + + verifyNoMoreInteractions(publishPoint); + } + + @Test + public void testStop() throws Exception { + final MqPublishPoint publishPoint = mock(MqPublishPoint.class); + final Producer producer = new Producer("producer", publishPoint); + verifyZeroInteractions(publishPoint); + + producer.stop(); + verify(publishPoint).put(eq(Message.POISON_PILL)); + + try { + producer.send("Hello!"); + fail("Expected 'IllegalStateException' at this point, since the producer has stopped!"); + } catch (IllegalStateException e) { + assertNotNull(e); + assertNotNull(e.getMessage()); + assertEquals("Producer Hello! was stopped and fail to deliver requested message [producer].", + e.getMessage()); + } + + verifyNoMoreInteractions(publishPoint); + } + +} diff --git a/poison-pill/src/test/java/com/iluwatar/poison/pill/SimpleMessageTest.java b/poison-pill/src/test/java/com/iluwatar/poison/pill/SimpleMessageTest.java new file mode 100644 index 000000000..0fc4ef036 --- /dev/null +++ b/poison-pill/src/test/java/com/iluwatar/poison/pill/SimpleMessageTest.java @@ -0,0 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 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.poison.pill; + +import org.junit.Test; + +import java.util.Map; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Date: 12/27/15 - 10:25 PM + * + * @author Jeroen Meulemeester + */ +public class SimpleMessageTest { + + @Test + public void testGetHeaders() { + final SimpleMessage message = new SimpleMessage(); + assertNotNull(message.getHeaders()); + assertTrue(message.getHeaders().isEmpty()); + + final String senderName = "test"; + message.addHeader(Message.Headers.SENDER, senderName); + assertNotNull(message.getHeaders()); + assertFalse(message.getHeaders().isEmpty()); + assertEquals(senderName, message.getHeaders().get(Message.Headers.SENDER)); + } + + @Test(expected = UnsupportedOperationException.class) + public void testUnModifiableHeaders() { + final SimpleMessage message = new SimpleMessage(); + final Map headers = message.getHeaders(); + headers.put(Message.Headers.SENDER, "test"); + } + + +} \ No newline at end of file diff --git a/poison-pill/src/test/java/com/iluwatar/poison/pill/StdOutTest.java b/poison-pill/src/test/java/com/iluwatar/poison/pill/StdOutTest.java new file mode 100644 index 000000000..f1b3c4840 --- /dev/null +++ b/poison-pill/src/test/java/com/iluwatar/poison/pill/StdOutTest.java @@ -0,0 +1,75 @@ +/** + * The MIT License + * Copyright (c) 2014 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.poison.pill; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since some actions don't have any + * influence on accessible objects, except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/pom.xml b/pom.xml index 3462cc864..6f3e0d698 100644 --- a/pom.xml +++ b/pom.xml @@ -1,25 +1,52 @@ - + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT pom + 2014 + UTF-8 5.0.1.Final - 1.9.0.RELEASE - 1.4.188 + 4.2.4.RELEASE + 1.3.3.RELEASE + 1.9.2.RELEASE + 1.4.190 4.12 3.0 4.0.0 0.7.2.201409121644 1.4 - 2.15.3 + 2.16.1 1.2.17 + 19.0 + 1.15.1 + 1.10.19 + 4.12.1 + 4.5.2 + 2.22 abstract-factory @@ -31,6 +58,7 @@ bridge composite dao + data-mapper decorator facade flyweight @@ -58,6 +86,7 @@ intercepting-filter producer-consumer poison-pill + reader-writer-lock lazy-loading service-layer specification @@ -68,6 +97,7 @@ multiton resource-acquisition-is-initialization thread-pool + twin private-class-data object-pool dependency-injection @@ -75,15 +105,30 @@ front-controller repository async-method-invocation - monostate + monostate step-builder - business-delegate - half-sync-half-async + business-delegate + half-sync-half-async layers message-channel fluentinterface reactor caching + publish-subscribe + delegation + event-driven-architecture + api-gateway + factory-kit + feature-toggle + value-object + monad + mute-idiom + mutex + semaphore + hexagonal + abstract-document + aggregator-microservices + page-object @@ -99,10 +144,37 @@ ${hibernate.version} + org.springframework + spring-test + ${spring.version} + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + org.springframework.data spring-data-jpa ${spring-data.version} + + org.springframework + spring-webmvc + ${spring.version} + + + org.springframework.boot + spring-boot-starter-web + ${spring-boot.version} + + + org.apache.httpcomponents + httpclient + ${apache-httpcomponents.version} + com.h2database h2 @@ -129,11 +201,40 @@ ${junit.version} test + + org.mockito + mockito-core + ${mockito.version} + test + log4j log4j ${log4j.version} + + com.google.guava + guava + ${guava.version} + + + com.github.stefanbirkner + system-rules + ${systemrules.version} + test + + + de.bechte.junit + junit-hierarchicalcontextrunner + ${hierarchical-junit-runner-version} + test + + + net.sourceforge.htmlunit + htmlunit + ${htmlunit.version} + test + @@ -141,8 +242,8 @@ - org.eclipse.m2e @@ -198,14 +299,15 @@ org.jacoco jacoco-maven-plugin ${jacoco.version} - - domainapp/dom/modules/simple/QSimpleObject.class + **com.steadystate* @@ -224,7 +326,7 @@ org.apache.maven.plugins maven-checkstyle-plugin - 2.15 + 2.17 validate @@ -234,14 +336,103 @@ validate checkstyle.xml + checkstyle-suppressions.xml UTF-8 - false - false + true + true + true + + + org.jacoco + jacoco-maven-plugin + 0.7.5.201505241946 + + + + prepare-agent + + + + report + prepare-package + + report + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18.1 + + + org.apache.maven.surefire + surefire-junit47 + 2.18.1 + + + + -Xmx1024M ${argLine} + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.6 + + true + 5 + true + + + + + check + + + exclude-pmd.properties + + + + + + + com.mycila + license-maven-plugin + 2.11 + +

com/mycila/maven/plugin/license/templates/MIT.txt
+ + Ilkka Seppälä + + true + + + + install-format + install + + format + + + + + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.6 + + + +
diff --git a/private-class-data/index.md b/private-class-data/README.md similarity index 68% rename from private-class-data/index.md rename to private-class-data/README.md index 83c95d308..981208fa3 100644 --- a/private-class-data/index.md +++ b/private-class-data/README.md @@ -4,15 +4,20 @@ title: Private Class Data folder: private-class-data permalink: /patterns/private-class-data/ categories: Other -tags: Java +tags: + - Java + - Difficulty-Beginner + - Idiom --- -**Intent:** Private Class Data design pattern seeks to reduce exposure of +## Intent +Private Class Data design pattern seeks to reduce exposure of attributes by limiting their visibility. It reduces the number of class attributes by encapsulating them in single Data object. ![alt text](./etc/private-class-data.png "Private Class Data") -**Applicability:** Use the Private Class Data pattern when +## Applicability +Use the Private Class Data pattern when * you want to prevent write access to class data members diff --git a/private-class-data/pom.xml b/private-class-data/pom.xml index 274739257..20484245b 100644 --- a/private-class-data/pom.xml +++ b/private-class-data/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT private-class-data @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/private-class-data/src/main/java/com/iluwatar/privateclassdata/App.java b/private-class-data/src/main/java/com/iluwatar/privateclassdata/App.java index a4e2ffa87..7a58fc6f3 100644 --- a/private-class-data/src/main/java/com/iluwatar/privateclassdata/App.java +++ b/private-class-data/src/main/java/com/iluwatar/privateclassdata/App.java @@ -1,38 +1,59 @@ +/** + * The MIT License + * Copyright (c) 2014 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.privateclassdata; /** * - * The Private Class Data design pattern seeks to reduce exposure of attributes - * by limiting their visibility. It reduces the number of class attributes by - * encapsulating them in single data object. It allows the class designer to - * remove write privilege of attributes that are intended to be set only during - * construction, even from methods of the target class. + * The Private Class Data design pattern seeks to reduce exposure of attributes by limiting their + * visibility. It reduces the number of class attributes by encapsulating them in single data + * object. It allows the class designer to remove write privilege of attributes that are intended to + * be set only during construction, even from methods of the target class. *

- * In the example we have normal {@link Stew} class with some ingredients given in - * constructor. Then we have methods to enumerate the ingredients and to taste - * the stew. The method for tasting the stew alters the private members of the - * {@link Stew} class. + * In the example we have normal {@link Stew} class with some ingredients given in constructor. Then + * we have methods to enumerate the ingredients and to taste the stew. The method for tasting the + * stew alters the private members of the {@link Stew} class. * - * The problem is solved with the Private Class Data pattern. We introduce - * {@link ImmutableStew} class that contains {@link StewData}. The private data members of - * {@link Stew} are now in {@link StewData} and cannot be altered by {@link ImmutableStew} methods. + * The problem is solved with the Private Class Data pattern. We introduce {@link ImmutableStew} + * class that contains {@link StewData}. The private data members of {@link Stew} are now in + * {@link StewData} and cannot be altered by {@link ImmutableStew} methods. * */ public class App { - - /** - * Program entry point - * @param args command line args - */ - public static void main( String[] args ) { - // stew is mutable - Stew stew = new Stew(1, 2, 3, 4); - stew.mix(); - stew.taste(); - stew.mix(); - - // immutable stew protected with Private Class Data pattern - ImmutableStew immutableStew = new ImmutableStew(2, 4, 3, 6); - immutableStew.mix(); - } + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + // stew is mutable + Stew stew = new Stew(1, 2, 3, 4); + stew.mix(); + stew.taste(); + stew.mix(); + + // immutable stew protected with Private Class Data pattern + ImmutableStew immutableStew = new ImmutableStew(2, 4, 3, 6); + immutableStew.mix(); + } } diff --git a/private-class-data/src/main/java/com/iluwatar/privateclassdata/ImmutableStew.java b/private-class-data/src/main/java/com/iluwatar/privateclassdata/ImmutableStew.java index ebaaf28a0..b9d2ad09c 100644 --- a/private-class-data/src/main/java/com/iluwatar/privateclassdata/ImmutableStew.java +++ b/private-class-data/src/main/java/com/iluwatar/privateclassdata/ImmutableStew.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.privateclassdata; /** @@ -7,14 +29,18 @@ package com.iluwatar.privateclassdata; */ public class ImmutableStew { - private StewData data; - - public ImmutableStew(int numPotatoes, int numCarrots, int numMeat, int numPeppers) { - data = new StewData(numPotatoes, numCarrots, numMeat, numPeppers); - } - - public void mix() { - System.out.println(String.format("Mixing the immutable stew we find: %d potatoes, %d carrots, %d meat and %d peppers", - data.getNumPotatoes(), data.getNumCarrots(), data.getNumMeat(), data.getNumPeppers())); - } + private StewData data; + + public ImmutableStew(int numPotatoes, int numCarrots, int numMeat, int numPeppers) { + data = new StewData(numPotatoes, numCarrots, numMeat, numPeppers); + } + + /** + * Mix the stew + */ + public void mix() { + System.out.println(String.format( + "Mixing the immutable stew we find: %d potatoes, %d carrots, %d meat and %d peppers", + data.getNumPotatoes(), data.getNumCarrots(), data.getNumMeat(), data.getNumPeppers())); + } } diff --git a/private-class-data/src/main/java/com/iluwatar/privateclassdata/Stew.java b/private-class-data/src/main/java/com/iluwatar/privateclassdata/Stew.java index 6ad05044b..9c9033018 100644 --- a/private-class-data/src/main/java/com/iluwatar/privateclassdata/Stew.java +++ b/private-class-data/src/main/java/com/iluwatar/privateclassdata/Stew.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.privateclassdata; /** @@ -6,37 +28,47 @@ package com.iluwatar.privateclassdata; * */ public class Stew { - - private int numPotatoes; - private int numCarrots; - private int numMeat; - private int numPeppers; - - public Stew(int numPotatoes, int numCarrots, int numMeat, int numPeppers) { - this.numPotatoes = numPotatoes; - this.numCarrots = numCarrots; - this.numMeat = numMeat; - this.numPeppers = numPeppers; - } - - public void mix() { - System.out.println(String.format("Mixing the stew we find: %d potatoes, %d carrots, %d meat and %d peppers", - numPotatoes, numCarrots, numMeat, numPeppers)); - } - - public void taste() { - System.out.println("Tasting the stew"); - if (numPotatoes > 0) { - numPotatoes--; - } - if (numCarrots > 0) { - numCarrots--; - } - if (numMeat > 0) { - numMeat--; - } - if (numPeppers > 0) { - numPeppers--; - } - } + + private int numPotatoes; + private int numCarrots; + private int numMeat; + private int numPeppers; + + /** + * Constructor + */ + public Stew(int numPotatoes, int numCarrots, int numMeat, int numPeppers) { + this.numPotatoes = numPotatoes; + this.numCarrots = numCarrots; + this.numMeat = numMeat; + this.numPeppers = numPeppers; + } + + /** + * Mix the stew + */ + public void mix() { + System.out.println(String.format( + "Mixing the stew we find: %d potatoes, %d carrots, %d meat and %d peppers", numPotatoes, + numCarrots, numMeat, numPeppers)); + } + + /** + * Taste the stew + */ + public void taste() { + System.out.println("Tasting the stew"); + if (numPotatoes > 0) { + numPotatoes--; + } + if (numCarrots > 0) { + numCarrots--; + } + if (numMeat > 0) { + numMeat--; + } + if (numPeppers > 0) { + numPeppers--; + } + } } diff --git a/private-class-data/src/main/java/com/iluwatar/privateclassdata/StewData.java b/private-class-data/src/main/java/com/iluwatar/privateclassdata/StewData.java index f90d3bd18..06c0b8c39 100644 --- a/private-class-data/src/main/java/com/iluwatar/privateclassdata/StewData.java +++ b/private-class-data/src/main/java/com/iluwatar/privateclassdata/StewData.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.privateclassdata; /** @@ -7,31 +29,34 @@ package com.iluwatar.privateclassdata; */ public class StewData { - private int numPotatoes; - private int numCarrots; - private int numMeat; - private int numPeppers; - - public StewData(int numPotatoes, int numCarrots, int numMeat, int numPeppers) { - this.numPotatoes = numPotatoes; - this.numCarrots = numCarrots; - this.numMeat = numMeat; - this.numPeppers = numPeppers; - } + private int numPotatoes; + private int numCarrots; + private int numMeat; + private int numPeppers; - public int getNumPotatoes() { - return numPotatoes; - } + /** + * Constructor + */ + public StewData(int numPotatoes, int numCarrots, int numMeat, int numPeppers) { + this.numPotatoes = numPotatoes; + this.numCarrots = numCarrots; + this.numMeat = numMeat; + this.numPeppers = numPeppers; + } - public int getNumCarrots() { - return numCarrots; - } + public int getNumPotatoes() { + return numPotatoes; + } - public int getNumMeat() { - return numMeat; - } + public int getNumCarrots() { + return numCarrots; + } - public int getNumPeppers() { - return numPeppers; - } + public int getNumMeat() { + return numMeat; + } + + public int getNumPeppers() { + return numPeppers; + } } diff --git a/private-class-data/src/test/java/com/iluwatar/privateclassdata/AppTest.java b/private-class-data/src/test/java/com/iluwatar/privateclassdata/AppTest.java index 92fc9b46a..2625c4906 100644 --- a/private-class-data/src/test/java/com/iluwatar/privateclassdata/AppTest.java +++ b/private-class-data/src/test/java/com/iluwatar/privateclassdata/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.privateclassdata; import org.junit.Test; -import com.iluwatar.privateclassdata.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.privateclassdata.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/private-class-data/src/test/java/com/iluwatar/privateclassdata/ImmutableStewTest.java b/private-class-data/src/test/java/com/iluwatar/privateclassdata/ImmutableStewTest.java new file mode 100644 index 000000000..58867d303 --- /dev/null +++ b/private-class-data/src/test/java/com/iluwatar/privateclassdata/ImmutableStewTest.java @@ -0,0 +1,74 @@ +/** + * The MIT License + * Copyright (c) 2014 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.privateclassdata; + +import org.junit.Test; +import org.mockito.InOrder; + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.verify; + +/** + * Date: 12/27/15 - 10:46 PM + * + * @author Jeroen Meulemeester + */ +public class ImmutableStewTest extends StdOutTest { + + /** + * Verify if mixing the stew doesn't change the internal state + */ + @Test + public void testMix() { + final Stew stew = new Stew(1, 2, 3, 4); + final String message = "Mixing the stew we find: 1 potatoes, 2 carrots, 3 meat and 4 peppers"; + + final InOrder inOrder = inOrder(getStdOutMock()); + for (int i = 0; i < 20; i++) { + stew.mix(); + inOrder.verify(getStdOutMock()).println(message); + } + + inOrder.verifyNoMoreInteractions(); + } + + /** + * Verify if tasting the stew actually removes one of each ingredient + */ + @Test + public void testDrink() { + final Stew stew = new Stew(1, 2, 3, 4); + stew.mix(); + + verify(getStdOutMock()) + .println("Mixing the stew we find: 1 potatoes, 2 carrots, 3 meat and 4 peppers"); + + stew.taste(); + verify(getStdOutMock()).println("Tasting the stew"); + + stew.mix(); + verify(getStdOutMock()) + .println("Mixing the stew we find: 0 potatoes, 1 carrots, 2 meat and 3 peppers"); + + } +} \ No newline at end of file diff --git a/private-class-data/src/test/java/com/iluwatar/privateclassdata/StdOutTest.java b/private-class-data/src/test/java/com/iluwatar/privateclassdata/StdOutTest.java new file mode 100644 index 000000000..f90551020 --- /dev/null +++ b/private-class-data/src/test/java/com/iluwatar/privateclassdata/StdOutTest.java @@ -0,0 +1,75 @@ +/** + * The MIT License + * Copyright (c) 2014 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.privateclassdata; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since some actions don't have any + * influence on accessible objects, except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/private-class-data/src/test/java/com/iluwatar/privateclassdata/StewTest.java b/private-class-data/src/test/java/com/iluwatar/privateclassdata/StewTest.java new file mode 100644 index 000000000..a894e4ae0 --- /dev/null +++ b/private-class-data/src/test/java/com/iluwatar/privateclassdata/StewTest.java @@ -0,0 +1,55 @@ +/** + * The MIT License + * Copyright (c) 2014 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.privateclassdata; + +import org.junit.Test; +import org.mockito.InOrder; + +import static org.mockito.Mockito.inOrder; + +/** + * Date: 12/27/15 - 10:46 PM + * + * @author Jeroen Meulemeester + */ +public class StewTest extends StdOutTest { + + /** + * Verify if mixing the stew doesn't change the internal state + */ + @Test + public void testMix() { + final ImmutableStew stew = new ImmutableStew(1, 2, 3, 4); + final String expectedMessage = "Mixing the immutable stew we find: 1 potatoes, " + + "2 carrots, 3 meat and 4 peppers"; + + final InOrder inOrder = inOrder(getStdOutMock()); + for (int i = 0; i < 20; i++) { + stew.mix(); + inOrder.verify(getStdOutMock()).println(expectedMessage); + } + + inOrder.verifyNoMoreInteractions(); + } + +} \ No newline at end of file diff --git a/producer-consumer/index.md b/producer-consumer/README.md similarity index 64% rename from producer-consumer/index.md rename to producer-consumer/README.md index 58dc45e0d..1bb84c35f 100644 --- a/producer-consumer/index.md +++ b/producer-consumer/README.md @@ -3,20 +3,23 @@ layout: pattern title: Producer Consumer folder: producer-consumer permalink: /patterns/producer-consumer/ -categories: Other -tags: Java +categories: Concurrency +tags: + - Java + - Difficulty-Intermediate + - I/O + - Reactive --- -**Intent:** Producer Consumer Design pattern is a classic concurrency or threading pattern which reduces +## Intent +Producer Consumer Design pattern is a classic concurrency pattern which reduces coupling between Producer and Consumer by separating Identification of work with Execution of - Work.. - - + Work. ![alt text](./etc/producer-consumer.png "Producer Consumer") -**Applicability:** Use the Producer Consumer idiom when +## Applicability +Use the Producer Consumer idiom when * decouple system by separate work in two process produce and consume. * addresses the issue of different timing require to produce work or consuming work - diff --git a/producer-consumer/pom.xml b/producer-consumer/pom.xml index 475c7fb6c..a72aa2c96 100644 --- a/producer-consumer/pom.xml +++ b/producer-consumer/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT producer-consumer @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/producer-consumer/src/main/java/com/iluwatar/producer/consumer/App.java b/producer-consumer/src/main/java/com/iluwatar/producer/consumer/App.java index 50d94d92f..42f6dfa79 100644 --- a/producer-consumer/src/main/java/com/iluwatar/producer/consumer/App.java +++ b/producer-consumer/src/main/java/com/iluwatar/producer/consumer/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.producer.consumer; import java.util.concurrent.ExecutorService; @@ -5,14 +27,12 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** - * Producer Consumer Design pattern is a classic concurrency or threading pattern which reduces - * coupling between Producer and Consumer by separating Identification of work with Execution of - * Work. + * Producer Consumer Design pattern is a classic concurrency or threading pattern which reduces coupling between + * Producer and Consumer by separating Identification of work with Execution of Work. *

- * In producer consumer design pattern a shared queue is used to control the flow and this - * separation allows you to code producer and consumer separately. It also addresses the issue of - * different timing require to produce item or consuming item. by using producer consumer pattern - * both Producer and Consumer Thread can work with different speed. + * In producer consumer design pattern a shared queue is used to control the flow and this separation allows you to code + * producer and consumer separately. It also addresses the issue of different timing require to produce item or + * consuming item. by using producer consumer pattern both Producer and Consumer Thread can work with different speed. * */ public class App { @@ -20,7 +40,8 @@ public class App { /** * Program entry point * - * @param args command line args + * @param args + * command line args */ public static void main(String[] args) { @@ -35,7 +56,7 @@ public class App { producer.produce(); } }); - }; + } for (int i = 0; i < 3; i++) { final Consumer consumer = new Consumer("Consumer_" + i, queue); diff --git a/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Consumer.java b/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Consumer.java index 8bb3b75b6..f1fa920f3 100644 --- a/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Consumer.java +++ b/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Consumer.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.producer.consumer; /** @@ -14,6 +36,9 @@ public class Consumer { this.queue = queue; } + /** + * Consume item from the queue + */ public void consume() throws InterruptedException { Item item = queue.take(); diff --git a/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Item.java b/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Item.java index 8d5be69a1..4bf66e599 100644 --- a/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Item.java +++ b/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Item.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.producer.consumer; /** diff --git a/producer-consumer/src/main/java/com/iluwatar/producer/consumer/ItemQueue.java b/producer-consumer/src/main/java/com/iluwatar/producer/consumer/ItemQueue.java index 8d41fb456..ea925152b 100644 --- a/producer-consumer/src/main/java/com/iluwatar/producer/consumer/ItemQueue.java +++ b/producer-consumer/src/main/java/com/iluwatar/producer/consumer/ItemQueue.java @@ -1,5 +1,28 @@ +/** + * The MIT License + * Copyright (c) 2014 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.producer.consumer; +import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; /** @@ -7,11 +30,11 @@ import java.util.concurrent.LinkedBlockingQueue; */ public class ItemQueue { - private LinkedBlockingQueue queue; + private BlockingQueue queue; public ItemQueue() { - queue = new LinkedBlockingQueue(5); + queue = new LinkedBlockingQueue<>(5); } public void put(Item item) throws InterruptedException { diff --git a/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Producer.java b/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Producer.java index 40e71c607..587614dc8 100644 --- a/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Producer.java +++ b/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Producer.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.producer.consumer; import java.util.Random; @@ -12,13 +34,16 @@ public class Producer { private final String name; - private int itemId = 0; + private int itemId; public Producer(String name, ItemQueue queue) { this.name = name; this.queue = queue; } + /** + * Put item in the queue + */ public void produce() throws InterruptedException { Item item = new Item(name, itemId++); diff --git a/producer-consumer/src/test/java/com/iluwatar/poison/pill/AppTest.java b/producer-consumer/src/test/java/com/iluwatar/poison/pill/AppTest.java deleted file mode 100644 index 26b38dec0..000000000 --- a/producer-consumer/src/test/java/com/iluwatar/poison/pill/AppTest.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.iluwatar.poison.pill; - -import org.junit.Test; - -import com.iluwatar.producer.consumer.App; - -/** - * - * Application test - * - */ -public class AppTest { - - @Test - public void test() throws Exception { - String[] args = {}; - App.main(args); - - } -} diff --git a/producer-consumer/src/test/java/com/iluwatar/producer/consumer/AppTest.java b/producer-consumer/src/test/java/com/iluwatar/producer/consumer/AppTest.java new file mode 100644 index 000000000..fcc6509a8 --- /dev/null +++ b/producer-consumer/src/test/java/com/iluwatar/producer/consumer/AppTest.java @@ -0,0 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 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.producer.consumer; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() throws Exception { + String[] args = {}; + App.main(args); + + } +} diff --git a/producer-consumer/src/test/java/com/iluwatar/producer/consumer/ConsumerTest.java b/producer-consumer/src/test/java/com/iluwatar/producer/consumer/ConsumerTest.java new file mode 100644 index 000000000..2ca547a0b --- /dev/null +++ b/producer-consumer/src/test/java/com/iluwatar/producer/consumer/ConsumerTest.java @@ -0,0 +1,61 @@ +/** + * The MIT License + * Copyright (c) 2014 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.producer.consumer; + +import org.junit.Test; +import org.mockito.InOrder; + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; + +/** + * Date: 12/27/15 - 11:01 PM + * + * @author Jeroen Meulemeester + */ +public class ConsumerTest extends StdOutTest { + + private static final int ITEM_COUNT = 5; + + @Test + public void testConsume() throws Exception { + final ItemQueue queue = spy(new ItemQueue()); + for (int id = 0; id < ITEM_COUNT; id++) { + queue.put(new Item("producer", id)); + } + + reset(queue); // Don't count the preparation above as interactions with the queue + final Consumer consumer = new Consumer("consumer", queue); + + final InOrder inOrder = inOrder(getStdOutMock()); + for (int id = 0; id < ITEM_COUNT; id++) { + consumer.consume(); + inOrder.verify(getStdOutMock()) + .println("Consumer [consumer] consume item [" + id + "] produced by [producer]"); + } + + inOrder.verifyNoMoreInteractions(); + } + +} diff --git a/producer-consumer/src/test/java/com/iluwatar/producer/consumer/ProducerTest.java b/producer-consumer/src/test/java/com/iluwatar/producer/consumer/ProducerTest.java new file mode 100644 index 000000000..94765268b --- /dev/null +++ b/producer-consumer/src/test/java/com/iluwatar/producer/consumer/ProducerTest.java @@ -0,0 +1,50 @@ +/** + * The MIT License + * Copyright (c) 2014 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.producer.consumer; + +import org.junit.Test; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/28/15 - 12:12 AM + * + * @author Jeroen Meulemeester + */ +public class ProducerTest { + + @Test(timeout = 6000) + public void testProduce() throws Exception { + final ItemQueue queue = mock(ItemQueue.class); + final Producer producer = new Producer("producer", queue); + + producer.produce(); + verify(queue).put(any(Item.class)); + + verifyNoMoreInteractions(queue); + } + +} \ No newline at end of file diff --git a/producer-consumer/src/test/java/com/iluwatar/producer/consumer/StdOutTest.java b/producer-consumer/src/test/java/com/iluwatar/producer/consumer/StdOutTest.java new file mode 100644 index 000000000..1e1c41f75 --- /dev/null +++ b/producer-consumer/src/test/java/com/iluwatar/producer/consumer/StdOutTest.java @@ -0,0 +1,75 @@ +/** + * The MIT License + * Copyright (c) 2014 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.producer.consumer; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since some actions don't have any + * influence on accessible objects, except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/property/index.md b/property/README.md similarity index 68% rename from property/index.md rename to property/README.md index 1c5b28db6..0ac5c7a6c 100644 --- a/property/index.md +++ b/property/README.md @@ -4,18 +4,22 @@ title: Property folder: property permalink: /patterns/property/ categories: Creational -tags: Java +tags: + - Java + - Difficulty-Beginner --- -**Intent:** Create hierarchy of objects and new objects using already existing +## Intent +Create hierarchy of objects and new objects using already existing objects as parents. ![alt text](./etc/property.png "Property") -**Applicability:** Use the Property pattern when +## Applicability +Use the Property pattern when * when you like to have objects with dynamic set of fields and prototype inheritance -**Real world examples:** +## Real world examples * [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain) prototype inheritance diff --git a/property/pom.xml b/property/pom.xml index f32a4008e..1e9d0f0c2 100644 --- a/property/pom.xml +++ b/property/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT property diff --git a/property/src/main/java/com/iluwatar/property/App.java b/property/src/main/java/com/iluwatar/property/App.java index ac46f5448..e3f7cc92b 100644 --- a/property/src/main/java/com/iluwatar/property/App.java +++ b/property/src/main/java/com/iluwatar/property/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.property; import com.iluwatar.property.Character.Type; @@ -6,53 +28,55 @@ import com.iluwatar.property.Character.Type; * * The Property pattern is also known as Prototype inheritance. *

- * In prototype inheritance instead of classes, as opposite to Java class inheritance, - * objects are used to create another objects and object hierarchies. Hierarchies are created using prototype chain - * through delegation: every object has link to parent object. Any base (parent) object can be amended at runtime - * (by adding or removal of some property), and all child objects will be affected as result. + * In prototype inheritance instead of classes, as opposite to Java class inheritance, objects are + * used to create another objects and object hierarchies. Hierarchies are created using prototype + * chain through delegation: every object has link to parent object. Any base (parent) object can be + * amended at runtime (by adding or removal of some property), and all child objects will be + * affected as result. *

* In this example we demonstrate {@link Character} instantiation using the Property pattern. * */ public class App { - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { - /* set up */ - Prototype charProto = new Character(); - charProto.set(Stats.STRENGTH, 10); - charProto.set(Stats.AGILITY, 10); - charProto.set(Stats.ARMOR, 10); - charProto.set(Stats.ATTACK_POWER, 10); + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + /* set up */ + Prototype charProto = new Character(); + charProto.set(Stats.STRENGTH, 10); + charProto.set(Stats.AGILITY, 10); + charProto.set(Stats.ARMOR, 10); + charProto.set(Stats.ATTACK_POWER, 10); - Character mageProto = new Character(Type.MAGE, charProto); - mageProto.set(Stats.INTELLECT, 15); - mageProto.set(Stats.SPIRIT, 10); + Character mageProto = new Character(Type.MAGE, charProto); + mageProto.set(Stats.INTELLECT, 15); + mageProto.set(Stats.SPIRIT, 10); - Character warProto = new Character(Type.WARRIOR, charProto); - warProto.set(Stats.RAGE, 15); - warProto.set(Stats.ARMOR, 15); // boost default armor for warrior + Character warProto = new Character(Type.WARRIOR, charProto); + warProto.set(Stats.RAGE, 15); + warProto.set(Stats.ARMOR, 15); // boost default armor for warrior - Character rogueProto = new Character(Type.ROGUE, charProto); - rogueProto.set(Stats.ENERGY, 15); - rogueProto.set(Stats.AGILITY, 15); // boost default agility for rogue + Character rogueProto = new Character(Type.ROGUE, charProto); + rogueProto.set(Stats.ENERGY, 15); + rogueProto.set(Stats.AGILITY, 15); // boost default agility for rogue - /* usage */ - Character mag = new Character("Player_1", mageProto); - mag.set(Stats.ARMOR, 8); - System.out.println(mag); + /* usage */ + Character mag = new Character("Player_1", mageProto); + mag.set(Stats.ARMOR, 8); + System.out.println(mag); - Character warrior = new Character("Player_2", warProto); - System.out.println(warrior); + Character warrior = new Character("Player_2", warProto); + System.out.println(warrior); - Character rogue = new Character("Player_3", rogueProto); - System.out.println(rogue); + Character rogue = new Character("Player_3", rogueProto); + System.out.println(rogue); - Character rogueDouble = new Character("Player_4", rogue); - rogueDouble.set(Stats.ATTACK_POWER, 12); - System.out.println(rogueDouble); - } + Character rogueDouble = new Character("Player_4", rogue); + rogueDouble.set(Stats.ATTACK_POWER, 12); + System.out.println(rogueDouble); + } } diff --git a/property/src/main/java/com/iluwatar/property/Character.java b/property/src/main/java/com/iluwatar/property/Character.java index cb2fdf583..6a45d16b2 100644 --- a/property/src/main/java/com/iluwatar/property/Character.java +++ b/property/src/main/java/com/iluwatar/property/Character.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.property; import java.util.HashMap; @@ -8,110 +30,106 @@ import java.util.Map; */ public class Character implements Prototype { - public enum Type { - WARRIOR, MAGE, ROGUE - } + public enum Type { + WARRIOR, MAGE, ROGUE + } - private final Prototype prototype; - private final Map properties = new HashMap<>(); + private final Prototype prototype; + private final Map properties = new HashMap<>(); - private String name; - private Type type; + private String name; + private Type type; - public Character() { - this.prototype = new Prototype() { // Null-value object - @Override - public Integer get(Stats stat) { - return null; - } - @Override - public boolean has(Stats stat) { - return false; - } - @Override - public void set(Stats stat, Integer val) { - } - @Override - public void remove(Stats stat) { - }} - ; - } + /** + * Constructor + */ + public Character() { + this.prototype = new Prototype() { // Null-value object + @Override + public Integer get(Stats stat) { + return null; + } - public Character(Type type, Prototype prototype) { - this.type = type; - this.prototype = prototype; - } + @Override + public boolean has(Stats stat) { + return false; + } - public Character(String name, Character prototype) { - this.name = name; - this.type = prototype.type; - this.prototype = prototype; - } + @Override + public void set(Stats stat, Integer val) {} - public String name() { - return name; - } + @Override + public void remove(Stats stat) {} + }; + } - public Type type() { - return type; - } + public Character(Type type, Prototype prototype) { + this.type = type; + this.prototype = prototype; + } - @Override - public Integer get(Stats stat) { - boolean containsValue = properties.containsKey(stat); - if (containsValue) { - return properties.get(stat); - } else { - return prototype.get(stat); - } - } + /** + * Constructor + */ + public Character(String name, Character prototype) { + this.name = name; + this.type = prototype.type; + this.prototype = prototype; + } - @Override - public boolean has(Stats stat) { - return get(stat) != null; - } + public String name() { + return name; + } - @Override - public void set(Stats stat, Integer val) { - properties.put(stat, val); - } + public Type type() { + return type; + } - @Override - public void remove(Stats stat) { - properties.put(stat, null); - } + @Override + public Integer get(Stats stat) { + boolean containsValue = properties.containsKey(stat); + if (containsValue) { + return properties.get(stat); + } else { + return prototype.get(stat); + } + } - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - if (name != null) { - builder - .append("Player: ") - .append(name) - .append("\n"); - } + @Override + public boolean has(Stats stat) { + return get(stat) != null; + } - if (type != null) { - builder - .append("Character type: ") - .append(type.name()) - .append("\n"); - } + @Override + public void set(Stats stat, Integer val) { + properties.put(stat, val); + } - builder.append("Stats:\n"); - for (Stats stat : Stats.values()) { - Integer value = this.get(stat); - if (value == null) { - continue; - } - builder - .append(" - ") - .append(stat.name()) - .append(":") - .append(value) - .append("\n"); - } - return builder.toString(); - } + @Override + public void remove(Stats stat) { + properties.put(stat, null); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + if (name != null) { + builder.append("Player: ").append(name).append('\n'); + } + + if (type != null) { + builder.append("Character type: ").append(type.name()).append('\n'); + } + + builder.append("Stats:\n"); + for (Stats stat : Stats.values()) { + Integer value = this.get(stat); + if (value == null) { + continue; + } + builder.append(" - ").append(stat.name()).append(':').append(value).append('\n'); + } + return builder.toString(); + } } diff --git a/property/src/main/java/com/iluwatar/property/Prototype.java b/property/src/main/java/com/iluwatar/property/Prototype.java index 12b4ce734..e21e98384 100644 --- a/property/src/main/java/com/iluwatar/property/Prototype.java +++ b/property/src/main/java/com/iluwatar/property/Prototype.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.property; /** @@ -5,8 +27,11 @@ package com.iluwatar.property; */ public interface Prototype { - public Integer get(Stats stat); - public boolean has(Stats stat); - public void set(Stats stat, Integer val); - public void remove(Stats stat); + Integer get(Stats stat); + + boolean has(Stats stat); + + void set(Stats stat, Integer val); + + void remove(Stats stat); } diff --git a/property/src/main/java/com/iluwatar/property/Stats.java b/property/src/main/java/com/iluwatar/property/Stats.java index b791a9dec..0c29c36f7 100644 --- a/property/src/main/java/com/iluwatar/property/Stats.java +++ b/property/src/main/java/com/iluwatar/property/Stats.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.property; /** @@ -5,5 +27,5 @@ package com.iluwatar.property; */ public enum Stats { - AGILITY, STRENGTH, ATTACK_POWER, ARMOR, INTELLECT, SPIRIT, ENERGY, RAGE + AGILITY, STRENGTH, ATTACK_POWER, ARMOR, INTELLECT, SPIRIT, ENERGY, RAGE } diff --git a/property/src/test/java/com/iluwatar/property/AppTest.java b/property/src/test/java/com/iluwatar/property/AppTest.java index 1e8078352..131742822 100644 --- a/property/src/test/java/com/iluwatar/property/AppTest.java +++ b/property/src/test/java/com/iluwatar/property/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.property; import org.junit.Test; -import com.iluwatar.property.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.property.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/property/src/test/java/com/iluwatar/property/CharacterTest.java b/property/src/test/java/com/iluwatar/property/CharacterTest.java new file mode 100644 index 000000000..859ea5f86 --- /dev/null +++ b/property/src/test/java/com/iluwatar/property/CharacterTest.java @@ -0,0 +1,125 @@ +/** + * The MIT License + * Copyright (c) 2014 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.property; + +import org.junit.Test; + +import static com.iluwatar.property.Character.Type; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +/** + * Date: 12/28/15 - 7:46 PM + * + * @author Jeroen Meulemeester + */ +public class CharacterTest { + + @Test + public void testPrototypeStats() throws Exception { + final Character prototype = new Character(); + + for (final Stats stat : Stats.values()) { + assertFalse(prototype.has(stat)); + assertNull(prototype.get(stat)); + + final Integer expectedValue = stat.ordinal(); + prototype.set(stat, expectedValue); + assertTrue(prototype.has(stat)); + assertEquals(expectedValue, prototype.get(stat)); + + prototype.remove(stat); + assertFalse(prototype.has(stat)); + assertNull(prototype.get(stat)); + } + + } + + @Test + public void testCharacterStats() throws Exception { + final Character prototype = new Character(); + for (final Stats stat : Stats.values()) { + prototype.set(stat, stat.ordinal()); + } + + final Character mage = new Character(Type.MAGE, prototype); + for (final Stats stat : Stats.values()) { + final Integer expectedValue = stat.ordinal(); + assertTrue(mage.has(stat)); + assertEquals(expectedValue, mage.get(stat)); + } + } + + @Test + public void testToString() throws Exception { + final Character prototype = new Character(); + prototype.set(Stats.ARMOR, 1); + prototype.set(Stats.AGILITY, 2); + prototype.set(Stats.INTELLECT, 3); + assertEquals("Stats:\n - AGILITY:2\n - ARMOR:1\n - INTELLECT:3\n", prototype.toString()); + + final Character stupid = new Character(Type.ROGUE, prototype); + stupid.remove(Stats.INTELLECT); + assertEquals("Character type: ROGUE\nStats:\n - AGILITY:2\n - ARMOR:1\n", stupid.toString()); + + final Character weak = new Character("weak", prototype); + weak.remove(Stats.ARMOR); + assertEquals("Player: weak\nStats:\n - AGILITY:2\n - INTELLECT:3\n", weak.toString()); + + } + + @Test + public void testName() throws Exception { + final Character prototype = new Character(); + prototype.set(Stats.ARMOR, 1); + prototype.set(Stats.INTELLECT, 2); + assertNull(prototype.name()); + + final Character stupid = new Character(Type.ROGUE, prototype); + stupid.remove(Stats.INTELLECT); + assertNull(stupid.name()); + + final Character weak = new Character("weak", prototype); + weak.remove(Stats.ARMOR); + assertEquals("weak", weak.name()); + } + + @Test + public void testType() throws Exception { + final Character prototype = new Character(); + prototype.set(Stats.ARMOR, 1); + prototype.set(Stats.INTELLECT, 2); + assertNull(prototype.type()); + + final Character stupid = new Character(Type.ROGUE, prototype); + stupid.remove(Stats.INTELLECT); + assertEquals(Type.ROGUE, stupid.type()); + + final Character weak = new Character("weak", prototype); + weak.remove(Stats.ARMOR); + assertNull(weak.type()); + } + +} \ No newline at end of file diff --git a/prototype/index.md b/prototype/README.md similarity index 78% rename from prototype/index.md rename to prototype/README.md index 9d108ff06..632daca93 100644 --- a/prototype/index.md +++ b/prototype/README.md @@ -7,23 +7,26 @@ categories: Creational tags: - Java - Gang Of Four + - Difficulty-Beginner --- -**Intent:** Specify the kinds of objects to create using a prototypical +## Intent +Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype. ![alt text](./etc/prototype_1.png "Prototype") -**Applicability:** Use the Prototype pattern when a system should be independent of how its products are created, composed and represented; and +## Applicability +Use the Prototype pattern when a system should be independent of how its products are created, composed and represented; and * when the classes to instantiate are specified at run-time, for example, by dynamic loading; or * to avoid building a class hierarchy of factories that parallels the class hierarchy of products; or * when instances of a class can have one of only a few different combinations of state. It may be more convenient to install a corresponding number of prototypes and clone them rather than instantiating the class manually, each time with the appropriate state -**Real world examples:** +## Real world examples * [java.lang.Object#clone()](http://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#clone%28%29) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/prototype/pom.xml b/prototype/pom.xml index d5772da60..e8bb4303a 100644 --- a/prototype/pom.xml +++ b/prototype/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT prototype @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/prototype/src/main/java/com/iluwatar/prototype/App.java b/prototype/src/main/java/com/iluwatar/prototype/App.java index 74bb6989d..cb04ec5f2 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/App.java +++ b/prototype/src/main/java/com/iluwatar/prototype/App.java @@ -1,45 +1,66 @@ +/** + * The MIT License + * Copyright (c) 2014 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.prototype; /** * - * The Prototype pattern is a creational design pattern in software development. It is - * used when the type of objects to create is determined by a prototypical instance, - * which is cloned to produce new objects. This pattern is used to: - * - avoid subclasses of an object creator in the client application, like the abstract factory pattern does. - * - avoid the inherent cost of creating a new object in the standard way (e.g., using the 'new' keyword) + * The Prototype pattern is a creational design pattern in software development. It is used when the + * type of objects to create is determined by a prototypical instance, which is cloned to produce + * new objects. This pattern is used to: - avoid subclasses of an object creator in the client + * application, like the abstract factory pattern does. - avoid the inherent cost of creating a new + * object in the standard way (e.g., using the 'new' keyword) *

- * In this example we have a factory class ({@link HeroFactoryImpl}) producing objects by - * cloning the existing ones. The factory's prototype objects are given as constructor parameters. + * In this example we have a factory class ({@link HeroFactoryImpl}) producing objects by cloning + * the existing ones. The factory's prototype objects are given as constructor parameters. * */ public class App { - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { - HeroFactory factory; - Mage mage; - Warlord warlord; - Beast beast; + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + HeroFactory factory; + Mage mage; + Warlord warlord; + Beast beast; - factory = new HeroFactoryImpl(new ElfMage(), new ElfWarlord(), - new ElfBeast()); - mage = factory.createMage(); - warlord = factory.createWarlord(); - beast = factory.createBeast(); - System.out.println(mage); - System.out.println(warlord); - System.out.println(beast); + factory = new HeroFactoryImpl(new ElfMage(), new ElfWarlord(), new ElfBeast()); + mage = factory.createMage(); + warlord = factory.createWarlord(); + beast = factory.createBeast(); + System.out.println(mage); + System.out.println(warlord); + System.out.println(beast); - factory = new HeroFactoryImpl(new OrcMage(), new OrcWarlord(), - new OrcBeast()); - mage = factory.createMage(); - warlord = factory.createWarlord(); - beast = factory.createBeast(); - System.out.println(mage); - System.out.println(warlord); - System.out.println(beast); - } + factory = new HeroFactoryImpl(new OrcMage(), new OrcWarlord(), new OrcBeast()); + mage = factory.createMage(); + warlord = factory.createWarlord(); + beast = factory.createBeast(); + System.out.println(mage); + System.out.println(warlord); + System.out.println(beast); + } } diff --git a/prototype/src/main/java/com/iluwatar/prototype/Beast.java b/prototype/src/main/java/com/iluwatar/prototype/Beast.java index 028fa11b8..255f44de5 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/Beast.java +++ b/prototype/src/main/java/com/iluwatar/prototype/Beast.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.prototype; /** @@ -7,7 +29,7 @@ package com.iluwatar.prototype; */ public abstract class Beast extends Prototype { - @Override - public abstract Beast clone() throws CloneNotSupportedException; + @Override + public abstract Beast clone() throws CloneNotSupportedException; } diff --git a/prototype/src/main/java/com/iluwatar/prototype/ElfBeast.java b/prototype/src/main/java/com/iluwatar/prototype/ElfBeast.java index b2b517207..1fd7d00fa 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/ElfBeast.java +++ b/prototype/src/main/java/com/iluwatar/prototype/ElfBeast.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.prototype; /** @@ -7,20 +29,16 @@ package com.iluwatar.prototype; */ public class ElfBeast extends Beast { - public ElfBeast() { - } + public ElfBeast() {} - public ElfBeast(ElfBeast beast) { - } + @Override + public Beast clone() throws CloneNotSupportedException { + return new ElfBeast(); + } - @Override - public Beast clone() throws CloneNotSupportedException { - return new ElfBeast(this); - } - - @Override - public String toString() { - return "Elven eagle"; - } + @Override + public String toString() { + return "Elven eagle"; + } } diff --git a/prototype/src/main/java/com/iluwatar/prototype/ElfMage.java b/prototype/src/main/java/com/iluwatar/prototype/ElfMage.java index b502350c3..823150ec4 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/ElfMage.java +++ b/prototype/src/main/java/com/iluwatar/prototype/ElfMage.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.prototype; /** @@ -7,20 +29,16 @@ package com.iluwatar.prototype; */ public class ElfMage extends Mage { - public ElfMage() { - } + public ElfMage() {} - public ElfMage(ElfMage mage) { - } + @Override + public Mage clone() throws CloneNotSupportedException { + return new ElfMage(); + } - @Override - public Mage clone() throws CloneNotSupportedException { - return new ElfMage(this); - } - - @Override - public String toString() { - return "Elven mage"; - } + @Override + public String toString() { + return "Elven mage"; + } } diff --git a/prototype/src/main/java/com/iluwatar/prototype/ElfWarlord.java b/prototype/src/main/java/com/iluwatar/prototype/ElfWarlord.java index 7f5829797..03ff81ecf 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/ElfWarlord.java +++ b/prototype/src/main/java/com/iluwatar/prototype/ElfWarlord.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.prototype; /** @@ -7,20 +29,16 @@ package com.iluwatar.prototype; */ public class ElfWarlord extends Warlord { - public ElfWarlord() { - } + public ElfWarlord() {} - public ElfWarlord(ElfWarlord warlord) { - } + @Override + public Warlord clone() throws CloneNotSupportedException { + return new ElfWarlord(); + } - @Override - public Warlord clone() throws CloneNotSupportedException { - return new ElfWarlord(this); - } - - @Override - public String toString() { - return "Elven warlord"; - } + @Override + public String toString() { + return "Elven warlord"; + } } diff --git a/prototype/src/main/java/com/iluwatar/prototype/HeroFactory.java b/prototype/src/main/java/com/iluwatar/prototype/HeroFactory.java index 468eefcb5..c152ae3e8 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/HeroFactory.java +++ b/prototype/src/main/java/com/iluwatar/prototype/HeroFactory.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.prototype; /** @@ -7,10 +29,10 @@ package com.iluwatar.prototype; */ public interface HeroFactory { - Mage createMage(); + Mage createMage(); - Warlord createWarlord(); + Warlord createWarlord(); - Beast createBeast(); + Beast createBeast(); } diff --git a/prototype/src/main/java/com/iluwatar/prototype/HeroFactoryImpl.java b/prototype/src/main/java/com/iluwatar/prototype/HeroFactoryImpl.java index dc440f41e..ba173d9bc 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/HeroFactoryImpl.java +++ b/prototype/src/main/java/com/iluwatar/prototype/HeroFactoryImpl.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.prototype; /** @@ -7,38 +29,50 @@ package com.iluwatar.prototype; */ public class HeroFactoryImpl implements HeroFactory { - private Mage mage; - private Warlord warlord; - private Beast beast; + private Mage mage; + private Warlord warlord; + private Beast beast; - public HeroFactoryImpl(Mage mage, Warlord warlord, Beast beast) { - this.mage = mage; - this.warlord = warlord; - this.beast = beast; - } + /** + * Constructor + */ + public HeroFactoryImpl(Mage mage, Warlord warlord, Beast beast) { + this.mage = mage; + this.warlord = warlord; + this.beast = beast; + } - public Mage createMage() { - try { - return mage.clone(); - } catch (CloneNotSupportedException e) { - return null; - } - } + /** + * Create mage + */ + public Mage createMage() { + try { + return mage.clone(); + } catch (CloneNotSupportedException e) { + return null; + } + } - public Warlord createWarlord() { - try { - return warlord.clone(); - } catch (CloneNotSupportedException e) { - return null; - } - } + /** + * Create warlord + */ + public Warlord createWarlord() { + try { + return warlord.clone(); + } catch (CloneNotSupportedException e) { + return null; + } + } - public Beast createBeast() { - try { - return beast.clone(); - } catch (CloneNotSupportedException e) { - return null; - } - } + /** + * Create beast + */ + public Beast createBeast() { + try { + return beast.clone(); + } catch (CloneNotSupportedException e) { + return null; + } + } } diff --git a/prototype/src/main/java/com/iluwatar/prototype/Mage.java b/prototype/src/main/java/com/iluwatar/prototype/Mage.java index 7ba192487..3151fc915 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/Mage.java +++ b/prototype/src/main/java/com/iluwatar/prototype/Mage.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.prototype; /** @@ -7,7 +29,7 @@ package com.iluwatar.prototype; */ public abstract class Mage extends Prototype { - @Override - public abstract Mage clone() throws CloneNotSupportedException; + @Override + public abstract Mage clone() throws CloneNotSupportedException; } diff --git a/prototype/src/main/java/com/iluwatar/prototype/OrcBeast.java b/prototype/src/main/java/com/iluwatar/prototype/OrcBeast.java index 2af2fefa9..8be1e0b93 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/OrcBeast.java +++ b/prototype/src/main/java/com/iluwatar/prototype/OrcBeast.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.prototype; /** @@ -7,20 +29,16 @@ package com.iluwatar.prototype; */ public class OrcBeast extends Beast { - public OrcBeast() { - } + public OrcBeast() {} - public OrcBeast(OrcBeast beast) { - } + @Override + public Beast clone() throws CloneNotSupportedException { + return new OrcBeast(); + } - @Override - public Beast clone() throws CloneNotSupportedException { - return new OrcBeast(this); - } - - @Override - public String toString() { - return "Orcish wolf"; - } + @Override + public String toString() { + return "Orcish wolf"; + } } diff --git a/prototype/src/main/java/com/iluwatar/prototype/OrcMage.java b/prototype/src/main/java/com/iluwatar/prototype/OrcMage.java index 1f52a25d9..3aedc1ca1 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/OrcMage.java +++ b/prototype/src/main/java/com/iluwatar/prototype/OrcMage.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.prototype; /** @@ -7,20 +29,16 @@ package com.iluwatar.prototype; */ public class OrcMage extends Mage { - public OrcMage() { - } + public OrcMage() {} - public OrcMage(OrcMage mage) { - } + @Override + public Mage clone() throws CloneNotSupportedException { + return new OrcMage(); + } - @Override - public Mage clone() throws CloneNotSupportedException { - return new OrcMage(this); - } - - @Override - public String toString() { - return "Orcish mage"; - } + @Override + public String toString() { + return "Orcish mage"; + } } diff --git a/prototype/src/main/java/com/iluwatar/prototype/OrcWarlord.java b/prototype/src/main/java/com/iluwatar/prototype/OrcWarlord.java index 53814d1c2..1cd4e72b7 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/OrcWarlord.java +++ b/prototype/src/main/java/com/iluwatar/prototype/OrcWarlord.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.prototype; /** @@ -7,20 +29,16 @@ package com.iluwatar.prototype; */ public class OrcWarlord extends Warlord { - public OrcWarlord() { - } + public OrcWarlord() {} - public OrcWarlord(OrcWarlord warlord) { - } + @Override + public Warlord clone() throws CloneNotSupportedException { + return new OrcWarlord(); + } - @Override - public Warlord clone() throws CloneNotSupportedException { - return new OrcWarlord(this); - } - - @Override - public String toString() { - return "Orcish warlord"; - } + @Override + public String toString() { + return "Orcish warlord"; + } } diff --git a/prototype/src/main/java/com/iluwatar/prototype/Prototype.java b/prototype/src/main/java/com/iluwatar/prototype/Prototype.java index eb2520c35..bf7980c4d 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/Prototype.java +++ b/prototype/src/main/java/com/iluwatar/prototype/Prototype.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.prototype; /** @@ -7,7 +29,7 @@ package com.iluwatar.prototype; */ public abstract class Prototype implements Cloneable { - @Override - public abstract Object clone() throws CloneNotSupportedException; + @Override + public abstract Object clone() throws CloneNotSupportedException; } diff --git a/prototype/src/main/java/com/iluwatar/prototype/Warlord.java b/prototype/src/main/java/com/iluwatar/prototype/Warlord.java index bfd5c594a..72b2cfa80 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/Warlord.java +++ b/prototype/src/main/java/com/iluwatar/prototype/Warlord.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.prototype; /** @@ -7,7 +29,7 @@ package com.iluwatar.prototype; */ public abstract class Warlord extends Prototype { - @Override - public abstract Warlord clone() throws CloneNotSupportedException; + @Override + public abstract Warlord clone() throws CloneNotSupportedException; } diff --git a/prototype/src/test/java/com/iluwatar/prototype/AppTest.java b/prototype/src/test/java/com/iluwatar/prototype/AppTest.java index 030f5472c..4b06f957c 100644 --- a/prototype/src/test/java/com/iluwatar/prototype/AppTest.java +++ b/prototype/src/test/java/com/iluwatar/prototype/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.prototype; import org.junit.Test; -import com.iluwatar.prototype.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.prototype.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/prototype/src/test/java/com/iluwatar/prototype/HeroFactoryImplTest.java b/prototype/src/test/java/com/iluwatar/prototype/HeroFactoryImplTest.java new file mode 100644 index 000000000..75a234a57 --- /dev/null +++ b/prototype/src/test/java/com/iluwatar/prototype/HeroFactoryImplTest.java @@ -0,0 +1,61 @@ +/** + * The MIT License + * Copyright (c) 2014 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.prototype; + +import org.junit.Test; + +import static org.junit.Assert.assertNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +/** + * Date: 12/28/15 - 8:34 PM + * + * @author Jeroen Meulemeester + */ +public class HeroFactoryImplTest { + + @Test + public void testFactory() throws Exception { + final Mage mage = mock(Mage.class); + final Warlord warlord = mock(Warlord.class); + final Beast beast = mock(Beast.class); + + when(mage.clone()).thenThrow(CloneNotSupportedException.class); + when(warlord.clone()).thenThrow(CloneNotSupportedException.class); + when(beast.clone()).thenThrow(CloneNotSupportedException.class); + + final HeroFactoryImpl factory = new HeroFactoryImpl(mage, warlord, beast); + assertNull(factory.createMage()); + assertNull(factory.createWarlord()); + assertNull(factory.createBeast()); + + verify(mage).clone(); + verify(warlord).clone(); + verify(beast).clone(); + verifyNoMoreInteractions(mage, warlord, beast); + } + +} \ No newline at end of file diff --git a/prototype/src/test/java/com/iluwatar/prototype/PrototypeTest.java b/prototype/src/test/java/com/iluwatar/prototype/PrototypeTest.java new file mode 100644 index 000000000..071b4dd0d --- /dev/null +++ b/prototype/src/test/java/com/iluwatar/prototype/PrototypeTest.java @@ -0,0 +1,88 @@ +/** + * The MIT License + * Copyright (c) 2014 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.prototype; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; +import java.util.Collection; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertSame; + +/** + * Date: 12/28/15 - 8:45 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class PrototypeTest

{ + + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList( + new Object[]{new OrcBeast(), "Orcish wolf"}, + new Object[]{new OrcMage(), "Orcish mage"}, + new Object[]{new OrcWarlord(), "Orcish warlord"}, + new Object[]{new ElfBeast(), "Elven eagle"}, + new Object[]{new ElfMage(), "Elven mage"}, + new Object[]{new ElfWarlord(), "Elven warlord"} + ); + } + + /** + * The tested prototype instance + */ + private final Prototype testedPrototype; + + /** + * The expected {@link Prototype#toString()} value + */ + private final String expectedToString; + + /** + * Create a new test instance, using the given test object and expected value + * + * @param testedPrototype The tested prototype instance + * @param expectedToString The expected {@link Prototype#toString()} value + */ + public PrototypeTest(final Prototype testedPrototype, final String expectedToString) { + this.expectedToString = expectedToString; + this.testedPrototype = testedPrototype; + } + + @Test + public void testPrototype() throws Exception { + assertEquals(this.expectedToString, this.testedPrototype.toString()); + + final Object clone = this.testedPrototype.clone(); + assertNotNull(clone); + assertNotSame(clone, this.testedPrototype); + assertSame(this.testedPrototype.getClass(), clone.getClass()); + } + +} diff --git a/proxy/index.md b/proxy/README.md similarity index 81% rename from proxy/index.md rename to proxy/README.md index f863a96a5..a3e03708e 100644 --- a/proxy/index.md +++ b/proxy/README.md @@ -7,15 +7,20 @@ categories: Structural tags: - Java - Gang Of Four - - Difficulty-Intermediate + - Difficulty-Beginner --- -**Intent:** Provide a surrogate or placeholder for another object to control +## Also known as +Surrogate + +## Intent +Provide a surrogate or placeholder for another object to control access to it. ![alt text](./etc/proxy_1.png "Proxy") -**Applicability:** Proxy is applicable whenever there is a need for a more +## Applicability +Proxy is applicable whenever there is a need for a more versatile or sophisticated reference to an object than a simple pointer. Here are several common situations in which the Proxy pattern is applicable @@ -23,7 +28,7 @@ are several common situations in which the Proxy pattern is applicable * a virtual proxy creates expensive objects on demand. * a protection proxy controls access to the original object. Protection proxies are useful when objects should have different access rights. -**Typical Use Case:** +## Typical Use Case * control access to another object * lazy initialization @@ -31,11 +36,11 @@ are several common situations in which the Proxy pattern is applicable * facilitate network connection * to count references to an object -**Real world examples:** +## Real world examples * [java.lang.reflect.Proxy](http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Proxy.html) * [Apache Commons Proxy](https://commons.apache.org/proper/commons-proxy/) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/proxy/pom.xml b/proxy/pom.xml index 750c12b84..f16736a2c 100644 --- a/proxy/pom.xml +++ b/proxy/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT proxy @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/proxy/src/main/java/com/iluwatar/proxy/App.java b/proxy/src/main/java/com/iluwatar/proxy/App.java index 420ad5c0a..9c27cfb01 100644 --- a/proxy/src/main/java/com/iluwatar/proxy/App.java +++ b/proxy/src/main/java/com/iluwatar/proxy/App.java @@ -1,31 +1,56 @@ +/** + * The MIT License + * Copyright (c) 2014 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.proxy; /** * - * A proxy, in its most general form, is a class functioning as an interface to something else. - * The proxy could interface to anything: a network connection, a large object in memory, a file, - * or some other resource that is expensive or impossible to duplicate. In short, a proxy is a - * wrapper or agent object that is being called by the client to access the real serving object - * behind the scenes. + * A proxy, in its most general form, is a class functioning as an interface to something else. The + * proxy could interface to anything: a network connection, a large object in memory, a file, or + * some other resource that is expensive or impossible to duplicate. In short, a proxy is a wrapper + * or agent object that is being called by the client to access the real serving object behind the + * scenes. *

- * The Proxy design pattern allows you to provide an interface to other objects by creating a - * wrapper class as the proxy. The wrapper class, which is the proxy, can add additional + * The Proxy design pattern allows you to provide an interface to other objects by creating a + * wrapper class as the proxy. The wrapper class, which is the proxy, can add additional * functionality to the object of interest without changing the object's code. *

- * In this example the proxy ({@link WizardTowerProxy}) controls access to the actual object - * ({@link WizardTower}). + * In this example the proxy ({@link WizardTowerProxy}) controls access to the actual object ( + * {@link WizardTower}). * */ public class App { - public static void main(String[] args) { + /** + * Program entry point + */ + public static void main(String[] args) { - WizardTowerProxy tower = new WizardTowerProxy(); - tower.enter(new Wizard("Red wizard")); - tower.enter(new Wizard("White wizard")); - tower.enter(new Wizard("Black wizard")); - tower.enter(new Wizard("Green wizard")); - tower.enter(new Wizard("Brown wizard")); + WizardTowerProxy tower = new WizardTowerProxy(); + tower.enter(new Wizard("Red wizard")); + tower.enter(new Wizard("White wizard")); + tower.enter(new Wizard("Black wizard")); + tower.enter(new Wizard("Green wizard")); + tower.enter(new Wizard("Brown wizard")); - } + } } diff --git a/proxy/src/main/java/com/iluwatar/proxy/Wizard.java b/proxy/src/main/java/com/iluwatar/proxy/Wizard.java index b6d5691f4..5ea2e8279 100644 --- a/proxy/src/main/java/com/iluwatar/proxy/Wizard.java +++ b/proxy/src/main/java/com/iluwatar/proxy/Wizard.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.proxy; /** @@ -7,15 +29,15 @@ package com.iluwatar.proxy; */ public class Wizard { - private String name; + private String name; - public Wizard(String name) { - this.name = name; - } + public Wizard(String name) { + this.name = name; + } - @Override - public String toString() { - return name; - } + @Override + public String toString() { + return name; + } } diff --git a/proxy/src/main/java/com/iluwatar/proxy/WizardTower.java b/proxy/src/main/java/com/iluwatar/proxy/WizardTower.java index d4aa27c81..573d38374 100644 --- a/proxy/src/main/java/com/iluwatar/proxy/WizardTower.java +++ b/proxy/src/main/java/com/iluwatar/proxy/WizardTower.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.proxy; /** @@ -7,8 +29,8 @@ package com.iluwatar.proxy; */ public class WizardTower { - public void enter(Wizard wizard) { - System.out.println(wizard + " enters the tower."); - } + public void enter(Wizard wizard) { + System.out.println(wizard + " enters the tower."); + } } diff --git a/proxy/src/main/java/com/iluwatar/proxy/WizardTowerProxy.java b/proxy/src/main/java/com/iluwatar/proxy/WizardTowerProxy.java index 567a998d1..985184afe 100644 --- a/proxy/src/main/java/com/iluwatar/proxy/WizardTowerProxy.java +++ b/proxy/src/main/java/com/iluwatar/proxy/WizardTowerProxy.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.proxy; /** @@ -7,17 +29,17 @@ package com.iluwatar.proxy; */ public class WizardTowerProxy extends WizardTower { - private static final int NUM_WIZARDS_ALLOWED = 3; + private static final int NUM_WIZARDS_ALLOWED = 3; - private int numWizards; + private int numWizards; - @Override - public void enter(Wizard wizard) { - if (numWizards < NUM_WIZARDS_ALLOWED) { - super.enter(wizard); - numWizards++; - } else { - System.out.println(wizard + " is not allowed to enter!"); - } - } + @Override + public void enter(Wizard wizard) { + if (numWizards < NUM_WIZARDS_ALLOWED) { + super.enter(wizard); + numWizards++; + } else { + System.out.println(wizard + " is not allowed to enter!"); + } + } } diff --git a/proxy/src/test/java/com/iluwatar/proxy/AppTest.java b/proxy/src/test/java/com/iluwatar/proxy/AppTest.java index 4d21b3d9a..e5c065bd5 100644 --- a/proxy/src/test/java/com/iluwatar/proxy/AppTest.java +++ b/proxy/src/test/java/com/iluwatar/proxy/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.proxy; import org.junit.Test; -import com.iluwatar.proxy.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.proxy.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/proxy/src/test/java/com/iluwatar/proxy/StdOutTest.java b/proxy/src/test/java/com/iluwatar/proxy/StdOutTest.java new file mode 100644 index 000000000..48831444a --- /dev/null +++ b/proxy/src/test/java/com/iluwatar/proxy/StdOutTest.java @@ -0,0 +1,75 @@ +/** + * The MIT License + * Copyright (c) 2014 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.proxy; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since some actions don't have any + * influence on accessible objects, except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/proxy/src/test/java/com/iluwatar/proxy/WizardTest.java b/proxy/src/test/java/com/iluwatar/proxy/WizardTest.java new file mode 100644 index 000000000..56ad74c8c --- /dev/null +++ b/proxy/src/test/java/com/iluwatar/proxy/WizardTest.java @@ -0,0 +1,44 @@ +/** + * The MIT License + * Copyright (c) 2014 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.proxy; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Date: 12/28/15 - 9:02 PM + * + * @author Jeroen Meulemeester + */ +public class WizardTest { + + @Test + public void testToString() throws Exception { + final String[] wizardNames = {"Gandalf", "Dumbledore", "Oz", "Merlin"}; + for (final String name : wizardNames) { + assertEquals(name, new Wizard(name).toString()); + } + } + +} \ No newline at end of file diff --git a/proxy/src/test/java/com/iluwatar/proxy/WizardTowerProxyTest.java b/proxy/src/test/java/com/iluwatar/proxy/WizardTowerProxyTest.java new file mode 100644 index 000000000..b87b7a0bc --- /dev/null +++ b/proxy/src/test/java/com/iluwatar/proxy/WizardTowerProxyTest.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 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.proxy; + +import org.junit.Test; +import org.mockito.InOrder; + +import static org.mockito.Mockito.inOrder; + +/** + * Date: 12/28/15 - 9:18 PM + * + * @author Jeroen Meulemeester + */ +public class WizardTowerProxyTest extends StdOutTest { + + @Test + public void testEnter() throws Exception { + final Wizard[] wizards = new Wizard[]{ + new Wizard("Gandalf"), + new Wizard("Dumbledore"), + new Wizard("Oz"), + new Wizard("Merlin") + }; + + final WizardTowerProxy tower = new WizardTowerProxy(); + for (final Wizard wizard : wizards) { + tower.enter(wizard); + } + + final InOrder inOrder = inOrder(getStdOutMock()); + inOrder.verify(getStdOutMock()).println("Gandalf enters the tower."); + inOrder.verify(getStdOutMock()).println("Dumbledore enters the tower."); + inOrder.verify(getStdOutMock()).println("Oz enters the tower."); + inOrder.verify(getStdOutMock()).println("Merlin is not allowed to enter!"); + inOrder.verifyNoMoreInteractions(); + + } + +} \ No newline at end of file diff --git a/proxy/src/test/java/com/iluwatar/proxy/WizardTowerTest.java b/proxy/src/test/java/com/iluwatar/proxy/WizardTowerTest.java new file mode 100644 index 000000000..9996434f5 --- /dev/null +++ b/proxy/src/test/java/com/iluwatar/proxy/WizardTowerTest.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 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.proxy; + +import org.junit.Test; +import org.mockito.InOrder; + +import static org.mockito.Mockito.inOrder; + +/** + * Date: 12/28/15 - 9:18 PM + * + * @author Jeroen Meulemeester + */ +public class WizardTowerTest extends StdOutTest { + + @Test + public void testEnter() throws Exception { + final Wizard[] wizards = new Wizard[]{ + new Wizard("Gandalf"), + new Wizard("Dumbledore"), + new Wizard("Oz"), + new Wizard("Merlin") + }; + + final WizardTower tower = new WizardTower(); + for (final Wizard wizard : wizards) { + tower.enter(wizard); + } + + final InOrder inOrder = inOrder(getStdOutMock()); + inOrder.verify(getStdOutMock()).println("Gandalf enters the tower."); + inOrder.verify(getStdOutMock()).println("Dumbledore enters the tower."); + inOrder.verify(getStdOutMock()).println("Oz enters the tower."); + inOrder.verify(getStdOutMock()).println("Merlin enters the tower."); + inOrder.verifyNoMoreInteractions(); + + } + +} \ No newline at end of file diff --git a/publish-subscribe/.gitignore b/publish-subscribe/.gitignore new file mode 100644 index 000000000..b83d22266 --- /dev/null +++ b/publish-subscribe/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/publish-subscribe/README.md b/publish-subscribe/README.md new file mode 100644 index 000000000..6a5b2dfa8 --- /dev/null +++ b/publish-subscribe/README.md @@ -0,0 +1,25 @@ +--- +layout: pattern +title: Publish Subscribe +folder: publish-subscribe +permalink: /patterns/publish-subscribe/ +categories: Integration +tags: + - Java + - EIP + - Apache Camel™ +--- + +## Intent +Broadcast messages from sender to all the interested receivers. + +![alt text](./etc/publish-subscribe.png "Publish Subscribe Channel") + +## Applicability +Use the Publish Subscribe Channel pattern when + +* two or more applications need to communicate using a messaging system for broadcasts. + +## Credits + +* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) diff --git a/publish-subscribe/etc/publish-subscribe.png b/publish-subscribe/etc/publish-subscribe.png new file mode 100644 index 000000000..99867da66 Binary files /dev/null and b/publish-subscribe/etc/publish-subscribe.png differ diff --git a/publish-subscribe/etc/publish-subscribe.ucls b/publish-subscribe/etc/publish-subscribe.ucls new file mode 100644 index 000000000..1b121506e --- /dev/null +++ b/publish-subscribe/etc/publish-subscribe.ucls @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/publish-subscribe/pom.xml b/publish-subscribe/pom.xml new file mode 100644 index 000000000..6db6b3538 --- /dev/null +++ b/publish-subscribe/pom.xml @@ -0,0 +1,48 @@ + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + publish-subscribe + + + org.apache.camel + camel-core + + + org.apache.camel + camel-stream + + + junit + junit + + + \ No newline at end of file diff --git a/publish-subscribe/src/main/java/com/iluwatar/publish/subscribe/App.java b/publish-subscribe/src/main/java/com/iluwatar/publish/subscribe/App.java new file mode 100644 index 000000000..c4e423b04 --- /dev/null +++ b/publish-subscribe/src/main/java/com/iluwatar/publish/subscribe/App.java @@ -0,0 +1,68 @@ +/** + * The MIT License + * Copyright (c) 2014 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.publish.subscribe; + +import org.apache.camel.CamelContext; +import org.apache.camel.ProducerTemplate; +import org.apache.camel.builder.RouteBuilder; +import org.apache.camel.impl.DefaultCamelContext; + +/** + * + * There are well-established patterns for implementing broadcasting. The Observer pattern describes + * the need to decouple observers from their subject (that is, the originator of the event) so that + * the subject can easily provide event notification to all interested observers no matter how many + * observers there are (even none). The Publish-Subscribe pattern expands upon Observer by adding + * the notion of an event channel for communicating event notifications. + *

+ * A Publish-Subscribe Channel works like this: It has one input channel that splits into multiple + * output channels, one for each subscriber. When an event is published into the channel, the + * Publish-Subscribe Channel delivers a copy of the message to each of the output channels. Each + * output end of the channel has only one subscriber, which is allowed to consume a message only + * once. In this way, each subscriber gets the message only once, and consumed copies disappear from + * their channels. + *

+ * In this example we use Apache Camel to establish a Publish-Subscribe Channel from "direct-origin" + * to "mock:foo", "mock:bar" and "stream:out". + * + */ +public class App { + + /** + * Program entry point + */ + public static void main(String[] args) throws Exception { + CamelContext context = new DefaultCamelContext(); + context.addRoutes(new RouteBuilder() { + @Override + public void configure() throws Exception { + from("direct:origin").multicast().to("mock:foo", "mock:bar", "stream:out"); + } + }); + ProducerTemplate template = context.createProducerTemplate(); + context.start(); + context.getRoutes().stream().forEach(r -> System.out.println(r)); + template.sendBody("direct:origin", "Hello from origin"); + context.stop(); + } +} diff --git a/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/AppTest.java b/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/AppTest.java new file mode 100644 index 000000000..9f387be33 --- /dev/null +++ b/publish-subscribe/src/test/java/com/iluwatar/publish/subscribe/AppTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 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.publish.subscribe; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() throws Exception { + String[] args = {}; + App.main(args); + } +} diff --git a/reactor/index.md b/reactor/README.md similarity index 60% rename from reactor/index.md rename to reactor/README.md index 6e20598d2..b9ba98948 100644 --- a/reactor/index.md +++ b/reactor/README.md @@ -3,27 +3,30 @@ layout: pattern title: Reactor folder: reactor permalink: /patterns/reactor/ -categories: Architectural +categories: Concurrency tags: - Java - Difficulty-Expert + - I/O --- -**Intent:** The Reactor design pattern handles service requests that are delivered concurrently to an application by one or more clients. The application can register specific handlers for processing which are called by reactor on specific events. Dispatching of event handlers is performed by an initiation dispatcher, which manages the registered event handlers. Demultiplexing of service requests is performed by a synchronous event demultiplexer. +## Intent +The Reactor design pattern handles service requests that are delivered concurrently to an application by one or more clients. The application can register specific handlers for processing which are called by reactor on specific events. Dispatching of event handlers is performed by an initiation dispatcher, which manages the registered event handlers. Demultiplexing of service requests is performed by a synchronous event demultiplexer. ![Reactor](./etc/reactor.png "Reactor") -**Applicability:** Use Reactor pattern when +## Applicability +Use Reactor pattern when * a server application needs to handle concurrent service requests from multiple clients. * a server application needs to be available for receiving requests from new clients even when handling older client requests. * a server must maximize throughput, minimize latency and use CPU efficiently without blocking. -**Real world examples:** +## Real world examples * [Spring Reactor](http://projectreactor.io/) -**Credits** +## Credits * [Douglas C. Schmidt - Reactor](https://www.dre.vanderbilt.edu/~schmidt/PDF/Reactor.pdf) * [Pattern Oriented Software Architecture Vol I-V](http://www.amazon.com/Pattern-Oriented-Software-Architecture-Volume-Patterns/dp/0471958697) diff --git a/reactor/pom.xml b/reactor/pom.xml index 516a4b93c..c06584c6f 100644 --- a/reactor/pom.xml +++ b/reactor/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT reactor diff --git a/reactor/src/main/java/com/iluwatar/reactor/app/App.java b/reactor/src/main/java/com/iluwatar/reactor/app/App.java index 7bb01ddc8..4228b92d3 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/app/App.java +++ b/reactor/src/main/java/com/iluwatar/reactor/app/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.reactor.app; import java.io.IOException; @@ -69,17 +91,17 @@ public class App { private Dispatcher dispatcher; /** - * Creates an instance of App which will use provided dispatcher for dispatching events on reactor. + * Creates an instance of App which will use provided dispatcher for dispatching events on + * reactor. + * * @param dispatcher the dispatcher that will be used to dispatch events. */ public App(Dispatcher dispatcher) { - this.dispatcher = dispatcher; + this.dispatcher = dispatcher; } /** * App entry. - * - * @throws IOException */ public static void main(String[] args) throws IOException { new App(new ThreadPoolDispatcher(2)).start(); @@ -106,7 +128,8 @@ public class App { * Our application binds to multiple channels and uses same logging handler to handle incoming * log requests. */ - reactor.registerChannel(tcpChannel(6666, loggingHandler)).registerChannel(tcpChannel(6667, loggingHandler)) + reactor.registerChannel(tcpChannel(6666, loggingHandler)) + .registerChannel(tcpChannel(6667, loggingHandler)) .registerChannel(udpChannel(6668, loggingHandler)).start(); } @@ -120,7 +143,7 @@ public class App { reactor.stop(); dispatcher.stop(); for (AbstractNioChannel channel : channels) { - channel.getJavaChannel().close(); + channel.getJavaChannel().close(); } } diff --git a/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java b/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java index 659f5da21..29c2f83fa 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java +++ b/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.reactor.app; import java.io.IOException; @@ -37,10 +59,10 @@ public class AppClient { * @throws IOException if any I/O error occurs. */ public void start() throws IOException { - service.execute(new TCPLoggingClient("Client 1", 6666)); - service.execute(new TCPLoggingClient("Client 2", 6667)); - service.execute(new UDPLoggingClient("Client 3", 6668)); - service.execute(new UDPLoggingClient("Client 4", 6668)); + service.execute(new TcpLoggingClient("Client 1", 6666)); + service.execute(new TcpLoggingClient("Client 2", 6667)); + service.execute(new UdpLoggingClient("Client 3", 6668)); + service.execute(new UdpLoggingClient("Client 4", 6668)); } /** @@ -69,7 +91,7 @@ public class AppClient { /** * A logging client that sends requests to Reactor on TCP socket. */ - static class TCPLoggingClient implements Runnable { + static class TcpLoggingClient implements Runnable { private final int serverPort; private final String clientName; @@ -80,7 +102,7 @@ public class AppClient { * @param clientName the name of the client to be sent in logging requests. * @param port the port on which client will send logging requests. */ - public TCPLoggingClient(String clientName, int serverPort) { + public TcpLoggingClient(String clientName, int serverPort) { this.clientName = clientName; this.serverPort = serverPort; } @@ -118,7 +140,7 @@ public class AppClient { /** * A logging client that sends requests to Reactor on UDP socket. */ - static class UDPLoggingClient implements Runnable { + static class UdpLoggingClient implements Runnable { private final String clientName; private final InetSocketAddress remoteAddress; @@ -129,7 +151,7 @@ public class AppClient { * @param port the port on which client will send logging requests. * @throws UnknownHostException if localhost is unknown */ - public UDPLoggingClient(String clientName, int port) throws UnknownHostException { + public UdpLoggingClient(String clientName, int port) throws UnknownHostException { this.clientName = clientName; this.remoteAddress = new InetSocketAddress(InetAddress.getLocalHost(), port); } @@ -140,7 +162,8 @@ public class AppClient { for (int i = 0; i < 4; i++) { String message = clientName + " - Log request: " + i; - DatagramPacket request = new DatagramPacket(message.getBytes(), message.getBytes().length, remoteAddress); + DatagramPacket request = + new DatagramPacket(message.getBytes(), message.getBytes().length, remoteAddress); socket.send(request); diff --git a/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java b/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java index 0845303df..ab2bcfb1a 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java +++ b/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.reactor.app; import java.nio.ByteBuffer; @@ -25,7 +47,7 @@ public class LoggingHandler implements ChannelHandler { * received is a ByteBuffer (from TCP channel) or a DatagramPacket (from UDP channel). */ if (readObject instanceof ByteBuffer) { - doLogging(((ByteBuffer) readObject)); + doLogging((ByteBuffer) readObject); sendReply(channel, key); } else if (readObject instanceof DatagramPacket) { DatagramPacket datagram = (DatagramPacket) readObject; @@ -36,7 +58,7 @@ public class LoggingHandler implements ChannelHandler { } } - private void sendReply(AbstractNioChannel channel, DatagramPacket incomingPacket, SelectionKey key) { + private static void sendReply(AbstractNioChannel channel, DatagramPacket incomingPacket, SelectionKey key) { /* * Create a reply acknowledgement datagram packet setting the receiver to the sender of incoming * message. @@ -47,12 +69,12 @@ public class LoggingHandler implements ChannelHandler { channel.write(replyPacket, key); } - private void sendReply(AbstractNioChannel channel, SelectionKey key) { + private static void sendReply(AbstractNioChannel channel, SelectionKey key) { ByteBuffer buffer = ByteBuffer.wrap(ACK); channel.write(buffer, key); } - private void doLogging(ByteBuffer data) { + private static void doLogging(ByteBuffer data) { // assuming UTF-8 :( System.out.println(new String(data.array(), 0, data.limit())); } diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java b/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java index 9f2f8a95c..ee830e3b1 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/AbstractNioChannel.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.reactor.framework; import java.io.IOException; @@ -24,7 +46,8 @@ public abstract class AbstractNioChannel { private final SelectableChannel channel; private final ChannelHandler handler; - private final Map> channelToPendingWrites = new ConcurrentHashMap<>(); + private final Map> channelToPendingWrites = + new ConcurrentHashMap<>(); private NioReactor reactor; /** @@ -130,6 +153,7 @@ public abstract class AbstractNioChannel { * channel.write(buffer, key); * } * + *

* * @param data the data to be written on underlying channel. * @param key the key which is writable. diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/ChannelHandler.java b/reactor/src/main/java/com/iluwatar/reactor/framework/ChannelHandler.java index 381738ecd..86b30b0dd 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/ChannelHandler.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/ChannelHandler.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.reactor.framework; import java.nio.channels.SelectionKey; diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/Dispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/framework/Dispatcher.java index 78aeb84df..d9c93190a 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/Dispatcher.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/Dispatcher.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.reactor.framework; import java.nio.channels.SelectionKey; diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java b/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java index a2ff3d3d8..4e493163f 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.reactor.framework; import java.io.IOException; diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java b/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java index 16c13e5f9..3d5ebf5a0 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.reactor.framework; import java.io.IOException; @@ -14,40 +36,41 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** - * This class acts as Synchronous Event De-multiplexer and Initiation Dispatcher of Reactor pattern. - * Multiple handles i.e. {@link AbstractNioChannel}s can be registered to the reactor and it blocks - * for events from all these handles. Whenever an event occurs on any of the registered handles, it - * synchronously de-multiplexes the event which can be any of read, write or accept, and dispatches - * the event to the appropriate {@link ChannelHandler} using the {@link Dispatcher}. + * This class acts as Synchronous Event De-multiplexer and Initiation Dispatcher of Reactor pattern. Multiple handles + * i.e. {@link AbstractNioChannel}s can be registered to the reactor and it blocks for events from all these handles. + * Whenever an event occurs on any of the registered handles, it synchronously de-multiplexes the event which can be any + * of read, write or accept, and dispatches the event to the appropriate {@link ChannelHandler} using the + * {@link Dispatcher}. * *

- * Implementation: A NIO reactor runs in its own thread when it is started using {@link #start()} - * method. {@link NioReactor} uses {@link Selector} for realizing Synchronous Event De-multiplexing. + * Implementation: A NIO reactor runs in its own thread when it is started using {@link #start()} method. + * {@link NioReactor} uses {@link Selector} for realizing Synchronous Event De-multiplexing. * *

- * NOTE: This is one of the ways to implement NIO reactor and it does not take care of all possible - * edge cases which are required in a real application. This implementation is meant to demonstrate - * the fundamental concepts that lie behind Reactor pattern. + * NOTE: This is one of the ways to implement NIO reactor and it does not take care of all possible edge cases which are + * required in a real application. This implementation is meant to demonstrate the fundamental concepts that lie behind + * Reactor pattern. */ public class NioReactor { private final Selector selector; private final Dispatcher dispatcher; /** - * All the work of altering the SelectionKey operations and Selector operations are performed in - * the context of main event loop of reactor. So when any channel needs to change its readability - * or writability, a new command is added in the command queue and then the event loop picks up - * the command and executes it in next iteration. + * All the work of altering the SelectionKey operations and Selector operations are performed in the context of main + * event loop of reactor. So when any channel needs to change its readability or writability, a new command is added + * in the command queue and then the event loop picks up the command and executes it in next iteration. */ private final Queue pendingCommands = new ConcurrentLinkedQueue<>(); private final ExecutorService reactorMain = Executors.newSingleThreadExecutor(); /** - * Creates a reactor which will use provided {@code dispatcher} to dispatch events. The - * application can provide various implementations of dispatcher which suits its needs. + * Creates a reactor which will use provided {@code dispatcher} to dispatch events. The application can provide + * various implementations of dispatcher which suits its needs. * - * @param dispatcher a non-null dispatcher used to dispatch events on registered channels. - * @throws IOException if any I/O error occurs. + * @param dispatcher + * a non-null dispatcher used to dispatch events on registered channels. + * @throws IOException + * if any I/O error occurs. */ public NioReactor(Dispatcher dispatcher) throws IOException { this.dispatcher = dispatcher; @@ -57,7 +80,8 @@ public class NioReactor { /** * Starts the reactor event loop in a new thread. * - * @throws IOException if any I/O error occurs. + * @throws IOException + * if any I/O error occurs. */ public void start() throws IOException { reactorMain.execute(() -> { @@ -73,8 +97,10 @@ public class NioReactor { /** * Stops the reactor and related resources such as dispatcher. * - * @throws InterruptedException if interrupted while stopping the reactor. - * @throws IOException if any I/O error occurs. + * @throws InterruptedException + * if interrupted while stopping the reactor. + * @throws IOException + * if any I/O error occurs. */ public void stop() throws InterruptedException, IOException { reactorMain.shutdownNow(); @@ -84,15 +110,15 @@ public class NioReactor { } /** - * Registers a new channel (handle) with this reactor. Reactor will start waiting for events on - * this channel and notify of any events. While registering the channel the reactor uses - * {@link AbstractNioChannel#getInterestedOps()} to know about the interested operation of this - * channel. + * Registers a new channel (handle) with this reactor. Reactor will start waiting for events on this channel and + * notify of any events. While registering the channel the reactor uses {@link AbstractNioChannel#getInterestedOps()} + * to know about the interested operation of this channel. * - * @param channel a new channel on which reactor will wait for events. The channel must be bound - * prior to being registered. + * @param channel + * a new channel on which reactor will wait for events. The channel must be bound prior to being registered. * @return this - * @throws IOException if any I/O error occurs. + * @throws IOException + * if any I/O error occurs. */ public NioReactor registerChannel(AbstractNioChannel channel) throws IOException { SelectionKey key = channel.getJavaChannel().register(selector, channel.getInterestedOps()); @@ -113,8 +139,8 @@ public class NioReactor { processPendingCommands(); /* - * Synchronous event de-multiplexing happens here, this is blocking call which returns when it - * is possible to initiate non-blocking operation on any of the registered channels. + * Synchronous event de-multiplexing happens here, this is blocking call which returns when it is possible to + * initiate non-blocking operation on any of the registered channels. */ selector.select(); @@ -147,8 +173,8 @@ public class NioReactor { } /* - * Initiation dispatcher logic, it checks the type of event and notifier application specific - * event handler to handle the event. + * Initiation dispatcher logic, it checks the type of event and notifier application specific event handler to handle + * the event. */ private void processKey(SelectionKey key) throws IOException { if (key.isAcceptable()) { @@ -160,7 +186,7 @@ public class NioReactor { } } - private void onChannelWritable(SelectionKey key) throws IOException { + private static void onChannelWritable(SelectionKey key) throws IOException { AbstractNioChannel channel = (AbstractNioChannel) key.attachment(); channel.flush(key); } @@ -196,14 +222,15 @@ public class NioReactor { } /** - * Queues the change of operations request of a channel, which will change the interested - * operations of the channel sometime in future. + * Queues the change of operations request of a channel, which will change the interested operations of the channel + * sometime in future. *

- * This is a non-blocking method and does not guarantee that the operations have changed when this - * method returns. + * This is a non-blocking method and does not guarantee that the operations have changed when this method returns. * - * @param key the key for which operations have to be changed. - * @param interestedOps the new interest operations. + * @param key + * the key for which operations have to be changed. + * @param interestedOps + * the new interest operations. */ public void changeOps(SelectionKey key, int interestedOps) { pendingCommands.add(new ChangeKeyOpsCommand(key, interestedOps)); diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java b/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java index c5caaa7ff..c7d67fd13 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.reactor.framework; import java.io.IOException; @@ -71,7 +93,8 @@ public class NioServerSocketChannel extends AbstractNioChannel { */ @Override public void bind() throws IOException { - ((ServerSocketChannel) getJavaChannel()).socket().bind(new InetSocketAddress(InetAddress.getLocalHost(), port)); + ((ServerSocketChannel) getJavaChannel()).socket().bind( + new InetSocketAddress(InetAddress.getLocalHost(), port)); ((ServerSocketChannel) getJavaChannel()).configureBlocking(false); System.out.println("Bound TCP socket at port: " + port); } diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/SameThreadDispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/framework/SameThreadDispatcher.java index ae995428e..855d8b090 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/SameThreadDispatcher.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/SameThreadDispatcher.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.reactor.framework; import java.nio.channels.SelectionKey; diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/ThreadPoolDispatcher.java b/reactor/src/main/java/com/iluwatar/reactor/framework/ThreadPoolDispatcher.java index 4a240659e..0ca114abe 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/ThreadPoolDispatcher.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/ThreadPoolDispatcher.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.reactor.framework; import java.nio.channels.SelectionKey; diff --git a/reactor/src/test/java/com/iluwatar/reactor/app/AppTest.java b/reactor/src/test/java/com/iluwatar/reactor/app/ReactorTest.java similarity index 58% rename from reactor/src/test/java/com/iluwatar/reactor/app/AppTest.java rename to reactor/src/test/java/com/iluwatar/reactor/app/ReactorTest.java index 9abb4e690..1ec70f87f 100644 --- a/reactor/src/test/java/com/iluwatar/reactor/app/AppTest.java +++ b/reactor/src/test/java/com/iluwatar/reactor/app/ReactorTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.reactor.app; import java.io.IOException; @@ -12,7 +34,7 @@ import com.iluwatar.reactor.framework.ThreadPoolDispatcher; * This class tests the Distributed Logging service by starting a Reactor and then sending it * concurrent logging requests using multiple clients. */ -public class AppTest { +public class ReactorTest { /** * Test the application using pooled thread dispatcher. @@ -39,7 +61,7 @@ public class AppTest { app.stop(); } - + /** * Test the application using same thread dispatcher. * diff --git a/reader-writer-lock/README.md b/reader-writer-lock/README.md new file mode 100644 index 000000000..40b711361 --- /dev/null +++ b/reader-writer-lock/README.md @@ -0,0 +1,32 @@ +--- +layout: pattern +title: Reader Writer Lock +folder: reader-writer-lock +permalink: /patterns/reader-writer-lock/ +categories: Concurrency +tags: + - Java + - Difficulty-Intermediate + - I/O + - Performance +--- + +**Intent:** + +Suppose we have a shared memory area with the basic constraints detailed above. It is possible to protect the shared data behind a mutual exclusion mutex, in which case no two threads can access the data at the same time. However, this solution is suboptimal, because it is possible that a reader R1 might have the lock, and then another reader R2 requests access. It would be foolish for R2 to wait until R1 was done before starting its own read operation; instead, R2 should start right away. This is the motivation for the Reader Writer Lock pattern. + +![alt text](./etc/reader-writer-lock.png "Reader writer lock") + +**Applicability:** + +Application need to increase the performance of resource synchronize for multiple thread, in particularly there are mixed read/write operations. + +**Real world examples:** + +* [Java Reader Writer Lock](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReadWriteLock.html) + +**Credits** + +* [Readers–writer lock](https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock) + +* [Readers–writers_problem](https://en.wikipedia.org/wiki/Readers%E2%80%93writers_problem) \ No newline at end of file diff --git a/reader-writer-lock/etc/reader-writer-lock.png b/reader-writer-lock/etc/reader-writer-lock.png new file mode 100644 index 000000000..f7b600530 Binary files /dev/null and b/reader-writer-lock/etc/reader-writer-lock.png differ diff --git a/reader-writer-lock/etc/reader-writer-lock.ucls b/reader-writer-lock/etc/reader-writer-lock.ucls new file mode 100644 index 000000000..920904e76 --- /dev/null +++ b/reader-writer-lock/etc/reader-writer-lock.ucls @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/reader-writer-lock/pom.xml b/reader-writer-lock/pom.xml new file mode 100644 index 000000000..2df3d5b14 --- /dev/null +++ b/reader-writer-lock/pom.xml @@ -0,0 +1,49 @@ + + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + reader-writer-lock + + + junit + junit + test + + + org.mockito + mockito-core + test + + + + diff --git a/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/App.java b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/App.java new file mode 100644 index 000000000..fd5d28ed5 --- /dev/null +++ b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/App.java @@ -0,0 +1,79 @@ +/** + * The MIT License + * Copyright (c) 2014 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.reader.writer.lock; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.stream.IntStream; + +/** + * + * In a multiple thread applications, the threads may try to synchronize the shared resources + * regardless of read or write operation. It leads to a low performance especially in a "read more + * write less" system as indeed the read operations are thread-safe to another read operation. + *

+ * Reader writer lock is a synchronization primitive that try to resolve this problem. This pattern + * allows concurrent access for read-only operations, while write operations require exclusive + * access. This means that multiple threads can read the data in parallel but an exclusive lock is + * needed for writing or modifying data. When a writer is writing the data, all other writers or + * readers will be blocked until the writer is finished writing. + * + *

+ * This example use two mutex to demonstrate the concurrent access of multiple readers and writers. + * + * + * @author hongshuwei@gmail.com + */ +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + + ExecutorService executeService = Executors.newFixedThreadPool(10); + ReaderWriterLock lock = new ReaderWriterLock(); + + // Start 5 readers + IntStream.range(0, 5) + .forEach(i -> executeService.submit(new Reader("Reader " + i, lock.readLock()))); + + // Start 5 writers + IntStream.range(0, 5) + .forEach(i -> executeService.submit(new Writer("Writer " + i, lock.writeLock()))); + // In the system console, it can see that the read operations are executed concurrently while + // write operations are exclusive. + executeService.shutdown(); + try { + executeService.awaitTermination(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + System.out.println("Error waiting for ExecutorService shutdown"); + } + + } + +} diff --git a/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Reader.java b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Reader.java new file mode 100644 index 000000000..2b837b341 --- /dev/null +++ b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Reader.java @@ -0,0 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 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.reader.writer.lock; + +import java.util.concurrent.locks.Lock; + +/** + * Reader class, read when it acquired the read lock + */ +public class Reader implements Runnable { + + private Lock readLock; + + private String name; + + public Reader(String name, Lock readLock) { + this.name = name; + this.readLock = readLock; + } + + @Override + public void run() { + readLock.lock(); + try { + read(); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + readLock.unlock(); + } + } + + /** + * Simulate the read operation + * + */ + public void read() throws InterruptedException { + System.out.println(name + " begin"); + Thread.sleep(250); + System.out.println(name + " finish"); + } +} diff --git a/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/ReaderWriterLock.java b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/ReaderWriterLock.java new file mode 100644 index 000000000..f08ed805d --- /dev/null +++ b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/ReaderWriterLock.java @@ -0,0 +1,233 @@ +/** + * The MIT License + * Copyright (c) 2014 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.reader.writer.lock; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; + +/** + * Class responsible for control the access for reader or writer + * + * Allows multiple readers to hold the lock at same time, but if any writer holds the lock then + * readers wait. If reader holds the lock then writer waits. This lock is not fair. + */ +public class ReaderWriterLock implements ReadWriteLock { + + + private Object readerMutex = new Object(); + + private int currentReaderCount; + + /** + * Global mutex is used to indicate that whether reader or writer gets the lock in the moment. + *

+ * 1. When it contains the reference of {@link readerLock}, it means that the lock is acquired by + * the reader, another reader can also do the read operation concurrently.
+ * 2. When it contains the reference of reference of {@link writerLock}, it means that the lock is + * acquired by the writer exclusively, no more reader or writer can get the lock. + *

+ * This is the most important field in this class to control the access for reader/writer. + */ + private Set globalMutex = new HashSet<>(); + + private ReadLock readerLock = new ReadLock(); + private WriteLock writerLock = new WriteLock(); + + @Override + public Lock readLock() { + return readerLock; + } + + @Override + public Lock writeLock() { + return writerLock; + } + + /** + * return true when globalMutex hold the reference of writerLock + */ + private boolean doesWriterOwnThisLock() { + return globalMutex.contains(writerLock); + } + + /** + * return true when globalMutex hold the reference of readerLock + */ + private boolean doesReaderOwnThisLock() { + return globalMutex.contains(readerLock); + } + + /** + * Nobody get the lock when globalMutex contains nothing + * + */ + private boolean isLockFree() { + return globalMutex.isEmpty(); + } + + private static void waitUninterruptibly(Object o) { + try { + o.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + /** + * Reader Lock, can be access for more than one reader concurrently if no writer get the lock + */ + private class ReadLock implements Lock { + + @Override + public void lock() { + + synchronized (readerMutex) { + + currentReaderCount++; + if (currentReaderCount == 1) { + // Try to get the globalMutex lock for the first reader + synchronized (globalMutex) { + while (true) { + // If the no one get the lock or the lock is locked by reader, just set the reference + // to the globalMutex to indicate that the lock is locked by Reader. + if (isLockFree() || doesReaderOwnThisLock()) { + globalMutex.add(this); + break; + } else { + // If lock is acquired by the write, let the thread wait until the writer release + // the lock + waitUninterruptibly(globalMutex); + } + } + } + + } + } + } + + @Override + public void unlock() { + + synchronized (readerMutex) { + currentReaderCount--; + // Release the lock only when it is the last reader, it is ensure that the lock is released + // when all reader is completely. + if (currentReaderCount == 0) { + synchronized (globalMutex) { + // Notify the waiter, mostly the writer + globalMutex.remove(this); + globalMutex.notifyAll(); + } + } + } + + } + + @Override + public void lockInterruptibly() throws InterruptedException { + throw new UnsupportedOperationException(); + } + + @Override + public boolean tryLock() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { + throw new UnsupportedOperationException(); + } + + @Override + public Condition newCondition() { + throw new UnsupportedOperationException(); + } + + } + + /** + * Writer Lock, can only be accessed by one writer concurrently + */ + private class WriteLock implements Lock { + + @Override + public void lock() { + + synchronized (globalMutex) { + + while (true) { + // When there is no one acquired the lock, just put the writeLock reference to the + // globalMutex to indicate that the lock is acquired by one writer. + // It is ensure that writer can only get the lock when no reader/writer acquired the lock. + if (isLockFree()) { + globalMutex.add(this); + break; + } else if (doesWriterOwnThisLock()) { + // Wait when other writer get the lock + waitUninterruptibly(globalMutex); + } else if (doesReaderOwnThisLock()) { + // Wait when other reader get the lock + waitUninterruptibly(globalMutex); + } else { + throw new AssertionError("it should never reach here"); + } + } + } + } + + @Override + public void unlock() { + + synchronized (globalMutex) { + globalMutex.remove(this); + // Notify the waiter, other writer or reader + globalMutex.notifyAll(); + } + } + + @Override + public void lockInterruptibly() throws InterruptedException { + throw new UnsupportedOperationException(); + } + + @Override + public boolean tryLock() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { + throw new UnsupportedOperationException(); + } + + @Override + public Condition newCondition() { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Writer.java b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Writer.java new file mode 100644 index 000000000..fc3c3bb88 --- /dev/null +++ b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Writer.java @@ -0,0 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 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.reader.writer.lock; + +import java.util.concurrent.locks.Lock; + +/** + * Writer class, write when it acquired the write lock + */ +public class Writer implements Runnable { + + private Lock writeLock; + + private String name; + + public Writer(String name, Lock writeLock) { + this.name = name; + this.writeLock = writeLock; + } + + + @Override + public void run() { + writeLock.lock(); + try { + write(); + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + writeLock.unlock(); + } + } + + /** + * Simulate the write operation + */ + public void write() throws InterruptedException { + System.out.println(name + " begin"); + Thread.sleep(250); + System.out.println(name + " finish"); + } +} diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/AppTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/AppTest.java new file mode 100644 index 000000000..09c591a48 --- /dev/null +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/AppTest.java @@ -0,0 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 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.reader.writer.lock; + +import org.junit.Test; + +/** + * Application test + */ +public class AppTest { + + @Test + public void test() throws Exception { + String[] args = {}; + App.main(args); + + } +} diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderAndWriterTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderAndWriterTest.java new file mode 100644 index 000000000..a2496a3c0 --- /dev/null +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderAndWriterTest.java @@ -0,0 +1,104 @@ +/** + * The MIT License + * Copyright (c) 2014 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.reader.writer.lock; + +import static org.mockito.Mockito.inOrder; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; +import org.mockito.InOrder; + +/** + * @author hongshuwei@gmail.com + */ +public class ReaderAndWriterTest extends StdOutTest { + + + + /** + * Verify reader and writer can only get the lock to read and write orderly + */ + @Test + public void testReadAndWrite() throws Exception { + + ReaderWriterLock lock = new ReaderWriterLock(); + + Reader reader1 = new Reader("Reader 1", lock.readLock()); + Writer writer1 = new Writer("Writer 1", lock.writeLock()); + + ExecutorService executeService = Executors.newFixedThreadPool(2); + executeService.submit(reader1); + // Let reader1 execute first + Thread.sleep(150); + executeService.submit(writer1); + + executeService.shutdown(); + try { + executeService.awaitTermination(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + System.out.println("Error waiting for ExecutorService shutdown"); + } + + final InOrder inOrder = inOrder(getStdOutMock()); + inOrder.verify(getStdOutMock()).println("Reader 1 begin"); + inOrder.verify(getStdOutMock()).println("Reader 1 finish"); + inOrder.verify(getStdOutMock()).println("Writer 1 begin"); + inOrder.verify(getStdOutMock()).println("Writer 1 finish"); + } + + /** + * Verify reader and writer can only get the lock to read and write orderly + */ + @Test + public void testWriteAndRead() throws Exception { + + ExecutorService executeService = Executors.newFixedThreadPool(2); + ReaderWriterLock lock = new ReaderWriterLock(); + + Reader reader1 = new Reader("Reader 1", lock.readLock()); + Writer writer1 = new Writer("Writer 1", lock.writeLock()); + + executeService.submit(writer1); + // Let writer1 execute first + Thread.sleep(150); + executeService.submit(reader1); + + executeService.shutdown(); + try { + executeService.awaitTermination(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + System.out.println("Error waiting for ExecutorService shutdown"); + } + + final InOrder inOrder = inOrder(getStdOutMock()); + inOrder.verify(getStdOutMock()).println("Writer 1 begin"); + inOrder.verify(getStdOutMock()).println("Writer 1 finish"); + inOrder.verify(getStdOutMock()).println("Reader 1 begin"); + inOrder.verify(getStdOutMock()).println("Reader 1 finish"); + } +} + diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderTest.java new file mode 100644 index 000000000..7d51e977c --- /dev/null +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderTest.java @@ -0,0 +1,72 @@ +/** + * The MIT License + * Copyright (c) 2014 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.reader.writer.lock; + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.spy; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; +import org.mockito.InOrder; + +/** + * @author hongshuwei@gmail.com + */ +public class ReaderTest extends StdOutTest { + + /** + * Verify that multiple readers can get the read lock concurrently + */ + @Test + public void testRead() throws Exception { + + ExecutorService executeService = Executors.newFixedThreadPool(2); + ReaderWriterLock lock = new ReaderWriterLock(); + + Reader reader1 = spy(new Reader("Reader 1", lock.readLock())); + Reader reader2 = spy(new Reader("Reader 2", lock.readLock())); + + executeService.submit(reader1); + Thread.sleep(150); + executeService.submit(reader2); + + executeService.shutdown(); + try { + executeService.awaitTermination(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + System.out.println("Error waiting for ExecutorService shutdown"); + } + + // Read operation will hold the read lock 250 milliseconds, so here we prove that multiple reads + // can be performed in the same time. + final InOrder inOrder = inOrder(getStdOutMock()); + inOrder.verify(getStdOutMock()).println("Reader 1 begin"); + inOrder.verify(getStdOutMock()).println("Reader 2 begin"); + inOrder.verify(getStdOutMock()).println("Reader 1 finish"); + inOrder.verify(getStdOutMock()).println("Reader 2 finish"); + + } +} diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/StdOutTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/StdOutTest.java new file mode 100644 index 000000000..7a1af09c0 --- /dev/null +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/StdOutTest.java @@ -0,0 +1,76 @@ +/** + * The MIT License + * Copyright (c) 2014 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.reader.writer.lock; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since some actions don't have any + * influence on accessible objects, except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/WriterTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/WriterTest.java new file mode 100644 index 000000000..765c491ff --- /dev/null +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/WriterTest.java @@ -0,0 +1,72 @@ +/** + * The MIT License + * Copyright (c) 2014 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.reader.writer.lock; + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.spy; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; +import org.mockito.InOrder; + +/** + * @author hongshuwei@gmail.com + */ +public class WriterTest extends StdOutTest { + + /** + * Verify that multiple writers will get the lock in order. + */ + @Test + public void testWrite() throws Exception { + + ExecutorService executeService = Executors.newFixedThreadPool(2); + ReaderWriterLock lock = new ReaderWriterLock(); + + Writer writer1 = spy(new Writer("Writer 1", lock.writeLock())); + Writer writer2 = spy(new Writer("Writer 2", lock.writeLock())); + + executeService.submit(writer1); + // Let write1 execute first + Thread.sleep(150); + executeService.submit(writer2); + + executeService.shutdown(); + try { + executeService.awaitTermination(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + System.out.println("Error waiting for ExecutorService shutdown"); + } + // Write operation will hold the write lock 250 milliseconds, so here we verify that when two + // writer execute concurrently, the second writer can only writes only when the first one is + // finished. + final InOrder inOrder = inOrder(getStdOutMock()); + inOrder.verify(getStdOutMock()).println("Writer 1 begin"); + inOrder.verify(getStdOutMock()).println("Writer 1 finish"); + inOrder.verify(getStdOutMock()).println("Writer 2 begin"); + inOrder.verify(getStdOutMock()).println("Writer 2 finish"); + } +} diff --git a/repository/index.md b/repository/README.md similarity index 56% rename from repository/index.md rename to repository/README.md index a65044d29..67b3ea44e 100644 --- a/repository/index.md +++ b/repository/README.md @@ -3,11 +3,15 @@ layout: pattern title: Repository folder: repository permalink: /patterns/repository/ -categories: Architectural -tags: Java +categories: Persistence Tier +tags: + - Java + - Difficulty-Intermediate + - Spring --- -**Intent:** Repository layer is added between the domain and data mapping +## Intent +Repository layer is added between the domain and data mapping layers to isolate domain objects from details of the database access code and to minimize scattering and duplication of query code. The Repository pattern is especially useful in systems where number of domain classes is large or heavy @@ -15,13 +19,19 @@ querying is utilized. ![alt text](./etc/repository.png "Repository") -**Applicability:** Use the Repository pattern when +## Applicability +Use the Repository pattern when * the number of domain objects is large * you want to avoid duplication of query code * you want to keep the database querying code in single place * you have multiple data sources -**Real world examples:** +## Real world examples * [Spring Data](http://projects.spring.io/spring-data/) + +## Credits + +* [Don’t use DAO, use Repository](http://thinkinginobjects.com/2012/08/26/dont-use-dao-use-repository/) +* [Advanced Spring Data JPA - Specifications and Querydsl](https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/) diff --git a/repository/etc/repository.png b/repository/etc/repository.png index 1e031f3ae..08d5d571d 100644 Binary files a/repository/etc/repository.png and b/repository/etc/repository.png differ diff --git a/repository/etc/repository.ucls b/repository/etc/repository.ucls index 6c42019e1..894e9434c 100644 --- a/repository/etc/repository.ucls +++ b/repository/etc/repository.ucls @@ -19,10 +19,48 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/repository/pom.xml b/repository/pom.xml index 70f2ff6b2..56a448ec5 100644 --- a/repository/pom.xml +++ b/repository/pom.xml @@ -1,35 +1,66 @@ - - 4.0.0 - - com.iluwatar - java-design-patterns - 1.7.0 - - repository - - - org.springframework.data - spring-data-jpa - - - org.hibernate - hibernate-entitymanager - - - commons-dbcp - commons-dbcp - - - com.h2database - h2 - - - junit - junit - test - - + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + repository + + + org.springframework + spring-test + + + org.springframework.data + spring-data-jpa + + + org.hibernate + hibernate-entitymanager + + + commons-dbcp + commons-dbcp + + + com.h2database + h2 + + + junit + junit + test + + + com.google.guava + guava + + diff --git a/repository/src/main/java/com/iluwatar/repository/App.java b/repository/src/main/java/com/iluwatar/repository/App.java index 37a5f7962..2807ae7ca 100644 --- a/repository/src/main/java/com/iluwatar/repository/App.java +++ b/repository/src/main/java/com/iluwatar/repository/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.repository; import java.util.List; @@ -5,63 +27,82 @@ import java.util.List; import org.springframework.context.support.ClassPathXmlApplicationContext; /** - * * Repository pattern mediates between the domain and data mapping layers using a collection-like - * interface for accessing domain objects. A system with complex domain model often benefits from - * a layer that isolates domain objects from the details of the database access code and in such + * interface for accessing domain objects. A system with complex domain model often benefits from a + * layer that isolates domain objects from the details of the database access code and in such * systems it can be worthwhile to build another layer of abstraction over the mapping layer where - * query construction code is concentrated. This becomes more important when there are a large + * query construction code is concentrated. This becomes more important when there are a large * number of domain classes or heavy querying. In these cases particularly, adding this layer helps * minimize duplicate query logic. *

- * In this example we utilize Spring Data to automatically generate a repository for us from the {@link Person} - * domain object. Using the {@link PersonRepository} we perform CRUD operations on the entity. Underneath we have - * configured in-memory H2 database for which schema is created and dropped on each run. - * + * In this example we utilize Spring Data to automatically generate a repository for us from the + * {@link Person} domain object. Using the {@link PersonRepository} we perform CRUD operations on + * the entity, moreover, the query by {@link org.springframework.data.jpa.domain.Specification} are + * also performed. Underneath we have configured in-memory H2 database for which schema is created + * and dropped on each run. */ public class App { - - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { - ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( - "applicationContext.xml"); - PersonRepository repository = context.getBean(PersonRepository.class); - Person peter = new Person("Peter", "Sagan"); - Person nasta = new Person("Nasta", "Kuzminova"); + /** + * Program entry point + * + * @param args + * command line args + */ + public static void main(String[] args) { - // Add new Person records - repository.save(peter); - repository.save(nasta); + ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( + "applicationContext.xml"); + PersonRepository repository = context.getBean(PersonRepository.class); - // Count Person records - System.out.println("Count Person records: " + repository.count()); + Person peter = new Person("Peter", "Sagan", 17); + Person nasta = new Person("Nasta", "Kuzminova", 25); + Person john = new Person("John", "lawrence", 35); + Person terry = new Person("Terry", "Law", 36); - // Print all records - List persons = (List) repository.findAll(); - for (Person person : persons) { - System.out.println(person); - } + // Add new Person records + repository.save(peter); + repository.save(nasta); + repository.save(john); + repository.save(terry); - // Find Person by surname - System.out.println("Find by surname 'Sagan': " + repository.findBySurname("Sagan")); + // Count Person records + System.out.println("Count Person records: " + repository.count()); - // Update Person - nasta.setName("Barbora"); - nasta.setSurname("Spotakova"); - repository.save(nasta); + // Print all records + List persons = (List) repository.findAll(); + for (Person person : persons) { + System.out.println(person); + } - System.out.println("Find by id 2: " + repository.findOne(2L)); + // Update Person + nasta.setName("Barbora"); + nasta.setSurname("Spotakova"); + repository.save(nasta); - // Remove record from Person - repository.delete(2L); + System.out.println("Find by id 2: " + repository.findOne(2L)); - // And finally count records - System.out.println("Count Person records: " + repository.count()); + // Remove record from Person + repository.delete(2L); - context.close(); - } + // count records + System.out.println("Count Person records: " + repository.count()); + + // find by name + Person p = repository.findOne(new PersonSpecifications.NameEqualSpec("John")); + System.out.println("Find by John is " + p); + + // find by age + persons = repository.findAll(new PersonSpecifications.AgeBetweenSpec(20, 40)); + + System.out.println("Find Person with age between 20,40: "); + for (Person person : persons) { + System.out.println(person); + } + + repository.deleteAll(); + + context.close(); + + } } diff --git a/repository/src/main/java/com/iluwatar/repository/AppConfig.java b/repository/src/main/java/com/iluwatar/repository/AppConfig.java new file mode 100644 index 000000000..3e7093358 --- /dev/null +++ b/repository/src/main/java/com/iluwatar/repository/AppConfig.java @@ -0,0 +1,157 @@ +/** + * The MIT License + * Copyright (c) 2014 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.repository; + +import java.sql.SQLException; +import java.util.List; +import java.util.Properties; + +import javax.sql.DataSource; + +import org.apache.commons.dbcp.BasicDataSource; +import org.hibernate.jpa.HibernatePersistenceProvider; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; + +/** + * This is the same example as in {@link App} but with annotations based + * configuration for Spring. + * + */ +@EnableJpaRepositories +public class AppConfig { + + /** + * Creation of H2 db + * + * @return A new Instance of DataSource + */ + @Bean(destroyMethod = "close") + public DataSource dataSource() { + BasicDataSource basicDataSource = new BasicDataSource(); + basicDataSource.setDriverClassName("org.h2.Driver"); + basicDataSource.setUrl("jdbc:h2:~/databases/person"); + basicDataSource.setUsername("sa"); + basicDataSource.setPassword("sa"); + return (DataSource) basicDataSource; + } + + /** + * Factory to create a especific instance of Entity Manager + */ + @Bean + public LocalContainerEntityManagerFactoryBean entityManagerFactory() { + LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean(); + entityManager.setDataSource(dataSource()); + entityManager.setPackagesToScan("com.iluwatar"); + entityManager.setPersistenceProvider(new HibernatePersistenceProvider()); + entityManager.setJpaProperties(jpaProperties()); + + return entityManager; + } + + /** + * Properties for Jpa + */ + private static Properties jpaProperties() { + Properties properties = new Properties(); + properties.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect"); + properties.setProperty("hibernate.hbm2ddl.auto", "create-drop"); + return properties; + } + + /** + * Get transaction manager + */ + @Bean + public JpaTransactionManager transactionManager() throws SQLException { + JpaTransactionManager transactionManager = new JpaTransactionManager(); + transactionManager.setEntityManagerFactory(entityManagerFactory().getObject()); + return transactionManager; + } + + /** + * Program entry point + * + * @param args + * command line args + */ + public static void main(String[] args) { + + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + AppConfig.class); + PersonRepository repository = context.getBean(PersonRepository.class); + + Person peter = new Person("Peter", "Sagan", 17); + Person nasta = new Person("Nasta", "Kuzminova", 25); + Person john = new Person("John", "lawrence", 35); + Person terry = new Person("Terry", "Law", 36); + + // Add new Person records + repository.save(peter); + repository.save(nasta); + repository.save(john); + repository.save(terry); + + // Count Person records + System.out.println("Count Person records: " + repository.count()); + + // Print all records + List persons = (List) repository.findAll(); + for (Person person : persons) { + System.out.println(person); + } + + // Update Person + nasta.setName("Barbora"); + nasta.setSurname("Spotakova"); + repository.save(nasta); + + System.out.println("Find by id 2: " + repository.findOne(2L)); + + // Remove record from Person + repository.delete(2L); + + // count records + System.out.println("Count Person records: " + repository.count()); + + // find by name + Person p = repository.findOne(new PersonSpecifications.NameEqualSpec("John")); + System.out.println("Find by John is " + p); + + // find by age + persons = repository.findAll(new PersonSpecifications.AgeBetweenSpec(20, 40)); + + System.out.println("Find Person with age between 20,40: "); + for (Person person : persons) { + System.out.println(person); + } + + context.close(); + + } + +} diff --git a/repository/src/main/java/com/iluwatar/repository/Person.java b/repository/src/main/java/com/iluwatar/repository/Person.java index 85d647b1a..b1559cf00 100644 --- a/repository/src/main/java/com/iluwatar/repository/Person.java +++ b/repository/src/main/java/com/iluwatar/repository/Person.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.repository; import javax.persistence.Entity; @@ -12,47 +34,112 @@ import javax.persistence.Id; @Entity public class Person { - @Id - @GeneratedValue - private Long id; - private String name; - private String surname; + @Id + @GeneratedValue + private Long id; + private String name; + private String surname; - public Person() { - } + private int age; - public Person(String name, String surname) { - this.name = name; - this.surname = surname; - } + public Person() { + } - public Long getId() { - return id; - } + /** + * Constructor + */ + public Person(String name, String surname, int age) { + this.name = name; + this.surname = surname; + this.age = age; + } - public void setId(Long id) { - this.id = id; - } + public Long getId() { + return id; + } - public String getName() { - return name; - } + public void setId(Long id) { + this.id = id; + } - public void setName(String name) { - this.name = name; - } + public String getName() { + return name; + } - public String getSurname() { - return surname; - } + public void setName(String name) { + this.name = name; + } - public void setSurname(String surname) { - this.surname = surname; - } + public String getSurname() { + return surname; + } + + public void setSurname(String surname) { + this.surname = surname; + } + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + @Override + public String toString() { + return "Person [id=" + id + ", name=" + name + ", surname=" + surname + ", age=" + age + "]"; + } + + @Override + public int hashCode() { + + final int prime = 31; + int result = 1; + result = prime * result + age; + result = prime * result + (id == null ? 0 : id.hashCode()); + result = prime * result + (name == null ? 0 : name.hashCode()); + result = prime * result + (surname == null ? 0 : surname.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Person other = (Person) obj; + if (age != other.age) { + return false; + } + if (id == null) { + if (other.id != null) { + return false; + } + } else if (!id.equals(other.id)) { + return false; + } + if (name == null) { + if (other.name != null) { + return false; + } + } else if (!name.equals(other.name)) { + return false; + } + if (surname == null) { + if (other.surname != null) { + return false; + } + } else if (!surname.equals(other.surname)) { + return false; + } + return true; + } - @Override - public String toString() { - return "Person [id=" + id + ", name=" + name + ", surname=" + surname - + "]"; - } } diff --git a/repository/src/main/java/com/iluwatar/repository/PersonRepository.java b/repository/src/main/java/com/iluwatar/repository/PersonRepository.java index fd20bc94c..eb9addd62 100644 --- a/repository/src/main/java/com/iluwatar/repository/PersonRepository.java +++ b/repository/src/main/java/com/iluwatar/repository/PersonRepository.java @@ -1,7 +1,28 @@ +/** + * The MIT License + * Copyright (c) 2014 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.repository; -import java.util.List; - +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; @@ -11,7 +32,8 @@ import org.springframework.stereotype.Repository; * */ @Repository -public interface PersonRepository extends CrudRepository { - - public List findBySurname(String surname); +public interface PersonRepository + extends CrudRepository, JpaSpecificationExecutor { + + Person findByName(String name); } diff --git a/repository/src/main/java/com/iluwatar/repository/PersonSpecifications.java b/repository/src/main/java/com/iluwatar/repository/PersonSpecifications.java new file mode 100644 index 000000000..81394fda3 --- /dev/null +++ b/repository/src/main/java/com/iluwatar/repository/PersonSpecifications.java @@ -0,0 +1,79 @@ +/** + * The MIT License + * Copyright (c) 2014 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.repository; + +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Predicate; +import javax.persistence.criteria.Root; + +import org.springframework.data.jpa.domain.Specification; + +/** + * Helper class, includes vary Specification as the abstraction of sql query criteria + */ +public class PersonSpecifications { + + public static class AgeBetweenSpec implements Specification { + + private int from; + + private int to; + + public AgeBetweenSpec(int from, int to) { + this.from = from; + this.to = to; + } + + @Override + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + + return cb.between(root.get("age"), from, to); + + } + + } + + /** + * Name specification + * + */ + public static class NameEqualSpec implements Specification { + + public String name; + + public NameEqualSpec(String name) { + this.name = name; + } + + /** + * Get predicate + */ + public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { + + return cb.equal(root.get("name"), this.name); + + } + } + +} diff --git a/repository/src/main/resources/META-INF/persistence.xml b/repository/src/main/resources/META-INF/persistence.xml index 0aded0dbd..db4c7be13 100644 --- a/repository/src/main/resources/META-INF/persistence.xml +++ b/repository/src/main/resources/META-INF/persistence.xml @@ -1,8 +1,32 @@ - + + + + diff --git a/repository/src/main/resources/applicationContext.xml b/repository/src/main/resources/applicationContext.xml index 3fe15b2f6..8b4b81bb7 100644 --- a/repository/src/main/resources/applicationContext.xml +++ b/repository/src/main/resources/applicationContext.xml @@ -1,39 +1,62 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/repository/src/test/java/com/iluwatar/repository/AnnotationBasedRepositoryTest.java b/repository/src/test/java/com/iluwatar/repository/AnnotationBasedRepositoryTest.java new file mode 100644 index 000000000..ccd3fcc41 --- /dev/null +++ b/repository/src/test/java/com/iluwatar/repository/AnnotationBasedRepositoryTest.java @@ -0,0 +1,132 @@ +/** + * The MIT License + * Copyright (c) 2014 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.repository; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.List; + +import javax.annotation.Resource; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; + +import com.google.common.collect.Lists; + +/** + * Test case to test the functions of {@link PersonRepository}, beside the CRUD functions, the query + * by {@link org.springframework.data.jpa.domain.Specification} are also test. + * + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = { AppConfig.class }, loader = AnnotationConfigContextLoader.class) +public class AnnotationBasedRepositoryTest { + + @Resource + private PersonRepository repository; + + Person peter = new Person("Peter", "Sagan", 17); + Person nasta = new Person("Nasta", "Kuzminova", 25); + Person john = new Person("John", "lawrence", 35); + Person terry = new Person("Terry", "Law", 36); + + List persons = Arrays.asList(peter, nasta, john, terry); + + /** + * Prepare data for test + */ + @Before + public void setup() { + + repository.save(persons); + } + + @Test + public void testFindAll() { + + List actuals = Lists.newArrayList(repository.findAll()); + assertTrue(actuals.containsAll(persons) && persons.containsAll(actuals)); + } + + @Test + public void testSave() { + + Person terry = repository.findByName("Terry"); + terry.setSurname("Lee"); + terry.setAge(47); + repository.save(terry); + + terry = repository.findByName("Terry"); + assertEquals(terry.getSurname(), "Lee"); + assertEquals(47, terry.getAge()); + } + + @Test + public void testDelete() { + + Person terry = repository.findByName("Terry"); + repository.delete(terry); + + assertEquals(3, repository.count()); + assertNull(repository.findByName("Terry")); + } + + @Test + public void testCount() { + + assertEquals(4, repository.count()); + } + + @Test + public void testFindAllByAgeBetweenSpec() { + + List persons = repository.findAll(new PersonSpecifications.AgeBetweenSpec(20, 40)); + + assertEquals(3, persons.size()); + assertTrue(persons.stream().allMatch((item) -> { + return item.getAge() > 20 && item.getAge() < 40; + })); + } + + @Test + public void testFindOneByNameEqualSpec() { + + Person actual = repository.findOne(new PersonSpecifications.NameEqualSpec("Terry")); + assertEquals(terry, actual); + } + + @After + public void cleanup() { + + repository.deleteAll(); + } + +} diff --git a/repository/src/test/java/com/iluwatar/repository/AppConfigTest.java b/repository/src/test/java/com/iluwatar/repository/AppConfigTest.java new file mode 100644 index 000000000..28623bf45 --- /dev/null +++ b/repository/src/test/java/com/iluwatar/repository/AppConfigTest.java @@ -0,0 +1,76 @@ +/** + * The MIT License + * Copyright (c) 2014 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.repository; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.sql.ResultSet; +import java.sql.SQLException; + +import javax.sql.DataSource; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; +import org.springframework.transaction.annotation.Transactional; + +/** + * This case is Just for test the Annotation Based configuration + * + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = { AppConfig.class }, loader = AnnotationConfigContextLoader.class) +public class AppConfigTest { + + @Autowired + DataSource dataSource; + + /** + * Test for bean instance + */ + @Test + public void testDataSource() { + assertNotNull(dataSource); + } + + /** + * Test for correct query execution + */ + @Test + @Transactional + public void testQuery() throws SQLException { + ResultSet resultSet = dataSource.getConnection().createStatement().executeQuery("SELECT 1"); + String result = null; + String expected = "1"; + while (resultSet.next()) { + result = resultSet.getString(1); + + } + assertTrue(result.equals(expected)); + } + +} diff --git a/repository/src/test/java/com/iluwatar/repository/AppTest.java b/repository/src/test/java/com/iluwatar/repository/AppTest.java index 14f597045..e1fc27bef 100644 --- a/repository/src/test/java/com/iluwatar/repository/AppTest.java +++ b/repository/src/test/java/com/iluwatar/repository/AppTest.java @@ -1,19 +1,38 @@ +/** + * The MIT License + * Copyright (c) 2014 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.repository; import org.junit.Test; -import com.iluwatar.repository.App; +import java.io.IOException; /** - * - * Application test - * + * Tests that Repository example runs without errors. */ public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } } diff --git a/repository/src/test/java/com/iluwatar/repository/RepositoryTest.java b/repository/src/test/java/com/iluwatar/repository/RepositoryTest.java new file mode 100644 index 000000000..63f9f7c6d --- /dev/null +++ b/repository/src/test/java/com/iluwatar/repository/RepositoryTest.java @@ -0,0 +1,130 @@ +/** + * The MIT License + * Copyright (c) 2014 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.repository; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.List; + +import javax.annotation.Resource; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import com.google.common.collect.Lists; + +/** + * Test case to test the functions of {@link PersonRepository}, beside the CRUD functions, the query + * by {@link org.springframework.data.jpa.domain.Specification} are also test. + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(locations = { "classpath:applicationContext.xml" }) +public class RepositoryTest { + + @Resource + private PersonRepository repository; + + Person peter = new Person("Peter", "Sagan", 17); + Person nasta = new Person("Nasta", "Kuzminova", 25); + Person john = new Person("John", "lawrence", 35); + Person terry = new Person("Terry", "Law", 36); + + List persons = Arrays.asList(peter, nasta, john, terry); + + /** + * Prepare data for test + */ + @Before + public void setup() { + + repository.save(persons); + } + + @Test + public void testFindAll() { + + List actuals = Lists.newArrayList(repository.findAll()); + assertTrue(actuals.containsAll(persons) && persons.containsAll(actuals)); + } + + @Test + public void testSave() { + + Person terry = repository.findByName("Terry"); + terry.setSurname("Lee"); + terry.setAge(47); + repository.save(terry); + + terry = repository.findByName("Terry"); + assertEquals(terry.getSurname(), "Lee"); + assertEquals(47, terry.getAge()); + } + + @Test + public void testDelete() { + + Person terry = repository.findByName("Terry"); + repository.delete(terry); + + assertEquals(3, repository.count()); + assertNull(repository.findByName("Terry")); + } + + @Test + public void testCount() { + + assertEquals(4, repository.count()); + } + + @Test + public void testFindAllByAgeBetweenSpec() { + + List persons = repository.findAll(new PersonSpecifications.AgeBetweenSpec(20, 40)); + + assertEquals(3, persons.size()); + assertTrue(persons.stream().allMatch((item) -> { + return item.getAge() > 20 && item.getAge() < 40; + })); + } + + @Test + public void testFindOneByNameEqualSpec() { + + Person actual = repository.findOne(new PersonSpecifications.NameEqualSpec("Terry")); + assertEquals(terry, actual); + } + + @After + public void cleanup() { + + repository.deleteAll(); + } + +} diff --git a/resource-acquisition-is-initialization/index.md b/resource-acquisition-is-initialization/README.md similarity index 60% rename from resource-acquisition-is-initialization/index.md rename to resource-acquisition-is-initialization/README.md index c3aa6c045..821f220d7 100644 --- a/resource-acquisition-is-initialization/index.md +++ b/resource-acquisition-is-initialization/README.md @@ -4,13 +4,18 @@ title: Resource Acquisition Is Initialization folder: resource-acquisition-is-initialization permalink: /patterns/resource-acquisition-is-initialization/ categories: Other -tags: Java +tags: + - Java + - Difficulty-Beginner + - Idiom --- -**Intent:** Resource Acquisition Is Initialization pattern can be used to implement exception safe resource management. +## Intent +Resource Acquisition Is Initialization pattern can be used to implement exception safe resource management. ![alt text](./etc/resource-acquisition-is-initialization.png "Resource Acquisition Is Initialization") -**Applicability:** Use the Resource Acquisition Is Initialization pattern when +## Applicability +Use the Resource Acquisition Is Initialization pattern when * you have resources that must be closed in every condition diff --git a/resource-acquisition-is-initialization/pom.xml b/resource-acquisition-is-initialization/pom.xml index a16eae745..7b411f46c 100644 --- a/resource-acquisition-is-initialization/pom.xml +++ b/resource-acquisition-is-initialization/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT resource-acquisition-is-initialization @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/App.java b/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/App.java index 141aea4ec..413fb0eab 100644 --- a/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/App.java +++ b/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/App.java @@ -1,44 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 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.resource.acquisition.is.initialization; /** * - * Resource Acquisition Is Initialization pattern was developed - * for exception safe resource management by C++ creator Bjarne - * Stroustrup. + * Resource Acquisition Is Initialization pattern was developed for exception safe resource + * management by C++ creator Bjarne Stroustrup. *

- * In RAII resource is tied to object lifetime: resource allocation - * is done during object creation while resource deallocation is - * done during object destruction. + * In RAII resource is tied to object lifetime: resource allocation is done during object creation + * while resource deallocation is done during object destruction. *

- * In Java RAII is achieved with try-with-resources statement and - * interfaces {@link Closeable} and {@link AutoCloseable}. The try-with-resources - * statement ensures that each resource is closed at the end of the - * statement. Any object that implements {@link java.lang.AutoCloseable}, which - * includes all objects which implement {@link java.io.Closeable}, can be used - * as a resource. + * In Java RAII is achieved with try-with-resources statement and interfaces {@link Closeable} and + * {@link AutoCloseable}. The try-with-resources statement ensures that each resource is closed at + * the end of the statement. Any object that implements {@link java.lang.AutoCloseable}, which + * includes all objects which implement {@link java.io.Closeable}, can be used as a resource. * - * In this example, {@link SlidingDoor} implements {@link AutoCloseable} and - * {@link TreasureChest} implements {@link Closeable}. Running the example, we can - * observe that both resources are automatically closed. + * In this example, {@link SlidingDoor} implements {@link AutoCloseable} and {@link TreasureChest} + * implements {@link Closeable}. Running the example, we can observe that both resources are + * automatically closed. *

* http://docs.oracle.com/javase/7/docs/technotes/guides/language/try-with-resources.html * */ public class App { - - /** - * Program entry point - * @param args command line args - * @throws Exception - */ - public static void main( String[] args ) throws Exception { - - try (SlidingDoor slidingDoor = new SlidingDoor()) { - System.out.println("Walking in."); - } - - try (TreasureChest treasureChest = new TreasureChest()) { - System.out.println("Looting contents."); - } + + /** + * Program entry point + */ + public static void main(String[] args) throws Exception { + + try (SlidingDoor slidingDoor = new SlidingDoor()) { + System.out.println("Walking in."); } + + try (TreasureChest treasureChest = new TreasureChest()) { + System.out.println("Looting contents."); + } + } } diff --git a/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/SlidingDoor.java b/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/SlidingDoor.java index 2033707e6..3a1a35f2d 100644 --- a/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/SlidingDoor.java +++ b/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/SlidingDoor.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.resource.acquisition.is.initialization; /** @@ -7,12 +29,12 @@ package com.iluwatar.resource.acquisition.is.initialization; */ public class SlidingDoor implements AutoCloseable { - public SlidingDoor() { - System.out.println("Sliding door opens."); - } - - @Override - public void close() throws Exception { - System.out.println("Sliding door closes."); - } + public SlidingDoor() { + System.out.println("Sliding door opens."); + } + + @Override + public void close() throws Exception { + System.out.println("Sliding door closes."); + } } diff --git a/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/TreasureChest.java b/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/TreasureChest.java index 4d6b1a863..e7b7ebab6 100644 --- a/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/TreasureChest.java +++ b/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/TreasureChest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.resource.acquisition.is.initialization; import java.io.Closeable; @@ -10,12 +32,12 @@ import java.io.IOException; */ public class TreasureChest implements Closeable { - public TreasureChest() { - System.out.println("Treasure chest opens."); - } - - @Override - public void close() throws IOException { - System.out.println("Treasure chest closes."); - } + public TreasureChest() { + System.out.println("Treasure chest opens."); + } + + @Override + public void close() throws IOException { + System.out.println("Treasure chest closes."); + } } diff --git a/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/AppTest.java b/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/AppTest.java index e20815c3f..c0e6a29b7 100644 --- a/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/AppTest.java +++ b/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.resource.acquisition.is.initialization; import org.junit.Test; -import com.iluwatar.resource.acquisition.is.initialization.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.resource.acquisition.is.initialization.App; */ public class AppTest { - @Test - public void test() throws Exception { - String[] args = {}; - App.main(args); - } + @Test + public void test() throws Exception { + String[] args = {}; + App.main(args); + } } diff --git a/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/ClosableTest.java b/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/ClosableTest.java new file mode 100644 index 000000000..55bdaf19c --- /dev/null +++ b/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/ClosableTest.java @@ -0,0 +1,49 @@ +/** + * The MIT License + * Copyright (c) 2014 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.resource.acquisition.is.initialization; + +import org.junit.Test; +import org.mockito.InOrder; + +import static org.mockito.Mockito.inOrder; + +/** + * Date: 12/28/15 - 9:31 PM + * + * @author Jeroen Meulemeester + */ +public class ClosableTest extends StdOutTest { + + @Test + public void testOpenClose() throws Exception { + final InOrder inOrder = inOrder(getStdOutMock()); + try (final SlidingDoor door = new SlidingDoor(); final TreasureChest chest = new TreasureChest()) { + inOrder.verify(getStdOutMock()).println("Sliding door opens."); + inOrder.verify(getStdOutMock()).println("Treasure chest opens."); + } + inOrder.verify(getStdOutMock()).println("Treasure chest closes."); + inOrder.verify(getStdOutMock()).println("Sliding door closes."); + inOrder.verifyNoMoreInteractions(); + } + +} \ No newline at end of file diff --git a/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/StdOutTest.java b/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/StdOutTest.java new file mode 100644 index 000000000..42cb42e6b --- /dev/null +++ b/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/StdOutTest.java @@ -0,0 +1,75 @@ +/** + * The MIT License + * Copyright (c) 2014 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.resource.acquisition.is.initialization; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since some actions don't have any + * influence on accessible objects, except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/semaphore/README.md b/semaphore/README.md new file mode 100644 index 000000000..46ccd7b8e --- /dev/null +++ b/semaphore/README.md @@ -0,0 +1,33 @@ +--- +layout: pattern +title: Semaphore +folder: semaphore +permalink: /patterns/semaphore/ +categories: Concurrency +tags: + - Java + - Difficulty-Intermediate +--- + +## Also known as +Counting Semaphore + +## Intent +Create a lock which mediates access to a pool of resources. +Only a limited number of threads, specified at the creation +of the semaphore, can access the resources at any given time. +A semaphore which only allows one concurrent access to a resource +is called a binary semaphore. + +![alt text](./etc/semaphore.png "Semaphore") + +## Applicability +Use a Semaphore when + +* you have a pool of resources to allocate to different threads +* concurrent access to a resource could lead to a race condition + +## Credits + +* [Semaphore(programming)] (http://en.wikipedia.org/wiki/Semaphore_(programming)) +* [Semaphores] (http://tutorials.jenkov.com/java-concurrency/semaphores.html) diff --git a/semaphore/etc/semaphore.png b/semaphore/etc/semaphore.png new file mode 100644 index 000000000..a2ff6d0c4 Binary files /dev/null and b/semaphore/etc/semaphore.png differ diff --git a/semaphore/pom.xml b/semaphore/pom.xml new file mode 100644 index 000000000..1b3bf8b5d --- /dev/null +++ b/semaphore/pom.xml @@ -0,0 +1,42 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + semaphore + + + junit + junit + test + + + diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/App.java b/semaphore/src/main/java/com/iluwatar/semaphore/App.java new file mode 100644 index 000000000..272de37b0 --- /dev/null +++ b/semaphore/src/main/java/com/iluwatar/semaphore/App.java @@ -0,0 +1,50 @@ +/** + * The MIT License + * Copyright (c) 2014 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.semaphore; + +/** + * A Semaphore mediates access by a group of threads to a pool of resources. + *

+ * In this example a group of customers are taking fruit from a fruit shop. + * There is a bowl each of apples, oranges and lemons. Only one customer can + * access a bowl simultaneously. A Semaphore is used to indicate how many + * resources are currently available and must be acquired in order for a bowl + * to be given to a customer. Customers continually try to take fruit until + * there is no fruit left in the shop. + */ +public class App { + + /** + * main method + */ + public static void main(String[] args) { + FruitShop shop = new FruitShop(); + new Customer("Peter", shop).start(); + new Customer("Paul", shop).start(); + new Customer("Mary", shop).start(); + new Customer("John", shop).start(); + new Customer("Ringo", shop).start(); + new Customer("George", shop).start(); + } + +} diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/Customer.java b/semaphore/src/main/java/com/iluwatar/semaphore/Customer.java new file mode 100644 index 000000000..0a4713438 --- /dev/null +++ b/semaphore/src/main/java/com/iluwatar/semaphore/Customer.java @@ -0,0 +1,76 @@ +/** + * The MIT License + * Copyright (c) 2014 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.semaphore; + +/** + * A Customer attempts to repeatedly take Fruit from the FruitShop by + * taking Fruit from FruitBowl instances. + */ +public class Customer extends Thread { + + /** + * Name of the Customer. + */ + private final String name; + + /** + * The FruitShop he is using. + */ + private final FruitShop fruitShop; + + /** + * Their bowl of Fruit. + */ + private final FruitBowl fruitBowl; + + /** + * Customer constructor + */ + public Customer(String name, FruitShop fruitShop) { + this.name = name; + this.fruitShop = fruitShop; + this.fruitBowl = new FruitBowl(); + } + + /** + * The Customer repeatedly takes Fruit from the FruitShop until no Fruit + * remains. + */ + public void run() { + + while (fruitShop.countFruit() > 0) { + FruitBowl bowl = fruitShop.takeBowl(); + Fruit fruit; + + if (bowl != null && (fruit = bowl.take()) != null) { + System.out.println(name + " took an " + fruit); + fruitBowl.put(fruit); + fruitShop.returnBowl(bowl); + } + } + + System.out.println(name + " took " + fruitBowl); + + } + +} diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/Fruit.java b/semaphore/src/main/java/com/iluwatar/semaphore/Fruit.java new file mode 100644 index 000000000..0a4224d20 --- /dev/null +++ b/semaphore/src/main/java/com/iluwatar/semaphore/Fruit.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 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.semaphore; + +/** + * Fruit is a resource stored in a FruitBowl. + */ +public class Fruit { + + public static enum FruitType { + ORANGE, APPLE, LEMON + } + + private FruitType type; + + public Fruit(FruitType type) { + this.type = type; + } + + public FruitType getType() { + return type; + } + + /** + * toString method + */ + public String toString() { + switch (type) { + case ORANGE: + return "Orange"; + case APPLE: + return "Apple"; + case LEMON: + return "Lemon"; + default: + return ""; + } + } + +} diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/FruitBowl.java b/semaphore/src/main/java/com/iluwatar/semaphore/FruitBowl.java new file mode 100644 index 000000000..ac2b87ada --- /dev/null +++ b/semaphore/src/main/java/com/iluwatar/semaphore/FruitBowl.java @@ -0,0 +1,88 @@ +/** + * The MIT License + * Copyright (c) 2014 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.semaphore; + +import java.util.ArrayList; + +/** + * A FruitBowl contains Fruit. + */ +public class FruitBowl { + + private ArrayList fruit = new ArrayList<>(); + + /** + * + * @return The amount of Fruit left in the bowl. + */ + public int countFruit() { + return fruit.size(); + } + + /** + * Put an item of Fruit into the bowl. + * + * @param f fruit + */ + public void put(Fruit f) { + fruit.add(f); + } + + /** + * Take an item of Fruit out of the bowl. + * @return The Fruit taken out of the bowl, or null if empty. + */ + public Fruit take() { + if (fruit.isEmpty()) { + return null; + } else { + return fruit.remove(0); + } + } + + /** + * toString method + */ + public String toString() { + int apples = 0; + int oranges = 0; + int lemons = 0; + + for (Fruit f : fruit) { + switch (f.getType()) { + case APPLE: + apples++; + break; + case ORANGE: + oranges++; + break; + case LEMON: + lemons++; + break; + default: + } + } + + return apples + " Apples, " + oranges + " Oranges, and " + lemons + " Lemons"; + } +} diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/FruitShop.java b/semaphore/src/main/java/com/iluwatar/semaphore/FruitShop.java new file mode 100644 index 000000000..315b46986 --- /dev/null +++ b/semaphore/src/main/java/com/iluwatar/semaphore/FruitShop.java @@ -0,0 +1,120 @@ +/** + * The MIT License + * Copyright (c) 2014 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.semaphore; + +/** + * A FruitShop contains three FruitBowl instances and controls access to them. + */ +public class FruitShop { + + /** + * The FruitBowl instances stored in the class. + */ + private FruitBowl[] bowls = { + new FruitBowl(), + new FruitBowl(), + new FruitBowl() + }; + + /** + * Access flags for each of the FruitBowl instances. + */ + private boolean[] available = { + true, + true, + true + }; + + /** + * The Semaphore that controls access to the class resources. + */ + private Semaphore semaphore; + + /** + * FruitShop constructor + */ + public FruitShop() { + for (int i = 0; i < 100; i++) { + bowls[0].put(new Fruit(Fruit.FruitType.APPLE)); + bowls[1].put(new Fruit(Fruit.FruitType.ORANGE)); + bowls[2].put(new Fruit(Fruit.FruitType.LEMON)); + } + + semaphore = new Semaphore(3); + } + + /** + * + * @return The amount of Fruit left in the shop. + */ + public synchronized int countFruit() { + return bowls[0].countFruit() + bowls[1].countFruit() + bowls[2].countFruit(); + } + + /** + * Method called by Customer to get a FruitBowl from the shop. This method + * will try to acquire the Semaphore before returning the first available + * FruitBowl. + */ + public synchronized FruitBowl takeBowl() { + + FruitBowl bowl = null; + + try { + semaphore.acquire(); + + if (available[0]) { + bowl = bowls[0]; + available[0] = false; + } else if (available[1]) { + bowl = bowls[1]; + available[1] = false; + } else if (available[2]) { + bowl = bowls[2]; + available[2] = false; + } + + } catch (InterruptedException e) { + e.printStackTrace(); + } finally { + semaphore.release(); + } + return bowl; + } + + /** + * Method called by a Customer instance to return a FruitBowl to the shop. + * This method releases the Semaphore, making the FruitBowl available to + * another Customer. + */ + public synchronized void returnBowl(FruitBowl bowl) { + if (bowl == bowls[0]) { + available[0] = true; + } else if (bowl == bowls[1]) { + available[1] = true; + } else if (bowl == bowls[2]) { + available [2] = true; + } + } + +} diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/Lock.java b/semaphore/src/main/java/com/iluwatar/semaphore/Lock.java new file mode 100644 index 000000000..4b651685c --- /dev/null +++ b/semaphore/src/main/java/com/iluwatar/semaphore/Lock.java @@ -0,0 +1,34 @@ +/** + * The MIT License + * Copyright (c) 2014 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.semaphore; + +/** + * Lock is an interface for a lock which can be acquired and released. + */ +public interface Lock { + + void acquire() throws InterruptedException; + + void release(); + +} diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/Semaphore.java b/semaphore/src/main/java/com/iluwatar/semaphore/Semaphore.java new file mode 100644 index 000000000..2e4a54c7c --- /dev/null +++ b/semaphore/src/main/java/com/iluwatar/semaphore/Semaphore.java @@ -0,0 +1,77 @@ +/** + * The MIT License + * Copyright (c) 2014 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.semaphore; + +/** + * Semaphore is an implementation of a semaphore lock. + */ +public class Semaphore implements Lock { + + private final int licenses; + /** + * The number of concurrent resource accesses which are allowed. + */ + private int counter; + + public Semaphore(int licenses) { + this.licenses = licenses; + this.counter = licenses; + } + + /** + * Returns the number of licenses managed by the Semaphore + */ + public int getNumLicenses() { + return licenses; + } + + /** + * Returns the number of available licenses + */ + public int getAvailableLicenses() { + return counter; + } + + /** + * Method called by a thread to acquire the lock. If there are no resources + * available this will wait until the lock has been released to re-attempt + * the acquire. + */ + public synchronized void acquire() throws InterruptedException { + while (counter == 0) { + wait(); + } + counter = counter - 1; + } + + /** + * Method called by a thread to release the lock. + */ + public synchronized void release() { + if (counter < licenses) { + counter = counter + 1; + notify(); + } + } + +} diff --git a/semaphore/src/test/java/com/iluwatar/semaphore/AppTest.java b/semaphore/src/test/java/com/iluwatar/semaphore/AppTest.java new file mode 100644 index 000000000..4b286588d --- /dev/null +++ b/semaphore/src/test/java/com/iluwatar/semaphore/AppTest.java @@ -0,0 +1,34 @@ +/** + * The MIT License + * Copyright (c) 2014 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.semaphore; + +import org.junit.Test; +import java.io.IOException; + +public class AppTest{ + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } +} \ No newline at end of file diff --git a/semaphore/src/test/java/com/iluwatar/semaphore/FruitBowlTest.java b/semaphore/src/test/java/com/iluwatar/semaphore/FruitBowlTest.java new file mode 100644 index 000000000..be31f77ab --- /dev/null +++ b/semaphore/src/test/java/com/iluwatar/semaphore/FruitBowlTest.java @@ -0,0 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014 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.semaphore; + +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * Test taking from and putting Fruit into a FruitBowl + */ +public class FruitBowlTest { + + @Test + public void fruitBowlTest() { + FruitBowl fbowl = new FruitBowl(); + + assertEquals(fbowl.countFruit(), 0); + + for (int i = 1; i <= 10; i++) { + fbowl.put(new Fruit(Fruit.FruitType.LEMON)); + assertEquals(fbowl.countFruit(), i); + } + + for (int i = 9; i >= 0; i--) { + assertNotNull(fbowl.take()); + assertEquals(fbowl.countFruit(), i); + } + + assertNull(fbowl.take()); + } +} diff --git a/semaphore/src/test/java/com/iluwatar/semaphore/SemaphoreTest.java b/semaphore/src/test/java/com/iluwatar/semaphore/SemaphoreTest.java new file mode 100644 index 000000000..14587a485 --- /dev/null +++ b/semaphore/src/test/java/com/iluwatar/semaphore/SemaphoreTest.java @@ -0,0 +1,56 @@ +/** + * The MIT License + * Copyright (c) 2014 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.semaphore; + +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * Test case for acquiring and releasing a Semaphore + */ +public class SemaphoreTest { + + @Test + public void acquireReleaseTest() { + Semaphore sphore = new Semaphore(3); + + assertEquals(sphore.getAvailableLicenses(), 3); + + for (int i = 2; i >= 0; i--) { + try { + sphore.acquire(); + assertEquals(sphore.getAvailableLicenses(), i); + } catch (InterruptedException e) { + fail(e.toString()); + } + } + + for (int i = 1; i <= 3; i++) { + sphore.release(); + assertEquals(sphore.getAvailableLicenses(), i); + } + + sphore.release(); + assertEquals(sphore.getAvailableLicenses(), 3); + } +} diff --git a/servant/index.md b/servant/README.md similarity index 58% rename from servant/index.md rename to servant/README.md index 38a8e2c60..3e82ab2cf 100644 --- a/servant/index.md +++ b/servant/README.md @@ -4,15 +4,22 @@ title: Servant folder: servant permalink: /patterns/servant/ categories: Structural -tags: Java +tags: + - Java + - Difficulty-Beginner --- -**Intent:** Servant is used for providing some behavior to a group of classes. +## Intent +Servant is used for providing some behavior to a group of classes. Instead of defining that behavior in each class - or when we cannot factor out this behavior in the common parent class - it is defined once in the Servant. ![alt text](./etc/servant-pattern.png "Servant") -**Applicability:** Use the Servant pattern when +## Applicability +Use the Servant pattern when * when we want some objects to perform a common action and don't want to define this action as a method in every class. + +## Credits +* [Let's Modify the Objects-First Approach into Design-Patterns-First](http://edu.pecinovsky.cz/papers/2006_ITiCSE_Design_Patterns_First.pdf) diff --git a/servant/pom.xml b/servant/pom.xml index 1928f83c9..c235b005c 100644 --- a/servant/pom.xml +++ b/servant/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT servant @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/servant/src/etc/servant.xml b/servant/src/etc/servant.xml index 8da8a9e0b..7b91d0900 100644 --- a/servant/src/etc/servant.xml +++ b/servant/src/etc/servant.xml @@ -1,4 +1,28 @@ +

diff --git a/servant/src/main/java/com/iluwatar/servant/App.java b/servant/src/main/java/com/iluwatar/servant/App.java index 832d3d52c..92829441d 100644 --- a/servant/src/main/java/com/iluwatar/servant/App.java +++ b/servant/src/main/java/com/iluwatar/servant/App.java @@ -1,58 +1,81 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servant; import java.util.ArrayList; /** - * Servant offers some functionality to a group of classes without defining that functionality in each of them. - * A Servant is a class whose instance provides methods that take care of a desired service, - * while objects for which the servant does something, are taken as parameters. + * Servant offers some functionality to a group of classes without defining that functionality in + * each of them. A Servant is a class whose instance provides methods that take care of a desired + * service, while objects for which the servant does something, are taken as parameters. *

* In this example {@link Servant} is serving {@link King} and {@link Queen}. * */ public class App { - - static Servant jenkins = new Servant("Jenkins"); - static Servant travis = new Servant("Travis"); - /** - * Program entry point - * @param args - */ - public static void main(String[] args) { - scenario(jenkins, 1); - scenario(travis, 0); + static Servant jenkins = new Servant("Jenkins"); + static Servant travis = new Servant("Travis"); + + /** + * Program entry point + */ + public static void main(String[] args) { + scenario(jenkins, 1); + scenario(travis, 0); + } + + /** + * Can add a List with enum Actions for variable scenarios + */ + public static void scenario(Servant servant, int compliment) { + King k = new King(); + Queen q = new Queen(); + + ArrayList guests = new ArrayList<>(); + guests.add(k); + guests.add(q); + + // feed + servant.feed(k); + servant.feed(q); + // serve drinks + servant.giveWine(k); + servant.giveWine(q); + // compliment + servant.giveCompliments(guests.get(compliment)); + + // outcome of the night + for (Royalty r : guests) { + r.changeMood(); } - /* - * Can add a List with enum Actions for variable scenarios - * */ - public static void scenario(Servant servant, int compliment) { - King k = new King(); - Queen q = new Queen(); - - ArrayList guests = new ArrayList<>(); - guests.add(k); - guests.add(q); - - //feed - servant.feed(k); - servant.feed(q); - //serve drinks - servant.giveWine(k); - servant.giveWine(q); - //compliment - servant.GiveCompliments(guests.get(compliment)); - - //outcome of the night - for (Royalty r : guests) - r.changeMood(); - - //check your luck - if (servant.checkIfYouWillBeHanged(guests)) - System.out.println(servant.name + " will live another day"); - else - System.out.println("Poor " + servant.name + ". His days are numbered"); + // check your luck + if (servant.checkIfYouWillBeHanged(guests)) { + System.out.println(servant.name + " will live another day"); + } else { + System.out.println("Poor " + servant.name + ". His days are numbered"); } + } } diff --git a/servant/src/main/java/com/iluwatar/servant/King.java b/servant/src/main/java/com/iluwatar/servant/King.java index 29ec88192..bc3f8cdcf 100644 --- a/servant/src/main/java/com/iluwatar/servant/King.java +++ b/servant/src/main/java/com/iluwatar/servant/King.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servant; /** @@ -6,34 +28,38 @@ package com.iluwatar.servant; * */ public class King implements Royalty { - - private boolean isDrunk; - private boolean isHungry = true; - private boolean isHappy; - private boolean complimentReceived; - @Override - public void getFed() { - isHungry = false; - } + private boolean isDrunk; + private boolean isHungry = true; + private boolean isHappy; + private boolean complimentReceived; - @Override - public void getDrink() { - isDrunk = true; - } + @Override + public void getFed() { + isHungry = false; + } - public void receiveCompliments() { - complimentReceived = true; - } + @Override + public void getDrink() { + isDrunk = true; + } - @Override - public void changeMood() { - if (!isHungry && isDrunk) isHappy = true; - if (complimentReceived) isHappy = false; - } + public void receiveCompliments() { + complimentReceived = true; + } - @Override - public boolean getMood() { - return isHappy; + @Override + public void changeMood() { + if (!isHungry && isDrunk) { + isHappy = true; } + if (complimentReceived) { + isHappy = false; + } + } + + @Override + public boolean getMood() { + return isHappy; + } } diff --git a/servant/src/main/java/com/iluwatar/servant/Queen.java b/servant/src/main/java/com/iluwatar/servant/Queen.java index 815106725..3b6203f3e 100644 --- a/servant/src/main/java/com/iluwatar/servant/Queen.java +++ b/servant/src/main/java/com/iluwatar/servant/Queen.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servant; /** @@ -6,39 +28,41 @@ package com.iluwatar.servant; * */ public class Queen implements Royalty { - - private boolean isDrunk = true; - private boolean isHungry; - private boolean isHappy; - private boolean isFlirty = true; - private boolean complimentReceived; - @Override - public void getFed() { - isHungry = false; - } + private boolean isDrunk = true; + private boolean isHungry; + private boolean isHappy; + private boolean isFlirty = true; + private boolean complimentReceived; - @Override - public void getDrink() { - isDrunk = true; - } + @Override + public void getFed() { + isHungry = false; + } - public void receiveCompliments() { - complimentReceived = true; - } + @Override + public void getDrink() { + isDrunk = true; + } - @Override - public void changeMood() { - if (complimentReceived && isFlirty && isDrunk) isHappy = true; - } + public void receiveCompliments() { + complimentReceived = true; + } - @Override - public boolean getMood() { - return isHappy; + @Override + public void changeMood() { + if (complimentReceived && isFlirty && isDrunk && !isHungry) { + isHappy = true; } + } - public void setFlirtiness(boolean f) { - this.isFlirty = f; - } + @Override + public boolean getMood() { + return isHappy; + } + + public void setFlirtiness(boolean f) { + this.isFlirty = f; + } } diff --git a/servant/src/main/java/com/iluwatar/servant/Royalty.java b/servant/src/main/java/com/iluwatar/servant/Royalty.java index 543ffd8d5..b628383c8 100644 --- a/servant/src/main/java/com/iluwatar/servant/Royalty.java +++ b/servant/src/main/java/com/iluwatar/servant/Royalty.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servant; /** @@ -7,13 +29,13 @@ package com.iluwatar.servant; */ interface Royalty { - void getFed(); + void getFed(); - void getDrink(); + void getDrink(); - void changeMood(); + void changeMood(); - void receiveCompliments(); + void receiveCompliments(); - boolean getMood(); + boolean getMood(); } diff --git a/servant/src/main/java/com/iluwatar/servant/Servant.java b/servant/src/main/java/com/iluwatar/servant/Servant.java index 8e61333d8..56d65bde2 100644 --- a/servant/src/main/java/com/iluwatar/servant/Servant.java +++ b/servant/src/main/java/com/iluwatar/servant/Servant.java @@ -1,6 +1,28 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servant; -import java.util.ArrayList; +import java.util.List; /** * @@ -8,30 +30,39 @@ import java.util.ArrayList; * */ public class Servant { - - public String name; - - public Servant(String name){ - this.name = name; - } - public void feed(Royalty r){ - r.getFed(); - } - - public void giveWine(Royalty r){ - r.getDrink(); - } - - public void GiveCompliments(Royalty r){ - r.receiveCompliments(); - } - - public boolean checkIfYouWillBeHanged(ArrayList tableGuests){ - boolean anotherDay = true; - for( Royalty r : tableGuests ) - if( !r.getMood() ) anotherDay = false; - - return anotherDay; - } + public String name; + + /** + * Constructor + */ + public Servant(String name) { + this.name = name; + } + + public void feed(Royalty r) { + r.getFed(); + } + + public void giveWine(Royalty r) { + r.getDrink(); + } + + public void giveCompliments(Royalty r) { + r.receiveCompliments(); + } + + /** + * Check if we will be hanged + */ + public boolean checkIfYouWillBeHanged(List tableGuests) { + boolean anotherDay = true; + for (Royalty r : tableGuests) { + if (!r.getMood()) { + anotherDay = false; + } + } + + return anotherDay; + } } diff --git a/servant/src/test/java/com/iluwatar/servant/AppTest.java b/servant/src/test/java/com/iluwatar/servant/AppTest.java index 94a12b207..a9e66e783 100644 --- a/servant/src/test/java/com/iluwatar/servant/AppTest.java +++ b/servant/src/test/java/com/iluwatar/servant/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servant; import org.junit.Test; -import com.iluwatar.servant.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.servant.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/servant/src/test/java/com/iluwatar/servant/KingTest.java b/servant/src/test/java/com/iluwatar/servant/KingTest.java new file mode 100644 index 000000000..c56d12b24 --- /dev/null +++ b/servant/src/test/java/com/iluwatar/servant/KingTest.java @@ -0,0 +1,104 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servant; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Date: 12/28/15 - 9:40 PM + * + * @author Jeroen Meulemeester + */ +public class KingTest { + + @Test + public void testHungrySoberUncomplimentedKing() { + final King king = new King(); + king.changeMood(); + assertFalse(king.getMood()); + } + + @Test + public void testFedSoberUncomplimentedKing() { + final King king = new King(); + king.getFed(); + king.changeMood(); + assertFalse(king.getMood()); + } + + @Test + public void testHungryDrunkUncomplimentedKing() { + final King king = new King(); + king.getDrink(); + king.changeMood(); + assertFalse(king.getMood()); + } + + @Test + public void testHungrySoberComplimentedKing() { + final King king = new King(); + king.receiveCompliments(); + king.changeMood(); + assertFalse(king.getMood()); + } + + @Test + public void testFedDrunkUncomplimentedKing() { + final King king = new King(); + king.getFed(); + king.getDrink(); + king.changeMood(); + assertTrue(king.getMood()); + } + + @Test + public void testFedSoberComplimentedKing() { + final King king = new King(); + king.getFed(); + king.receiveCompliments(); + king.changeMood(); + assertFalse(king.getMood()); + } + + @Test + public void testFedDrunkComplimentedKing() { + final King king = new King(); + king.getFed(); + king.getDrink(); + king.receiveCompliments(); + king.changeMood(); + assertFalse(king.getMood()); + } + + @Test + public void testHungryDrunkComplimentedKing() { + final King king = new King(); + king.getDrink(); + king.receiveCompliments(); + king.changeMood(); + assertFalse(king.getMood()); + } + +} \ No newline at end of file diff --git a/servant/src/test/java/com/iluwatar/servant/QueenTest.java b/servant/src/test/java/com/iluwatar/servant/QueenTest.java new file mode 100644 index 000000000..85b22fb42 --- /dev/null +++ b/servant/src/test/java/com/iluwatar/servant/QueenTest.java @@ -0,0 +1,68 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servant; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Date: 12/28/15 - 9:52 PM + * + * @author Jeroen Meulemeester + */ +public class QueenTest { + + @Test + public void testNotFlirtyUncomplemented() throws Exception { + final Queen queen = new Queen(); + queen.setFlirtiness(false); + queen.changeMood(); + assertFalse(queen.getMood()); + } + + @Test + public void testNotFlirtyComplemented() throws Exception { + final Queen queen = new Queen(); + queen.setFlirtiness(false); + queen.receiveCompliments(); + queen.changeMood(); + assertFalse(queen.getMood()); + } + + @Test + public void testFlirtyUncomplemented() throws Exception { + final Queen queen = new Queen(); + queen.changeMood(); + assertFalse(queen.getMood()); + } + + @Test + public void testFlirtyComplemented() throws Exception { + final Queen queen = new Queen(); + queen.receiveCompliments(); + queen.changeMood(); + assertTrue(queen.getMood()); + } + +} \ No newline at end of file diff --git a/servant/src/test/java/com/iluwatar/servant/ServantTest.java b/servant/src/test/java/com/iluwatar/servant/ServantTest.java new file mode 100644 index 000000000..e4087d86d --- /dev/null +++ b/servant/src/test/java/com/iluwatar/servant/ServantTest.java @@ -0,0 +1,92 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servant; + +import org.junit.Test; + +import java.util.ArrayList; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +/** + * Date: 12/28/15 - 10:02 PM + * + * @author Jeroen Meulemeester + */ +public class ServantTest { + + @Test + public void testFeed() throws Exception { + final Royalty royalty = mock(Royalty.class); + final Servant servant = new Servant("test"); + servant.feed(royalty); + verify(royalty).getFed(); + verifyNoMoreInteractions(royalty); + } + + @Test + public void testGiveWine() throws Exception { + final Royalty royalty = mock(Royalty.class); + final Servant servant = new Servant("test"); + servant.giveWine(royalty); + verify(royalty).getDrink(); + verifyNoMoreInteractions(royalty); + } + + @Test + public void testGiveCompliments() throws Exception { + final Royalty royalty = mock(Royalty.class); + final Servant servant = new Servant("test"); + servant.giveCompliments(royalty); + verify(royalty).receiveCompliments(); + verifyNoMoreInteractions(royalty); + } + + @Test + public void testCheckIfYouWillBeHanged() throws Exception { + final Royalty goodMoodRoyalty = mock(Royalty.class); + when(goodMoodRoyalty.getMood()).thenReturn(true); + + final Royalty badMoodRoyalty = mock(Royalty.class); + when(badMoodRoyalty.getMood()).thenReturn(true); + + final ArrayList goodCompany = new ArrayList<>(); + goodCompany.add(goodMoodRoyalty); + goodCompany.add(goodMoodRoyalty); + goodCompany.add(goodMoodRoyalty); + + final ArrayList badCompany = new ArrayList<>(); + goodCompany.add(goodMoodRoyalty); + goodCompany.add(goodMoodRoyalty); + goodCompany.add(badMoodRoyalty); + + assertTrue(new Servant("test").checkIfYouWillBeHanged(goodCompany)); + assertTrue(new Servant("test").checkIfYouWillBeHanged(badCompany)); + + } + +} \ No newline at end of file diff --git a/service-layer/index.md b/service-layer/README.md similarity index 83% rename from service-layer/index.md rename to service-layer/README.md index ea3f3d0ba..9b685d4e3 100644 --- a/service-layer/index.md +++ b/service-layer/README.md @@ -4,10 +4,13 @@ title: Service Layer folder: service-layer permalink: /patterns/service-layer/ categories: Architectural -tags: Java +tags: + - Java + - Difficulty-Intermediate --- -**Intent:** Service Layer is an abstraction over domain logic. Typically +## Intent +Service Layer is an abstraction over domain logic. Typically applications require multiple kinds of interfaces to the data they store and logic they implement: data loaders, user interfaces, integration gateways, and others. Despite their different purposes, these interfaces often need common @@ -16,12 +19,13 @@ its business logic. The Service Layer fulfills this role. ![alt text](./etc/service-layer.png "Service Layer") -**Applicability:** Use the Service Layer pattern when +## Applicability +Use the Service Layer pattern when * you want to encapsulate domain logic under API * you need to implement multiple interfaces with common logic and data -**Credits:** +## Credits * [Martin Fowler - Service Layer](http://martinfowler.com/eaaCatalog/serviceLayer.html) * [Patterns of Enterprise Application Architecture](http://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420) diff --git a/service-layer/bin/pom.xml b/service-layer/bin/pom.xml index 40aa93005..05ed6860d 100644 --- a/service-layer/bin/pom.xml +++ b/service-layer/bin/pom.xml @@ -1,4 +1,28 @@ + 4.0.0 diff --git a/service-layer/pom.xml b/service-layer/pom.xml index a42d07c5e..23d788595 100644 --- a/service-layer/pom.xml +++ b/service-layer/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT service-layer @@ -22,5 +46,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/app/App.java b/service-layer/src/main/java/com/iluwatar/servicelayer/app/App.java index 47eb5231e..8282d800c 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/app/App.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/app/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelayer.app; import java.util.List; @@ -16,161 +38,169 @@ import com.iluwatar.servicelayer.wizard.WizardDaoImpl; /** - * Service layer defines an application's boundary with a layer of services that establishes - * a set of available operations and coordinates the application's response in each operation. + * Service layer defines an application's boundary with a layer of services that establishes a set + * of available operations and coordinates the application's response in each operation. *

- * Enterprise applications typically require different kinds of interfaces to the data - * they store and the logic they implement: data loaders, user interfaces, integration gateways, - * and others. Despite their different purposes, these interfaces often need common interactions - * with the application to access and manipulate its data and invoke its business logic. The - * interactions may be complex, involving transactions across multiple resources and the - * coordination of several responses to an action. Encoding the logic of the interactions - * separately in each interface causes a lot of duplication. + * Enterprise applications typically require different kinds of interfaces to the data they store + * and the logic they implement: data loaders, user interfaces, integration gateways, and others. + * Despite their different purposes, these interfaces often need common interactions with the + * application to access and manipulate its data and invoke its business logic. The interactions may + * be complex, involving transactions across multiple resources and the coordination of several + * responses to an action. Encoding the logic of the interactions separately in each interface + * causes a lot of duplication. *

- * The example application demonstrates interactions between a client ({@link App}) and a service - * ({@link MagicService}). The service is implemented with 3-layer architecture (entity, dao, service). - * For persistence the example uses in-memory H2 database which is populated on each application - * startup. + * The example application demonstrates interactions between a client ({@link App}) and a service ( + * {@link MagicService}). The service is implemented with 3-layer architecture (entity, dao, + * service). For persistence the example uses in-memory H2 database which is populated on each + * application startup. * */ public class App { - - /** - * Program entry point - * @param args command line args - */ - public static void main( String[] args ) { - // populate the in-memory database - initData(); - // query the data using the service - queryData(); + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + // populate the in-memory database + initData(); + // query the data using the service + queryData(); + } + + /** + * Initialize data + */ + public static void initData() { + // spells + Spell spell1 = new Spell("Ice dart"); + Spell spell2 = new Spell("Invisibility"); + Spell spell3 = new Spell("Stun bolt"); + Spell spell4 = new Spell("Confusion"); + Spell spell5 = new Spell("Darkness"); + Spell spell6 = new Spell("Fireball"); + Spell spell7 = new Spell("Enchant weapon"); + Spell spell8 = new Spell("Rock armour"); + Spell spell9 = new Spell("Light"); + Spell spell10 = new Spell("Bee swarm"); + Spell spell11 = new Spell("Haste"); + Spell spell12 = new Spell("Levitation"); + Spell spell13 = new Spell("Magic lock"); + Spell spell14 = new Spell("Summon hell bat"); + Spell spell15 = new Spell("Water walking"); + Spell spell16 = new Spell("Magic storm"); + Spell spell17 = new Spell("Entangle"); + SpellDao spellDao = new SpellDaoImpl(); + spellDao.persist(spell1); + spellDao.persist(spell2); + spellDao.persist(spell3); + spellDao.persist(spell4); + spellDao.persist(spell5); + spellDao.persist(spell6); + spellDao.persist(spell7); + spellDao.persist(spell8); + spellDao.persist(spell9); + spellDao.persist(spell10); + spellDao.persist(spell11); + spellDao.persist(spell12); + spellDao.persist(spell13); + spellDao.persist(spell14); + spellDao.persist(spell15); + spellDao.persist(spell16); + spellDao.persist(spell17); + + // spellbooks + SpellbookDao spellbookDao = new SpellbookDaoImpl(); + Spellbook spellbook1 = new Spellbook("Book of Orgymon"); + spellbookDao.persist(spellbook1); + spellbook1.addSpell(spell1); + spellbook1.addSpell(spell2); + spellbook1.addSpell(spell3); + spellbook1.addSpell(spell4); + spellbookDao.merge(spellbook1); + Spellbook spellbook2 = new Spellbook("Book of Aras"); + spellbookDao.persist(spellbook2); + spellbook2.addSpell(spell5); + spellbook2.addSpell(spell6); + spellbookDao.merge(spellbook2); + Spellbook spellbook3 = new Spellbook("Book of Kritior"); + spellbookDao.persist(spellbook3); + spellbook3.addSpell(spell7); + spellbook3.addSpell(spell8); + spellbook3.addSpell(spell9); + spellbookDao.merge(spellbook3); + Spellbook spellbook4 = new Spellbook("Book of Tamaex"); + spellbookDao.persist(spellbook4); + spellbook4.addSpell(spell10); + spellbook4.addSpell(spell11); + spellbook4.addSpell(spell12); + spellbookDao.merge(spellbook4); + Spellbook spellbook5 = new Spellbook("Book of Idores"); + spellbookDao.persist(spellbook5); + spellbook5.addSpell(spell13); + spellbookDao.merge(spellbook5); + Spellbook spellbook6 = new Spellbook("Book of Opaen"); + spellbookDao.persist(spellbook6); + spellbook6.addSpell(spell14); + spellbook6.addSpell(spell15); + spellbookDao.merge(spellbook6); + Spellbook spellbook7 = new Spellbook("Book of Kihione"); + spellbookDao.persist(spellbook7); + spellbook7.addSpell(spell16); + spellbook7.addSpell(spell17); + spellbookDao.merge(spellbook7); + + // wizards + WizardDao wizardDao = new WizardDaoImpl(); + Wizard wizard1 = new Wizard("Aderlard Boud"); + wizardDao.persist(wizard1); + wizard1.addSpellbook(spellbookDao.findByName("Book of Orgymon")); + wizard1.addSpellbook(spellbookDao.findByName("Book of Aras")); + wizardDao.merge(wizard1); + Wizard wizard2 = new Wizard("Anaxis Bajraktari"); + wizardDao.persist(wizard2); + wizard2.addSpellbook(spellbookDao.findByName("Book of Kritior")); + wizard2.addSpellbook(spellbookDao.findByName("Book of Tamaex")); + wizardDao.merge(wizard2); + Wizard wizard3 = new Wizard("Xuban Munoa"); + wizardDao.persist(wizard3); + wizard3.addSpellbook(spellbookDao.findByName("Book of Idores")); + wizard3.addSpellbook(spellbookDao.findByName("Book of Opaen")); + wizardDao.merge(wizard3); + Wizard wizard4 = new Wizard("Blasius Dehooge"); + wizardDao.persist(wizard4); + wizard4.addSpellbook(spellbookDao.findByName("Book of Kihione")); + wizardDao.merge(wizard4); + } + + /** + * Query the data + */ + public static void queryData() { + MagicService service = + new MagicServiceImpl(new WizardDaoImpl(), new SpellbookDaoImpl(), new SpellDaoImpl()); + System.out.println("Enumerating all wizards"); + for (Wizard w : service.findAllWizards()) { + System.out.println(w.getName()); } - - public static void initData() { - // spells - Spell spell1 = new Spell("Ice dart"); - Spell spell2 = new Spell("Invisibility"); - Spell spell3 = new Spell("Stun bolt"); - Spell spell4 = new Spell("Confusion"); - Spell spell5 = new Spell("Darkness"); - Spell spell6 = new Spell("Fireball"); - Spell spell7 = new Spell("Enchant weapon"); - Spell spell8 = new Spell("Rock armour"); - Spell spell9 = new Spell("Light"); - Spell spell10 = new Spell("Bee swarm"); - Spell spell11 = new Spell("Haste"); - Spell spell12 = new Spell("Levitation"); - Spell spell13 = new Spell("Magic lock"); - Spell spell14 = new Spell("Summon hell bat"); - Spell spell15 = new Spell("Water walking"); - Spell spell16 = new Spell("Magic storm"); - Spell spell17 = new Spell("Entangle"); - SpellDao spellDao = new SpellDaoImpl(); - spellDao.persist(spell1); - spellDao.persist(spell2); - spellDao.persist(spell3); - spellDao.persist(spell4); - spellDao.persist(spell5); - spellDao.persist(spell6); - spellDao.persist(spell7); - spellDao.persist(spell8); - spellDao.persist(spell9); - spellDao.persist(spell10); - spellDao.persist(spell11); - spellDao.persist(spell12); - spellDao.persist(spell13); - spellDao.persist(spell14); - spellDao.persist(spell15); - spellDao.persist(spell16); - spellDao.persist(spell17); - - // spellbooks - SpellbookDao spellbookDao = new SpellbookDaoImpl(); - Spellbook spellbook1 = new Spellbook("Book of Orgymon"); - spellbookDao.persist(spellbook1); - spellbook1.addSpell(spell1); - spellbook1.addSpell(spell2); - spellbook1.addSpell(spell3); - spellbook1.addSpell(spell4); - spellbookDao.merge(spellbook1); - Spellbook spellbook2 = new Spellbook("Book of Aras"); - spellbookDao.persist(spellbook2); - spellbook2.addSpell(spell5); - spellbook2.addSpell(spell6); - spellbookDao.merge(spellbook2); - Spellbook spellbook3 = new Spellbook("Book of Kritior"); - spellbookDao.persist(spellbook3); - spellbook3.addSpell(spell7); - spellbook3.addSpell(spell8); - spellbook3.addSpell(spell9); - spellbookDao.merge(spellbook3); - Spellbook spellbook4 = new Spellbook("Book of Tamaex"); - spellbookDao.persist(spellbook4); - spellbook4.addSpell(spell10); - spellbook4.addSpell(spell11); - spellbook4.addSpell(spell12); - spellbookDao.merge(spellbook4); - Spellbook spellbook5 = new Spellbook("Book of Idores"); - spellbookDao.persist(spellbook5); - spellbook5.addSpell(spell13); - spellbookDao.merge(spellbook5); - Spellbook spellbook6 = new Spellbook("Book of Opaen"); - spellbookDao.persist(spellbook6); - spellbook6.addSpell(spell14); - spellbook6.addSpell(spell15); - spellbookDao.merge(spellbook6); - Spellbook spellbook7 = new Spellbook("Book of Kihione"); - spellbookDao.persist(spellbook7); - spellbook7.addSpell(spell16); - spellbook7.addSpell(spell17); - spellbookDao.merge(spellbook7); - - // wizards - WizardDao wizardDao = new WizardDaoImpl(); - Wizard wizard1 = new Wizard("Aderlard Boud"); - wizardDao.persist(wizard1); - wizard1.addSpellbook(spellbookDao.findByName("Book of Orgymon")); - wizard1.addSpellbook(spellbookDao.findByName("Book of Aras")); - wizardDao.merge(wizard1); - Wizard wizard2 = new Wizard("Anaxis Bajraktari"); - wizardDao.persist(wizard2); - wizard2.addSpellbook(spellbookDao.findByName("Book of Kritior")); - wizard2.addSpellbook(spellbookDao.findByName("Book of Tamaex")); - wizardDao.merge(wizard2); - Wizard wizard3 = new Wizard("Xuban Munoa"); - wizardDao.persist(wizard3); - wizard3.addSpellbook(spellbookDao.findByName("Book of Idores")); - wizard3.addSpellbook(spellbookDao.findByName("Book of Opaen")); - wizardDao.merge(wizard3); - Wizard wizard4 = new Wizard("Blasius Dehooge"); - wizardDao.persist(wizard4); - wizard4.addSpellbook(spellbookDao.findByName("Book of Kihione")); - wizardDao.merge(wizard4); + System.out.println("Enumerating all spellbooks"); + for (Spellbook s : service.findAllSpellbooks()) { + System.out.println(s.getName()); } - - public static void queryData() { - MagicService service = new MagicServiceImpl(new WizardDaoImpl(), new SpellbookDaoImpl(), new SpellDaoImpl()); - System.out.println("Enumerating all wizards"); - for (Wizard w: service.findAllWizards()) { - System.out.println(w.getName()); - } - System.out.println("Enumerating all spellbooks"); - for (Spellbook s: service.findAllSpellbooks()) { - System.out.println(s.getName()); - } - System.out.println("Enumerating all spells"); - for (Spell s: service.findAllSpells()) { - System.out.println(s.getName()); - } - System.out.println("Find wizards with spellbook 'Book of Idores'"); - List wizardsWithSpellbook = service.findWizardsWithSpellbook("Book of Idores"); - for (Wizard w: wizardsWithSpellbook) { - System.out.println(String.format("%s has 'Book of Idores'", w.getName())); - } - System.out.println("Find wizards with spell 'Fireball'"); - List wizardsWithSpell = service.findWizardsWithSpell("Fireball"); - for (Wizard w: wizardsWithSpell) { - System.out.println(String.format("%s has 'Fireball'", w.getName())); - } + System.out.println("Enumerating all spells"); + for (Spell s : service.findAllSpells()) { + System.out.println(s.getName()); } + System.out.println("Find wizards with spellbook 'Book of Idores'"); + List wizardsWithSpellbook = service.findWizardsWithSpellbook("Book of Idores"); + for (Wizard w : wizardsWithSpellbook) { + System.out.println(String.format("%s has 'Book of Idores'", w.getName())); + } + System.out.println("Find wizards with spell 'Fireball'"); + List wizardsWithSpell = service.findWizardsWithSpell("Fireball"); + for (Wizard w : wizardsWithSpell) { + System.out.println(String.format("%s has 'Fireball'", w.getName())); + } + } } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/common/BaseEntity.java b/service-layer/src/main/java/com/iluwatar/servicelayer/common/BaseEntity.java index ec0f8e197..53f5f7b0f 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/common/BaseEntity.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/common/BaseEntity.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelayer.common; import javax.persistence.Inheritance; @@ -11,9 +33,38 @@ import javax.persistence.Version; * */ @MappedSuperclass -@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS) -public class BaseEntity { - - @Version - private Long version; +@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS) +public abstract class BaseEntity { + + @Version + private Long version; + + /** + * Indicates the unique id of this entity + * + * @return The id of the entity, or 'null' when not persisted + */ + public abstract Long getId(); + + /** + * Set the id of this entity + * + * @param id The new id + */ + public abstract void setId(Long id); + + /** + * Get the name of this entity + * + * @return The name of the entity + */ + public abstract String getName(); + + /** + * Set the name of this entity + * + * @param name The new name + */ + public abstract void setName(final String name); + } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/common/Dao.java b/service-layer/src/main/java/com/iluwatar/servicelayer/common/Dao.java index e1851b16f..1fed0864b 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/common/Dao.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/common/Dao.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelayer.common; import java.util.List; @@ -11,13 +33,13 @@ import java.util.List; */ public interface Dao { - E find(Long id); - - void persist(E entity); - - E merge(E entity); - - void delete(E entity); - - List findAll(); + E find(Long id); + + void persist(E entity); + + E merge(E entity); + + void delete(E entity); + + List findAll(); } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/common/DaoBaseImpl.java b/service-layer/src/main/java/com/iluwatar/servicelayer/common/DaoBaseImpl.java index bc0116dac..88ac2597d 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/common/DaoBaseImpl.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/common/DaoBaseImpl.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelayer.common; import java.lang.reflect.ParameterizedType; @@ -18,110 +40,110 @@ import com.iluwatar.servicelayer.hibernate.HibernateUtil; * */ public abstract class DaoBaseImpl implements Dao { - - @SuppressWarnings("unchecked") - protected Class persistentClass = (Class) ((ParameterizedType) getClass() - .getGenericSuperclass()).getActualTypeArguments()[0]; - protected Session getSession() { - return HibernateUtil.getSessionFactory().openSession(); - } - - @Override - public E find(Long id) { - Session session = getSession(); - Transaction tx = null; - E result = null; - try { - tx = session.beginTransaction(); - Criteria criteria = session.createCriteria(persistentClass); - criteria.add(Restrictions.idEq(id)); - result = (E) criteria.uniqueResult(); - tx.commit(); - } - catch (Exception e) { - if (tx!=null) tx.rollback(); - throw e; - } - finally { - session.close(); - } - return result; - } - - @Override - public void persist(E entity) { - Session session = getSession(); - Transaction tx = null; - try { - tx = session.beginTransaction(); - session.persist(entity); - tx.commit(); - } - catch (Exception e) { - if (tx!=null) tx.rollback(); - throw e; - } - finally { - session.close(); - } - } - - @Override - public E merge(E entity) { - Session session = getSession(); - Transaction tx = null; - E result = null; - try { - tx = session.beginTransaction(); - result = (E) session.merge(entity); - tx.commit(); - } - catch (Exception e) { - if (tx!=null) tx.rollback(); - throw e; - } - finally { - session.close(); - } - return result; - } - - @Override - public void delete(E entity) { - Session session = getSession(); - Transaction tx = null; - try { - tx = session.beginTransaction(); - session.delete(entity); - tx.commit(); - } - catch (Exception e) { - if (tx!=null) tx.rollback(); - throw e; - } - finally { - session.close(); - } - } - - @Override - public List findAll() { - Session session = getSession(); - Transaction tx = null; - List result = null; - try { - tx = session.beginTransaction(); - Criteria criteria = session.createCriteria(persistentClass); - result = criteria.list(); - } - catch (Exception e) { - if (tx!=null) tx.rollback(); - throw e; - } - finally { - session.close(); - } - return result; - } + @SuppressWarnings("unchecked") + protected Class persistentClass = (Class) ((ParameterizedType) getClass() + .getGenericSuperclass()).getActualTypeArguments()[0]; + + protected Session getSession() { + return HibernateUtil.getSessionFactory().openSession(); + } + + @Override + public E find(Long id) { + Session session = getSession(); + Transaction tx = null; + E result = null; + try { + tx = session.beginTransaction(); + Criteria criteria = session.createCriteria(persistentClass); + criteria.add(Restrictions.idEq(id)); + result = (E) criteria.uniqueResult(); + tx.commit(); + } catch (Exception e) { + if (tx != null) { + tx.rollback(); + } + throw e; + } finally { + session.close(); + } + return result; + } + + @Override + public void persist(E entity) { + Session session = getSession(); + Transaction tx = null; + try { + tx = session.beginTransaction(); + session.persist(entity); + tx.commit(); + } catch (Exception e) { + if (tx != null) { + tx.rollback(); + } + throw e; + } finally { + session.close(); + } + } + + @Override + public E merge(E entity) { + Session session = getSession(); + Transaction tx = null; + E result = null; + try { + tx = session.beginTransaction(); + result = (E) session.merge(entity); + tx.commit(); + } catch (Exception e) { + if (tx != null) { + tx.rollback(); + } + throw e; + } finally { + session.close(); + } + return result; + } + + @Override + public void delete(E entity) { + Session session = getSession(); + Transaction tx = null; + try { + tx = session.beginTransaction(); + session.delete(entity); + tx.commit(); + } catch (Exception e) { + if (tx != null) { + tx.rollback(); + } + throw e; + } finally { + session.close(); + } + } + + @Override + public List findAll() { + Session session = getSession(); + Transaction tx = null; + List result = null; + try { + tx = session.beginTransaction(); + Criteria criteria = session.createCriteria(persistentClass); + result = criteria.list(); + } catch (Exception e) { + if (tx != null) { + tx.rollback(); + } + throw e; + } finally { + session.close(); + } + return result; + } } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/hibernate/HibernateUtil.java b/service-layer/src/main/java/com/iluwatar/servicelayer/hibernate/HibernateUtil.java index 61d0f5d5d..b30b97b65 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/hibernate/HibernateUtil.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/hibernate/HibernateUtil.java @@ -1,40 +1,78 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelayer.hibernate; -import org.hibernate.SessionFactory; -import org.hibernate.cfg.Configuration; - import com.iluwatar.servicelayer.spell.Spell; import com.iluwatar.servicelayer.spellbook.Spellbook; import com.iluwatar.servicelayer.wizard.Wizard; +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; + /** - * * Produces the Hibernate {@link SessionFactory}. - * */ -public class HibernateUtil { +public final class HibernateUtil { - private static final SessionFactory sessionFactory; + /** + * The cached session factory + */ + private static volatile SessionFactory sessionFactory; - static { - try { - sessionFactory = new Configuration() - .addAnnotatedClass(Wizard.class) - .addAnnotatedClass(Spellbook.class) - .addAnnotatedClass(Spell.class) - .setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect") - .setProperty("hibernate.connection.url", "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1") - .setProperty("hibernate.current_session_context_class", "thread") - .setProperty("hibernate.show_sql", "true") - .setProperty("hibernate.hbm2ddl.auto", "create-drop") - .buildSessionFactory(); - } catch (Throwable ex) { - System.err.println("Initial SessionFactory creation failed." + ex); - throw new ExceptionInInitializerError(ex); - } - } + private HibernateUtil() { + } + + /** + * Create the current session factory instance, create a new one when there is none yet. + * + * @return The session factory + */ + public static synchronized SessionFactory getSessionFactory() { + if (sessionFactory == null) { + try { + sessionFactory = + new Configuration().addAnnotatedClass(Wizard.class).addAnnotatedClass(Spellbook.class) + .addAnnotatedClass(Spell.class) + .setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect") + .setProperty("hibernate.connection.url", "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1") + .setProperty("hibernate.current_session_context_class", "thread") + .setProperty("hibernate.show_sql", "true") + .setProperty("hibernate.hbm2ddl.auto", "create-drop").buildSessionFactory(); + } catch (Throwable ex) { + System.err.println("Initial SessionFactory creation failed." + ex); + throw new ExceptionInInitializerError(ex); + } + } + return sessionFactory; + } + + /** + * Drop the current connection, resulting in a create-drop clean database next time. This is + * mainly used for JUnit testing since one test should not influence the other + */ + public static void dropSession() { + getSessionFactory().close(); + sessionFactory = null; + } - public static SessionFactory getSessionFactory() { - return sessionFactory; - } } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/magic/MagicService.java b/service-layer/src/main/java/com/iluwatar/servicelayer/magic/MagicService.java index 38742c78d..c88e1f538 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/magic/MagicService.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/magic/MagicService.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelayer.magic; import java.util.List; @@ -14,13 +36,13 @@ import com.iluwatar.servicelayer.wizard.Wizard; */ public interface MagicService { - List findAllWizards(); + List findAllWizards(); - List findAllSpellbooks(); - - List findAllSpells(); + List findAllSpellbooks(); - List findWizardsWithSpellbook(String name); + List findAllSpells(); - List findWizardsWithSpell(String name); + List findWizardsWithSpellbook(String name); + + List findWizardsWithSpell(String name); } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/magic/MagicServiceImpl.java b/service-layer/src/main/java/com/iluwatar/servicelayer/magic/MagicServiceImpl.java index 37249894a..71756d4cf 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/magic/MagicServiceImpl.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/magic/MagicServiceImpl.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelayer.magic; import java.util.ArrayList; @@ -16,42 +38,45 @@ import com.iluwatar.servicelayer.wizard.WizardDao; * */ public class MagicServiceImpl implements MagicService { - - private WizardDao wizardDao; - private SpellbookDao spellbookDao; - private SpellDao spellDao; - public MagicServiceImpl(WizardDao wizardDao, SpellbookDao spellbookDao, SpellDao spellDao) { - this.wizardDao = wizardDao; - this.spellbookDao = spellbookDao; - this.spellDao = spellDao; - } + private WizardDao wizardDao; + private SpellbookDao spellbookDao; + private SpellDao spellDao; - @Override - public List findAllWizards() { - return wizardDao.findAll(); - } + /** + * Constructor + */ + public MagicServiceImpl(WizardDao wizardDao, SpellbookDao spellbookDao, SpellDao spellDao) { + this.wizardDao = wizardDao; + this.spellbookDao = spellbookDao; + this.spellDao = spellDao; + } - @Override - public List findAllSpellbooks() { - return spellbookDao.findAll(); - } + @Override + public List findAllWizards() { + return wizardDao.findAll(); + } - @Override - public List findAllSpells() { - return spellDao.findAll(); - } + @Override + public List findAllSpellbooks() { + return spellbookDao.findAll(); + } - @Override - public List findWizardsWithSpellbook(String name) { - Spellbook spellbook = spellbookDao.findByName(name); - return new ArrayList(spellbook.getWizards()); - } + @Override + public List findAllSpells() { + return spellDao.findAll(); + } - @Override - public List findWizardsWithSpell(String name) { - Spell spell = spellDao.findByName(name); - Spellbook spellbook = spell.getSpellbook(); - return new ArrayList(spellbook.getWizards()); - } + @Override + public List findWizardsWithSpellbook(String name) { + Spellbook spellbook = spellbookDao.findByName(name); + return new ArrayList<>(spellbook.getWizards()); + } + + @Override + public List findWizardsWithSpell(String name) { + Spell spell = spellDao.findByName(name); + Spellbook spellbook = spell.getSpellbook(); + return new ArrayList<>(spellbook.getWizards()); + } } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/spell/Spell.java b/service-layer/src/main/java/com/iluwatar/servicelayer/spell/Spell.java index ed166eccc..caf1b2c91 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/spell/Spell.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/spell/Spell.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelayer.spell; import javax.persistence.Column; @@ -17,54 +39,53 @@ import com.iluwatar.servicelayer.spellbook.Spellbook; * */ @Entity -@Table(name="SPELL") +@Table(name = "SPELL") public class Spell extends BaseEntity { - - private String name; - - public Spell() { - } - - public Spell(String name) { - this(); - this.name = name; - } - @Id - @GeneratedValue - @Column(name = "SPELL_ID") - private Long id; + private String name; - public Long getId() { - return id; - } + @Id + @GeneratedValue + @Column(name = "SPELL_ID") + private Long id; - public void setId(Long id) { - this.id = id; - } - - @ManyToOne - @JoinColumn(name="SPELLBOOK_ID_FK", referencedColumnName="SPELLBOOK_ID") - private Spellbook spellbook; - - public String getName() { - return name; - } + @ManyToOne + @JoinColumn(name = "SPELLBOOK_ID_FK", referencedColumnName = "SPELLBOOK_ID") + private Spellbook spellbook; - public void setName(String name) { - this.name = name; - } - - public Spellbook getSpellbook() { - return spellbook; - } + public Spell() {} - public void setSpellbook(Spellbook spellbook) { - this.spellbook = spellbook; - } + public Spell(String name) { + this(); + this.name = name; + } + + public Long getId() { + return id; + } - @Override - public String toString() { - return name; - } + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Spellbook getSpellbook() { + return spellbook; + } + + public void setSpellbook(Spellbook spellbook) { + this.spellbook = spellbook; + } + + @Override + public String toString() { + return name; + } } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/spell/SpellDao.java b/service-layer/src/main/java/com/iluwatar/servicelayer/spell/SpellDao.java index a59307e7f..10a35b73a 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/spell/SpellDao.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/spell/SpellDao.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelayer.spell; import com.iluwatar.servicelayer.common.Dao; @@ -8,7 +30,7 @@ import com.iluwatar.servicelayer.common.Dao; * */ public interface SpellDao extends Dao { - - Spell findByName(String name); + + Spell findByName(String name); } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/spell/SpellDaoImpl.java b/service-layer/src/main/java/com/iluwatar/servicelayer/spell/SpellDaoImpl.java index d278defe2..bd1860a6b 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/spell/SpellDaoImpl.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/spell/SpellDaoImpl.java @@ -1,12 +1,34 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelayer.spell; +import com.iluwatar.servicelayer.common.DaoBaseImpl; + import org.hibernate.Criteria; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.criterion.Restrictions; -import com.iluwatar.servicelayer.common.DaoBaseImpl; - /** * * SpellDao implementation. @@ -14,26 +36,25 @@ import com.iluwatar.servicelayer.common.DaoBaseImpl; */ public class SpellDaoImpl extends DaoBaseImpl implements SpellDao { - @Override - public Spell findByName(String name) { - Session session = getSession(); - Transaction tx = null; - Spell result = null; - try { - tx = session.beginTransaction(); - Criteria criteria = session.createCriteria(persistentClass); - criteria.add(Restrictions.eq("name", name)); - result = (Spell) criteria.uniqueResult(); - result.getSpellbook().getWizards().size(); - tx.commit(); - } - catch (Exception e) { - if (tx!=null) tx.rollback(); - throw e; - } - finally { - session.close(); - } - return result; - } + @Override + public Spell findByName(String name) { + Session session = getSession(); + Transaction tx = null; + Spell result = null; + try { + tx = session.beginTransaction(); + Criteria criteria = session.createCriteria(persistentClass); + criteria.add(Restrictions.eq("name", name)); + result = (Spell) criteria.uniqueResult(); + tx.commit(); + } catch (Exception e) { + if (tx != null) { + tx.rollback(); + } + throw e; + } finally { + session.close(); + } + return result; + } } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/Spellbook.java b/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/Spellbook.java index c596c9926..918ce4664 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/Spellbook.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/Spellbook.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelayer.spellbook; import java.util.HashSet; @@ -6,6 +28,7 @@ import java.util.Set; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.ManyToMany; @@ -22,71 +45,71 @@ import com.iluwatar.servicelayer.wizard.Wizard; * */ @Entity -@Table(name="SPELLBOOK") +@Table(name = "SPELLBOOK") public class Spellbook extends BaseEntity { - - public Spellbook() { - spells = new HashSet(); - wizards = new HashSet(); - } - - public Spellbook(String name) { - this(); - this.name = name; - } - @Id - @GeneratedValue - @Column(name = "SPELLBOOK_ID") - private Long id; + @Id + @GeneratedValue + @Column(name = "SPELLBOOK_ID") + private Long id; - public Long getId() { - return id; - } + private String name; - public void setId(Long id) { - this.id = id; - } - - private String name; + @ManyToMany(mappedBy = "spellbooks", fetch = FetchType.EAGER) + private Set wizards; - @ManyToMany(mappedBy = "spellbooks") - private Set wizards; + @OneToMany(mappedBy = "spellbook", orphanRemoval = true, cascade = CascadeType.ALL) + private Set spells; - @OneToMany(mappedBy = "spellbook", orphanRemoval = true, cascade = CascadeType.ALL) - private Set spells; - - public String getName() { - return name; - } + public Spellbook() { + spells = new HashSet<>(); + wizards = new HashSet<>(); + } - public void setName(String name) { - this.name = name; - } + public Spellbook(String name) { + this(); + this.name = name; + } + + public Long getId() { + return id; + } - public Set getWizards() { - return wizards; - } + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } - public void setWizards(Set wizards) { - this.wizards = wizards; - } + public void setName(String name) { + this.name = name; + } - public Set getSpells() { - return spells; - } + public Set getWizards() { + return wizards; + } - public void setSpells(Set spells) { - this.spells = spells; - } + public void setWizards(Set wizards) { + this.wizards = wizards; + } - public void addSpell(Spell spell) { - spell.setSpellbook(this); - spells.add(spell); - } - - @Override - public String toString() { - return name; - } + public Set getSpells() { + return spells; + } + + public void setSpells(Set spells) { + this.spells = spells; + } + + public void addSpell(Spell spell) { + spell.setSpellbook(this); + spells.add(spell); + } + + @Override + public String toString() { + return name; + } } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/SpellbookDao.java b/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/SpellbookDao.java index 9731cdf2e..ddf52cd73 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/SpellbookDao.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/SpellbookDao.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelayer.spellbook; import com.iluwatar.servicelayer.common.Dao; @@ -8,7 +30,7 @@ import com.iluwatar.servicelayer.common.Dao; * */ public interface SpellbookDao extends Dao { - - Spellbook findByName(String name); + + Spellbook findByName(String name); } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/SpellbookDaoImpl.java b/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/SpellbookDaoImpl.java index 5ab604621..3e2859d59 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/SpellbookDaoImpl.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/spellbook/SpellbookDaoImpl.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelayer.spellbook; import org.hibernate.Criteria; @@ -14,28 +36,28 @@ import com.iluwatar.servicelayer.common.DaoBaseImpl; */ public class SpellbookDaoImpl extends DaoBaseImpl implements SpellbookDao { - @Override - public Spellbook findByName(String name) { - Session session = getSession(); - Transaction tx = null; - Spellbook result = null; - try { - tx = session.beginTransaction(); - Criteria criteria = session.createCriteria(persistentClass); - criteria.add(Restrictions.eq("name", name)); - result = (Spellbook) criteria.uniqueResult(); - result.getSpells().size(); - result.getWizards().size(); - tx.commit(); - } - catch (Exception e) { - if (tx!=null) tx.rollback(); - throw e; - } - finally { - session.close(); - } - return result; - } + @Override + public Spellbook findByName(String name) { + Session session = getSession(); + Transaction tx = null; + Spellbook result = null; + try { + tx = session.beginTransaction(); + Criteria criteria = session.createCriteria(persistentClass); + criteria.add(Restrictions.eq("name", name)); + result = (Spellbook) criteria.uniqueResult(); + result.getSpells().size(); + result.getWizards().size(); + tx.commit(); + } catch (Exception e) { + if (tx != null) { + tx.rollback(); + } + throw e; + } finally { + session.close(); + } + return result; + } } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/wizard/Wizard.java b/service-layer/src/main/java/com/iluwatar/servicelayer/wizard/Wizard.java index 10f811a3c..40e0b3ac7 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/wizard/Wizard.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/wizard/Wizard.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelayer.wizard; import java.util.HashSet; @@ -20,59 +42,59 @@ import com.iluwatar.servicelayer.spellbook.Spellbook; * */ @Entity -@Table(name="WIZARD") +@Table(name = "WIZARD") public class Wizard extends BaseEntity { - - public Wizard() { - spellbooks = new HashSet(); - } - - public Wizard(String name) { - this(); - this.name = name; - } - @Id - @GeneratedValue - @Column(name = "WIZARD_ID") - private Long id; + @Id + @GeneratedValue + @Column(name = "WIZARD_ID") + private Long id; - public Long getId() { - return id; - } + private String name; - public void setId(Long id) { - this.id = id; - } - - private String name; + @ManyToMany(cascade = CascadeType.ALL) + private Set spellbooks; - @ManyToMany(cascade = CascadeType.ALL) - private Set spellbooks; - - public String getName() { - return name; - } + public Wizard() { + spellbooks = new HashSet<>(); + } - public void setName(String name) { - this.name = name; - } + public Wizard(String name) { + this(); + this.name = name; + } + + public Long getId() { + return id; + } - public Set getSpellbooks() { - return spellbooks; - } + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } - public void setSpellbooks(Set spellbooks) { - this.spellbooks = spellbooks; - } + public void setName(String name) { + this.name = name; + } - public void addSpellbook(Spellbook spellbook) { - spellbook.getWizards().add(this); - spellbooks.add(spellbook); - } - - @Override - public String toString() { - return name; - } + public Set getSpellbooks() { + return spellbooks; + } + + public void setSpellbooks(Set spellbooks) { + this.spellbooks = spellbooks; + } + + public void addSpellbook(Spellbook spellbook) { + spellbook.getWizards().add(this); + spellbooks.add(spellbook); + } + + @Override + public String toString() { + return name; + } } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/wizard/WizardDao.java b/service-layer/src/main/java/com/iluwatar/servicelayer/wizard/WizardDao.java index f4f415d2f..ddf05fb04 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/wizard/WizardDao.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/wizard/WizardDao.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelayer.wizard; import com.iluwatar.servicelayer.common.Dao; @@ -8,7 +30,7 @@ import com.iluwatar.servicelayer.common.Dao; * */ public interface WizardDao extends Dao { - - Wizard findByName(String name); + + Wizard findByName(String name); } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/wizard/WizardDaoImpl.java b/service-layer/src/main/java/com/iluwatar/servicelayer/wizard/WizardDaoImpl.java index 28c3da85b..8243e3d5d 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/wizard/WizardDaoImpl.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/wizard/WizardDaoImpl.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelayer.wizard; import org.hibernate.Criteria; @@ -15,28 +37,28 @@ import com.iluwatar.servicelayer.spellbook.Spellbook; */ public class WizardDaoImpl extends DaoBaseImpl implements WizardDao { - @Override - public Wizard findByName(String name) { - Session session = getSession(); - Transaction tx = null; - Wizard result = null; - try { - tx = session.beginTransaction(); - Criteria criteria = session.createCriteria(persistentClass); - criteria.add(Restrictions.eq("name", name)); - result = (Wizard) criteria.uniqueResult(); - for (Spellbook s: result.getSpellbooks()) { - s.getSpells().size(); - } - tx.commit(); - } - catch (Exception e) { - if (tx!=null) tx.rollback(); - throw e; - } - finally { - session.close(); - } - return result; - } + @Override + public Wizard findByName(String name) { + Session session = getSession(); + Transaction tx = null; + Wizard result = null; + try { + tx = session.beginTransaction(); + Criteria criteria = session.createCriteria(persistentClass); + criteria.add(Restrictions.eq("name", name)); + result = (Wizard) criteria.uniqueResult(); + for (Spellbook s : result.getSpellbooks()) { + s.getSpells().size(); + } + tx.commit(); + } catch (Exception e) { + if (tx != null) { + tx.rollback(); + } + throw e; + } finally { + session.close(); + } + return result; + } } diff --git a/service-layer/src/test/java/com/iluwatar/servicelayer/app/AppTest.java b/service-layer/src/test/java/com/iluwatar/servicelayer/app/AppTest.java index bbf476c0b..3626c3339 100644 --- a/service-layer/src/test/java/com/iluwatar/servicelayer/app/AppTest.java +++ b/service-layer/src/test/java/com/iluwatar/servicelayer/app/AppTest.java @@ -1,8 +1,31 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelayer.app; -import org.junit.Test; +import com.iluwatar.servicelayer.hibernate.HibernateUtil; -import com.iluwatar.servicelayer.app.App; +import org.junit.After; +import org.junit.Test; /** * @@ -10,10 +33,16 @@ import com.iluwatar.servicelayer.app.App; * */ public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } + + @Test + public void test() { + String[] args = {}; + App.main(args); + } + + @After + public void tearDown() throws Exception { + HibernateUtil.dropSession(); + } + } diff --git a/service-layer/src/test/java/com/iluwatar/servicelayer/common/BaseDaoTest.java b/service-layer/src/test/java/com/iluwatar/servicelayer/common/BaseDaoTest.java new file mode 100644 index 000000000..789ded428 --- /dev/null +++ b/service-layer/src/test/java/com/iluwatar/servicelayer/common/BaseDaoTest.java @@ -0,0 +1,145 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelayer.common; + +import com.iluwatar.servicelayer.hibernate.HibernateUtil; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +/** + * Date: 12/28/15 - 10:53 PM + * + * @author Jeroen Meulemeester + */ +public abstract class BaseDaoTest> { + + /** + * The number of entities stored before each test + */ + private static final int INITIAL_COUNT = 5; + + /** + * The unique id generator, shared between all entities + */ + private static final AtomicInteger ID_GENERATOR = new AtomicInteger(); + + /** + * Factory, used to create new entity instances with the given name + */ + private final Function factory; + + /** + * The tested data access object + */ + private final D dao; + + /** + * Create a new test using the given factory and dao + * + * @param factory The factory, used to create new entity instances with the given name + * @param dao The tested data access object + */ + public BaseDaoTest(final Function factory, final D dao) { + this.factory = factory; + this.dao = dao; + } + + @Before + public void setUp() throws Exception { + for (int i = 0; i < INITIAL_COUNT; i++) { + final String className = dao.persistentClass.getSimpleName(); + final String entityName = String.format("%s%d", className, ID_GENERATOR.incrementAndGet()); + this.dao.persist(this.factory.apply(entityName)); + } + } + + @After + public void tearDown() throws Exception { + HibernateUtil.dropSession(); + } + + protected final D getDao() { + return this.dao; + } + + @Test + public void testFind() throws Exception { + final List all = this.dao.findAll(); + for (final E entity : all) { + final E byId = this.dao.find(entity.getId()); + assertNotNull(byId); + assertEquals(byId.getId(), byId.getId()); + } + } + + @Test + public void testDelete() throws Exception { + final List originalEntities = this.dao.findAll(); + this.dao.delete(originalEntities.get(1)); + this.dao.delete(originalEntities.get(2)); + + final List entitiesLeft = this.dao.findAll(); + assertNotNull(entitiesLeft); + assertEquals(INITIAL_COUNT - 2, entitiesLeft.size()); + } + + @Test + public void testFindAll() throws Exception { + final List all = this.dao.findAll(); + assertNotNull(all); + assertEquals(INITIAL_COUNT, all.size()); + } + + @Test + public void testSetId() throws Exception { + final E entity = this.factory.apply("name"); + assertNull(entity.getId()); + + final Long expectedId = Long.valueOf(1); + entity.setId(expectedId); + assertEquals(expectedId, entity.getId()); + } + + @Test + public void testSetName() throws Exception { + final E entity = this.factory.apply("name"); + assertEquals("name", entity.getName()); + assertEquals("name", entity.toString()); + + final String expectedName = "new name"; + entity.setName(expectedName); + assertEquals(expectedName, entity.getName()); + assertEquals(expectedName, entity.toString()); + } + +} \ No newline at end of file diff --git a/service-layer/src/test/java/com/iluwatar/servicelayer/magic/MagicServiceImplTest.java b/service-layer/src/test/java/com/iluwatar/servicelayer/magic/MagicServiceImplTest.java new file mode 100644 index 000000000..dddc46916 --- /dev/null +++ b/service-layer/src/test/java/com/iluwatar/servicelayer/magic/MagicServiceImplTest.java @@ -0,0 +1,160 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelayer.magic; + +import com.iluwatar.servicelayer.spell.Spell; +import com.iluwatar.servicelayer.spell.SpellDao; +import com.iluwatar.servicelayer.spellbook.Spellbook; +import com.iluwatar.servicelayer.spellbook.SpellbookDao; +import com.iluwatar.servicelayer.wizard.Wizard; +import com.iluwatar.servicelayer.wizard.WizardDao; + +import org.junit.Test; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +/** + * Date: 12/29/15 - 12:06 AM + * + * @author Jeroen Meulemeester + */ +public class MagicServiceImplTest { + + @Test + public void testFindAllWizards() throws Exception { + final WizardDao wizardDao = mock(WizardDao.class); + final SpellbookDao spellbookDao = mock(SpellbookDao.class); + final SpellDao spellDao = mock(SpellDao.class); + + final MagicServiceImpl service = new MagicServiceImpl(wizardDao, spellbookDao, spellDao); + verifyZeroInteractions(wizardDao, spellbookDao, spellDao); + + service.findAllWizards(); + verify(wizardDao).findAll(); + verifyNoMoreInteractions(wizardDao, spellbookDao, spellDao); + } + + @Test + public void testFindAllSpellbooks() throws Exception { + final WizardDao wizardDao = mock(WizardDao.class); + final SpellbookDao spellbookDao = mock(SpellbookDao.class); + final SpellDao spellDao = mock(SpellDao.class); + + final MagicServiceImpl service = new MagicServiceImpl(wizardDao, spellbookDao, spellDao); + verifyZeroInteractions(wizardDao, spellbookDao, spellDao); + + service.findAllSpellbooks(); + verify(spellbookDao).findAll(); + verifyNoMoreInteractions(wizardDao, spellbookDao, spellDao); + } + + @Test + public void testFindAllSpells() throws Exception { + final WizardDao wizardDao = mock(WizardDao.class); + final SpellbookDao spellbookDao = mock(SpellbookDao.class); + final SpellDao spellDao = mock(SpellDao.class); + + final MagicServiceImpl service = new MagicServiceImpl(wizardDao, spellbookDao, spellDao); + verifyZeroInteractions(wizardDao, spellbookDao, spellDao); + + service.findAllSpells(); + verify(spellDao).findAll(); + verifyNoMoreInteractions(wizardDao, spellbookDao, spellDao); + } + + @Test + public void testFindWizardsWithSpellbook() throws Exception { + final String bookname = "bookname"; + final Spellbook spellbook = mock(Spellbook.class); + final Set wizards = new HashSet<>(); + wizards.add(mock(Wizard.class)); + wizards.add(mock(Wizard.class)); + wizards.add(mock(Wizard.class)); + + when(spellbook.getWizards()).thenReturn(wizards); + + final SpellbookDao spellbookDao = mock(SpellbookDao.class); + when(spellbookDao.findByName(eq(bookname))).thenReturn(spellbook); + + final WizardDao wizardDao = mock(WizardDao.class); + final SpellDao spellDao = mock(SpellDao.class); + + + final MagicServiceImpl service = new MagicServiceImpl(wizardDao, spellbookDao, spellDao); + verifyZeroInteractions(wizardDao, spellbookDao, spellDao, spellbook); + + final List result = service.findWizardsWithSpellbook(bookname); + verify(spellbookDao).findByName(eq(bookname)); + verify(spellbook).getWizards(); + + assertNotNull(result); + assertEquals(3, result.size()); + + verifyNoMoreInteractions(wizardDao, spellbookDao, spellDao); + } + + @Test + public void testFindWizardsWithSpell() throws Exception { + final Set wizards = new HashSet<>(); + wizards.add(mock(Wizard.class)); + wizards.add(mock(Wizard.class)); + wizards.add(mock(Wizard.class)); + + final Spellbook spellbook = mock(Spellbook.class); + when(spellbook.getWizards()).thenReturn(wizards); + + final SpellbookDao spellbookDao = mock(SpellbookDao.class); + final WizardDao wizardDao = mock(WizardDao.class); + + final Spell spell = mock(Spell.class); + when(spell.getSpellbook()).thenReturn(spellbook); + + final String spellName = "spellname"; + final SpellDao spellDao = mock(SpellDao.class); + when(spellDao.findByName(eq(spellName))).thenReturn(spell); + + final MagicServiceImpl service = new MagicServiceImpl(wizardDao, spellbookDao, spellDao); + verifyZeroInteractions(wizardDao, spellbookDao, spellDao, spellbook); + + final List result = service.findWizardsWithSpell(spellName); + verify(spellDao).findByName(eq(spellName)); + verify(spellbook).getWizards(); + + assertNotNull(result); + assertEquals(3, result.size()); + + verifyNoMoreInteractions(wizardDao, spellbookDao, spellDao); + } + +} diff --git a/service-layer/src/test/java/com/iluwatar/servicelayer/spell/SpellDaoImplTest.java b/service-layer/src/test/java/com/iluwatar/servicelayer/spell/SpellDaoImplTest.java new file mode 100644 index 000000000..892ec6d2e --- /dev/null +++ b/service-layer/src/test/java/com/iluwatar/servicelayer/spell/SpellDaoImplTest.java @@ -0,0 +1,57 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelayer.spell; + +import com.iluwatar.servicelayer.common.BaseDaoTest; + +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/28/15 - 11:02 PM + * + * @author Jeroen Meulemeester + */ +public class SpellDaoImplTest extends BaseDaoTest { + + public SpellDaoImplTest() { + super(Spell::new, new SpellDaoImpl()); + } + + @Test + public void testFindByName() throws Exception { + final SpellDaoImpl dao = getDao(); + final List allSpells = dao.findAll(); + for (final Spell spell : allSpells) { + final Spell spellByName = dao.findByName(spell.getName()); + assertNotNull(spellByName); + assertEquals(spell.getId(), spellByName.getId()); + assertEquals(spell.getName(), spellByName.getName()); + } + } + +} diff --git a/service-layer/src/test/java/com/iluwatar/servicelayer/spellbook/SpellbookDaoImplTest.java b/service-layer/src/test/java/com/iluwatar/servicelayer/spellbook/SpellbookDaoImplTest.java new file mode 100644 index 000000000..957c07bea --- /dev/null +++ b/service-layer/src/test/java/com/iluwatar/servicelayer/spellbook/SpellbookDaoImplTest.java @@ -0,0 +1,57 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelayer.spellbook; + +import com.iluwatar.servicelayer.common.BaseDaoTest; + +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/28/15 - 11:44 PM + * + * @author Jeroen Meulemeester + */ +public class SpellbookDaoImplTest extends BaseDaoTest { + + public SpellbookDaoImplTest() { + super(Spellbook::new, new SpellbookDaoImpl()); + } + + @Test + public void testFindByName() throws Exception { + final SpellbookDaoImpl dao = getDao(); + final List allBooks = dao.findAll(); + for (final Spellbook book : allBooks) { + final Spellbook spellByName = dao.findByName(book.getName()); + assertNotNull(spellByName); + assertEquals(book.getId(), spellByName.getId()); + assertEquals(book.getName(), spellByName.getName()); + } + } + +} diff --git a/service-layer/src/test/java/com/iluwatar/servicelayer/wizard/WizardDaoImplTest.java b/service-layer/src/test/java/com/iluwatar/servicelayer/wizard/WizardDaoImplTest.java new file mode 100644 index 000000000..8649ee0a0 --- /dev/null +++ b/service-layer/src/test/java/com/iluwatar/servicelayer/wizard/WizardDaoImplTest.java @@ -0,0 +1,57 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelayer.wizard; + +import com.iluwatar.servicelayer.common.BaseDaoTest; + +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/28/15 - 11:46 PM + * + * @author Jeroen Meulemeester + */ +public class WizardDaoImplTest extends BaseDaoTest { + + public WizardDaoImplTest() { + super(Wizard::new, new WizardDaoImpl()); + } + + @Test + public void testFindByName() throws Exception { + final WizardDaoImpl dao = getDao(); + final List allWizards = dao.findAll(); + for (final Wizard spell : allWizards) { + final Wizard byName = dao.findByName(spell.getName()); + assertNotNull(byName); + assertEquals(spell.getId(), byName.getId()); + assertEquals(spell.getName(), byName.getName()); + } + } + +} diff --git a/service-locator/index.md b/service-locator/README.md similarity index 80% rename from service-locator/index.md rename to service-locator/README.md index 03c432749..af4d8c3ac 100644 --- a/service-locator/index.md +++ b/service-locator/README.md @@ -4,15 +4,20 @@ title: Service Locator folder: service-locator permalink: /patterns/service-locator/ categories: Structural -tags: Java +tags: + - Java + - Difficulty-Beginner + - Performance --- -**Intent:** Encapsulate the processes involved in obtaining a service with a +## Intent +Encapsulate the processes involved in obtaining a service with a strong abstraction layer. ![alt text](./etc/service-locator.png "Service Locator") -**Applicability:** The service locator pattern is applicable whenever we want +## Applicability +The service locator pattern is applicable whenever we want to locate/fetch various services using JNDI which, typically, is a redundant and expensive lookup. The service Locator pattern addresses this expensive lookup by making use of caching techniques ie. for the very first time a @@ -21,12 +26,12 @@ the relevant service and then finally caches this service object. Now, further lookups of the same service via Service Locator is done in its cache which improves the performance of application to great extent. -**Typical Use Case:** +## Typical Use Case * when network hits are expensive and time consuming * lookups of services are done quite frequently * large number of services are being used -**Credits:** +## Credits * [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) diff --git a/service-locator/pom.xml b/service-locator/pom.xml index f210c74b6..0f17b3dcc 100644 --- a/service-locator/pom.xml +++ b/service-locator/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT service-locator diff --git a/service-locator/src/main/java/com/iluwatar/servicelocator/App.java b/service-locator/src/main/java/com/iluwatar/servicelocator/App.java index 5b6615495..72612fb21 100644 --- a/service-locator/src/main/java/com/iluwatar/servicelocator/App.java +++ b/service-locator/src/main/java/com/iluwatar/servicelocator/App.java @@ -1,32 +1,56 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelocator; /** * - * The Service Locator pattern is a design pattern used in software development - * to encapsulate the processes involved in obtaining a service with a strong - * abstraction layer. This pattern uses a central registry known as the "service - * locator", which on request returns the information necessary to perform a certain task. + * The Service Locator pattern is a design pattern used in software development to encapsulate the + * processes involved in obtaining a service with a strong abstraction layer. This pattern uses a + * central registry known as the "service locator", which on request returns the information + * necessary to perform a certain task. *

- * In this example we use the Service locator pattern to lookup JNDI-services - * and cache them for subsequent requests. + * In this example we use the Service locator pattern to lookup JNDI-services and cache them for + * subsequent requests. *

+ * * @author saifasif * */ public class App { - - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { - Service service = ServiceLocator.getService("jndi/serviceA"); - service.execute(); - service = ServiceLocator.getService("jndi/serviceB"); - service.execute(); - service = ServiceLocator.getService("jndi/serviceA"); - service.execute(); - service = ServiceLocator.getService("jndi/serviceA"); - service.execute(); - } + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + Service service = ServiceLocator.getService("jndi/serviceA"); + service.execute(); + service = ServiceLocator.getService("jndi/serviceB"); + service.execute(); + service = ServiceLocator.getService("jndi/serviceA"); + service.execute(); + service = ServiceLocator.getService("jndi/serviceA"); + service.execute(); + } } diff --git a/service-locator/src/main/java/com/iluwatar/servicelocator/InitContext.java b/service-locator/src/main/java/com/iluwatar/servicelocator/InitContext.java index 64b65ea1d..8063fc818 100644 --- a/service-locator/src/main/java/com/iluwatar/servicelocator/InitContext.java +++ b/service-locator/src/main/java/com/iluwatar/servicelocator/InitContext.java @@ -1,29 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelocator; /** - * For JNDI lookup of services from the web.xml. Will match name of the service name that - * is being requested and return a newly created service object with the name + * For JNDI lookup of services from the web.xml. Will match name of the service name that is being + * requested and return a newly created service object with the name * * @author saifasif */ public class InitContext { - /** - * Perform the lookup based on the service name. The returned object will need to be - * casted into a {@link Service} - * - * @param serviceName a string - * @return an {@link Object} - */ - public Object lookup(String serviceName) { - if (serviceName.equals("jndi/serviceA")) { - System.out.println("Looking up service A and creating new service for A"); - return new ServiceImpl("jndi/serviceA"); - } else if (serviceName.equals("jndi/serviceB")) { - System.out.println("Looking up service B and creating new service for B"); - return new ServiceImpl("jndi/serviceB"); - } else { - return null; - } + /** + * Perform the lookup based on the service name. The returned object will need to be casted into a + * {@link Service} + * + * @param serviceName a string + * @return an {@link Object} + */ + public Object lookup(String serviceName) { + if (serviceName.equals("jndi/serviceA")) { + System.out.println("Looking up service A and creating new service for A"); + return new ServiceImpl("jndi/serviceA"); + } else if (serviceName.equals("jndi/serviceB")) { + System.out.println("Looking up service B and creating new service for B"); + return new ServiceImpl("jndi/serviceB"); + } else { + return null; } + } } diff --git a/service-locator/src/main/java/com/iluwatar/servicelocator/Service.java b/service-locator/src/main/java/com/iluwatar/servicelocator/Service.java index 4831add95..4f5890bba 100644 --- a/service-locator/src/main/java/com/iluwatar/servicelocator/Service.java +++ b/service-locator/src/main/java/com/iluwatar/servicelocator/Service.java @@ -1,29 +1,48 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelocator; /** - * This is going to be the parent service interface which we will - * use to create our services. All services will have a - *

  • service name
  • - *
  • unique id
  • - *
  • execution work flow
  • + * This is going to be the parent service interface which we will use to create our services. All + * services will have a
  • service name
  • unique id
  • execution work flow
  • * * @author saifasif * */ public interface Service { - - /* - * The human readable name of the service - */ - String getName(); - - /* - * Unique ID of the particular service - */ - int getId(); - - /* - * The workflow method that defines what this service does - */ - void execute(); + + /* + * The human readable name of the service + */ + String getName(); + + /* + * Unique ID of the particular service + */ + int getId(); + + /* + * The workflow method that defines what this service does + */ + void execute(); } diff --git a/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceCache.java b/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceCache.java index c48e4c7af..7e2169fcb 100644 --- a/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceCache.java +++ b/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceCache.java @@ -1,47 +1,70 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelocator; import java.util.HashMap; import java.util.Map; /** - * The service cache implementation which will cache services that are being created. - * On first hit, the cache will be empty and thus any service that is being requested, will be - * created fresh and then placed into the cache map. On next hit, if same service name will - * be requested, it will be returned from the cache + * The service cache implementation which will cache services that are being created. On first hit, + * the cache will be empty and thus any service that is being requested, will be created fresh and + * then placed into the cache map. On next hit, if same service name will be requested, it will be + * returned from the cache * * @author saifasif */ public class ServiceCache { - private final Map serviceCache; + private final Map serviceCache; - public ServiceCache() { - serviceCache = new HashMap(); - } + public ServiceCache() { + serviceCache = new HashMap<>(); + } - /** - * Get the service from the cache. null if no service is found matching the name - * - * @param serviceName a string - * @return {@link Service} - */ - public Service getService(String serviceName) { - Service cachedService = null; - for (String serviceJndiName : serviceCache.keySet()) { - if (serviceJndiName.equals(serviceName)) { - cachedService = serviceCache.get(serviceJndiName); - System.out.println("(cache call) Fetched service " + cachedService.getName() + "(" + cachedService.getId() + ") from cache... !"); - } - } - return cachedService; + /** + * Get the service from the cache. null if no service is found matching the name + * + * @param serviceName a string + * @return {@link Service} + */ + public Service getService(String serviceName) { + Service cachedService = null; + for (String serviceJndiName : serviceCache.keySet()) { + if (serviceJndiName.equals(serviceName)) { + cachedService = serviceCache.get(serviceJndiName); + System.out.println("(cache call) Fetched service " + cachedService.getName() + "(" + + cachedService.getId() + ") from cache... !"); + } } + return cachedService; + } - /** - * Adds the service into the cache map - * - * @param newService a {@link Service} - */ - public void addService(Service newService) { - serviceCache.put(newService.getName(), newService); - } + /** + * Adds the service into the cache map + * + * @param newService a {@link Service} + */ + public void addService(Service newService) { + serviceCache.put(newService.getName(), newService); + } } diff --git a/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceImpl.java b/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceImpl.java index ec7ab215b..543fb8480 100644 --- a/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceImpl.java +++ b/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceImpl.java @@ -1,37 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelocator; /** - * This is a single service implementation of a sample service. This is the actual - * service that will process the request. The reference for this service is to - * be looked upon in the JNDI server that can be set in the web.xml deployment descriptor + * This is a single service implementation of a sample service. This is the actual service that will + * process the request. The reference for this service is to be looked upon in the JNDI server that + * can be set in the web.xml deployment descriptor * * @author saifasif */ public class ServiceImpl implements Service { - private final String serviceName; - private final int id; + private final String serviceName; + private final int id; - public ServiceImpl(String serviceName) { - // set the service name - this.serviceName = serviceName; + /** + * Constructor + */ + public ServiceImpl(String serviceName) { + // set the service name + this.serviceName = serviceName; - // Generate a random id to this service object - this.id = (int) Math.floor(Math.random() * 1000) + 1; - } + // Generate a random id to this service object + this.id = (int) Math.floor(Math.random() * 1000) + 1; + } - @Override - public String getName() { - return serviceName; - } + @Override + public String getName() { + return serviceName; + } - @Override - public int getId() { - return id; - } + @Override + public int getId() { + return id; + } - @Override - public void execute() { - System.out.println("Service " + getName() + " is now executing with id " + getId()); - } + @Override + public void execute() { + System.out.println("Service " + getName() + " is now executing with id " + getId()); + } } diff --git a/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceLocator.java b/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceLocator.java index 95df24926..4d356a567 100644 --- a/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceLocator.java +++ b/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceLocator.java @@ -1,36 +1,63 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelocator; /** - * The service locator module. - * Will fetch service from cache, otherwise creates a fresh service and update cache + * The service locator module. Will fetch service from cache, otherwise creates a fresh service and + * update cache * * @author saifasif */ -public class ServiceLocator { +public final class ServiceLocator { - private static ServiceCache serviceCache = new ServiceCache(); + private static ServiceCache serviceCache = new ServiceCache(); - /** - * Fetch the service with the name param from the cache first, - * if no service is found, lookup the service from the {@link InitContext} and - * then add the newly created service into the cache map for future requests. - * - * @param serviceJndiName a string - * @return {@link Service} - */ - public static Service getService(String serviceJndiName) { - Service serviceObj = serviceCache.getService(serviceJndiName); - if (serviceObj != null) { - return serviceObj; - } else { - /* - * If we are unable to retrive anything from cache, then - * lookup the service and add it in the cache map - */ - InitContext ctx = new InitContext(); - serviceObj = (Service) ctx.lookup(serviceJndiName); - serviceCache.addService(serviceObj); - return serviceObj; - } + private ServiceLocator() { + } + + /** + * Fetch the service with the name param from the cache first, if no service is found, lookup the + * service from the {@link InitContext} and then add the newly created service into the cache map + * for future requests. + * + * @param serviceJndiName a string + * @return {@link Service} + */ + public static Service getService(String serviceJndiName) { + Service serviceObj = serviceCache.getService(serviceJndiName); + if (serviceObj != null) { + return serviceObj; + } else { + /* + * If we are unable to retrive anything from cache, then lookup the service and add it in the + * cache map + */ + InitContext ctx = new InitContext(); + serviceObj = (Service) ctx.lookup(serviceJndiName); + if (serviceObj != null) { // Only cache a service if it actually exists + serviceCache.addService(serviceObj); + } + return serviceObj; } + } } diff --git a/service-locator/src/test/java/com/iluwatar/servicelocator/AppTest.java b/service-locator/src/test/java/com/iluwatar/servicelocator/AppTest.java index 90e091905..40e3820e9 100644 --- a/service-locator/src/test/java/com/iluwatar/servicelocator/AppTest.java +++ b/service-locator/src/test/java/com/iluwatar/servicelocator/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelocator; import org.junit.Test; -import com.iluwatar.servicelocator.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.servicelocator.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/service-locator/src/test/java/com/iluwatar/servicelocator/ServiceLocatorTest.java b/service-locator/src/test/java/com/iluwatar/servicelocator/ServiceLocatorTest.java new file mode 100644 index 000000000..b9f25ff44 --- /dev/null +++ b/service-locator/src/test/java/com/iluwatar/servicelocator/ServiceLocatorTest.java @@ -0,0 +1,68 @@ +/** + * The MIT License + * Copyright (c) 2014 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.servicelocator; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +/** + * Date: 12/29/15 - 19:07 PM + * + * @author Jeroen Meulemeester + */ +public class ServiceLocatorTest { + + /** + * Verify if we just receive 'null' when requesting a non-existing service + */ + @Test + public void testGetNonExistentService() { + assertNull(ServiceLocator.getService("fantastic/unicorn/service")); + assertNull(ServiceLocator.getService("another/fantastic/unicorn/service")); + } + + /** + * Verify if we get the same cached instance when requesting the same service twice + */ + @Test + public void testServiceCache() { + final String[] serviceNames = new String[]{ + "jndi/serviceA", "jndi/serviceB" + }; + + for (final String serviceName : serviceNames) { + final Service service = ServiceLocator.getService(serviceName); + assertNotNull(service); + assertEquals(serviceName, service.getName()); + assertTrue(service.getId() > 0); // The id is generated randomly, but the minimum value is '1' + assertSame(service, ServiceLocator.getService(serviceName)); + } + + } + +} \ No newline at end of file diff --git a/singleton/index.md b/singleton/README.md similarity index 74% rename from singleton/index.md rename to singleton/README.md index 18c137448..2a481f5c8 100644 --- a/singleton/index.md +++ b/singleton/README.md @@ -10,26 +10,29 @@ tags: - Difficulty-Beginner --- -**Intent:** Ensure a class only has one instance, and provide a global point of +## Intent +Ensure a class only has one instance, and provide a global point of access to it. ![alt text](./etc/singleton_1.png "Singleton") -**Applicability:** Use the Singleton pattern when +## Applicability +Use the Singleton pattern when * there must be exactly one instance of a class, and it must be accessible to clients from a well-known access point * when the sole instance should be extensible by subclassing, and clients should be able to use an extended instance without modifying their code -**Typical Use Case:** +## Typical Use Case * the logging class * managing a connection to a database * file manager -**Real world examples:** +## Real world examples * [java.lang.Runtime#getRuntime()](http://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#getRuntime%28%29) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) +* [Effective Java (2nd Edition)](http://www.amazon.com/Effective-Java-Edition-Joshua-Bloch/dp/0321356683) diff --git a/singleton/pom.xml b/singleton/pom.xml index ab118a1c3..ab9405c5e 100644 --- a/singleton/pom.xml +++ b/singleton/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT singleton diff --git a/singleton/src/main/java/com/iluwatar/singleton/App.java b/singleton/src/main/java/com/iluwatar/singleton/App.java index 7566c9c4d..4b505085a 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/App.java +++ b/singleton/src/main/java/com/iluwatar/singleton/App.java @@ -1,35 +1,62 @@ +/** + * The MIT License + * Copyright (c) 2014 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.singleton; /** - * Singleton pattern ensures that the class can have only one existing instance per Java classloader instance - * and provides global access to it. + * Singleton pattern ensures that the class can have only one existing instance per Java classloader + * instance and provides global access to it. *

    - * One of the risks of this pattern is that bugs resulting from setting a singleton up in - * a distributed environment can be tricky to debug, since it will work fine if you - * debug with a single classloader. Additionally, these problems can crop up a while - * after the implementation of a singleton, since they may start out synchronous and - * only become async with time, so you it may not be clear why you are seeing certain - * changes in behaviour. + * One of the risks of this pattern is that bugs resulting from setting a singleton up in a + * distributed environment can be tricky to debug, since it will work fine if you debug with a + * single classloader. Additionally, these problems can crop up a while after the implementation of + * a singleton, since they may start out synchronous and only become async with time, so you it may + * not be clear why you are seeing certain changes in behaviour. *

    - * There are many ways to implement the Singleton. The first one is the eagerly initialized instance in - * {@link IvoryTower}. Eager initialization implies that the implementation is thread safe. If you can - * afford giving up control of the instantiation moment, then this implementation will suit you fine. + * There are many ways to implement the Singleton. The first one is the eagerly initialized instance + * in {@link IvoryTower}. Eager initialization implies that the implementation is thread safe. If + * you can afford giving up control of the instantiation moment, then this implementation will suit + * you fine. *

    - * The other option to implement eagerly initialized Singleton is enum based Singleton. The example is - * found in {@link EnumIvoryTower}. At first glance the code looks short and simple. However, you should - * be aware of the downsides including committing to implementation strategy, extending the enum class, - * serializability and restrictions to coding. These are extensively discussed in Stack Overflow: - * http://programmers.stackexchange.com/questions/179386/what-are-the-downsides-of-implementing-a-singleton-with-javas-enum + * The other option to implement eagerly initialized Singleton is enum based Singleton. The example + * is found in {@link EnumIvoryTower}. At first glance the code looks short and simple. However, you + * should be aware of the downsides including committing to implementation strategy, extending the + * enum class, serializability and restrictions to coding. These are extensively discussed in Stack + * Overflow: + * http://programmers.stackexchange.com/questions/179386/what-are-the-downsides-of-implementing + * -a-singleton-with-javas-enum *

    - * {@link ThreadSafeLazyLoadedIvoryTower} is a Singleton implementation that is initialized on demand. - * The downside is that it is very slow to access since the whole access method is synchronized. + * {@link ThreadSafeLazyLoadedIvoryTower} is a Singleton implementation that is initialized on + * demand. The downside is that it is very slow to access since the whole access method is + * synchronized. *

    - * Another Singleton implementation that is initialized on demand is found in {@link ThreadSafeDoubleCheckLocking}. It - * is somewhat faster than {@link ThreadSafeLazyLoadedIvoryTower} since it doesn't synchronize the whole access method - * but only the method internals on specific conditions. + * Another Singleton implementation that is initialized on demand is found in + * {@link ThreadSafeDoubleCheckLocking}. It is somewhat faster than + * {@link ThreadSafeLazyLoadedIvoryTower} since it doesn't synchronize the whole access method but + * only the method internals on specific conditions. *

    - * Yet another way to implement thread safe lazily initialized Singleton can be found in {@link InitializingOnDemandHolderIdiom}. - * However, this implementation requires at least Java 8 API level to work. + * Yet another way to implement thread safe lazily initialized Singleton can be found in + * {@link InitializingOnDemandHolderIdiom}. However, this implementation requires at least Java 8 + * API level to work. */ public class App { @@ -47,10 +74,10 @@ public class App { System.out.println("ivoryTower2=" + ivoryTower2); // lazily initialized singleton - ThreadSafeLazyLoadedIvoryTower threadSafeIvoryTower1 = ThreadSafeLazyLoadedIvoryTower - .getInstance(); - ThreadSafeLazyLoadedIvoryTower threadSafeIvoryTower2 = ThreadSafeLazyLoadedIvoryTower - .getInstance(); + ThreadSafeLazyLoadedIvoryTower threadSafeIvoryTower1 = + ThreadSafeLazyLoadedIvoryTower.getInstance(); + ThreadSafeLazyLoadedIvoryTower threadSafeIvoryTower2 = + ThreadSafeLazyLoadedIvoryTower.getInstance(); System.out.println("threadSafeIvoryTower1=" + threadSafeIvoryTower1); System.out.println("threadSafeIvoryTower2=" + threadSafeIvoryTower2); @@ -65,13 +92,13 @@ public class App { System.out.println(dcl1); ThreadSafeDoubleCheckLocking dcl2 = ThreadSafeDoubleCheckLocking.getInstance(); System.out.println(dcl2); - + // initialize on demand holder idiom InitializingOnDemandHolderIdiom demandHolderIdiom = - InitializingOnDemandHolderIdiom.getInstance(); + InitializingOnDemandHolderIdiom.getInstance(); System.out.println(demandHolderIdiom); InitializingOnDemandHolderIdiom demandHolderIdiom2 = - InitializingOnDemandHolderIdiom.getInstance(); + InitializingOnDemandHolderIdiom.getInstance(); System.out.println(demandHolderIdiom2); } } diff --git a/singleton/src/main/java/com/iluwatar/singleton/EnumIvoryTower.java b/singleton/src/main/java/com/iluwatar/singleton/EnumIvoryTower.java index f39babe45..eea1cd8cb 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/EnumIvoryTower.java +++ b/singleton/src/main/java/com/iluwatar/singleton/EnumIvoryTower.java @@ -1,8 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.singleton; /** - * Enum based singleton implementation. - * Effective Java 2nd Edition (Joshua Bloch) p. 18 + * Enum based singleton implementation. Effective Java 2nd Edition (Joshua Bloch) p. 18 */ public enum EnumIvoryTower { diff --git a/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java b/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java index 88738b8ca..4aa6afe12 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java +++ b/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java @@ -1,36 +1,59 @@ +/** + * The MIT License + * Copyright (c) 2014 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.singleton; -import java.io.Serializable; - /** - * The Initialize-on-demand-holder idiom is a secure way of - * creating lazy initialized singleton object in Java. - * refer to "The CERT Oracle Secure Coding Standard for Java" - * By Dhruv Mohindra, Robert C. Seacord p.378 - *

    - * Singleton objects usually are heavy to create and sometimes need to serialize them. - * This class also shows how to preserve singleton in serialized version of singleton. + * The Initialize-on-demand-holder idiom is a secure way of creating a lazy initialized singleton + * object in Java. + *

    + * The technique is is as lazy as possible and works in all known versions of Java. It takes advantage + * of language guarantees about class initialization, and will therefore work correctly in all + * Java-compliant compilers and virtual machines. + *

    + * The inner class is referenced no earlier (and therefore loaded no earlier by the class loader) than + * the moment that getInstance() is called. Thus, this solution is thread-safe without requiring special + * language constructs (i.e. volatile or synchronized). * - * @author mortezaadi@gmail.com */ -public class InitializingOnDemandHolderIdiom implements Serializable { +public final class InitializingOnDemandHolderIdiom { - private static final long serialVersionUID = 1L; - - private InitializingOnDemandHolderIdiom() { - } + /** + * Private constructor. + */ + private InitializingOnDemandHolderIdiom() {} + /** + * @return Singleton instance + */ public static InitializingOnDemandHolderIdiom getInstance() { return HelperHolder.INSTANCE; } - protected Object readResolve() { - return getInstance(); - } - + /** + * Provides the lazy-loaded Singleton instance. + */ private static class HelperHolder { public static final InitializingOnDemandHolderIdiom INSTANCE = - new InitializingOnDemandHolderIdiom(); + new InitializingOnDemandHolderIdiom(); } - } diff --git a/singleton/src/main/java/com/iluwatar/singleton/IvoryTower.java b/singleton/src/main/java/com/iluwatar/singleton/IvoryTower.java index 7470c3f29..1dbffa00b 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/IvoryTower.java +++ b/singleton/src/main/java/com/iluwatar/singleton/IvoryTower.java @@ -1,30 +1,48 @@ +/** + * The MIT License + * Copyright (c) 2014 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.singleton; /** - * Singleton class. - * Eagerly initialized static instance guarantees thread - * safety. + * Singleton class. Eagerly initialized static instance guarantees thread safety. */ public final class IvoryTower { /** * Static to class instance of the class. */ - private static final IvoryTower instance = new IvoryTower(); + private static final IvoryTower INSTANCE = new IvoryTower(); /** * Private constructor so nobody can instantiate the class. */ - private IvoryTower() { - } + private IvoryTower() {} /** - * To be called by user to - * obtain instance of the class. + * To be called by user to obtain instance of the class. * * @return instance of the singleton. */ public static IvoryTower getInstance() { - return instance; + return INSTANCE; } } diff --git a/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLocking.java b/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLocking.java index 26b57d4cf..50203609c 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLocking.java +++ b/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLocking.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.singleton; /** @@ -9,16 +31,16 @@ package com.iluwatar.singleton; * * @author mortezaadi@gmail.com */ -public class ThreadSafeDoubleCheckLocking { +public final class ThreadSafeDoubleCheckLocking { - private static volatile ThreadSafeDoubleCheckLocking INSTANCE; + private static volatile ThreadSafeDoubleCheckLocking instance; /** * private constructor to prevent client from instantiating. */ private ThreadSafeDoubleCheckLocking() { - //to prevent instantiating by Reflection call - if (INSTANCE != null) { + // to prevent instantiating by Reflection call + if (instance != null) { throw new IllegalStateException("Already initialized."); } } @@ -29,14 +51,14 @@ public class ThreadSafeDoubleCheckLocking { * @return an instance of the class. */ public static ThreadSafeDoubleCheckLocking getInstance() { - //local variable increases performance by 25 percent - //Joshua Bloch "Effective Java, Second Edition", p. 283-284 - ThreadSafeDoubleCheckLocking result = INSTANCE; + // local variable increases performance by 25 percent + // Joshua Bloch "Effective Java, Second Edition", p. 283-284 + ThreadSafeDoubleCheckLocking result = instance; if (result == null) { synchronized (ThreadSafeDoubleCheckLocking.class) { - result = INSTANCE; + result = instance; if (result == null) { - INSTANCE = result = new ThreadSafeDoubleCheckLocking(); + instance = result = new ThreadSafeDoubleCheckLocking(); } } } diff --git a/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java b/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java index c50c99e65..472325786 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java +++ b/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java @@ -1,24 +1,44 @@ +/** + * The MIT License + * Copyright (c) 2014 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.singleton; /** - * Thread-safe Singleton class. - * The instance is lazily initialized and thus needs synchronization + * Thread-safe Singleton class. The instance is lazily initialized and thus needs synchronization * mechanism. * - * Note: if created by reflection then a singleton will not be created but multiple options in the same classloader + * Note: if created by reflection then a singleton will not be created but multiple options in the + * same classloader */ -public class ThreadSafeLazyLoadedIvoryTower { +public final class ThreadSafeLazyLoadedIvoryTower { - private static ThreadSafeLazyLoadedIvoryTower instance = null; + private static ThreadSafeLazyLoadedIvoryTower instance; - private ThreadSafeLazyLoadedIvoryTower() { - } + private ThreadSafeLazyLoadedIvoryTower() {} /** - * The instance gets created only when it is called for first time. - * Lazy-loading + * The instance gets created only when it is called for first time. Lazy-loading */ - public synchronized static ThreadSafeLazyLoadedIvoryTower getInstance() { + public static synchronized ThreadSafeLazyLoadedIvoryTower getInstance() { if (instance == null) { instance = new ThreadSafeLazyLoadedIvoryTower(); diff --git a/singleton/src/test/java/com/iluwatar/singleton/AppTest.java b/singleton/src/test/java/com/iluwatar/singleton/AppTest.java index c83232037..c2def43a0 100644 --- a/singleton/src/test/java/com/iluwatar/singleton/AppTest.java +++ b/singleton/src/test/java/com/iluwatar/singleton/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.singleton; import org.junit.Test; -import com.iluwatar.singleton.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.singleton.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/singleton/src/test/java/com/iluwatar/singleton/EnumIvoryTowerTest.java b/singleton/src/test/java/com/iluwatar/singleton/EnumIvoryTowerTest.java new file mode 100644 index 000000000..ff821c6eb --- /dev/null +++ b/singleton/src/test/java/com/iluwatar/singleton/EnumIvoryTowerTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 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.singleton; + +/** + * Date: 12/29/15 - 19:20 PM + * + * @author Jeroen Meulemeester + */ +public class EnumIvoryTowerTest extends SingletonTest { + + /** + * Create a new singleton test instance using the given 'getInstance' method + */ + public EnumIvoryTowerTest() { + super(() -> EnumIvoryTower.INSTANCE); + } + +} diff --git a/singleton/src/test/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiomTest.java b/singleton/src/test/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiomTest.java new file mode 100644 index 000000000..1aacb3d08 --- /dev/null +++ b/singleton/src/test/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiomTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 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.singleton; + +/** + * Date: 12/29/15 - 19:22 PM + * + * @author Jeroen Meulemeester + */ +public class InitializingOnDemandHolderIdiomTest extends SingletonTest { + + /** + * Create a new singleton test instance using the given 'getInstance' method + */ + public InitializingOnDemandHolderIdiomTest() { + super(InitializingOnDemandHolderIdiom::getInstance); + } + +} \ No newline at end of file diff --git a/singleton/src/test/java/com/iluwatar/singleton/IvoryTowerTest.java b/singleton/src/test/java/com/iluwatar/singleton/IvoryTowerTest.java new file mode 100644 index 000000000..67769d87d --- /dev/null +++ b/singleton/src/test/java/com/iluwatar/singleton/IvoryTowerTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 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.singleton; + +/** + * Date: 12/29/15 - 19:23 PM + * + * @author Jeroen Meulemeester + */ +public class IvoryTowerTest extends SingletonTest { + + /** + * Create a new singleton test instance using the given 'getInstance' method + */ + public IvoryTowerTest() { + super(IvoryTower::getInstance); + } + +} \ No newline at end of file diff --git a/singleton/src/test/java/com/iluwatar/singleton/LazyLoadedSingletonThreadSafetyTest.java b/singleton/src/test/java/com/iluwatar/singleton/LazyLoadedSingletonThreadSafetyTest.java deleted file mode 100644 index 07f99005e..000000000 --- a/singleton/src/test/java/com/iluwatar/singleton/LazyLoadedSingletonThreadSafetyTest.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.iluwatar.singleton; - -import org.junit.Test; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.*; - -import static org.junit.Assert.assertEquals; - -/** - * This class provides several test case that test singleton construction. - * - * The first proves that multiple calls to the singleton getInstance object are the same when called in the SAME thread. - * The second proves that multiple calls to the singleton getInstance object are the same when called in the DIFFERENT thread. - * - */ -public class LazyLoadedSingletonThreadSafetyTest { - - private static final int NUM_THREADS = 5; - private List threadObjects = Collections.synchronizedList(new ArrayList<>()); - - //NullObject class so Callable has to return something - private class NullObject{private NullObject(){}} - - @Test - public void test_MultipleCallsReturnTheSameObjectInSameThread() { - //Create several instances in the same calling thread - ThreadSafeLazyLoadedIvoryTower instance1 = ThreadSafeLazyLoadedIvoryTower.getInstance(); - ThreadSafeLazyLoadedIvoryTower instance2 = ThreadSafeLazyLoadedIvoryTower.getInstance(); - ThreadSafeLazyLoadedIvoryTower instance3 = ThreadSafeLazyLoadedIvoryTower.getInstance(); - //now check they are equal - assertEquals(instance1, instance1); - assertEquals(instance1, instance2); - assertEquals(instance2, instance3); - assertEquals(instance1, instance3); - } - - @Test - public void test_MultipleCallsReturnTheSameObjectInDifferentThreads() throws InterruptedException, ExecutionException { - {//create several threads and inside each callable instantiate the singleton class - ExecutorService executorService = Executors.newSingleThreadExecutor(); - - List> threadList = new ArrayList<>(); - for (int i = 0; i < NUM_THREADS; i++) { - threadList.add(new SingletonCreatingThread()); - } - - ExecutorService service = Executors.newCachedThreadPool(); - List> results = service.invokeAll(threadList); - - //wait for all of the threads to complete - for (Future res : results) { - res.get(); - } - - //tidy up the executor - executorService.shutdown(); - } - {//now check the contents that were added to threadObjects by each thread - assertEquals(NUM_THREADS, threadObjects.size()); - assertEquals(threadObjects.get(0), threadObjects.get(1)); - assertEquals(threadObjects.get(1), threadObjects.get(2)); - assertEquals(threadObjects.get(2), threadObjects.get(3)); - assertEquals(threadObjects.get(3), threadObjects.get(4)); - } - } - - private class SingletonCreatingThread implements Callable { - @Override - public NullObject call() { - //instantiate the thread safety class and add to list to test afterwards - ThreadSafeLazyLoadedIvoryTower instance = ThreadSafeLazyLoadedIvoryTower.getInstance(); - threadObjects.add(instance); - return new NullObject();//return null object (cannot return Void) - } - } -} diff --git a/singleton/src/test/java/com/iluwatar/singleton/SingletonTest.java b/singleton/src/test/java/com/iluwatar/singleton/SingletonTest.java new file mode 100644 index 000000000..0d9d0aee9 --- /dev/null +++ b/singleton/src/test/java/com/iluwatar/singleton/SingletonTest.java @@ -0,0 +1,110 @@ +/** + * The MIT License + * Copyright (c) 2014 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.singleton; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.function.Supplier; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; + +/** + * This class provides several test case that test singleton construction. + * + * The first proves that multiple calls to the singleton getInstance object are the same when called + * in the SAME thread. The second proves that multiple calls to the singleton getInstance object are + * the same when called in the DIFFERENT thread. + * + * Date: 12/29/15 - 19:25 PM + * + * @author Jeroen Meulemeester + * @author Richard Jones + */ +public abstract class SingletonTest { + + /** + * The singleton's getInstance method + */ + private final Supplier singletonInstanceMethod; + + /** + * Create a new singleton test instance using the given 'getInstance' method + * + * @param singletonInstanceMethod The singleton's getInstance method + */ + public SingletonTest(final Supplier singletonInstanceMethod) { + this.singletonInstanceMethod = singletonInstanceMethod; + } + + /** + * Test the singleton in a non-concurrent setting + */ + @Test + public void testMultipleCallsReturnTheSameObjectInSameThread() { + // Create several instances in the same calling thread + S instance1 = this.singletonInstanceMethod.get(); + S instance2 = this.singletonInstanceMethod.get(); + S instance3 = this.singletonInstanceMethod.get(); + // now check they are equal + assertSame(instance1, instance2); + assertSame(instance1, instance3); + assertSame(instance2, instance3); + } + + /** + * Test singleton instance in a concurrent setting + */ + @Test(timeout = 10000) + public void testMultipleCallsReturnTheSameObjectInDifferentThreads() throws Exception { + + // Create 10000 tasks and inside each callable instantiate the singleton class + final List> tasks = new ArrayList<>(); + for (int i = 0; i < 10000; i++) { + tasks.add(this.singletonInstanceMethod::get); + } + + // Use up to 8 concurrent threads to handle the tasks + final ExecutorService executorService = Executors.newFixedThreadPool(8); + final List> results = executorService.invokeAll(tasks); + + // wait for all of the threads to complete + final S expectedInstance = this.singletonInstanceMethod.get(); + for (Future res : results) { + final S instance = res.get(); + assertNotNull(instance); + assertSame(expectedInstance, instance); + } + + // tidy up the executor + executorService.shutdown(); + + } + +} diff --git a/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLockingTest.java b/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLockingTest.java new file mode 100644 index 000000000..61f29d9e8 --- /dev/null +++ b/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLockingTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 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.singleton; + +/** + * Date: 12/29/15 - 19:26 PM + * + * @author Jeroen Meulemeester + */ +public class ThreadSafeDoubleCheckLockingTest extends SingletonTest { + + /** + * Create a new singleton test instance using the given 'getInstance' method + */ + public ThreadSafeDoubleCheckLockingTest() { + super(ThreadSafeDoubleCheckLocking::getInstance); + } + +} \ No newline at end of file diff --git a/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTowerTest.java b/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTowerTest.java new file mode 100644 index 000000000..188749d1c --- /dev/null +++ b/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTowerTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 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.singleton; + +/** + * Date: 12/29/15 - 19:26 PM + * + * @author Jeroen Meulemeester + */ +public class ThreadSafeLazyLoadedIvoryTowerTest extends SingletonTest { + + /** + * Create a new singleton test instance using the given 'getInstance' method + */ + public ThreadSafeLazyLoadedIvoryTowerTest() { + super(ThreadSafeLazyLoadedIvoryTower::getInstance); + } + +} diff --git a/specification/index.md b/specification/README.md similarity index 71% rename from specification/index.md rename to specification/README.md index 6b76d5102..564830653 100644 --- a/specification/index.md +++ b/specification/README.md @@ -4,21 +4,32 @@ title: Specification folder: specification permalink: /patterns/specification/ categories: Behavioral -tags: Java +tags: + - Java + - Difficulty-Beginner --- -**Intent:** Specification pattern separates the statement of how to match a +## Also known as +Filter, Criteria + +## Intent +Specification pattern separates the statement of how to match a candidate, from the candidate object that it is matched against. As well as its usefulness in selection, it is also valuable for validation and for building to order ![alt text](./etc/specification.png "Specification") -**Applicability:** Use the Specification pattern when +## Applicability +Use the Specification pattern when * you need to select a subset of objects based on some criteria, and to refresh the selection at various times * you need to check that only suitable objects are used for a certain role (validation) -**Credits:** +## Related patterns + +* Repository + +## Credits * [Martin Fowler - Specifications](http://martinfowler.com/apsupp/spec.pdf) diff --git a/specification/pom.xml b/specification/pom.xml index 3c58aa29f..03c66540d 100644 --- a/specification/pom.xml +++ b/specification/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT specification @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/specification/src/main/java/com/iluwatar/specification/app/App.java b/specification/src/main/java/com/iluwatar/specification/app/App.java index 642278f16..7cbd38470 100644 --- a/specification/src/main/java/com/iluwatar/specification/app/App.java +++ b/specification/src/main/java/com/iluwatar/specification/app/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.app; import java.util.Arrays; @@ -18,33 +40,44 @@ import com.iluwatar.specification.selector.MovementSelector; /** * - * The central idea of the Specification pattern is to separate the statement of how to match a candidate, from the - * candidate object that it is matched against. As well as its usefulness in selection, it is also valuable for - * validation and for building to order. + * The central idea of the Specification pattern is to separate the statement of how to match a + * candidate, from the candidate object that it is matched against. As well as its usefulness in + * selection, it is also valuable for validation and for building to order. *

    - * In this example we have a pool of creatures with different properties. We then have defined separate selection - * rules (Specifications) that we apply to the collection and as output receive only the creatures that match - * the selection criteria. + * In this example we have a pool of creatures with different properties. We then have defined + * separate selection rules (Specifications) that we apply to the collection and as output receive + * only the creatures that match the selection criteria. *

    * http://martinfowler.com/apsupp/spec.pdf * */ public class App { - - public static void main( String[] args ) { - // initialize creatures list - List creatures = Arrays.asList(new Goblin(), new Octopus(), new Dragon(), new Shark(), new Troll(), new KillerBee()); - // find all walking creatures - System.out.println("Find all walking creatures"); - List walkingCreatures = creatures.stream().filter(new MovementSelector(Movement.WALKING)).collect(Collectors.toList()); - walkingCreatures.stream().forEach(System.out::println); - // find all dark creatures - System.out.println("Find all dark creatures"); - List darkCreatures = creatures.stream().filter(new ColorSelector(Color.DARK)).collect(Collectors.toList()); - darkCreatures.stream().forEach(System.out::println); - // find all red and flying creatures - System.out.println("Find all red and flying creatures"); - List redAndFlyingCreatures = creatures.stream().filter(new ColorSelector(Color.RED).and(new MovementSelector(Movement.FLYING))).collect(Collectors.toList()); - redAndFlyingCreatures.stream().forEach(System.out::println); - } + + /** + * Program entry point + */ + public static void main(String[] args) { + // initialize creatures list + List creatures = + Arrays.asList(new Goblin(), new Octopus(), new Dragon(), new Shark(), new Troll(), + new KillerBee()); + // find all walking creatures + System.out.println("Find all walking creatures"); + List walkingCreatures = + creatures.stream().filter(new MovementSelector(Movement.WALKING)) + .collect(Collectors.toList()); + walkingCreatures.stream().forEach(System.out::println); + // find all dark creatures + System.out.println("Find all dark creatures"); + List darkCreatures = + creatures.stream().filter(new ColorSelector(Color.DARK)).collect(Collectors.toList()); + darkCreatures.stream().forEach(System.out::println); + // find all red and flying creatures + System.out.println("Find all red and flying creatures"); + List redAndFlyingCreatures = + creatures.stream() + .filter(new ColorSelector(Color.RED).and(new MovementSelector(Movement.FLYING))) + .collect(Collectors.toList()); + redAndFlyingCreatures.stream().forEach(System.out::println); + } } diff --git a/specification/src/main/java/com/iluwatar/specification/creature/AbstractCreature.java b/specification/src/main/java/com/iluwatar/specification/creature/AbstractCreature.java index 12dae6cc9..8e88f13ae 100644 --- a/specification/src/main/java/com/iluwatar/specification/creature/AbstractCreature.java +++ b/specification/src/main/java/com/iluwatar/specification/creature/AbstractCreature.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.creature; import com.iluwatar.specification.property.Color; @@ -11,40 +33,43 @@ import com.iluwatar.specification.property.Size; */ public abstract class AbstractCreature implements Creature { - private String name; - private Size size; - private Movement movement; - private Color color; + private String name; + private Size size; + private Movement movement; + private Color color; - public AbstractCreature(String name, Size size, Movement movement, Color color) { - this.name = name; - this.size = size; - this.movement = movement; - this.color = color; - } - - @Override - public String toString() { - return String.format("%s [size=%s, movement=%s, color=%s]", name, size, movement, color); - } - - @Override - public String getName() { - return name; - } - - @Override - public Size getSize() { - return size; - } - - @Override - public Movement getMovement() { - return movement; - } - - @Override - public Color getColor() { - return color; - } + /** + * Constructor + */ + public AbstractCreature(String name, Size size, Movement movement, Color color) { + this.name = name; + this.size = size; + this.movement = movement; + this.color = color; + } + + @Override + public String toString() { + return String.format("%s [size=%s, movement=%s, color=%s]", name, size, movement, color); + } + + @Override + public String getName() { + return name; + } + + @Override + public Size getSize() { + return size; + } + + @Override + public Movement getMovement() { + return movement; + } + + @Override + public Color getColor() { + return color; + } } diff --git a/specification/src/main/java/com/iluwatar/specification/creature/Creature.java b/specification/src/main/java/com/iluwatar/specification/creature/Creature.java index f2d1d38d7..a330af4e7 100644 --- a/specification/src/main/java/com/iluwatar/specification/creature/Creature.java +++ b/specification/src/main/java/com/iluwatar/specification/creature/Creature.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.creature; import com.iluwatar.specification.property.Color; @@ -11,11 +33,11 @@ import com.iluwatar.specification.property.Size; */ public interface Creature { - String getName(); - - Size getSize(); - - Movement getMovement(); - - Color getColor(); + String getName(); + + Size getSize(); + + Movement getMovement(); + + Color getColor(); } diff --git a/specification/src/main/java/com/iluwatar/specification/creature/Dragon.java b/specification/src/main/java/com/iluwatar/specification/creature/Dragon.java index 0a6fd31ba..ab07001f7 100644 --- a/specification/src/main/java/com/iluwatar/specification/creature/Dragon.java +++ b/specification/src/main/java/com/iluwatar/specification/creature/Dragon.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.creature; import com.iluwatar.specification.property.Color; @@ -11,7 +33,7 @@ import com.iluwatar.specification.property.Size; */ public class Dragon extends AbstractCreature { - public Dragon() { - super("Dragon", Size.LARGE, Movement.FLYING, Color.RED); - } + public Dragon() { + super("Dragon", Size.LARGE, Movement.FLYING, Color.RED); + } } diff --git a/specification/src/main/java/com/iluwatar/specification/creature/Goblin.java b/specification/src/main/java/com/iluwatar/specification/creature/Goblin.java index f7cc1ef0b..1b53a7e77 100644 --- a/specification/src/main/java/com/iluwatar/specification/creature/Goblin.java +++ b/specification/src/main/java/com/iluwatar/specification/creature/Goblin.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.creature; import com.iluwatar.specification.property.Color; @@ -11,7 +33,7 @@ import com.iluwatar.specification.property.Size; */ public class Goblin extends AbstractCreature { - public Goblin() { - super("Goblin", Size.SMALL, Movement.WALKING, Color.GREEN); - } + public Goblin() { + super("Goblin", Size.SMALL, Movement.WALKING, Color.GREEN); + } } diff --git a/specification/src/main/java/com/iluwatar/specification/creature/KillerBee.java b/specification/src/main/java/com/iluwatar/specification/creature/KillerBee.java index 11a4711c7..4c98e9041 100644 --- a/specification/src/main/java/com/iluwatar/specification/creature/KillerBee.java +++ b/specification/src/main/java/com/iluwatar/specification/creature/KillerBee.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.creature; import com.iluwatar.specification.property.Color; @@ -11,7 +33,7 @@ import com.iluwatar.specification.property.Size; */ public class KillerBee extends AbstractCreature { - public KillerBee() { - super("KillerBee", Size.SMALL, Movement.FLYING, Color.LIGHT); - } + public KillerBee() { + super("KillerBee", Size.SMALL, Movement.FLYING, Color.LIGHT); + } } diff --git a/specification/src/main/java/com/iluwatar/specification/creature/Octopus.java b/specification/src/main/java/com/iluwatar/specification/creature/Octopus.java index 7a2ae2c18..d74ba357f 100644 --- a/specification/src/main/java/com/iluwatar/specification/creature/Octopus.java +++ b/specification/src/main/java/com/iluwatar/specification/creature/Octopus.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.creature; import com.iluwatar.specification.property.Color; @@ -11,7 +33,7 @@ import com.iluwatar.specification.property.Size; */ public class Octopus extends AbstractCreature { - public Octopus() { - super("Octopus", Size.NORMAL, Movement.SWIMMING, Color.DARK); - } + public Octopus() { + super("Octopus", Size.NORMAL, Movement.SWIMMING, Color.DARK); + } } diff --git a/specification/src/main/java/com/iluwatar/specification/creature/Shark.java b/specification/src/main/java/com/iluwatar/specification/creature/Shark.java index 42090500d..69f4c9b38 100644 --- a/specification/src/main/java/com/iluwatar/specification/creature/Shark.java +++ b/specification/src/main/java/com/iluwatar/specification/creature/Shark.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.creature; import com.iluwatar.specification.property.Color; @@ -11,7 +33,7 @@ import com.iluwatar.specification.property.Size; */ public class Shark extends AbstractCreature { - public Shark() { - super("Shark", Size.NORMAL, Movement.SWIMMING, Color.LIGHT); - } + public Shark() { + super("Shark", Size.NORMAL, Movement.SWIMMING, Color.LIGHT); + } } diff --git a/specification/src/main/java/com/iluwatar/specification/creature/Troll.java b/specification/src/main/java/com/iluwatar/specification/creature/Troll.java index 1dd31c17f..f480a0723 100644 --- a/specification/src/main/java/com/iluwatar/specification/creature/Troll.java +++ b/specification/src/main/java/com/iluwatar/specification/creature/Troll.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.creature; import com.iluwatar.specification.property.Color; @@ -10,8 +32,8 @@ import com.iluwatar.specification.property.Size; * */ public class Troll extends AbstractCreature { - - public Troll() { - super("Troll", Size.LARGE, Movement.WALKING, Color.DARK); - } + + public Troll() { + super("Troll", Size.LARGE, Movement.WALKING, Color.DARK); + } } diff --git a/specification/src/main/java/com/iluwatar/specification/property/Color.java b/specification/src/main/java/com/iluwatar/specification/property/Color.java index e23cb1585..583602990 100644 --- a/specification/src/main/java/com/iluwatar/specification/property/Color.java +++ b/specification/src/main/java/com/iluwatar/specification/property/Color.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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; /** @@ -7,16 +29,16 @@ package com.iluwatar.specification.property; */ public enum Color { - DARK("dark"), LIGHT("light"), GREEN("green"), RED("red"); - - private String title; + DARK("dark"), LIGHT("light"), GREEN("green"), RED("red"); - Color(String title) { - this.title = title; - } + private String title; - @Override - public String toString() { - return title; - } + Color(String title) { + this.title = title; + } + + @Override + public String toString() { + return title; + } } diff --git a/specification/src/main/java/com/iluwatar/specification/property/Movement.java b/specification/src/main/java/com/iluwatar/specification/property/Movement.java index 1b4b0ec9e..ae26d1a30 100644 --- a/specification/src/main/java/com/iluwatar/specification/property/Movement.java +++ b/specification/src/main/java/com/iluwatar/specification/property/Movement.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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; /** @@ -7,16 +29,16 @@ package com.iluwatar.specification.property; */ public enum Movement { - WALKING("walking"), SWIMMING("swimming"), FLYING("flying"); - - private String title; + WALKING("walking"), SWIMMING("swimming"), FLYING("flying"); - Movement(String title) { - this.title = title; - } + private String title; - @Override - public String toString() { - return title; - } + Movement(String title) { + this.title = title; + } + + @Override + public String toString() { + return title; + } } diff --git a/specification/src/main/java/com/iluwatar/specification/property/Size.java b/specification/src/main/java/com/iluwatar/specification/property/Size.java index 7f07fe147..239caa586 100644 --- a/specification/src/main/java/com/iluwatar/specification/property/Size.java +++ b/specification/src/main/java/com/iluwatar/specification/property/Size.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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; /** @@ -7,16 +29,16 @@ package com.iluwatar.specification.property; */ public enum Size { - SMALL("small"), NORMAL("normal"), LARGE("large"); - - private String title; + SMALL("small"), NORMAL("normal"), LARGE("large"); - Size(String title) { - this.title = title; - } + private String title; - @Override - public String toString() { - return title; - } + Size(String title) { + this.title = title; + } + + @Override + public String toString() { + return title; + } } diff --git a/specification/src/main/java/com/iluwatar/specification/selector/ColorSelector.java b/specification/src/main/java/com/iluwatar/specification/selector/ColorSelector.java index d68127599..c1c178e23 100644 --- a/specification/src/main/java/com/iluwatar/specification/selector/ColorSelector.java +++ b/specification/src/main/java/com/iluwatar/specification/selector/ColorSelector.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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 java.util.function.Predicate; @@ -12,14 +34,14 @@ import com.iluwatar.specification.property.Color; */ public class ColorSelector implements Predicate { - private final Color c; + private final Color c; - public ColorSelector(Color c) { - this.c = c; - } - - @Override - public boolean test(Creature t) { - return t.getColor().equals(c); - } + public ColorSelector(Color c) { + this.c = c; + } + + @Override + public boolean test(Creature t) { + return t.getColor().equals(c); + } } diff --git a/specification/src/main/java/com/iluwatar/specification/selector/MovementSelector.java b/specification/src/main/java/com/iluwatar/specification/selector/MovementSelector.java index 260abd0e3..67d7abd61 100644 --- a/specification/src/main/java/com/iluwatar/specification/selector/MovementSelector.java +++ b/specification/src/main/java/com/iluwatar/specification/selector/MovementSelector.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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 java.util.function.Predicate; @@ -11,15 +33,15 @@ import com.iluwatar.specification.property.Movement; * */ public class MovementSelector implements Predicate { - - private final Movement m; - public MovementSelector(Movement m) { - this.m = m; - } + private final Movement m; - @Override - public boolean test(Creature t) { - return t.getMovement().equals(m); - } + public MovementSelector(Movement m) { + this.m = m; + } + + @Override + public boolean test(Creature t) { + return t.getMovement().equals(m); + } } diff --git a/specification/src/main/java/com/iluwatar/specification/selector/SizeSelector.java b/specification/src/main/java/com/iluwatar/specification/selector/SizeSelector.java index a54eaf16c..2792531d0 100644 --- a/specification/src/main/java/com/iluwatar/specification/selector/SizeSelector.java +++ b/specification/src/main/java/com/iluwatar/specification/selector/SizeSelector.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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 java.util.function.Predicate; @@ -12,14 +34,14 @@ import com.iluwatar.specification.property.Size; */ public class SizeSelector implements Predicate { - private final Size s; + private final Size s; - public SizeSelector(Size s) { - this.s = s; - } - - @Override - public boolean test(Creature t) { - return t.getSize().equals(s); - } + public SizeSelector(Size s) { + this.s = s; + } + + @Override + public boolean test(Creature t) { + return t.getSize().equals(s); + } } diff --git a/specification/src/test/java/com/iluwatar/specification/app/AppTest.java b/specification/src/test/java/com/iluwatar/specification/app/AppTest.java index 31965336c..13e6c9b5d 100644 --- a/specification/src/test/java/com/iluwatar/specification/app/AppTest.java +++ b/specification/src/test/java/com/iluwatar/specification/app/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.app; import org.junit.Test; -import com.iluwatar.specification.app.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.specification.app.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/specification/src/test/java/com/iluwatar/specification/creature/CreatureTest.java b/specification/src/test/java/com/iluwatar/specification/creature/CreatureTest.java new file mode 100644 index 000000000..22b27c8a0 --- /dev/null +++ b/specification/src/test/java/com/iluwatar/specification/creature/CreatureTest.java @@ -0,0 +1,133 @@ +/** + * The MIT License + * Copyright (c) 2014 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.creature; + +import com.iluwatar.specification.property.Color; +import com.iluwatar.specification.property.Movement; +import com.iluwatar.specification.property.Size; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Arrays; +import java.util.Collection; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/29/15 - 7:47 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class CreatureTest { + + /** + * @return The tested {@link Creature} instance and its expected specs + */ + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList( + new Object[]{new Dragon(), "Dragon", Size.LARGE, Movement.FLYING, Color.RED}, + new Object[]{new Goblin(), "Goblin", Size.SMALL, Movement.WALKING, Color.GREEN}, + new Object[]{new KillerBee(), "KillerBee", Size.SMALL, Movement.FLYING, Color.LIGHT}, + new Object[]{new Octopus(), "Octopus", Size.NORMAL, Movement.SWIMMING, Color.DARK}, + new Object[]{new Shark(), "Shark", Size.NORMAL, Movement.SWIMMING, Color.LIGHT}, + new Object[]{new Troll(), "Troll", Size.LARGE, Movement.WALKING, Color.DARK} + ); + } + + /** + * The tested creature + */ + private final Creature testedCreature; + + /** + * The expected name of the tested creature + */ + private final String name; + + /** + * The expected size of the tested creature + */ + private final Size size; + + /** + * The expected movement type of the tested creature + */ + private final Movement movement; + + /** + * The expected color of the tested creature + */ + private final Color color; + + /** + * @param testedCreature The tested creature + * @param name The expected name of the creature + * @param size The expected size of the creature + * @param movement The expected movement type of the creature + * @param color The expected color of the creature + */ + public CreatureTest(final Creature testedCreature, final String name, final Size size, + final Movement movement, final Color color) { + this.testedCreature = testedCreature; + this.name = name; + this.size = size; + this.movement = movement; + this.color = color; + } + + + @Test + public void testGetName() throws Exception { + assertEquals(this.name, this.testedCreature.getName()); + } + + @Test + public void testGetSize() throws Exception { + assertEquals(this.size, this.testedCreature.getSize()); + } + + @Test + public void testGetMovement() throws Exception { + assertEquals(this.movement, this.testedCreature.getMovement()); + } + + @Test + public void testGetColor() throws Exception { + assertEquals(this.color, this.testedCreature.getColor()); + } + + @Test + public void testToString() throws Exception { + final String toString = this.testedCreature.toString(); + assertNotNull(toString); + assertEquals( + String.format("%s [size=%s, movement=%s, color=%s]", name, size, movement, color), + toString + ); + } +} \ No newline at end of file diff --git a/specification/src/test/java/com/iluwatar/specification/selector/ColorSelectorTest.java b/specification/src/test/java/com/iluwatar/specification/selector/ColorSelectorTest.java new file mode 100644 index 000000000..0d6dfa080 --- /dev/null +++ b/specification/src/test/java/com/iluwatar/specification/selector/ColorSelectorTest.java @@ -0,0 +1,59 @@ +/** + * The MIT License + * Copyright (c) 2014 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.Color; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Date: 12/29/15 - 7:35 PM + * + * @author Jeroen Meulemeester + */ +public class ColorSelectorTest { + + /** + * Verify if the color selector gives the correct results + */ + @Test + public void testColor() { + final Creature greenCreature = mock(Creature.class); + when(greenCreature.getColor()).thenReturn(Color.GREEN); + + final Creature redCreature = mock(Creature.class); + when(redCreature.getColor()).thenReturn(Color.RED); + + final ColorSelector greenSelector = new ColorSelector(Color.GREEN); + assertTrue(greenSelector.test(greenCreature)); + assertFalse(greenSelector.test(redCreature)); + + } + +} \ No newline at end of file diff --git a/specification/src/test/java/com/iluwatar/specification/selector/MovementSelectorTest.java b/specification/src/test/java/com/iluwatar/specification/selector/MovementSelectorTest.java new file mode 100644 index 000000000..451c776dd --- /dev/null +++ b/specification/src/test/java/com/iluwatar/specification/selector/MovementSelectorTest.java @@ -0,0 +1,60 @@ +/** + * The MIT License + * Copyright (c) 2014 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.Color; +import com.iluwatar.specification.property.Movement; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Date: 12/29/15 - 7:37 PM + * + * @author Jeroen Meulemeester + */ +public class MovementSelectorTest { + + /** + * Verify if the movement selector gives the correct results + */ + @Test + public void testMovement() { + final Creature swimmingCreature = mock(Creature.class); + when(swimmingCreature.getMovement()).thenReturn(Movement.SWIMMING); + + final Creature flyingCreature = mock(Creature.class); + when(flyingCreature.getMovement()).thenReturn(Movement.FLYING); + + final MovementSelector swimmingSelector = new MovementSelector(Movement.SWIMMING); + assertTrue(swimmingSelector.test(swimmingCreature)); + assertFalse(swimmingSelector.test(flyingCreature)); + + } + +} \ No newline at end of file diff --git a/specification/src/test/java/com/iluwatar/specification/selector/SizeSelectorTest.java b/specification/src/test/java/com/iluwatar/specification/selector/SizeSelectorTest.java new file mode 100644 index 000000000..de1228f1b --- /dev/null +++ b/specification/src/test/java/com/iluwatar/specification/selector/SizeSelectorTest.java @@ -0,0 +1,58 @@ +/** + * The MIT License + * Copyright (c) 2014 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.Size; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Date: 12/29/15 - 7:43 PM + * + * @author Jeroen Meulemeester + */ +public class SizeSelectorTest { + + /** + * Verify if the size selector gives the correct results + */ + @Test + public void testMovement() { + final Creature normalCreature = mock(Creature.class); + when(normalCreature.getSize()).thenReturn(Size.NORMAL); + + final Creature smallCreature = mock(Creature.class); + when(smallCreature.getSize()).thenReturn(Size.SMALL); + + final SizeSelector normalSelector = new SizeSelector(Size.NORMAL); + assertTrue(normalSelector.test(normalCreature)); + assertFalse(normalSelector.test(smallCreature)); + } + +} diff --git a/state/index.md b/state/README.md similarity index 83% rename from state/index.md rename to state/README.md index b743cb8be..f5cb189fd 100644 --- a/state/index.md +++ b/state/README.md @@ -10,16 +10,21 @@ tags: - Gang Of Four --- -**Intent:** Allow an object to alter its behavior when its internal state +## Also known as +Objects for States + +## Intent +Allow an object to alter its behavior when its internal state changes. The object will appear to change its class. ![alt text](./etc/state_1.png "State") -**Applicability:** Use the State pattern in either of the following cases +## Applicability +Use the State pattern in either of the following cases * an object's behavior depends on its state, and it must change its behavior at run-time depending on that state * operations have large, multipart conditional statements that depend on the object's state. This state is usually represented by one or more enumerated constants. Often, several operations will contain this same conditional structure. The State pattern puts each branch of the conditional in a separate class. This lets you treat the object's state as an object in its own right that can vary independently from other objects. -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/state/pom.xml b/state/pom.xml index 2d87a796a..134fbabe1 100644 --- a/state/pom.xml +++ b/state/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT state @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/state/src/main/java/com/iluwatar/state/AngryState.java b/state/src/main/java/com/iluwatar/state/AngryState.java index f09fbb518..c58f85ae1 100644 --- a/state/src/main/java/com/iluwatar/state/AngryState.java +++ b/state/src/main/java/com/iluwatar/state/AngryState.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.state; /** @@ -7,20 +29,20 @@ package com.iluwatar.state; */ public class AngryState implements State { - private Mammoth mammoth; + private Mammoth mammoth; - public AngryState(Mammoth mammoth) { - this.mammoth = mammoth; - } + public AngryState(Mammoth mammoth) { + this.mammoth = mammoth; + } - @Override - public void observe() { - System.out.println(String.format("%s is furious!", mammoth)); - } + @Override + public void observe() { + System.out.println(String.format("%s is furious!", mammoth)); + } - @Override - public void onEnterState() { - System.out.println(String.format("%s gets angry!", mammoth)); - } + @Override + public void onEnterState() { + System.out.println(String.format("%s gets angry!", mammoth)); + } } diff --git a/state/src/main/java/com/iluwatar/state/App.java b/state/src/main/java/com/iluwatar/state/App.java index 95d411076..9f9d8d29a 100644 --- a/state/src/main/java/com/iluwatar/state/App.java +++ b/state/src/main/java/com/iluwatar/state/App.java @@ -1,28 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014 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.state; /** * - * In State pattern the container object has an internal state object that - * defines the current behavior. The state object can be changed to alter the - * behavior. + * In State pattern the container object has an internal state object that defines the current + * behavior. The state object can be changed to alter the behavior. *

    - * This can be a cleaner way for an object to change its behavior at runtime - * without resorting to large monolithic conditional statements and thus improves - * maintainability. + * This can be a cleaner way for an object to change its behavior at runtime without resorting to + * large monolithic conditional statements and thus improves maintainability. *

    * In this example the {@link Mammoth} changes its behavior as time passes by. * */ public class App { - public static void main(String[] args) { + /** + * Program entry point + */ + public static void main(String[] args) { - Mammoth mammoth = new Mammoth(); - mammoth.observe(); - mammoth.timePasses(); - mammoth.observe(); - mammoth.timePasses(); - mammoth.observe(); + Mammoth mammoth = new Mammoth(); + mammoth.observe(); + mammoth.timePasses(); + mammoth.observe(); + mammoth.timePasses(); + mammoth.observe(); - } + } } diff --git a/state/src/main/java/com/iluwatar/state/Mammoth.java b/state/src/main/java/com/iluwatar/state/Mammoth.java index f98da3b5e..ffa07ed68 100644 --- a/state/src/main/java/com/iluwatar/state/Mammoth.java +++ b/state/src/main/java/com/iluwatar/state/Mammoth.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.state; /** @@ -7,31 +29,34 @@ package com.iluwatar.state; */ public class Mammoth { - private State state; + private State state; - public Mammoth() { - state = new PeacefulState(this); - } + public Mammoth() { + state = new PeacefulState(this); + } - public void timePasses() { - if (state.getClass().equals(PeacefulState.class)) { - changeStateTo(new AngryState(this)); - } else { - changeStateTo(new PeacefulState(this)); - } - } + /** + * Makes time pass for the mammoth + */ + public void timePasses() { + if (state.getClass().equals(PeacefulState.class)) { + changeStateTo(new AngryState(this)); + } else { + changeStateTo(new PeacefulState(this)); + } + } - private void changeStateTo(State newState) { - this.state = newState; - this.state.onEnterState(); - } + private void changeStateTo(State newState) { + this.state = newState; + this.state.onEnterState(); + } - @Override - public String toString() { - return "The mammoth"; - } + @Override + public String toString() { + return "The mammoth"; + } - public void observe() { - this.state.observe(); - } + public void observe() { + this.state.observe(); + } } diff --git a/state/src/main/java/com/iluwatar/state/PeacefulState.java b/state/src/main/java/com/iluwatar/state/PeacefulState.java index 237fe7c2c..23f4e893c 100644 --- a/state/src/main/java/com/iluwatar/state/PeacefulState.java +++ b/state/src/main/java/com/iluwatar/state/PeacefulState.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.state; /** @@ -7,20 +29,20 @@ package com.iluwatar.state; */ public class PeacefulState implements State { - private Mammoth mammoth; + private Mammoth mammoth; - public PeacefulState(Mammoth mammoth) { - this.mammoth = mammoth; - } + public PeacefulState(Mammoth mammoth) { + this.mammoth = mammoth; + } - @Override - public void observe() { - System.out.println(String.format("%s is calm and peaceful.", mammoth)); - } + @Override + public void observe() { + System.out.println(String.format("%s is calm and peaceful.", mammoth)); + } - @Override - public void onEnterState() { - System.out.println(String.format("%s calms down.", mammoth)); - } + @Override + public void onEnterState() { + System.out.println(String.format("%s calms down.", mammoth)); + } } diff --git a/state/src/main/java/com/iluwatar/state/State.java b/state/src/main/java/com/iluwatar/state/State.java index 3f65e3532..65c59af16 100644 --- a/state/src/main/java/com/iluwatar/state/State.java +++ b/state/src/main/java/com/iluwatar/state/State.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.state; /** @@ -7,8 +29,8 @@ package com.iluwatar.state; */ public interface State { - void onEnterState(); + void onEnterState(); - void observe(); + void observe(); } diff --git a/state/src/test/java/com/iluwatar/state/AppTest.java b/state/src/test/java/com/iluwatar/state/AppTest.java index 556cbc01c..9dc3790c7 100644 --- a/state/src/test/java/com/iluwatar/state/AppTest.java +++ b/state/src/test/java/com/iluwatar/state/AppTest.java @@ -1,19 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 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.state; import org.junit.Test; -import com.iluwatar.state.App; - /** - * + * * Application test * */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/state/src/test/java/com/iluwatar/state/MammothTest.java b/state/src/test/java/com/iluwatar/state/MammothTest.java new file mode 100644 index 000000000..4fe37bfd1 --- /dev/null +++ b/state/src/test/java/com/iluwatar/state/MammothTest.java @@ -0,0 +1,112 @@ +/** + * The MIT License + * Copyright (c) 2014 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.state; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; +import org.mockito.Mockito; + +import java.io.PrintStream; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; + +/** + * Date: 12/29/15 - 8:27 PM + * + * @author Jeroen Meulemeester + */ +public class MammothTest { + + /** + * The mocked standard out {@link PrintStream}, required since some actions don't have any + * influence on accessible objects, except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Switch to a complete mammoth 'mood'-cycle and verify if the observed mood matches the expected + * value. + */ + @Test + public void testTimePasses() { + final InOrder inOrder = Mockito.inOrder(this.stdOutMock); + final Mammoth mammoth = new Mammoth(); + + mammoth.observe(); + inOrder.verify(this.stdOutMock).println("The mammoth is calm and peaceful."); + inOrder.verifyNoMoreInteractions(); + + mammoth.timePasses(); + inOrder.verify(this.stdOutMock).println("The mammoth gets angry!"); + inOrder.verifyNoMoreInteractions(); + + mammoth.observe(); + inOrder.verify(this.stdOutMock).println("The mammoth is furious!"); + inOrder.verifyNoMoreInteractions(); + + mammoth.timePasses(); + inOrder.verify(this.stdOutMock).println("The mammoth calms down."); + inOrder.verifyNoMoreInteractions(); + + mammoth.observe(); + inOrder.verify(this.stdOutMock).println("The mammoth is calm and peaceful."); + inOrder.verifyNoMoreInteractions(); + + } + + /** + * Verify if {@link Mammoth#toString()} gives the expected value + */ + @Test + public void testToString() { + final String toString = new Mammoth().toString(); + assertNotNull(toString); + assertEquals("The mammoth", toString); + } + +} \ No newline at end of file diff --git a/step-builder/README.md b/step-builder/README.md new file mode 100644 index 000000000..bc636e37a --- /dev/null +++ b/step-builder/README.md @@ -0,0 +1,23 @@ +--- +layout: pattern +title: Step Builder +folder: step-builder +permalink: /patterns/step-builder/ +categories: Creational +tags: + - Java + - Difficulty-Intermediate +--- + +## Intent +An extension of the Builder pattern that fully guides the user through the creation of the object with no chances of confusion. +The user experience will be much more improved by the fact that he will only see the next step methods available, NO build method until is the right time to build the object. + +![alt text](./etc/step-builder.png "Step Builder") + +## Applicability +Use the Step Builder pattern when the algorithm for creating a complex object should be independent of the parts that make up the object and how they're assembled the construction process must allow different representations for the object that's constructed when in the process of constructing the order is important. + +## Credits + +* [Marco Castigliego - Step Builder](http://rdafbn.blogspot.co.uk/2012/07/step-builder-pattern_28.html) diff --git a/step-builder/index.md b/step-builder/index.md deleted file mode 100644 index 766479358..000000000 --- a/step-builder/index.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -layout: pattern -title: Step Builder -folder: step-builder -permalink: /patterns/step-builder/ -categories: Creational -tags: Java ---- - -**Intent:** An extension of the Builder pattern that fully guides the user through the creation of the object with no chances of confusion. -The user experience will be much more improved by the fact that he will only see the next step methods available, NO build method until is the right time to build the object. - -![alt text](./etc/step-builder.png "Step Builder") - -**Applicability:** Use the Step Builder pattern when the algorithm for creating a complex object should be independent of the parts that make up the object and how they're assembled the construction process must allow different representations for the object that's constructed when in the process of constructing the order is important. - -**Credits:** - -* [Marco Castigliego - Step Builder](http://rdafbn.blogspot.co.uk/2012/07/step-builder-pattern_28.html) diff --git a/step-builder/pom.xml b/step-builder/pom.xml index 7c7e8ac69..96098eabc 100644 --- a/step-builder/pom.xml +++ b/step-builder/pom.xml @@ -1,4 +1,28 @@ + @@ -6,7 +30,7 @@ java-design-patterns com.iluwatar - 1.7.0 + 1.13.0-SNAPSHOT step-builder diff --git a/step-builder/src/main/java/com/iluwatar/stepbuilder/App.java b/step-builder/src/main/java/com/iluwatar/stepbuilder/App.java index 533394acb..aeb759ba8 100644 --- a/step-builder/src/main/java/com/iluwatar/stepbuilder/App.java +++ b/step-builder/src/main/java/com/iluwatar/stepbuilder/App.java @@ -1,74 +1,84 @@ +/** + * The MIT License + * Copyright (c) 2014 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.stepbuilder; /** * Step Builder Pattern * - *

    Intent - *
    - * An extension of the Builder pattern that fully guides the user - * through the creation of the object with no chances of confusion. - *
    - * The user experience will be much more improved by the fact that - * he will only see the next step methods available, NO build method - * until is the right time to build the object. + *

    + * Intent
    + * An extension of the Builder pattern that fully guides the user through the creation of the object + * with no chances of confusion.
    + * The user experience will be much more improved by the fact that he will only see the next step + * methods available, NO build method until is the right time to build the object. * - *

    Implementation - *
    - *

      The concept is simple: + *

      + * Implementation + *

        + * The concept is simple: * - *
      • Write creational steps inner classes or interfaces where each - * method knows what can be displayed next.
      • + *
      • Write creational steps inner classes or interfaces where each method knows what can be + * displayed next.
      • * - *
      • Implement all your steps interfaces in an inner static class.
      • + *
      • Implement all your steps interfaces in an inner static class.
      • * - *
      • Last step is the BuildStep, in charge of creating the object - * you need to build.
      • + *
      • Last step is the BuildStep, in charge of creating the object you need to build.
      • *
      * - *

      Applicability - *
      - * Use the Step Builder pattern when the algorithm for creating a - * complex object should be independent of the parts that make up - * the object and how they're assembled the construction process must - * allow different representations for the object that's constructed - * when in the process of constructing the order is important. + *

      + * Applicability
      + * Use the Step Builder pattern when the algorithm for creating a complex object should be + * independent of the parts that make up the object and how they're assembled the construction + * process must allow different representations for the object that's constructed when in the + * process of constructing the order is important. *

      * http://rdafbn.blogspot.co.uk/2012/07/step-builder-pattern_28.html */ public class App { - - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { - Character warrior = CharacterStepBuilder.newBuilder() - .name("Amberjill") - .fighterClass("Paladin") - .withWeapon("Sword") - .noAbilities() - .build(); + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { - System.out.println(warrior); + Character warrior = + CharacterStepBuilder.newBuilder().name("Amberjill").fighterClass("Paladin") + .withWeapon("Sword").noAbilities().build(); - Character mage = CharacterStepBuilder.newBuilder() - .name("Riobard") - .wizardClass("Sorcerer") - .withSpell("Fireball") - .withAbility("Fire Aura") - .withAbility("Teleport") - .noMoreAbilities() - .build(); + System.out.println(warrior); - System.out.println(mage); + Character mage = + CharacterStepBuilder.newBuilder().name("Riobard").wizardClass("Sorcerer") + .withSpell("Fireball").withAbility("Fire Aura").withAbility("Teleport") + .noMoreAbilities().build(); - Character thief = CharacterStepBuilder.newBuilder() - .name("Desmond") - .fighterClass("Rogue") - .noWeapon() - .build(); + System.out.println(mage); - System.out.println(thief); - } + Character thief = + CharacterStepBuilder.newBuilder().name("Desmond").fighterClass("Rogue").noWeapon().build(); + + System.out.println(thief); + } } diff --git a/step-builder/src/main/java/com/iluwatar/stepbuilder/Character.java b/step-builder/src/main/java/com/iluwatar/stepbuilder/Character.java index 70727f386..1e21758a3 100644 --- a/step-builder/src/main/java/com/iluwatar/stepbuilder/Character.java +++ b/step-builder/src/main/java/com/iluwatar/stepbuilder/Character.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.stepbuilder; import java.util.List; @@ -7,76 +29,76 @@ import java.util.List; */ public class Character { - private String name; - private String fighterClass; - private String wizardClass; - private String weapon; - private String spell; - private List abilities; + private String name; + private String fighterClass; + private String wizardClass; + private String weapon; + private String spell; + private List abilities; - public Character(String name) { - this.name = name; - } + public Character(String name) { + this.name = name; + } - public String getName() { - return name; - } + public String getName() { + return name; + } - public void setName(String name) { - this.name = name; - } + public void setName(String name) { + this.name = name; + } - public String getFighterClass() { - return fighterClass; - } + public String getFighterClass() { + return fighterClass; + } - public void setFighterClass(String fighterClass) { - this.fighterClass = fighterClass; - } + public void setFighterClass(String fighterClass) { + this.fighterClass = fighterClass; + } - public String getWizardClass() { - return wizardClass; - } + public String getWizardClass() { + return wizardClass; + } - public void setWizardClass(String wizardClass) { - this.wizardClass = wizardClass; - } + public void setWizardClass(String wizardClass) { + this.wizardClass = wizardClass; + } - public String getWeapon() { - return weapon; - } + public String getWeapon() { + return weapon; + } - public void setWeapon(String weapon) { - this.weapon = weapon; - } + public void setWeapon(String weapon) { + this.weapon = weapon; + } - public String getSpell() { - return spell; - } + public String getSpell() { + return spell; + } - public void setSpell(String spell) { - this.spell = spell; - } + public void setSpell(String spell) { + this.spell = spell; + } - public List getAbilities() { - return abilities; - } + public List getAbilities() { + return abilities; + } - public void setAbilities(List abilities) { - this.abilities = abilities; - } + public void setAbilities(List abilities) { + this.abilities = abilities; + } - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("This is a "); - sb.append(fighterClass != null ? fighterClass : wizardClass); - sb.append(" named "); - sb.append(name); - sb.append(" armed with a "); - sb.append(weapon != null ? weapon : spell != null ? spell : "with nothing"); - sb.append(abilities != null ? (" and wielding " + abilities + " abilities") : ""); - sb.append("."); - return sb.toString(); - } + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("This is a ") + .append(fighterClass != null ? fighterClass : wizardClass) + .append(" named ") + .append(name) + .append(" armed with a ") + .append(weapon != null ? weapon : spell != null ? spell : "with nothing") + .append(abilities != null ? " and wielding " + abilities + " abilities" : "") + .append('.'); + return sb.toString(); + } } diff --git a/step-builder/src/main/java/com/iluwatar/stepbuilder/CharacterStepBuilder.java b/step-builder/src/main/java/com/iluwatar/stepbuilder/CharacterStepBuilder.java index b1ef3b5af..35e671b4c 100644 --- a/step-builder/src/main/java/com/iluwatar/stepbuilder/CharacterStepBuilder.java +++ b/step-builder/src/main/java/com/iluwatar/stepbuilder/CharacterStepBuilder.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.stepbuilder; import java.util.ArrayList; @@ -6,162 +28,160 @@ import java.util.List; /** * The Step Builder class. */ -public class CharacterStepBuilder { +public final class CharacterStepBuilder { - private CharacterStepBuilder() { - } + private CharacterStepBuilder() {} - public static NameStep newBuilder() { - return new CharacterSteps(); - } + public static NameStep newBuilder() { + return new CharacterSteps(); + } - /** - * First Builder Step in charge of the Character name. - * Next Step available : ClassStep - */ - public interface NameStep { - ClassStep name(String name); - } + /** + * First Builder Step in charge of the Character name. Next Step available : ClassStep + */ + public interface NameStep { + ClassStep name(String name); + } - /** - * This step is in charge of setting the Character class (fighter or wizard). - * Fighter choice : Next Step available : WeaponStep - * Wizard choice : Next Step available : SpellStep - */ - public interface ClassStep { - WeaponStep fighterClass(String fighterClass); - SpellStep wizardClass(String wizardClass); - } + /** + * This step is in charge of setting the Character class (fighter or wizard). Fighter choice : + * Next Step available : WeaponStep Wizard choice : Next Step available : SpellStep + */ + public interface ClassStep { + WeaponStep fighterClass(String fighterClass); - /** - * This step is in charge of the weapon. - * Weapon choice : Next Step available : AbilityStep - * No weapon choice : Next Step available : BuildStep - */ - public interface WeaponStep { - AbilityStep withWeapon(String weapon); - BuildStep noWeapon(); - } + SpellStep wizardClass(String wizardClass); + } - /** - * This step is in charge of the spell. - * Spell choice : Next Step available : AbilityStep - * No spell choice : Next Step available : BuildStep - */ - public interface SpellStep { - AbilityStep withSpell(String spell); - BuildStep noSpell(); - } + /** + * This step is in charge of the weapon. Weapon choice : Next Step available : AbilityStep No + * weapon choice : Next Step available : BuildStep + */ + public interface WeaponStep { + AbilityStep withWeapon(String weapon); - /** - * This step is in charge of abilities. - * Next Step available : BuildStep - */ - public interface AbilityStep { - AbilityStep withAbility(String ability); - BuildStep noMoreAbilities(); - BuildStep noAbilities(); - } + BuildStep noWeapon(); + } - /** - * This is the final step in charge of building the Character Object. - * Validation should be here. - */ - public interface BuildStep { - Character build(); - } + /** + * This step is in charge of the spell. Spell choice : Next Step available : AbilityStep No spell + * choice : Next Step available : BuildStep + */ + public interface SpellStep { + AbilityStep withSpell(String spell); + + BuildStep noSpell(); + } + + /** + * This step is in charge of abilities. Next Step available : BuildStep + */ + public interface AbilityStep { + AbilityStep withAbility(String ability); + + BuildStep noMoreAbilities(); + + BuildStep noAbilities(); + } + + /** + * This is the final step in charge of building the Character Object. Validation should be here. + */ + public interface BuildStep { + Character build(); + } - /** - * Step Builder implementation. - */ - private static class CharacterSteps - implements NameStep, ClassStep, WeaponStep, SpellStep, AbilityStep, BuildStep { + /** + * Step Builder implementation. + */ + private static class CharacterSteps implements NameStep, ClassStep, WeaponStep, SpellStep, + AbilityStep, BuildStep { - private String name; - private String fighterClass; - private String wizardClass; - private String weapon; - private String spell; - private List abilities = new ArrayList<>(); + private String name; + private String fighterClass; + private String wizardClass; + private String weapon; + private String spell; + private List abilities = new ArrayList<>(); - @Override - public ClassStep name(String name) { - this.name = name; - return this; - } + @Override + public ClassStep name(String name) { + this.name = name; + return this; + } - @Override - public WeaponStep fighterClass(String fighterClass) { - this.fighterClass = fighterClass; - return this; - } + @Override + public WeaponStep fighterClass(String fighterClass) { + this.fighterClass = fighterClass; + return this; + } - @Override - public SpellStep wizardClass(String wizardClass) { - this.wizardClass = wizardClass; - return this; - } + @Override + public SpellStep wizardClass(String wizardClass) { + this.wizardClass = wizardClass; + return this; + } - @Override - public AbilityStep withWeapon(String weapon) { - this.weapon = weapon; - return this; - } + @Override + public AbilityStep withWeapon(String weapon) { + this.weapon = weapon; + return this; + } - @Override - public BuildStep noWeapon() { - return this; - } + @Override + public BuildStep noWeapon() { + return this; + } - @Override - public AbilityStep withSpell(String spell) { - this.spell = spell; - return this; - } + @Override + public AbilityStep withSpell(String spell) { + this.spell = spell; + return this; + } - @Override - public BuildStep noSpell() { - return this; - } + @Override + public BuildStep noSpell() { + return this; + } - @Override - public AbilityStep withAbility(String ability) { - this.abilities.add(ability); - return this; - } + @Override + public AbilityStep withAbility(String ability) { + this.abilities.add(ability); + return this; + } - @Override - public BuildStep noMoreAbilities() { - return this; - } + @Override + public BuildStep noMoreAbilities() { + return this; + } - @Override - public BuildStep noAbilities() { - return this; - } + @Override + public BuildStep noAbilities() { + return this; + } - @Override - public Character build() { - Character character = new Character(name); + @Override + public Character build() { + Character character = new Character(name); - if (fighterClass != null) { - character.setFighterClass(fighterClass); - } else { - character.setWizardClass(wizardClass); - } + if (fighterClass != null) { + character.setFighterClass(fighterClass); + } else { + character.setWizardClass(wizardClass); + } - if (weapon != null) { - character.setWeapon(weapon); - } else { - character.setSpell(spell); - } + if (weapon != null) { + character.setWeapon(weapon); + } else { + character.setSpell(spell); + } - if (!abilities.isEmpty()) { - character.setAbilities(abilities); - } + if (!abilities.isEmpty()) { + character.setAbilities(abilities); + } - return character; - } - } + return character; + } + } } diff --git a/step-builder/src/test/java/com/iluwatar/stepbuilder/AppTest.java b/step-builder/src/test/java/com/iluwatar/stepbuilder/AppTest.java index fbed6798e..41511d913 100644 --- a/step-builder/src/test/java/com/iluwatar/stepbuilder/AppTest.java +++ b/step-builder/src/test/java/com/iluwatar/stepbuilder/AppTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.stepbuilder; import org.junit.Test; @@ -9,9 +31,9 @@ import org.junit.Test; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/step-builder/src/test/java/com/iluwatar/stepbuilder/CharacterStepBuilderTest.java b/step-builder/src/test/java/com/iluwatar/stepbuilder/CharacterStepBuilderTest.java new file mode 100644 index 000000000..baa8f9d1d --- /dev/null +++ b/step-builder/src/test/java/com/iluwatar/stepbuilder/CharacterStepBuilderTest.java @@ -0,0 +1,177 @@ +/** + * The MIT License + * Copyright (c) 2014 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.stepbuilder; + +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +/** + * Date: 12/29/15 - 9:21 PM + * + * @author Jeroen Meulemeester + */ +public class CharacterStepBuilderTest { + + /** + * Build a new wizard {@link Character} and verify if it has the expected attributes + */ + @Test + public void testBuildWizard() { + final Character character = CharacterStepBuilder.newBuilder() + .name("Merlin") + .wizardClass("alchemist") + .withSpell("poison") + .withAbility("invisibility") + .withAbility("wisdom") + .noMoreAbilities() + .build(); + + assertEquals("Merlin", character.getName()); + assertEquals("alchemist", character.getWizardClass()); + assertEquals("poison", character.getSpell()); + assertNotNull(character.toString()); + + final List abilities = character.getAbilities(); + assertNotNull(abilities); + assertEquals(2, abilities.size()); + assertTrue(abilities.contains("invisibility")); + assertTrue(abilities.contains("wisdom")); + + } + + /** + * Build a new wizard {@link Character} without spell or abilities and verify if it has the + * expected attributes + */ + @Test + public void testBuildPoorWizard() { + final Character character = CharacterStepBuilder.newBuilder() + .name("Merlin") + .wizardClass("alchemist") + .noSpell() + .build(); + + assertEquals("Merlin", character.getName()); + assertEquals("alchemist", character.getWizardClass()); + assertNull(character.getSpell()); + assertNull(character.getAbilities()); + assertNotNull(character.toString()); + + } + + /** + * Build a new wizard {@link Character} and verify if it has the expected attributes + */ + @Test + public void testBuildWeakWizard() { + final Character character = CharacterStepBuilder.newBuilder() + .name("Merlin") + .wizardClass("alchemist") + .withSpell("poison") + .noAbilities() + .build(); + + assertEquals("Merlin", character.getName()); + assertEquals("alchemist", character.getWizardClass()); + assertEquals("poison", character.getSpell()); + assertNull(character.getAbilities()); + assertNotNull(character.toString()); + + } + + + /** + * Build a new warrior {@link Character} and verify if it has the expected attributes + */ + @Test + public void testBuildWarrior() { + final Character character = CharacterStepBuilder.newBuilder() + .name("Cuauhtemoc") + .fighterClass("aztec") + .withWeapon("spear") + .withAbility("speed") + .withAbility("strength") + .noMoreAbilities() + .build(); + + assertEquals("Cuauhtemoc", character.getName()); + assertEquals("aztec", character.getFighterClass()); + assertEquals("spear", character.getWeapon()); + assertNotNull(character.toString()); + + final List abilities = character.getAbilities(); + assertNotNull(abilities); + assertEquals(2, abilities.size()); + assertTrue(abilities.contains("speed")); + assertTrue(abilities.contains("strength")); + + } + + /** + * Build a new wizard {@link Character} without weapon and abilities and verify if it has the + * expected attributes + */ + @Test + public void testBuildPoorWarrior() { + final Character character = CharacterStepBuilder.newBuilder() + .name("Poor warrior") + .fighterClass("none") + .noWeapon() + .build(); + + assertEquals("Poor warrior", character.getName()); + assertEquals("none", character.getFighterClass()); + assertNull(character.getWeapon()); + assertNull(character.getAbilities()); + assertNotNull(character.toString()); + + } + + /** + * Build a new warrior {@link Character} without any abilities, but with a weapon and verify if it + * has the expected attributes + */ + @Test + public void testBuildWeakWarrior() { + final Character character = CharacterStepBuilder.newBuilder() + .name("Weak warrior") + .fighterClass("none") + .withWeapon("Slingshot") + .noAbilities() + .build(); + + assertEquals("Weak warrior", character.getName()); + assertEquals("none", character.getFighterClass()); + assertEquals("Slingshot", character.getWeapon()); + assertNull(character.getAbilities()); + assertNotNull(character.toString()); + + } + +} \ No newline at end of file diff --git a/strategy/index.md b/strategy/README.md similarity index 77% rename from strategy/index.md rename to strategy/README.md index 120dd64d4..f07397f67 100644 --- a/strategy/index.md +++ b/strategy/README.md @@ -10,19 +10,25 @@ tags: - Gang Of Four --- -**Intent:** Define a family of algorithms, encapsulate each one, and make them +## Also known as +Policy + +## Intent +Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. ![alt text](./etc/strategy_1.png "Strategy") -**Applicability:** Use the Strategy pattern when +## Applicability +Use the Strategy pattern when * many related classes differ only in their behavior. Strategies provide a way to configure a class either one of many behaviors * you need different variants of an algorithm. for example, you might define algorithms reflecting different space/time trade-offs. Strategies can be used when these variants are implemented as a class hierarchy of algorithms * an algorithm uses data that clients shouldn't know about. Use the Strategy pattern to avoid exposing complex, algorithm-specific data structures * a class defines many behaviors, and these appear as multiple conditional statements in its operations. Instead of many conditionals, move related conditional branches into their own Strategy class -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) +* [Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions](http://www.amazon.com/Functional-Programming-Java-Harnessing-Expressions/dp/1937785467/ref=sr_1_1) diff --git a/strategy/pom.xml b/strategy/pom.xml index 0b71652ba..e6fa58081 100644 --- a/strategy/pom.xml +++ b/strategy/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT strategy @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/strategy/src/main/java/com/iluwatar/strategy/App.java b/strategy/src/main/java/com/iluwatar/strategy/App.java index b88eae242..be8826fe3 100644 --- a/strategy/src/main/java/com/iluwatar/strategy/App.java +++ b/strategy/src/main/java/com/iluwatar/strategy/App.java @@ -1,29 +1,71 @@ +/** + * The MIT License + * Copyright (c) 2014 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.strategy; /** * - * The Strategy pattern (also known as the policy pattern) is a software design pattern that - * enables an algorithm's behavior to be selected at runtime. + * The Strategy pattern (also known as the policy pattern) is a software design pattern that enables + * an algorithm's behavior to be selected at runtime. *

      - * In this example ({@link DragonSlayingStrategy}) encapsulates an algorithm. The containing - * object ({@link DragonSlayer}) can alter its behavior by changing its strategy. + * Before Java 8 the Strategies needed to be separate classes forcing the developer + * to write lots of boilerplate code. With modern Java it is easy to pass behavior + * with method references and lambdas making the code shorter and more readable. + *

      + * In this example ({@link DragonSlayingStrategy}) encapsulates an algorithm. The containing object + * ({@link DragonSlayer}) can alter its behavior by changing its strategy. * */ public class App { - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { - System.out.println("Green dragon spotted ahead!"); - DragonSlayer dragonSlayer = new DragonSlayer(new MeleeStrategy()); - dragonSlayer.goToBattle(); - System.out.println("Red dragon emerges."); - dragonSlayer.changeStrategy(new ProjectileStrategy()); - dragonSlayer.goToBattle(); - System.out.println("Black dragon lands before you."); - dragonSlayer.changeStrategy(new SpellStrategy()); - dragonSlayer.goToBattle(); - } + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + // GoF Strategy pattern + System.out.println("Green dragon spotted ahead!"); + DragonSlayer dragonSlayer = new DragonSlayer(new MeleeStrategy()); + dragonSlayer.goToBattle(); + System.out.println("Red dragon emerges."); + dragonSlayer.changeStrategy(new ProjectileStrategy()); + dragonSlayer.goToBattle(); + System.out.println("Black dragon lands before you."); + dragonSlayer.changeStrategy(new SpellStrategy()); + dragonSlayer.goToBattle(); + + // Java 8 Strategy pattern + System.out.println("Green dragon spotted ahead!"); + dragonSlayer = new DragonSlayer( + () -> System.out.println("With your Excalibur you severe the dragon's head!")); + dragonSlayer.goToBattle(); + System.out.println("Red dragon emerges."); + dragonSlayer.changeStrategy(() -> System.out.println( + "You shoot the dragon with the magical crossbow and it falls dead on the ground!")); + dragonSlayer.goToBattle(); + System.out.println("Black dragon lands before you."); + dragonSlayer.changeStrategy(() -> System.out.println( + "You cast the spell of disintegration and the dragon vaporizes in a pile of dust!")); + dragonSlayer.goToBattle(); + } } diff --git a/strategy/src/main/java/com/iluwatar/strategy/DragonSlayer.java b/strategy/src/main/java/com/iluwatar/strategy/DragonSlayer.java index fc255dee0..93214ffbf 100644 --- a/strategy/src/main/java/com/iluwatar/strategy/DragonSlayer.java +++ b/strategy/src/main/java/com/iluwatar/strategy/DragonSlayer.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.strategy; /** @@ -7,17 +29,17 @@ package com.iluwatar.strategy; */ public class DragonSlayer { - private DragonSlayingStrategy strategy; + private DragonSlayingStrategy strategy; - public DragonSlayer(DragonSlayingStrategy strategy) { - this.strategy = strategy; - } + public DragonSlayer(DragonSlayingStrategy strategy) { + this.strategy = strategy; + } - public void changeStrategy(DragonSlayingStrategy strategy) { - this.strategy = strategy; - } + public void changeStrategy(DragonSlayingStrategy strategy) { + this.strategy = strategy; + } - public void goToBattle() { - strategy.execute(); - } + public void goToBattle() { + strategy.execute(); + } } diff --git a/strategy/src/main/java/com/iluwatar/strategy/DragonSlayingStrategy.java b/strategy/src/main/java/com/iluwatar/strategy/DragonSlayingStrategy.java index 28cea2aca..23e296279 100644 --- a/strategy/src/main/java/com/iluwatar/strategy/DragonSlayingStrategy.java +++ b/strategy/src/main/java/com/iluwatar/strategy/DragonSlayingStrategy.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.strategy; /** @@ -5,8 +27,9 @@ package com.iluwatar.strategy; * Strategy interface. * */ +@FunctionalInterface public interface DragonSlayingStrategy { - void execute(); + void execute(); } diff --git a/strategy/src/main/java/com/iluwatar/strategy/MeleeStrategy.java b/strategy/src/main/java/com/iluwatar/strategy/MeleeStrategy.java index 6c2aa550e..d17ff9041 100644 --- a/strategy/src/main/java/com/iluwatar/strategy/MeleeStrategy.java +++ b/strategy/src/main/java/com/iluwatar/strategy/MeleeStrategy.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.strategy; /** @@ -7,9 +29,8 @@ package com.iluwatar.strategy; */ public class MeleeStrategy implements DragonSlayingStrategy { - @Override - public void execute() { - System.out.println("With your Excalibur you severe the dragon's head!"); - } - + @Override + public void execute() { + System.out.println("With your Excalibur you sever the dragon's head!"); + } } diff --git a/strategy/src/main/java/com/iluwatar/strategy/ProjectileStrategy.java b/strategy/src/main/java/com/iluwatar/strategy/ProjectileStrategy.java index eab93b16e..7cd731167 100644 --- a/strategy/src/main/java/com/iluwatar/strategy/ProjectileStrategy.java +++ b/strategy/src/main/java/com/iluwatar/strategy/ProjectileStrategy.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.strategy; /** @@ -7,10 +29,9 @@ package com.iluwatar.strategy; */ public class ProjectileStrategy implements DragonSlayingStrategy { - @Override - public void execute() { - System.out - .println("You shoot the dragon with the magical crossbow and it falls dead on the ground!"); - } - + @Override + public void execute() { + System.out + .println("You shoot the dragon with the magical crossbow and it falls dead on the ground!"); + } } diff --git a/strategy/src/main/java/com/iluwatar/strategy/SpellStrategy.java b/strategy/src/main/java/com/iluwatar/strategy/SpellStrategy.java index 4a74b9b0d..6309ed31b 100644 --- a/strategy/src/main/java/com/iluwatar/strategy/SpellStrategy.java +++ b/strategy/src/main/java/com/iluwatar/strategy/SpellStrategy.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.strategy; /** @@ -7,10 +29,10 @@ package com.iluwatar.strategy; */ public class SpellStrategy implements DragonSlayingStrategy { - @Override - public void execute() { - System.out - .println("You cast the spell of disintegration and the dragon vaporizes in a pile of dust!"); - } + @Override + public void execute() { + System.out + .println("You cast the spell of disintegration and the dragon vaporizes in a pile of dust!"); + } } diff --git a/strategy/src/test/java/com/iluwatar/strategy/AppTest.java b/strategy/src/test/java/com/iluwatar/strategy/AppTest.java index e6748d30d..fa81ae747 100644 --- a/strategy/src/test/java/com/iluwatar/strategy/AppTest.java +++ b/strategy/src/test/java/com/iluwatar/strategy/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.strategy; import org.junit.Test; -import com.iluwatar.strategy.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.strategy.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/strategy/src/test/java/com/iluwatar/strategy/DragonSlayerTest.java b/strategy/src/test/java/com/iluwatar/strategy/DragonSlayerTest.java new file mode 100644 index 000000000..ff7e6840a --- /dev/null +++ b/strategy/src/test/java/com/iluwatar/strategy/DragonSlayerTest.java @@ -0,0 +1,70 @@ +/** + * The MIT License + * Copyright (c) 2014 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.strategy; + +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/29/15 - 10:50 PM + * + * @author Jeroen Meulemeester + */ +public class DragonSlayerTest { + + /** + * Verify if the dragon slayer uses the strategy during battle + */ + @Test + public void testGoToBattle() { + final DragonSlayingStrategy strategy = mock(DragonSlayingStrategy.class); + final DragonSlayer dragonSlayer = new DragonSlayer(strategy); + + dragonSlayer.goToBattle(); + verify(strategy).execute(); + verifyNoMoreInteractions(strategy); + } + + /** + * Verify if the dragon slayer uses the new strategy during battle after a change of strategy + */ + @Test + public void testChangeStrategy() throws Exception { + final DragonSlayingStrategy initialStrategy = mock(DragonSlayingStrategy.class); + final DragonSlayer dragonSlayer = new DragonSlayer(initialStrategy); + + dragonSlayer.goToBattle(); + verify(initialStrategy).execute(); + + final DragonSlayingStrategy newStrategy = mock(DragonSlayingStrategy.class); + dragonSlayer.changeStrategy(newStrategy); + + dragonSlayer.goToBattle(); + verify(newStrategy).execute(); + + verifyNoMoreInteractions(initialStrategy, newStrategy); + } +} \ No newline at end of file diff --git a/strategy/src/test/java/com/iluwatar/strategy/DragonSlayingStrategyTest.java b/strategy/src/test/java/com/iluwatar/strategy/DragonSlayingStrategyTest.java new file mode 100644 index 000000000..35f4c1a82 --- /dev/null +++ b/strategy/src/test/java/com/iluwatar/strategy/DragonSlayingStrategyTest.java @@ -0,0 +1,126 @@ +/** + * The MIT License + * Copyright (c) 2014 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.strategy; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.io.PrintStream; +import java.util.Arrays; +import java.util.Collection; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/29/15 - 10:58 PM + * + * @author Jeroen Meulemeester + */ +@RunWith(Parameterized.class) +public class DragonSlayingStrategyTest { + + /** + * @return The test parameters for each cycle + */ + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList( + new Object[]{ + new MeleeStrategy(), + "With your Excalibur you sever the dragon's head!" + }, + new Object[]{ + new ProjectileStrategy(), + "You shoot the dragon with the magical crossbow and it falls dead on the ground!" + }, + new Object[]{ + new SpellStrategy(), + "You cast the spell of disintegration and the dragon vaporizes in a pile of dust!" + } + ); + } + + /** + * The tested strategy + */ + private final DragonSlayingStrategy strategy; + + /** + * The expected action on the std-out + */ + private final String expectedResult; + + /** + * The mocked standard out {@link PrintStream}, required since some actions don't have any + * influence on accessible objects, except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Create a new test instance for the given strategy + * + * @param strategy The tested strategy + * @param expectedResult The expected result + */ + public DragonSlayingStrategyTest(final DragonSlayingStrategy strategy, final String expectedResult) { + this.strategy = strategy; + this.expectedResult = expectedResult; + } + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Test if executing the strategy gives the correct response + */ + @Test + public void testExecute() { + this.strategy.execute(); + verify(this.stdOutMock).println(this.expectedResult); + verifyNoMoreInteractions(this.stdOutMock); + } + +} \ No newline at end of file diff --git a/template-method/index.md b/template-method/README.md similarity index 89% rename from template-method/index.md rename to template-method/README.md index 8b8b7878e..ad972f06b 100644 --- a/template-method/index.md +++ b/template-method/README.md @@ -10,18 +10,20 @@ tags: - Gang Of Four --- -**Intent:** Define the skeleton of an algorithm in an operation, deferring some +## Intent +Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure. ![alt text](./etc/template-method_1.png "Template Method") -**Applicability:** The Template Method pattern should be used +## Applicability +The Template Method pattern should be used * to implement the invariant parts of an algorithm once and leave it up to subclasses to implement the behavior that can vary * when common behavior among subclasses should be factored and localized in a common class to avoid code duplication. This is good example of "refactoring to generalize" as described by Opdyke and Johnson. You first identify the differences in the existing code and then separate the differences into new operations. Finally, you replace the differing code with a template method that calls one of these new operations * to control subclasses extensions. You can define a template method that calls "hook" operations at specific points, thereby permitting extensions only at those points -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/template-method/pom.xml b/template-method/pom.xml index 4aa776591..ee533df8f 100644 --- a/template-method/pom.xml +++ b/template-method/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT template-method @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/template-method/src/main/java/com/iluwatar/templatemethod/App.java b/template-method/src/main/java/com/iluwatar/templatemethod/App.java index c9927e91b..ead9c64f4 100644 --- a/template-method/src/main/java/com/iluwatar/templatemethod/App.java +++ b/template-method/src/main/java/com/iluwatar/templatemethod/App.java @@ -1,24 +1,47 @@ +/** + * The MIT License + * Copyright (c) 2014 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.templatemethod; /** * - * Template Method defines a skeleton for an algorithm. The algorithm subclasses - * provide implementation for the blank parts. + * Template Method defines a skeleton for an algorithm. The algorithm subclasses provide + * implementation for the blank parts. *

      - * In this example {@link HalflingThief} contains {@link StealingMethod} that can be changed. - * First the thief hits with {@link HitAndRunMethod} and then with {@link SubtleMethod}. + * In this example {@link HalflingThief} contains {@link StealingMethod} that can be changed. First + * the thief hits with {@link HitAndRunMethod} and then with {@link SubtleMethod}. * */ public class App { - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { - HalflingThief thief = new HalflingThief(new HitAndRunMethod()); - thief.steal(); - thief.changeMethod(new SubtleMethod()); - thief.steal(); - } + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { + HalflingThief thief = new HalflingThief(new HitAndRunMethod()); + thief.steal(); + thief.changeMethod(new SubtleMethod()); + thief.steal(); + } } diff --git a/template-method/src/main/java/com/iluwatar/templatemethod/HalflingThief.java b/template-method/src/main/java/com/iluwatar/templatemethod/HalflingThief.java index 4d6cecb9f..e776044f6 100644 --- a/template-method/src/main/java/com/iluwatar/templatemethod/HalflingThief.java +++ b/template-method/src/main/java/com/iluwatar/templatemethod/HalflingThief.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.templatemethod; /** @@ -7,17 +29,17 @@ package com.iluwatar.templatemethod; */ public class HalflingThief { - private StealingMethod method; + private StealingMethod method; - public HalflingThief(StealingMethod method) { - this.method = method; - } + public HalflingThief(StealingMethod method) { + this.method = method; + } - public void steal() { - method.steal(); - } + public void steal() { + method.steal(); + } - public void changeMethod(StealingMethod method) { - this.method = method; - } + public void changeMethod(StealingMethod method) { + this.method = method; + } } diff --git a/template-method/src/main/java/com/iluwatar/templatemethod/HitAndRunMethod.java b/template-method/src/main/java/com/iluwatar/templatemethod/HitAndRunMethod.java index 633e52895..7a78576a1 100644 --- a/template-method/src/main/java/com/iluwatar/templatemethod/HitAndRunMethod.java +++ b/template-method/src/main/java/com/iluwatar/templatemethod/HitAndRunMethod.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.templatemethod; /** @@ -7,19 +29,18 @@ package com.iluwatar.templatemethod; */ public class HitAndRunMethod extends StealingMethod { - @Override - protected String pickTarget() { - return "old goblin woman"; - } + @Override + protected String pickTarget() { + return "old goblin woman"; + } - @Override - protected void confuseTarget(String target) { - System.out.println("Approach the " + target + " from behind."); - } - - @Override - protected void stealTheItem(String target) { - System.out.println("Grab the handbag and run away fast!"); - } + @Override + protected void confuseTarget(String target) { + System.out.println("Approach the " + target + " from behind."); + } + @Override + protected void stealTheItem(String target) { + System.out.println("Grab the handbag and run away fast!"); + } } diff --git a/template-method/src/main/java/com/iluwatar/templatemethod/StealingMethod.java b/template-method/src/main/java/com/iluwatar/templatemethod/StealingMethod.java index 20e077750..c8c584cdd 100644 --- a/template-method/src/main/java/com/iluwatar/templatemethod/StealingMethod.java +++ b/template-method/src/main/java/com/iluwatar/templatemethod/StealingMethod.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.templatemethod; /** @@ -7,16 +29,19 @@ package com.iluwatar.templatemethod; */ public abstract class StealingMethod { - protected abstract String pickTarget(); + protected abstract String pickTarget(); - protected abstract void confuseTarget(String target); + protected abstract void confuseTarget(String target); - protected abstract void stealTheItem(String target); + protected abstract void stealTheItem(String target); - public void steal() { - String target = pickTarget(); - System.out.println("The target has been chosen as " + target + "."); - confuseTarget(target); - stealTheItem(target); - } + /** + * Steal + */ + public void steal() { + String target = pickTarget(); + System.out.println("The target has been chosen as " + target + "."); + confuseTarget(target); + stealTheItem(target); + } } diff --git a/template-method/src/main/java/com/iluwatar/templatemethod/SubtleMethod.java b/template-method/src/main/java/com/iluwatar/templatemethod/SubtleMethod.java index f506d682b..4fdb5d758 100644 --- a/template-method/src/main/java/com/iluwatar/templatemethod/SubtleMethod.java +++ b/template-method/src/main/java/com/iluwatar/templatemethod/SubtleMethod.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.templatemethod; /** @@ -7,21 +29,18 @@ package com.iluwatar.templatemethod; */ public class SubtleMethod extends StealingMethod { - @Override - protected String pickTarget() { - return "shop keeper"; - } + @Override + protected String pickTarget() { + return "shop keeper"; + } - @Override - protected void confuseTarget(String target) { - System.out.println("Approach the " + target - + " with tears running and hug him!"); - } - - @Override - protected void stealTheItem(String target) { - System.out.println("While in close contact grab the " + target - + "'s wallet."); - } + @Override + protected void confuseTarget(String target) { + System.out.println("Approach the " + target + " with tears running and hug him!"); + } + @Override + protected void stealTheItem(String target) { + System.out.println("While in close contact grab the " + target + "'s wallet."); + } } diff --git a/template-method/src/test/java/com/iluwatar/templatemethod/AppTest.java b/template-method/src/test/java/com/iluwatar/templatemethod/AppTest.java index bd4e2d332..e0baf40cb 100644 --- a/template-method/src/test/java/com/iluwatar/templatemethod/AppTest.java +++ b/template-method/src/test/java/com/iluwatar/templatemethod/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.templatemethod; import org.junit.Test; -import com.iluwatar.templatemethod.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.templatemethod.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/template-method/src/test/java/com/iluwatar/templatemethod/HalflingThiefTest.java b/template-method/src/test/java/com/iluwatar/templatemethod/HalflingThiefTest.java new file mode 100644 index 000000000..31cb078e3 --- /dev/null +++ b/template-method/src/test/java/com/iluwatar/templatemethod/HalflingThiefTest.java @@ -0,0 +1,72 @@ +/** + * The MIT License + * Copyright (c) 2014 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.templatemethod; + +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/29/15 - 18:15 PM + * + * @author Jeroen Meulemeester + */ +public class HalflingThiefTest { + + /** + * Verify if the thief uses the provided stealing method + */ + @Test + public void testSteal() { + final StealingMethod method = mock(StealingMethod.class); + final HalflingThief thief = new HalflingThief(method); + + thief.steal(); + verify(method).steal(); + + verifyNoMoreInteractions(method); + } + + /** + * Verify if the thief uses the provided stealing method, and the new method after changing it + */ + @Test + public void testChangeMethod() { + final StealingMethod initialMethod = mock(StealingMethod.class); + final HalflingThief thief = new HalflingThief(initialMethod); + + thief.steal(); + verify(initialMethod).steal(); + + final StealingMethod newMethod = mock(StealingMethod.class); + thief.changeMethod(newMethod); + + thief.steal(); + verify(newMethod).steal(); + + verifyNoMoreInteractions(initialMethod, newMethod); + + } +} \ No newline at end of file diff --git a/template-method/src/test/java/com/iluwatar/templatemethod/HitAndRunMethodTest.java b/template-method/src/test/java/com/iluwatar/templatemethod/HitAndRunMethodTest.java new file mode 100644 index 000000000..27d601ac3 --- /dev/null +++ b/template-method/src/test/java/com/iluwatar/templatemethod/HitAndRunMethodTest.java @@ -0,0 +1,45 @@ +/** + * The MIT License + * Copyright (c) 2014 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.templatemethod; + +/** + * Date: 12/30/15 - 18:12 PM + * + * @author Jeroen Meulemeester + */ +public class HitAndRunMethodTest extends StealingMethodTest { + + /** + * Create a new test for the {@link HitAndRunMethod} + */ + public HitAndRunMethodTest() { + super( + new HitAndRunMethod(), + "old goblin woman", + "The target has been chosen as old goblin woman.", + "Approach the old goblin woman from behind.", + "Grab the handbag and run away fast!" + ); + } + +} \ No newline at end of file diff --git a/template-method/src/test/java/com/iluwatar/templatemethod/StealingMethodTest.java b/template-method/src/test/java/com/iluwatar/templatemethod/StealingMethodTest.java new file mode 100644 index 000000000..e0cb90d42 --- /dev/null +++ b/template-method/src/test/java/com/iluwatar/templatemethod/StealingMethodTest.java @@ -0,0 +1,164 @@ +/** + * The MIT License + * Copyright (c) 2014 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.templatemethod; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.InOrder; + +import java.io.PrintStream; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/30/15 - 18:12 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StealingMethodTest { + + /** + * The tested stealing method + */ + private final M method; + + /** + * The expected target + */ + private final String expectedTarget; + + /** + * The expected target picking result + */ + private final String expectedTargetResult; + + /** + * The expected confusion method + */ + private final String expectedConfuseMethod; + + /** + * The expected stealing method + */ + private final String expectedStealMethod; + + /** + * The mocked standard out {@link PrintStream}, required since some actions don't have any + * influence on accessible objects, except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Create a new test for the given stealing method, together with the expected results + * + * @param method The tested stealing method + * @param expectedTarget The expected target name + * @param expectedTargetResult The expected target picking result + * @param expectedConfuseMethod The expected confusion method + * @param expectedStealMethod The expected stealing method + */ + public StealingMethodTest(final M method, String expectedTarget, final String expectedTargetResult, + final String expectedConfuseMethod, final String expectedStealMethod) { + + this.method = method; + this.expectedTarget = expectedTarget; + this.expectedTargetResult = expectedTargetResult; + this.expectedConfuseMethod = expectedConfuseMethod; + this.expectedStealMethod = expectedStealMethod; + } + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Verify if the thief picks the correct target + */ + @Test + public void testPickTarget() { + assertEquals(expectedTarget, this.method.pickTarget()); + } + + /** + * Verify if the target confusing step goes as planned + */ + @Test + public void testConfuseTarget() { + verifyZeroInteractions(this.stdOutMock); + + this.method.confuseTarget(this.expectedTarget); + verify(this.stdOutMock).println(this.expectedConfuseMethod); + verifyNoMoreInteractions(this.stdOutMock); + } + + /** + * Verify if the stealing step goes as planned + */ + @Test + public void testStealTheItem() { + verifyZeroInteractions(this.stdOutMock); + + this.method.stealTheItem(this.expectedTarget); + verify(this.stdOutMock).println(this.expectedStealMethod); + verifyNoMoreInteractions(this.stdOutMock); + } + + /** + * Verify if the complete steal process goes as planned + */ + @Test + public void testSteal() { + final InOrder inOrder = inOrder(this.stdOutMock); + + this.method.steal(); + + inOrder.verify(this.stdOutMock).println(this.expectedTargetResult); + inOrder.verify(this.stdOutMock).println(this.expectedConfuseMethod); + inOrder.verify(this.stdOutMock).println(this.expectedStealMethod); + inOrder.verifyNoMoreInteractions(); + } + +} \ No newline at end of file diff --git a/template-method/src/test/java/com/iluwatar/templatemethod/SubtleMethodTest.java b/template-method/src/test/java/com/iluwatar/templatemethod/SubtleMethodTest.java new file mode 100644 index 000000000..78c86adfc --- /dev/null +++ b/template-method/src/test/java/com/iluwatar/templatemethod/SubtleMethodTest.java @@ -0,0 +1,45 @@ +/** + * The MIT License + * Copyright (c) 2014 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.templatemethod; + +/** + * Date: 12/30/15 - 18:19 PM + * + * @author Jeroen Meulemeester + */ +public class SubtleMethodTest extends StealingMethodTest { + + /** + * Create a new test for the {@link SubtleMethod} + */ + public SubtleMethodTest() { + super( + new SubtleMethod(), + "shop keeper", + "The target has been chosen as shop keeper.", + "Approach the shop keeper with tears running and hug him!", + "While in close contact grab the shop keeper's wallet." + ); + } + +} \ No newline at end of file diff --git a/thread-pool/index.md b/thread-pool/README.md similarity index 74% rename from thread-pool/index.md rename to thread-pool/README.md index d9f00f428..9806fa8e0 100644 --- a/thread-pool/index.md +++ b/thread-pool/README.md @@ -4,10 +4,14 @@ title: Thread Pool folder: thread-pool permalink: /patterns/thread-pool/ categories: Concurrency -tags: Java +tags: + - Java + - Difficulty-Intermediate + - Performance --- -**Intent:** It is often the case that tasks to be executed are short-lived and +## Intent +It is often the case that tasks to be executed are short-lived and the number of tasks is large. Creating a new thread for each task would make the system spend more time creating and destroying the threads than executing the actual tasks. Thread Pool solves this problem by reusing existing threads @@ -15,6 +19,7 @@ and eliminating the latency of creating new threads. ![alt text](./etc/thread-pool.png "Thread Pool") -**Applicability:** Use the Thread Pool pattern when +## Applicability +Use the Thread Pool pattern when * you have a large number of short-lived tasks to be executed in parallel diff --git a/thread-pool/pom.xml b/thread-pool/pom.xml index e335b06e5..e7fa43103 100644 --- a/thread-pool/pom.xml +++ b/thread-pool/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT thread-pool @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/thread-pool/src/main/java/com/iluwatar/threadpool/App.java b/thread-pool/src/main/java/com/iluwatar/threadpool/App.java index bfaaead31..16fbca35a 100644 --- a/thread-pool/src/main/java/com/iluwatar/threadpool/App.java +++ b/thread-pool/src/main/java/com/iluwatar/threadpool/App.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.threadpool; import java.util.ArrayList; @@ -7,64 +29,66 @@ import java.util.concurrent.Executors; /** * - * Thread Pool pattern is where a number of threads are created to perform a number of tasks, - * which are usually organized in a queue. The results from the tasks being executed might - * also be placed in a queue, or the tasks might return no result. Typically, there are many - * more tasks than threads. As soon as a thread completes its task, it will request the next - * task from the queue until all tasks have been completed. The thread can then terminate, or - * sleep until there are new tasks available. - *

      - * In this example we create a list of tasks presenting work to be done. Each task is then - * wrapped into a {@link Worker} object that implements {@link Runnable}. We create an - * {@link ExecutorService} with fixed number of threads (Thread Pool) and use them to execute - * the {@link Worker}s. + * Thread Pool pattern is where a number of threads are created to perform a number of tasks, which + * are usually organized in a queue. The results from the tasks being executed might also be placed + * in a queue, or the tasks might return no result. Typically, there are many more tasks than + * threads. As soon as a thread completes its task, it will request the next task from the queue + * until all tasks have been completed. The thread can then terminate, or sleep until there are new + * tasks available. + *

      + * In this example we create a list of tasks presenting work to be done. Each task is then wrapped + * into a {@link Worker} object that implements {@link Runnable}. We create an + * {@link ExecutorService} with fixed number of threads (Thread Pool) and use them to execute the + * {@link Worker}s. * */ public class App { - - /** - * Program entry point - * @param args command line args - */ - public static void main( String[] args ) { - - System.out.println("Program started"); - - // Create a list of tasks to be executed - List tasks = new ArrayList<>(); - tasks.add(new PotatoPeelingTask(3)); - tasks.add(new PotatoPeelingTask(6)); - tasks.add(new CoffeeMakingTask(2)); - tasks.add(new CoffeeMakingTask(6)); - tasks.add(new PotatoPeelingTask(4)); - tasks.add(new CoffeeMakingTask(2)); - tasks.add(new PotatoPeelingTask(4)); - tasks.add(new CoffeeMakingTask(9)); - tasks.add(new PotatoPeelingTask(3)); - tasks.add(new CoffeeMakingTask(2)); - tasks.add(new PotatoPeelingTask(4)); - tasks.add(new CoffeeMakingTask(2)); - tasks.add(new CoffeeMakingTask(7)); - tasks.add(new PotatoPeelingTask(4)); - tasks.add(new PotatoPeelingTask(5)); - - // Creates a thread pool that reuses a fixed number of threads operating off a shared - // unbounded queue. At any point, at most nThreads threads will be active processing - // tasks. If additional tasks are submitted when all threads are active, they will wait - // in the queue until a thread is available. - ExecutorService executor = Executors.newFixedThreadPool(3); - - // Allocate new worker for each task - // The worker is executed when a thread becomes - // available in the thread pool - for (int i=0; i tasks = new ArrayList<>(); + tasks.add(new PotatoPeelingTask(3)); + tasks.add(new PotatoPeelingTask(6)); + tasks.add(new CoffeeMakingTask(2)); + tasks.add(new CoffeeMakingTask(6)); + tasks.add(new PotatoPeelingTask(4)); + tasks.add(new CoffeeMakingTask(2)); + tasks.add(new PotatoPeelingTask(4)); + tasks.add(new CoffeeMakingTask(9)); + tasks.add(new PotatoPeelingTask(3)); + tasks.add(new CoffeeMakingTask(2)); + tasks.add(new PotatoPeelingTask(4)); + tasks.add(new CoffeeMakingTask(2)); + tasks.add(new CoffeeMakingTask(7)); + tasks.add(new PotatoPeelingTask(4)); + tasks.add(new PotatoPeelingTask(5)); + + // Creates a thread pool that reuses a fixed number of threads operating off a shared + // unbounded queue. At any point, at most nThreads threads will be active processing + // tasks. If additional tasks are submitted when all threads are active, they will wait + // in the queue until a thread is available. + ExecutorService executor = Executors.newFixedThreadPool(3); + + // Allocate new worker for each task + // The worker is executed when a thread becomes + // available in the thread pool + for (int i = 0; i < tasks.size(); i++) { + Runnable worker = new Worker(tasks.get(i)); + executor.execute(worker); } + // All tasks were executed, now shutdown + executor.shutdown(); + while (!executor.isTerminated()) { + Thread.yield(); + } + System.out.println("Program finished"); + } } diff --git a/thread-pool/src/main/java/com/iluwatar/threadpool/CoffeeMakingTask.java b/thread-pool/src/main/java/com/iluwatar/threadpool/CoffeeMakingTask.java index 9bbabfd0d..fce9ada9c 100644 --- a/thread-pool/src/main/java/com/iluwatar/threadpool/CoffeeMakingTask.java +++ b/thread-pool/src/main/java/com/iluwatar/threadpool/CoffeeMakingTask.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.threadpool; /** @@ -7,14 +29,14 @@ package com.iluwatar.threadpool; */ public class CoffeeMakingTask extends Task { - private static final int TIME_PER_CUP = 300; - - public CoffeeMakingTask(int numCups) { - super(numCups * TIME_PER_CUP); - } + private static final int TIME_PER_CUP = 100; - @Override - public String toString() { - return String.format("%s %s", this.getClass().getSimpleName(), super.toString()); - } + public CoffeeMakingTask(int numCups) { + super(numCups * TIME_PER_CUP); + } + + @Override + public String toString() { + return String.format("%s %s", this.getClass().getSimpleName(), super.toString()); + } } diff --git a/thread-pool/src/main/java/com/iluwatar/threadpool/PotatoPeelingTask.java b/thread-pool/src/main/java/com/iluwatar/threadpool/PotatoPeelingTask.java index 6b2169961..e55debe28 100644 --- a/thread-pool/src/main/java/com/iluwatar/threadpool/PotatoPeelingTask.java +++ b/thread-pool/src/main/java/com/iluwatar/threadpool/PotatoPeelingTask.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.threadpool; /** @@ -7,14 +29,14 @@ package com.iluwatar.threadpool; */ public class PotatoPeelingTask extends Task { - private static final int TIME_PER_POTATO = 500; - - public PotatoPeelingTask(int numPotatoes) { - super(numPotatoes * TIME_PER_POTATO); - } - - @Override - public String toString() { - return String.format("%s %s", this.getClass().getSimpleName(), super.toString()); - } + private static final int TIME_PER_POTATO = 200; + + public PotatoPeelingTask(int numPotatoes) { + super(numPotatoes * TIME_PER_POTATO); + } + + @Override + public String toString() { + return String.format("%s %s", this.getClass().getSimpleName(), super.toString()); + } } diff --git a/thread-pool/src/main/java/com/iluwatar/threadpool/Task.java b/thread-pool/src/main/java/com/iluwatar/threadpool/Task.java index 4766b6eee..623d2b78e 100644 --- a/thread-pool/src/main/java/com/iluwatar/threadpool/Task.java +++ b/thread-pool/src/main/java/com/iluwatar/threadpool/Task.java @@ -1,32 +1,56 @@ +/** + * The MIT License + * Copyright (c) 2014 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.threadpool; +import java.util.concurrent.atomic.AtomicInteger; + /** - * + * * Abstract base class for tasks * */ public abstract class Task { - private static int nextId = 1; - - private final int id; - private final int timeMs; - - public Task(final int timeMs) { - this.id = nextId++; - this.timeMs = timeMs; - } - - public int getId() { - return id; - } - - public int getTimeMs() { - return timeMs; - } - - @Override - public String toString() { - return String.format("id=%d timeMs=%d", id, timeMs); - } + private static final AtomicInteger ID_GENERATOR = new AtomicInteger(); + + private final int id; + private final int timeMs; + + public Task(final int timeMs) { + this.id = ID_GENERATOR.incrementAndGet(); + this.timeMs = timeMs; + } + + public int getId() { + return id; + } + + public int getTimeMs() { + return timeMs; + } + + @Override + public String toString() { + return String.format("id=%d timeMs=%d", id, timeMs); + } } diff --git a/thread-pool/src/main/java/com/iluwatar/threadpool/Worker.java b/thread-pool/src/main/java/com/iluwatar/threadpool/Worker.java index 92da4b5dd..1354cab41 100644 --- a/thread-pool/src/main/java/com/iluwatar/threadpool/Worker.java +++ b/thread-pool/src/main/java/com/iluwatar/threadpool/Worker.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.threadpool; /** @@ -6,20 +28,21 @@ package com.iluwatar.threadpool; * */ public class Worker implements Runnable { - - private final Task task; - public Worker(final Task task) { - this.task = task; - } - - @Override - public void run() { - System.out.println(String.format("%s processing %s", Thread.currentThread().getName(), task.toString())); - try { - Thread.sleep(task.getTimeMs()); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } + private final Task task; + + public Worker(final Task task) { + this.task = task; + } + + @Override + public void run() { + System.out.println(String.format("%s processing %s", Thread.currentThread().getName(), + task.toString())); + try { + Thread.sleep(task.getTimeMs()); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } } diff --git a/thread-pool/src/test/java/com/iluwatar/threadpool/AppTest.java b/thread-pool/src/test/java/com/iluwatar/threadpool/AppTest.java index 71cfea5f7..5536d6631 100644 --- a/thread-pool/src/test/java/com/iluwatar/threadpool/AppTest.java +++ b/thread-pool/src/test/java/com/iluwatar/threadpool/AppTest.java @@ -1,19 +1,40 @@ +/** + * The MIT License + * Copyright (c) 2014 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.threadpool; import org.junit.Test; -import com.iluwatar.threadpool.App; - /** * Application test + * * @author ilkka * */ public class AppTest { - - @Test - public void test() { - String[] args = {}; - App.main(args); - } + + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/thread-pool/src/test/java/com/iluwatar/threadpool/CoffeeMakingTaskTest.java b/thread-pool/src/test/java/com/iluwatar/threadpool/CoffeeMakingTaskTest.java new file mode 100644 index 000000000..281ef16ad --- /dev/null +++ b/thread-pool/src/test/java/com/iluwatar/threadpool/CoffeeMakingTaskTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 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.threadpool; + +/** + * Date: 12/30/15 - 18:23 PM + * + * @author Jeroen Meulemeester + */ +public class CoffeeMakingTaskTest extends TaskTest { + + /** + * Create a new test instance + */ + public CoffeeMakingTaskTest() { + super(CoffeeMakingTask::new, 100); + } + +} diff --git a/thread-pool/src/test/java/com/iluwatar/threadpool/PotatoPeelingTaskTest.java b/thread-pool/src/test/java/com/iluwatar/threadpool/PotatoPeelingTaskTest.java new file mode 100644 index 000000000..d27e6ba5d --- /dev/null +++ b/thread-pool/src/test/java/com/iluwatar/threadpool/PotatoPeelingTaskTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 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.threadpool; + +/** + * Date: 12/30/15 - 18:23 PM + * + * @author Jeroen Meulemeester + */ +public class PotatoPeelingTaskTest extends TaskTest { + + /** + * Create a new test instance + */ + public PotatoPeelingTaskTest() { + super(PotatoPeelingTask::new, 200); + } + +} \ No newline at end of file diff --git a/thread-pool/src/test/java/com/iluwatar/threadpool/TaskTest.java b/thread-pool/src/test/java/com/iluwatar/threadpool/TaskTest.java new file mode 100644 index 000000000..ded3e9d42 --- /dev/null +++ b/thread-pool/src/test/java/com/iluwatar/threadpool/TaskTest.java @@ -0,0 +1,143 @@ +/** + * The MIT License + * Copyright (c) 2014 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.threadpool; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * Date: 12/30/15 - 18:22 PM + * + * @author Jeroen Meulemeester + */ +public abstract class TaskTest { + + /** + * The number of tasks used during the concurrency test + */ + private static final int TASK_COUNT = 128 * 1024; + + /** + * The number of threads used during the concurrency test + */ + private static final int THREAD_COUNT = 8; + + /** + * The task factory, used to create new test items + */ + private final Function factory; + + /** + * The expected time needed to run the task 1 single time, in milli seconds + */ + private final int expectedExecutionTime; + + /** + * Create a new test instance + * + * @param factory The task factory, used to create new test items + * @param expectedExecutionTime The expected time needed to run the task 1 time, in milli seconds + */ + public TaskTest(final Function factory, final int expectedExecutionTime) { + this.factory = factory; + this.expectedExecutionTime = expectedExecutionTime; + } + + /** + * Verify if the generated id is unique for each task, even if the tasks are created in separate + * threads + */ + @Test(timeout = 10000) + public void testIdGeneration() throws Exception { + final ExecutorService service = Executors.newFixedThreadPool(THREAD_COUNT); + + final List> tasks = new ArrayList<>(); + for (int i = 0; i < TASK_COUNT; i++) { + tasks.add(() -> factory.apply(1).getId()); + } + + final List ids = service.invokeAll(tasks) + .stream() + .map(TaskTest::get) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + service.shutdownNow(); + + final long uniqueIdCount = ids.stream() + .distinct() + .count(); + + assertEquals(TASK_COUNT, ids.size()); + assertEquals(TASK_COUNT, uniqueIdCount); + + } + + /** + * Verify if the time per execution of a task matches the actual time required to execute the task + * a given number of times + */ + @Test + public void testTimeMs() { + for (int i = 0; i < 10; i++) { + assertEquals(this.expectedExecutionTime * i, this.factory.apply(i).getTimeMs()); + } + } + + /** + * Verify if the task has some sort of {@link T#toString()}, different from 'null' + */ + @Test + public void testToString() { + assertNotNull(this.factory.apply(0).toString()); + } + + /** + * Extract the result from a future or returns 'null' when an exception occurred + * + * @param future The future we want the result from + * @param The result type + * @return The result or 'null' when a checked exception occurred + */ + private static O get(Future future) { + try { + return future.get(); + } catch (InterruptedException | ExecutionException e) { + return null; + } + } + +} \ No newline at end of file diff --git a/thread-pool/src/test/java/com/iluwatar/threadpool/WorkerTest.java b/thread-pool/src/test/java/com/iluwatar/threadpool/WorkerTest.java new file mode 100644 index 000000000..24fe87548 --- /dev/null +++ b/thread-pool/src/test/java/com/iluwatar/threadpool/WorkerTest.java @@ -0,0 +1,53 @@ +/** + * The MIT License + * Copyright (c) 2014 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.threadpool; + +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/30/15 - 18:21 PM + * + * @author Jeroen Meulemeester + */ +public class WorkerTest { + + /** + * Verify if a worker does the actual job + */ + @Test + public void testRun() { + final Task task = mock(Task.class); + final Worker worker = new Worker(task); + verifyZeroInteractions(task); + + worker.run(); + verify(task).getTimeMs(); + verifyNoMoreInteractions(task); + } + +} \ No newline at end of file diff --git a/tolerant-reader/index.md b/tolerant-reader/README.md similarity index 76% rename from tolerant-reader/index.md rename to tolerant-reader/README.md index b2bfd376a..be0085f2c 100644 --- a/tolerant-reader/index.md +++ b/tolerant-reader/README.md @@ -4,20 +4,24 @@ title: Tolerant Reader folder: tolerant-reader permalink: /patterns/tolerant-reader/ categories: Integration -tags: Java +tags: + - Java + - Difficulty-Beginner --- -**Intent:** Tolerant Reader is an integration pattern that helps creating +## Intent +Tolerant Reader is an integration pattern that helps creating robust communication systems. The idea is to be as tolerant as possible when reading data from another service. This way, when the communication schema changes, the readers must not break. ![alt text](./etc/tolerant-reader.png "Tolerant Reader") -**Applicability:** Use the Tolerant Reader pattern when +## Applicability +Use the Tolerant Reader pattern when * the communication schema can evolve and change and yet the receiving side should not break -**Credits:** +## Credits * [Martin Fowler - Tolerant Reader](http://martinfowler.com/bliki/TolerantReader.html) diff --git a/tolerant-reader/pom.xml b/tolerant-reader/pom.xml index 086251772..e5dd3ba88 100644 --- a/tolerant-reader/pom.xml +++ b/tolerant-reader/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT tolerant-reader diff --git a/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/App.java b/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/App.java index d4ff933d7..066b6e737 100644 --- a/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/App.java +++ b/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/App.java @@ -1,42 +1,71 @@ +/** + * The MIT License + * Copyright (c) 2014 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.tolerantreader; import java.io.IOException; /** * - * Tolerant Reader is an integration pattern that helps creating robust communication - * systems. The idea is to be as tolerant as possible when reading data from another - * service. This way, when the communication schema changes, the readers must not break. + * Tolerant Reader is an integration pattern that helps creating robust communication systems. The + * idea is to be as tolerant as possible when reading data from another service. This way, when the + * communication schema changes, the readers must not break. *

      - * In this example we use Java serialization to write representations of {@link RainbowFish} - * objects to file. {@link RainbowFish} is the initial version which we can easily read and - * write using {@link RainbowFishSerializer} methods. {@link RainbowFish} then evolves to {@link RainbowFishV2} - * and we again write it to file with a method designed to do just that. However, the reader - * client does not know about the new format and still reads with the method designed for - * V1 schema. Fortunately the reading method has been designed with the Tolerant Reader - * pattern and does not break even though {@link RainbowFishV2} has new fields that are serialized. + * In this example we use Java serialization to write representations of {@link RainbowFish} objects + * to file. {@link RainbowFish} is the initial version which we can easily read and write using + * {@link RainbowFishSerializer} methods. {@link RainbowFish} then evolves to {@link RainbowFishV2} + * and we again write it to file with a method designed to do just that. However, the reader client + * does not know about the new format and still reads with the method designed for V1 schema. + * Fortunately the reading method has been designed with the Tolerant Reader pattern and does not + * break even though {@link RainbowFishV2} has new fields that are serialized. * */ public class App { - - public static void main( String[] args ) throws IOException, ClassNotFoundException { - // Write V1 - RainbowFish fishV1 = new RainbowFish("Zed", 10, 11, 12); - System.out.println(String.format("fishV1 name=%s age=%d length=%d weight=%d", fishV1.getName(), - fishV1.getAge(), fishV1.getLengthMeters(), fishV1.getWeightTons())); - RainbowFishSerializer.writeV1(fishV1, "fish1.out"); - // Read V1 - RainbowFish deserializedFishV1 = RainbowFishSerializer.readV1("fish1.out"); - System.out.println(String.format("deserializedFishV1 name=%s age=%d length=%d weight=%d", deserializedFishV1.getName(), - deserializedFishV1.getAge(), deserializedFishV1.getLengthMeters(), deserializedFishV1.getWeightTons())); - // Write V2 - RainbowFishV2 fishV2 = new RainbowFishV2("Scar", 5, 12, 15, true, true, true); - System.out.println(String.format("fishV2 name=%s age=%d length=%d weight=%d sleeping=%b hungry=%b angry=%b", fishV2.getName(), - fishV2.getAge(), fishV2.getLengthMeters(), fishV2.getWeightTons(), fishV2.getHungry(), fishV2.getAngry(), fishV2.getSleeping())); - RainbowFishSerializer.writeV2(fishV2, "fish2.out"); - // Read V2 with V1 method - RainbowFish deserializedFishV2 = RainbowFishSerializer.readV1("fish2.out"); - System.out.println(String.format("deserializedFishV2 name=%s age=%d length=%d weight=%d", deserializedFishV2.getName(), - deserializedFishV2.getAge(), deserializedFishV2.getLengthMeters(), deserializedFishV2.getWeightTons())); - } + + /** + * Program entry point + */ + public static void main(String[] args) throws IOException, ClassNotFoundException { + // Write V1 + RainbowFish fishV1 = new RainbowFish("Zed", 10, 11, 12); + System.out.println(String.format("fishV1 name=%s age=%d length=%d weight=%d", fishV1.getName(), + fishV1.getAge(), fishV1.getLengthMeters(), fishV1.getWeightTons())); + RainbowFishSerializer.writeV1(fishV1, "fish1.out"); + // Read V1 + RainbowFish deserializedFishV1 = RainbowFishSerializer.readV1("fish1.out"); + System.out.println(String.format("deserializedFishV1 name=%s age=%d length=%d weight=%d", + deserializedFishV1.getName(), deserializedFishV1.getAge(), + deserializedFishV1.getLengthMeters(), deserializedFishV1.getWeightTons())); + // Write V2 + RainbowFishV2 fishV2 = new RainbowFishV2("Scar", 5, 12, 15, true, true, true); + System.out.println(String.format( + "fishV2 name=%s age=%d length=%d weight=%d sleeping=%b hungry=%b angry=%b", + fishV2.getName(), fishV2.getAge(), fishV2.getLengthMeters(), fishV2.getWeightTons(), + fishV2.getHungry(), fishV2.getAngry(), fishV2.getSleeping())); + RainbowFishSerializer.writeV2(fishV2, "fish2.out"); + // Read V2 with V1 method + RainbowFish deserializedFishV2 = RainbowFishSerializer.readV1("fish2.out"); + System.out.println(String.format("deserializedFishV2 name=%s age=%d length=%d weight=%d", + deserializedFishV2.getName(), deserializedFishV2.getAge(), + deserializedFishV2.getLengthMeters(), deserializedFishV2.getWeightTons())); + } } diff --git a/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/RainbowFish.java b/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/RainbowFish.java index 5e1fb1832..1b1825a3e 100644 --- a/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/RainbowFish.java +++ b/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/RainbowFish.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.tolerantreader; import java.io.Serializable; @@ -9,34 +31,37 @@ import java.io.Serializable; */ public class RainbowFish implements Serializable { - private static final long serialVersionUID = 1L; - - private String name; - private int age; - private int lengthMeters; - private int weightTons; - - public RainbowFish(String name, int age, int lengthMeters, int weightTons) { - this.name = name; - this.age = age; - this.lengthMeters = lengthMeters; - this.weightTons = weightTons; - } - - public String getName() { - return name; - } + private static final long serialVersionUID = 1L; - public int getAge() { - return age; - } + private String name; + private int age; + private int lengthMeters; + private int weightTons; - public int getLengthMeters() { - return lengthMeters; - } + /** + * Constructor + */ + public RainbowFish(String name, int age, int lengthMeters, int weightTons) { + this.name = name; + this.age = age; + this.lengthMeters = lengthMeters; + this.weightTons = weightTons; + } - public int getWeightTons() { - return weightTons; - } + public String getName() { + return name; + } + + public int getAge() { + return age; + } + + public int getLengthMeters() { + return lengthMeters; + } + + public int getWeightTons() { + return weightTons; + } } diff --git a/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/RainbowFishSerializer.java b/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/RainbowFishSerializer.java index e788bcad4..948dfa6d2 100644 --- a/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/RainbowFishSerializer.java +++ b/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/RainbowFishSerializer.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.tolerantreader; import java.io.FileInputStream; @@ -10,71 +32,62 @@ import java.util.Map; /** * - * RainbowFishSerializer provides methods for reading and writing {@link RainbowFish} objects to file. - * Tolerant Reader pattern is implemented here by serializing maps instead of {@link RainbowFish} objects. - * This way the reader does not break even though new properties are added to the schema. + * RainbowFishSerializer provides methods for reading and writing {@link RainbowFish} objects to + * file. Tolerant Reader pattern is implemented here by serializing maps instead of + * {@link RainbowFish} objects. This way the reader does not break even though new properties are + * added to the schema. * */ -public class RainbowFishSerializer { +public final class RainbowFishSerializer { - /** - * Write V1 RainbowFish to file - * @param rainbowFish - * @param filename - * @throws IOException - */ - public static void writeV1(RainbowFish rainbowFish, String filename) throws IOException { - Map map = new HashMap<>(); - map.put("name", rainbowFish.getName()); - map.put("age", String.format("%d", rainbowFish.getAge())); - map.put("lengthMeters", String.format("%d", rainbowFish.getLengthMeters())); - map.put("weightTons", String.format("%d", rainbowFish.getWeightTons())); - FileOutputStream fileOut = new FileOutputStream(filename); - ObjectOutputStream objOut = new ObjectOutputStream(fileOut); - objOut.writeObject(map); - objOut.close(); - fileOut.close(); - } + private RainbowFishSerializer() { + } - /** - * Write V2 RainbowFish to file - * @param rainbowFish - * @param filename - * @throws IOException - */ - public static void writeV2(RainbowFishV2 rainbowFish, String filename) throws IOException { - Map map = new HashMap<>(); - map.put("name", rainbowFish.getName()); - map.put("age", String.format("%d", rainbowFish.getAge())); - map.put("lengthMeters", String.format("%d", rainbowFish.getLengthMeters())); - map.put("weightTons", String.format("%d", rainbowFish.getWeightTons())); - map.put("angry", Boolean.toString(rainbowFish.getAngry())); - map.put("hungry", Boolean.toString(rainbowFish.getHungry())); - map.put("sleeping", Boolean.toString(rainbowFish.getSleeping())); - FileOutputStream fileOut = new FileOutputStream(filename); - ObjectOutputStream objOut = new ObjectOutputStream(fileOut); - objOut.writeObject(map); - objOut.close(); - fileOut.close(); - } - - /** - * Read V1 RainbowFish from file - * @param filename - * @return - * @throws IOException - * @throws ClassNotFoundException - */ - public static RainbowFish readV1(String filename) throws IOException, ClassNotFoundException { - Map map = null; - FileInputStream fileIn = new FileInputStream(filename); - ObjectInputStream objIn = new ObjectInputStream(fileIn); - map = (Map) objIn.readObject(); - objIn.close(); - fileIn.close(); - return new RainbowFish(map.get("name"), - Integer.parseInt(map.get("age")), - Integer.parseInt(map.get("lengthMeters")), - Integer.parseInt(map.get("weightTons"))); - } + /** + * Write V1 RainbowFish to file + */ + public static void writeV1(RainbowFish rainbowFish, String filename) throws IOException { + Map map = new HashMap<>(); + map.put("name", rainbowFish.getName()); + map.put("age", String.format("%d", rainbowFish.getAge())); + map.put("lengthMeters", String.format("%d", rainbowFish.getLengthMeters())); + map.put("weightTons", String.format("%d", rainbowFish.getWeightTons())); + FileOutputStream fileOut = new FileOutputStream(filename); + ObjectOutputStream objOut = new ObjectOutputStream(fileOut); + objOut.writeObject(map); + objOut.close(); + fileOut.close(); + } + + /** + * Write V2 RainbowFish to file + */ + public static void writeV2(RainbowFishV2 rainbowFish, String filename) throws IOException { + Map map = new HashMap<>(); + map.put("name", rainbowFish.getName()); + map.put("age", String.format("%d", rainbowFish.getAge())); + map.put("lengthMeters", String.format("%d", rainbowFish.getLengthMeters())); + map.put("weightTons", String.format("%d", rainbowFish.getWeightTons())); + map.put("angry", Boolean.toString(rainbowFish.getAngry())); + map.put("hungry", Boolean.toString(rainbowFish.getHungry())); + map.put("sleeping", Boolean.toString(rainbowFish.getSleeping())); + FileOutputStream fileOut = new FileOutputStream(filename); + ObjectOutputStream objOut = new ObjectOutputStream(fileOut); + objOut.writeObject(map); + objOut.close(); + fileOut.close(); + } + + /** + * Read V1 RainbowFish from file + */ + public static RainbowFish readV1(String filename) throws IOException, ClassNotFoundException { + FileInputStream fileIn = new FileInputStream(filename); + ObjectInputStream objIn = new ObjectInputStream(fileIn); + Map map = (Map) objIn.readObject(); + objIn.close(); + fileIn.close(); + return new RainbowFish(map.get("name"), Integer.parseInt(map.get("age")), Integer.parseInt(map + .get("lengthMeters")), Integer.parseInt(map.get("weightTons"))); + } } diff --git a/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/RainbowFishV2.java b/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/RainbowFishV2.java index dfa08ed01..55a416734 100644 --- a/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/RainbowFishV2.java +++ b/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/RainbowFishV2.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.tolerantreader; /** @@ -7,32 +29,36 @@ package com.iluwatar.tolerantreader; */ public class RainbowFishV2 extends RainbowFish { - private static final long serialVersionUID = 1L; - - private boolean sleeping; - private boolean hungry; - private boolean angry; + private static final long serialVersionUID = 1L; - public RainbowFishV2(String name, int age, int lengthMeters, int weightTons) { - super(name, age, lengthMeters, weightTons); - } - - public RainbowFishV2(String name, int age, int lengthMeters, int weightTons, boolean sleeping, boolean hungry, boolean angry) { - this(name, age, lengthMeters, weightTons); - this.sleeping = sleeping; - this.hungry = hungry; - this.angry = angry; - } - - public boolean getSleeping() { - return sleeping; - } - - public boolean getHungry() { - return hungry; - } - - public boolean getAngry() { - return angry; - } + private boolean sleeping; + private boolean hungry; + private boolean angry; + + public RainbowFishV2(String name, int age, int lengthMeters, int weightTons) { + super(name, age, lengthMeters, weightTons); + } + + /** + * Constructor + */ + public RainbowFishV2(String name, int age, int lengthMeters, int weightTons, boolean sleeping, + boolean hungry, boolean angry) { + this(name, age, lengthMeters, weightTons); + this.sleeping = sleeping; + this.hungry = hungry; + this.angry = angry; + } + + public boolean getSleeping() { + return sleeping; + } + + public boolean getHungry() { + return hungry; + } + + public boolean getAngry() { + return angry; + } } diff --git a/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/AppTest.java b/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/AppTest.java index bc5066866..e1a2ca5f9 100644 --- a/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/AppTest.java +++ b/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/AppTest.java @@ -1,13 +1,33 @@ +/** + * The MIT License + * Copyright (c) 2014 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.tolerantreader; -import java.io.File; -import java.io.IOException; - import org.junit.After; import org.junit.Before; import org.junit.Test; -import com.iluwatar.tolerantreader.App; +import java.io.File; +import java.io.IOException; /** * @@ -15,19 +35,19 @@ import com.iluwatar.tolerantreader.App; * */ public class AppTest { - - @Test - public void test() throws ClassNotFoundException, IOException { - String[] args = {}; - App.main(args); - } - - @Before - @After - public void cleanup() { - File file1 = new File("fish1.out"); - file1.delete(); - File file2 = new File("fish2.out"); - file2.delete(); - } + + @Test + public void test() throws ClassNotFoundException, IOException { + String[] args = {}; + App.main(args); + } + + @Before + @After + public void cleanup() { + File file1 = new File("fish1.out"); + file1.delete(); + File file2 = new File("fish2.out"); + file2.delete(); + } } diff --git a/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/RainbowFishSerializerTest.java b/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/RainbowFishSerializerTest.java new file mode 100644 index 000000000..1028e7bde --- /dev/null +++ b/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/RainbowFishSerializerTest.java @@ -0,0 +1,90 @@ +/** + * The MIT License + * Copyright (c) 2014 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.tolerantreader; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; + +/** + * Date: 12/30/15 - 18:39 PM + * + * @author Jeroen Meulemeester + */ +public class RainbowFishSerializerTest { + + /** + * Create a temporary folder, used to generate files in during this test + */ + @Rule + public final TemporaryFolder testFolder = new TemporaryFolder(); + + /** + * Rainbow fish version 1 used during the tests + */ + private static final RainbowFish V1 = new RainbowFish("version1", 1, 2, 3); + + /** + * Rainbow fish version 2 used during the tests + */ + private static final RainbowFishV2 V2 = new RainbowFishV2("version2", 4, 5, 6, true, false, true); + + /** + * Verify if a fish, written as version 1 can be read back as version 1 + */ + @Test + public void testWriteV1ReadV1() throws Exception { + final File outputFile = this.testFolder.newFile(); + RainbowFishSerializer.writeV1(V1, outputFile.getPath()); + + final RainbowFish fish = RainbowFishSerializer.readV1(outputFile.getPath()); + assertNotSame(V1, fish); + assertEquals(V1.getName(), fish.getName()); + assertEquals(V1.getAge(), fish.getAge()); + assertEquals(V1.getLengthMeters(), fish.getLengthMeters()); + assertEquals(V1.getWeightTons(), fish.getWeightTons()); + + } + + /** + * Verify if a fish, written as version 2 can be read back as version 1 + */ + @Test + public void testWriteV2ReadV1() throws Exception { + final File outputFile = this.testFolder.newFile(); + RainbowFishSerializer.writeV2(V2, outputFile.getPath()); + + final RainbowFish fish = RainbowFishSerializer.readV1(outputFile.getPath()); + assertNotSame(V2, fish); + assertEquals(V2.getName(), fish.getName()); + assertEquals(V2.getAge(), fish.getAge()); + assertEquals(V2.getLengthMeters(), fish.getLengthMeters()); + assertEquals(V2.getWeightTons(), fish.getWeightTons()); + } + +} \ No newline at end of file diff --git a/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/RainbowFishTest.java b/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/RainbowFishTest.java new file mode 100644 index 000000000..f9dcc5e6d --- /dev/null +++ b/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/RainbowFishTest.java @@ -0,0 +1,48 @@ +/** + * The MIT License + * Copyright (c) 2014 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.tolerantreader; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Date: 12/30/15 - 18:34 PM + * + * @author Jeroen Meulemeester + */ +public class RainbowFishTest { + + /** + * Verify if the getters of a {@link RainbowFish} return the expected values + */ + @Test + public void testValues() { + final RainbowFish fish = new RainbowFish("name", 1, 2, 3); + assertEquals("name", fish.getName()); + assertEquals(1, fish.getAge()); + assertEquals(2, fish.getLengthMeters()); + assertEquals(3, fish.getWeightTons()); + } + +} \ No newline at end of file diff --git a/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/RainbowFishV2Test.java b/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/RainbowFishV2Test.java new file mode 100644 index 000000000..680e3c4ed --- /dev/null +++ b/tolerant-reader/src/test/java/com/iluwatar/tolerantreader/RainbowFishV2Test.java @@ -0,0 +1,51 @@ +/** + * The MIT License + * Copyright (c) 2014 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.tolerantreader; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Date: 12/30/15 - 18:35 PM + * + * @author Jeroen Meulemeester + */ +public class RainbowFishV2Test { + + /** + * Verify if the getters of a {@link RainbowFish} return the expected values + */ + @Test + public void testValues() { + final RainbowFishV2 fish = new RainbowFishV2("name", 1, 2, 3, false, true, false); + assertEquals("name", fish.getName()); + assertEquals(1, fish.getAge()); + assertEquals(2, fish.getLengthMeters()); + assertEquals(3, fish.getWeightTons()); + assertEquals(false, fish.getSleeping()); + assertEquals(true, fish.getHungry()); + assertEquals(false, fish.getAngry()); + } + +} \ No newline at end of file diff --git a/twin/.gitignore b/twin/.gitignore new file mode 100644 index 000000000..b83d22266 --- /dev/null +++ b/twin/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/twin/README.md b/twin/README.md new file mode 100644 index 000000000..3795236bb --- /dev/null +++ b/twin/README.md @@ -0,0 +1,26 @@ +--- +layout: pattern +title: Twin +folder: twin +permalink: /patterns/twin/ +categories: Creational +tags: + - Java + - Difficulty-Intermediate +--- + +## Intent + Twin pattern is a design pattern which provides a standard solution to simulate multiple +inheritance in java + +![alt text](./etc/twin.png "Twin") + +## Applicability +Use the Twin idiom when + +* to simulate multiple inheritance in a language that does not support this feature. +* to avoid certain problems of multiple inheritance such as name clashes. + +## Credits + +* [Twin – A Design Pattern for Modeling Multiple Inheritance](http://www.ssw.uni-linz.ac.at/Research/Papers/Moe99/Paper.pdf) diff --git a/twin/etc/twin.png b/twin/etc/twin.png new file mode 100644 index 000000000..724092525 Binary files /dev/null and b/twin/etc/twin.png differ diff --git a/twin/etc/twin.ucls b/twin/etc/twin.ucls new file mode 100644 index 000000000..6948dbf6a --- /dev/null +++ b/twin/etc/twin.ucls @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/twin/pom.xml b/twin/pom.xml new file mode 100644 index 000000000..1eb854b87 --- /dev/null +++ b/twin/pom.xml @@ -0,0 +1,47 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + twin + + + junit + junit + test + + + org.mockito + mockito-core + test + + + diff --git a/twin/src/main/java/com/iluwatar/twin/App.java b/twin/src/main/java/com/iluwatar/twin/App.java new file mode 100644 index 000000000..95998df33 --- /dev/null +++ b/twin/src/main/java/com/iluwatar/twin/App.java @@ -0,0 +1,68 @@ +/** + * The MIT License + * Copyright (c) 2014 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.twin; + +/** + * Twin pattern is a design pattern which provides a standard solution to simulate multiple + * inheritance in java. + *

      + * In this example, the essence of the Twin pattern is the {@link BallItem} class and + * {@link BallThread} class represent the twin objects to coordinate with each other(via the twin + * reference) like a single class inheriting from {@link GameItem} and {@link Thread}. + */ + +public class App { + + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) throws Exception { + + BallItem ballItem = new BallItem(); + BallThread ballThread = new BallThread(); + + ballItem.setTwin(ballThread); + ballThread.setTwin(ballItem); + + ballThread.start(); + + waiting(); + + ballItem.click(); + + waiting(); + + ballItem.click(); + + waiting(); + + // exit + ballThread.stopMe(); + } + + private static void waiting() throws Exception { + Thread.sleep(750); + } +} diff --git a/twin/src/main/java/com/iluwatar/twin/BallItem.java b/twin/src/main/java/com/iluwatar/twin/BallItem.java new file mode 100644 index 000000000..0188b5731 --- /dev/null +++ b/twin/src/main/java/com/iluwatar/twin/BallItem.java @@ -0,0 +1,63 @@ +/** + * The MIT License + * Copyright (c) 2014 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.twin; + +/** + * This class represents a Ball which extends {@link GameItem} and implements the logic for ball + * item, like move and draw. It hold a reference of {@link BallThread} to delegate the suspend and + * resume task. + */ +public class BallItem extends GameItem { + + private boolean isSuspended; + + private BallThread twin; + + public void setTwin(BallThread twin) { + this.twin = twin; + } + + @Override + public void doDraw() { + + System.out.println("doDraw"); + } + + public void move() { + System.out.println("move"); + } + + @Override + public void click() { + + isSuspended = !isSuspended; + + if (isSuspended) { + twin.suspendMe(); + } else { + twin.resumeMe(); + } + } +} + diff --git a/twin/src/main/java/com/iluwatar/twin/BallThread.java b/twin/src/main/java/com/iluwatar/twin/BallThread.java new file mode 100644 index 000000000..194d85b06 --- /dev/null +++ b/twin/src/main/java/com/iluwatar/twin/BallThread.java @@ -0,0 +1,77 @@ +/** + * The MIT License + * Copyright (c) 2014 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.twin; + +/** + * This class is a UI thread for drawing the {@link BallItem}, and provide the method for suspend + * and resume. It hold the reference of {@link BallItem} to delegate the draw task. + * + */ + +public class BallThread extends Thread { + + private BallItem twin; + + private volatile boolean isSuspended; + + private volatile boolean isRunning = true; + + public void setTwin(BallItem twin) { + this.twin = twin; + } + + /** + * Run the thread + */ + public void run() { + + while (isRunning) { + if (!isSuspended) { + twin.draw(); + twin.move(); + } + try { + Thread.sleep(250); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + + public void suspendMe() { + isSuspended = true; + System.out.println("Begin to suspend BallThread"); + } + + public void resumeMe() { + isSuspended = false; + System.out.println("Begin to resume BallThread"); + } + + public void stopMe() { + this.isRunning = false; + this.isSuspended = true; + } +} + diff --git a/twin/src/main/java/com/iluwatar/twin/GameItem.java b/twin/src/main/java/com/iluwatar/twin/GameItem.java new file mode 100644 index 000000000..08d7dcce7 --- /dev/null +++ b/twin/src/main/java/com/iluwatar/twin/GameItem.java @@ -0,0 +1,44 @@ +/** + * The MIT License + * Copyright (c) 2014 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.twin; + +/** + * GameItem is a common class which provides some common methods for game object. + */ +public abstract class GameItem { + + /** + * Template method, do some common logic before draw + */ + public void draw() { + System.out.println("draw"); + doDraw(); + } + + public abstract void doDraw(); + + + public abstract void click(); +} diff --git a/twin/src/test/java/com/iluwatar/twin/AppTest.java b/twin/src/test/java/com/iluwatar/twin/AppTest.java new file mode 100644 index 000000000..73a1d6322 --- /dev/null +++ b/twin/src/test/java/com/iluwatar/twin/AppTest.java @@ -0,0 +1,39 @@ +/** + * The MIT License + * Copyright (c) 2014 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.twin; + +import org.junit.Test; + +/** + * + * Application test + * + */ +public class AppTest { + + @Test + public void test() throws Exception { + String[] args = {}; + App.main(args); + } +} diff --git a/twin/src/test/java/com/iluwatar/twin/BallItemTest.java b/twin/src/test/java/com/iluwatar/twin/BallItemTest.java new file mode 100644 index 000000000..4bb9a2111 --- /dev/null +++ b/twin/src/test/java/com/iluwatar/twin/BallItemTest.java @@ -0,0 +1,84 @@ +/** + * The MIT License + * Copyright (c) 2014 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.twin; + +import org.junit.Test; +import org.mockito.InOrder; + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/30/15 - 18:44 PM + * + * @author Jeroen Meulemeester + */ +public class BallItemTest extends StdOutTest { + + @Test + public void testClick() { + final BallThread ballThread = mock(BallThread.class); + final BallItem ballItem = new BallItem(); + ballItem.setTwin(ballThread); + + final InOrder inOrder = inOrder(ballThread); + + for (int i = 0; i < 10; i++) { + ballItem.click(); + inOrder.verify(ballThread).suspendMe(); + + ballItem.click(); + inOrder.verify(ballThread).resumeMe(); + } + + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testDoDraw() { + final BallItem ballItem = new BallItem(); + final BallThread ballThread = mock(BallThread.class); + ballItem.setTwin(ballThread); + + ballItem.draw(); + verify(getStdOutMock()).println("draw"); + verify(getStdOutMock()).println("doDraw"); + + verifyNoMoreInteractions(ballThread, getStdOutMock()); + } + + @Test + public void testMove() { + final BallItem ballItem = new BallItem(); + final BallThread ballThread = mock(BallThread.class); + ballItem.setTwin(ballThread); + + ballItem.move(); + verify(getStdOutMock()).println("move"); + + verifyNoMoreInteractions(ballThread, getStdOutMock()); + } + +} \ No newline at end of file diff --git a/twin/src/test/java/com/iluwatar/twin/BallThreadTest.java b/twin/src/test/java/com/iluwatar/twin/BallThreadTest.java new file mode 100644 index 000000000..453109e5a --- /dev/null +++ b/twin/src/test/java/com/iluwatar/twin/BallThreadTest.java @@ -0,0 +1,111 @@ +/** + * The MIT License + * Copyright (c) 2014 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.twin; + +import org.junit.Test; + +import static java.lang.Thread.UncaughtExceptionHandler; +import static java.lang.Thread.sleep; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/30/15 - 18:55 PM + * + * @author Jeroen Meulemeester + */ +public class BallThreadTest { + + /** + * Verify if the {@link BallThread} can be resumed + */ + @Test(timeout = 5000) + public void testSuspend() throws Exception { + final BallThread ballThread = new BallThread(); + + final BallItem ballItem = mock(BallItem.class); + ballThread.setTwin(ballItem); + + ballThread.start(); + + verify(ballItem, timeout(2000).atLeastOnce()).draw(); + verify(ballItem, timeout(2000).atLeastOnce()).move(); + ballThread.suspendMe(); + + sleep(1000); + + ballThread.stopMe(); + ballThread.join(); + + verifyNoMoreInteractions(ballItem); + } + + /** + * Verify if the {@link BallThread} can be resumed + */ + @Test(timeout = 5000) + public void testResume() throws Exception { + final BallThread ballThread = new BallThread(); + + final BallItem ballItem = mock(BallItem.class); + ballThread.setTwin(ballItem); + + ballThread.suspendMe(); + ballThread.start(); + + sleep(1000); + + verifyZeroInteractions(ballItem); + + ballThread.resumeMe(); + verify(ballItem, timeout(2000).atLeastOnce()).draw(); + verify(ballItem, timeout(2000).atLeastOnce()).move(); + + ballThread.stopMe(); + ballThread.join(); + + verifyNoMoreInteractions(ballItem); + } + + /** + * Verify if the {@link BallThread} is interruptible + */ + @Test(timeout = 5000) + public void testInterrupt() throws Exception { + final BallThread ballThread = new BallThread(); + final UncaughtExceptionHandler exceptionHandler = mock(UncaughtExceptionHandler.class); + ballThread.setUncaughtExceptionHandler(exceptionHandler); + ballThread.setTwin(mock(BallItem.class)); + ballThread.start(); + ballThread.interrupt(); + ballThread.join(); + + verify(exceptionHandler).uncaughtException(eq(ballThread), any(RuntimeException.class)); + verifyNoMoreInteractions(exceptionHandler); + } +} \ No newline at end of file diff --git a/twin/src/test/java/com/iluwatar/twin/StdOutTest.java b/twin/src/test/java/com/iluwatar/twin/StdOutTest.java new file mode 100644 index 000000000..b3baf8abd --- /dev/null +++ b/twin/src/test/java/com/iluwatar/twin/StdOutTest.java @@ -0,0 +1,75 @@ +/** + * The MIT License + * Copyright (c) 2014 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.twin; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since some actions don't have any + * influence on accessible objects, except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/update-ghpages.sh b/update-ghpages.sh index 82486d5a4..aee888ba9 100644 --- a/update-ghpages.sh +++ b/update-ghpages.sh @@ -1,4 +1,27 @@ #!/bin/bash +# +# The MIT License +# Copyright (c) 2014 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. +# + # Clone gh-pages git clone -b gh-pages "https://${GH_REF}" ghpagesclone diff --git a/value-object/README.md b/value-object/README.md new file mode 100644 index 000000000..83223d8a2 --- /dev/null +++ b/value-object/README.md @@ -0,0 +1,34 @@ +--- +layout: pattern +title: Value Object +folder: value-object +permalink: /patterns/value-object/ +categories: Creational +tags: + - Java + - Difficulty-Beginner +--- + +## Intent +Provide objects which follow value semantics rather than reference semantics. +This means value objects' equality are not based on identity. Two value objects are +equal when they have the same value, not necessarily being the same object. + +![alt text](./etc/value-object.png "Value Object") + +## Applicability +Use the Value Object when + +* you need to measure the objects' equality based on the objects' value + +## Real world examples + +* [java.util.Optional](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html) +* [java.time.LocalDate](https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html) +* [joda-time, money, beans](http://www.joda.org/) + +## Credits + +* [Patterns of Enterprise Application Architecture](http://www.martinfowler.com/books/eaa.html) +* [VALJOs - Value Java Objects : Stephen Colebourne's blog](http://blog.joda.org/2014/03/valjos-value-java-objects.html) +* [Value Object : Wikipedia](https://en.wikipedia.org/wiki/Value_object) diff --git a/value-object/etc/value-object.png b/value-object/etc/value-object.png new file mode 100644 index 000000000..69a244c80 Binary files /dev/null and b/value-object/etc/value-object.png differ diff --git a/value-object/etc/value-object.ucls b/value-object/etc/value-object.ucls new file mode 100644 index 000000000..7bbf5e47c --- /dev/null +++ b/value-object/etc/value-object.ucls @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/value-object/pom.xml b/value-object/pom.xml new file mode 100644 index 000000000..ec8de1681 --- /dev/null +++ b/value-object/pom.xml @@ -0,0 +1,54 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + value-object + + + com.google.guava + guava-testlib + 19.0 + test + + + junit + junit + test + + + org.mockito + mockito-core + test + + + diff --git a/value-object/src/main/java/com/iluwatar/value/object/App.java b/value-object/src/main/java/com/iluwatar/value/object/App.java new file mode 100644 index 000000000..1e943d054 --- /dev/null +++ b/value-object/src/main/java/com/iluwatar/value/object/App.java @@ -0,0 +1,54 @@ +/** + * The MIT License + * Copyright (c) 2014 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.value.object; + +/** + * A Value Object are objects which follow value semantics rather than reference semantics. This + * means value objects' equality are not based on identity. Two value objects are equal when they + * have the same value, not necessarily being the same object.. + * + * Value Objects must override equals(), hashCode() to check the equality with values. + * Value Objects should be immutable so declare members final. + * Obtain instances by static factory methods. + * The elements of the state must be other values, including primitive types. + * Provide methods, typically simple getters, to get the elements of the state. + * A Value Object must check equality with equals() not == + * + * For more specific and strict rules to implement value objects check the rules from Stephen + * Colebourne's term VALJO : http://blog.joda.org/2014/03/valjos-value-java-objects.html + */ +public class App { + /** + * This practice creates three HeroStats(Value object) and checks equality between those. + */ + public static void main(String[] args) { + HeroStat statA = HeroStat.valueOf(10, 5, 0); + HeroStat statB = HeroStat.valueOf(10, 5, 0); + HeroStat statC = HeroStat.valueOf(5, 1, 8); + + System.out.println(statA.toString()); + + System.out.println("Is statA and statB equal : " + statA.equals(statB)); + System.out.println("Is statA and statC equal : " + statA.equals(statC)); + } +} diff --git a/value-object/src/main/java/com/iluwatar/value/object/HeroStat.java b/value-object/src/main/java/com/iluwatar/value/object/HeroStat.java new file mode 100644 index 000000000..258c4d6a0 --- /dev/null +++ b/value-object/src/main/java/com/iluwatar/value/object/HeroStat.java @@ -0,0 +1,112 @@ +/** + * The MIT License + * Copyright (c) 2014 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.value.object; + +/** + * HeroStat is a value object + * + * {@link http://docs.oracle.com/javase/8/docs/api/java/lang/doc-files/ValueBased.html} + */ +public class HeroStat { + + // Stats for a hero + + private final int strength; + private final int intelligence; + private final int luck; + + // All constructors must be private. + private HeroStat(int strength, int intelligence, int luck) { + super(); + this.strength = strength; + this.intelligence = intelligence; + this.luck = luck; + } + + // Static factory method to create new instances. + public static HeroStat valueOf(int strength, int intelligence, int luck) { + return new HeroStat(strength, intelligence, luck); + } + + public int getStrength() { + return strength; + } + + public int getIntelligence() { + return intelligence; + } + + public int getLuck() { + return luck; + } + + /* + * Recommended to provide a static factory method capable of creating an instance from the formal + * string representation declared like this. public static HeroStat parse(String string) {} + */ + + // toString, hashCode, equals + + @Override + public String toString() { + return "HeroStat [strength=" + strength + ", intelligence=" + intelligence + + ", luck=" + luck + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + intelligence; + result = prime * result + luck; + result = prime * result + strength; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + HeroStat other = (HeroStat) obj; + if (intelligence != other.intelligence) { + return false; + } + if (luck != other.luck) { + return false; + } + if (strength != other.strength) { + return false; + } + return true; + } + + // The clone() method should not be public. Just don't override it. + +} diff --git a/value-object/src/test/java/com/iluwatar/value/object/AppTest.java b/value-object/src/test/java/com/iluwatar/value/object/AppTest.java new file mode 100644 index 000000000..85ef8b84e --- /dev/null +++ b/value-object/src/test/java/com/iluwatar/value/object/AppTest.java @@ -0,0 +1,37 @@ +/** + * The MIT License + * Copyright (c) 2014 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.value.object; + +import org.junit.Test; + +/** + * Application test + */ +public class AppTest { + + @Test + public void test() { + String[] args = {}; + App.main(args); + } +} diff --git a/value-object/src/test/java/com/iluwatar/value/object/HeroStatTest.java b/value-object/src/test/java/com/iluwatar/value/object/HeroStatTest.java new file mode 100644 index 000000000..4a8034b0b --- /dev/null +++ b/value-object/src/test/java/com/iluwatar/value/object/HeroStatTest.java @@ -0,0 +1,66 @@ +/** + * The MIT License + * Copyright (c) 2014 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.value.object; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; + +import static org.junit.Assert.assertThat; + +import com.google.common.testing.EqualsTester; + +import org.junit.Test; + +/** + * Unit test for HeroStat. + */ +public class HeroStatTest { + + /** + * Tester for equals() and hashCode() methods of a class. Using guava's EqualsTester. + * + * @see http://static.javadoc.io/com.google.guava/guava-testlib/19.0/com/google/common/testing/ + * EqualsTester.html + */ + @Test + public void testEquals() { + HeroStat heroStatA = HeroStat.valueOf(3, 9, 2); + HeroStat heroStatB = HeroStat.valueOf(3, 9, 2); + new EqualsTester().addEqualityGroup(heroStatA, heroStatB).testEquals(); + } + + /** + * The toString() for two equal values must be the same. For two non-equal values it must be + * different. + */ + @Test + public void testToString() { + HeroStat heroStatA = HeroStat.valueOf(3, 9, 2); + HeroStat heroStatB = HeroStat.valueOf(3, 9, 2); + HeroStat heroStatC = HeroStat.valueOf(3, 9, 8); + + assertThat(heroStatA.toString(), is(heroStatB.toString())); + assertThat(heroStatA.toString(), is(not(heroStatC.toString()))); + } + +} diff --git a/visitor/index.md b/visitor/README.md similarity index 89% rename from visitor/index.md rename to visitor/README.md index 5f32edbbd..c1e24a624 100644 --- a/visitor/index.md +++ b/visitor/README.md @@ -6,26 +6,28 @@ permalink: /patterns/visitor/ categories: Behavioral tags: - Java - - Difficulty-Expert + - Difficulty-Intermediate - Gang Of Four --- -**Intent:** Represent an operation to be performed on the elements of an object +## Intent +Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates. ![alt text](./etc/visitor_1.png "Visitor") -**Applicability:** Use the Visitor pattern when +## Applicability +Use the Visitor pattern when * an object structure contains many classes of objects with differing interfaces, and you want to perform operations on these objects that depend on their concrete classes * many distinct and unrelated operations need to be performed on objects in an object structure, and you want to avoid "polluting" their classes with these operations. Visitor lets you keep related operations together by defining them in one class. When the object structure is shared by many applications, use Visitor to put operations in just those applications that need them * the classes defining the object structure rarely change, but you often want to define new operations over the structure. Changing the object structure classes requires redefining the interface to all visitors, which is potentially costly. If the object structure classes change often, then it's probably better to define the operations in those classes -**Real world examples:** +## Real world examples * [Apache Wicket](https://github.com/apache/wicket) component tree, see [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java) -**Credits** +## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/visitor/pom.xml b/visitor/pom.xml index ca5f72be8..54a90e184 100644 --- a/visitor/pom.xml +++ b/visitor/pom.xml @@ -1,11 +1,35 @@ + 4.0.0 com.iluwatar java-design-patterns - 1.7.0 + 1.13.0-SNAPSHOT visitor @@ -14,5 +38,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/visitor/src/main/java/com/iluwatar/visitor/App.java b/visitor/src/main/java/com/iluwatar/visitor/App.java index dfdd4fcb6..371756b84 100644 --- a/visitor/src/main/java/com/iluwatar/visitor/App.java +++ b/visitor/src/main/java/com/iluwatar/visitor/App.java @@ -1,31 +1,52 @@ +/** + * The MIT License + * Copyright (c) 2014 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.visitor; /** * - * Visitor pattern defines mechanism to apply operations on nodes - * in hierarchy. New operations can be added without altering the node - * interface. + * Visitor pattern defines mechanism to apply operations on nodes in hierarchy. New operations can + * be added without altering the node interface. *

      - * In this example there is a unit hierarchy beginning from {@link Commander}. - * This hierarchy is traversed by visitors. {@link SoldierVisitor} applies - * its operation on {@link Soldier}s, {@link SergeantVisitor} on {@link Sergeant}s and so - * on. + * In this example there is a unit hierarchy beginning from {@link Commander}. This hierarchy is + * traversed by visitors. {@link SoldierVisitor} applies its operation on {@link Soldier}s, + * {@link SergeantVisitor} on {@link Sergeant}s and so on. * */ public class App { - /** - * Program entry point - * @param args command line args - */ - public static void main(String[] args) { + /** + * Program entry point + * + * @param args command line args + */ + public static void main(String[] args) { - Commander commander = new Commander(new Sergeant(new Soldier(), - new Soldier(), new Soldier()), new Sergeant(new Soldier(), - new Soldier(), new Soldier())); - commander.accept(new SoldierVisitor()); - commander.accept(new SergeantVisitor()); - commander.accept(new CommanderVisitor()); + Commander commander = + new Commander(new Sergeant(new Soldier(), new Soldier(), new Soldier()), new Sergeant( + new Soldier(), new Soldier(), new Soldier())); + commander.accept(new SoldierVisitor()); + commander.accept(new SergeantVisitor()); + commander.accept(new CommanderVisitor()); - } + } } diff --git a/visitor/src/main/java/com/iluwatar/visitor/Commander.java b/visitor/src/main/java/com/iluwatar/visitor/Commander.java index 18f418b01..a1969a41c 100644 --- a/visitor/src/main/java/com/iluwatar/visitor/Commander.java +++ b/visitor/src/main/java/com/iluwatar/visitor/Commander.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.visitor; /** @@ -7,18 +29,18 @@ package com.iluwatar.visitor; */ public class Commander extends Unit { - public Commander(Unit... children) { - super(children); - } + public Commander(Unit... children) { + super(children); + } - @Override - public void accept(UnitVisitor visitor) { - visitor.visitCommander(this); - super.accept(visitor); - } + @Override + public void accept(UnitVisitor visitor) { + visitor.visitCommander(this); + super.accept(visitor); + } - @Override - public String toString() { - return "commander"; - } + @Override + public String toString() { + return "commander"; + } } diff --git a/visitor/src/main/java/com/iluwatar/visitor/CommanderVisitor.java b/visitor/src/main/java/com/iluwatar/visitor/CommanderVisitor.java index 71e127d14..6e54c7861 100644 --- a/visitor/src/main/java/com/iluwatar/visitor/CommanderVisitor.java +++ b/visitor/src/main/java/com/iluwatar/visitor/CommanderVisitor.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.visitor; /** @@ -7,17 +29,18 @@ package com.iluwatar.visitor; */ public class CommanderVisitor implements UnitVisitor { - @Override - public void visitSoldier(Soldier soldier) { - } + @Override + public void visitSoldier(Soldier soldier) { + // Do nothing + } - @Override - public void visitSergeant(Sergeant sergeant) { - } - - @Override - public void visitCommander(Commander commander) { - System.out.println("Good to see you " + commander); - } + @Override + public void visitSergeant(Sergeant sergeant) { + // Do nothing + } + @Override + public void visitCommander(Commander commander) { + System.out.println("Good to see you " + commander); + } } diff --git a/visitor/src/main/java/com/iluwatar/visitor/Sergeant.java b/visitor/src/main/java/com/iluwatar/visitor/Sergeant.java index 17a19780a..3b0087582 100644 --- a/visitor/src/main/java/com/iluwatar/visitor/Sergeant.java +++ b/visitor/src/main/java/com/iluwatar/visitor/Sergeant.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.visitor; /** @@ -7,18 +29,18 @@ package com.iluwatar.visitor; */ public class Sergeant extends Unit { - public Sergeant(Unit... children) { - super(children); - } + public Sergeant(Unit... children) { + super(children); + } - @Override - public void accept(UnitVisitor visitor) { - visitor.visitSergeant(this); - super.accept(visitor); - } + @Override + public void accept(UnitVisitor visitor) { + visitor.visitSergeant(this); + super.accept(visitor); + } - @Override - public String toString() { - return "sergeant"; - } + @Override + public String toString() { + return "sergeant"; + } } diff --git a/visitor/src/main/java/com/iluwatar/visitor/SergeantVisitor.java b/visitor/src/main/java/com/iluwatar/visitor/SergeantVisitor.java index a1b9165eb..4fca0a5bd 100644 --- a/visitor/src/main/java/com/iluwatar/visitor/SergeantVisitor.java +++ b/visitor/src/main/java/com/iluwatar/visitor/SergeantVisitor.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.visitor; /** @@ -7,17 +29,18 @@ package com.iluwatar.visitor; */ public class SergeantVisitor implements UnitVisitor { - @Override - public void visitSoldier(Soldier soldier) { - } + @Override + public void visitSoldier(Soldier soldier) { + // Do nothing + } - @Override - public void visitSergeant(Sergeant sergeant) { - System.out.println("Hello " + sergeant); - } - - @Override - public void visitCommander(Commander commander) { - } + @Override + public void visitSergeant(Sergeant sergeant) { + System.out.println("Hello " + sergeant); + } + @Override + public void visitCommander(Commander commander) { + // Do nothing + } } diff --git a/visitor/src/main/java/com/iluwatar/visitor/Soldier.java b/visitor/src/main/java/com/iluwatar/visitor/Soldier.java index f71fe5bc0..6e82a936b 100644 --- a/visitor/src/main/java/com/iluwatar/visitor/Soldier.java +++ b/visitor/src/main/java/com/iluwatar/visitor/Soldier.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.visitor; /** @@ -7,18 +29,18 @@ package com.iluwatar.visitor; */ public class Soldier extends Unit { - public Soldier(Unit... children) { - super(children); - } + public Soldier(Unit... children) { + super(children); + } - @Override - public void accept(UnitVisitor visitor) { - visitor.visitSoldier(this); - super.accept(visitor); - } + @Override + public void accept(UnitVisitor visitor) { + visitor.visitSoldier(this); + super.accept(visitor); + } - @Override - public String toString() { - return "soldier"; - } + @Override + public String toString() { + return "soldier"; + } } diff --git a/visitor/src/main/java/com/iluwatar/visitor/SoldierVisitor.java b/visitor/src/main/java/com/iluwatar/visitor/SoldierVisitor.java index 828212dee..fff24f699 100644 --- a/visitor/src/main/java/com/iluwatar/visitor/SoldierVisitor.java +++ b/visitor/src/main/java/com/iluwatar/visitor/SoldierVisitor.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.visitor; /** @@ -7,17 +29,18 @@ package com.iluwatar.visitor; */ public class SoldierVisitor implements UnitVisitor { - @Override - public void visitSoldier(Soldier soldier) { - System.out.println("Greetings " + soldier); - } + @Override + public void visitSoldier(Soldier soldier) { + System.out.println("Greetings " + soldier); + } - @Override - public void visitSergeant(Sergeant sergeant) { - } - - @Override - public void visitCommander(Commander commander) { - } + @Override + public void visitSergeant(Sergeant sergeant) { + // Do nothing + } + @Override + public void visitCommander(Commander commander) { + // Do nothing + } } diff --git a/visitor/src/main/java/com/iluwatar/visitor/Unit.java b/visitor/src/main/java/com/iluwatar/visitor/Unit.java index fbf1faae1..dc8bf2a28 100644 --- a/visitor/src/main/java/com/iluwatar/visitor/Unit.java +++ b/visitor/src/main/java/com/iluwatar/visitor/Unit.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.visitor; /** @@ -7,15 +29,18 @@ package com.iluwatar.visitor; */ public abstract class Unit { - private Unit[] children; + private Unit[] children; - public Unit(Unit... children) { - this.children = children; - } + public Unit(Unit... children) { + this.children = children; + } - public void accept(UnitVisitor visitor) { - for (Unit child : children) { - child.accept(visitor); - } - } + /** + * Accept visitor + */ + public void accept(UnitVisitor visitor) { + for (Unit child : children) { + child.accept(visitor); + } + } } diff --git a/visitor/src/main/java/com/iluwatar/visitor/UnitVisitor.java b/visitor/src/main/java/com/iluwatar/visitor/UnitVisitor.java index b0148bc7e..e465a6473 100644 --- a/visitor/src/main/java/com/iluwatar/visitor/UnitVisitor.java +++ b/visitor/src/main/java/com/iluwatar/visitor/UnitVisitor.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 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.visitor; /** @@ -7,10 +29,10 @@ package com.iluwatar.visitor; */ public interface UnitVisitor { - void visitSoldier(Soldier soldier); + void visitSoldier(Soldier soldier); - void visitSergeant(Sergeant sergeant); + void visitSergeant(Sergeant sergeant); - void visitCommander(Commander commander); + void visitCommander(Commander commander); } diff --git a/visitor/src/test/java/com/iluwatar/visitor/AppTest.java b/visitor/src/test/java/com/iluwatar/visitor/AppTest.java index 66db8c2e3..33674053e 100644 --- a/visitor/src/test/java/com/iluwatar/visitor/AppTest.java +++ b/visitor/src/test/java/com/iluwatar/visitor/AppTest.java @@ -1,9 +1,29 @@ +/** + * The MIT License + * Copyright (c) 2014 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.visitor; import org.junit.Test; -import com.iluwatar.visitor.App; - /** * * Application test @@ -11,9 +31,9 @@ import com.iluwatar.visitor.App; */ public class AppTest { - @Test - public void test() { - String[] args = {}; - App.main(args); - } + @Test + public void test() { + String[] args = {}; + App.main(args); + } } diff --git a/visitor/src/test/java/com/iluwatar/visitor/CommanderTest.java b/visitor/src/test/java/com/iluwatar/visitor/CommanderTest.java new file mode 100644 index 000000000..abf92765f --- /dev/null +++ b/visitor/src/test/java/com/iluwatar/visitor/CommanderTest.java @@ -0,0 +1,47 @@ +/** + * The MIT License + * Copyright (c) 2014 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.visitor; + +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; + +/** + * Date: 12/30/15 - 19:45 PM + * + * @author Jeroen Meulemeester + */ +public class CommanderTest extends UnitTest { + + /** + * Create a new test instance for the given {@link Commander} + */ + public CommanderTest() { + super(Commander::new); + } + + @Override + void verifyVisit(Commander unit, UnitVisitor mockedVisitor) { + verify(mockedVisitor).visitCommander(eq(unit)); + } + +} \ No newline at end of file diff --git a/visitor/src/test/java/com/iluwatar/visitor/CommanderVisitorTest.java b/visitor/src/test/java/com/iluwatar/visitor/CommanderVisitorTest.java new file mode 100644 index 000000000..1331d25a9 --- /dev/null +++ b/visitor/src/test/java/com/iluwatar/visitor/CommanderVisitorTest.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 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.visitor; + +import java.util.Optional; + +/** + * Date: 12/30/15 - 18:43 PM + * + * @author Jeroen Meulemeester + */ +public class CommanderVisitorTest extends VisitorTest { + + /** + * Create a new test instance for the given visitor + */ + public CommanderVisitorTest() { + super( + new CommanderVisitor(), + Optional.of("Good to see you commander"), + Optional.empty(), + Optional.empty() + ); + } + +} diff --git a/visitor/src/test/java/com/iluwatar/visitor/SergeantTest.java b/visitor/src/test/java/com/iluwatar/visitor/SergeantTest.java new file mode 100644 index 000000000..fdc93265d --- /dev/null +++ b/visitor/src/test/java/com/iluwatar/visitor/SergeantTest.java @@ -0,0 +1,47 @@ +/** + * The MIT License + * Copyright (c) 2014 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.visitor; + +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; + +/** + * Date: 12/30/15 - 19:45 PM + * + * @author Jeroen Meulemeester + */ +public class SergeantTest extends UnitTest { + + /** + * Create a new test instance for the given {@link Sergeant} + */ + public SergeantTest() { + super(Sergeant::new); + } + + @Override + void verifyVisit(Sergeant unit, UnitVisitor mockedVisitor) { + verify(mockedVisitor).visitSergeant(eq(unit)); + } + +} \ No newline at end of file diff --git a/visitor/src/test/java/com/iluwatar/visitor/SergeantVisitorTest.java b/visitor/src/test/java/com/iluwatar/visitor/SergeantVisitorTest.java new file mode 100644 index 000000000..dc84d0b54 --- /dev/null +++ b/visitor/src/test/java/com/iluwatar/visitor/SergeantVisitorTest.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 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.visitor; + +import java.util.Optional; + +/** + * Date: 12/30/15 - 18:36 PM + * + * @author Jeroen Meulemeester + */ +public class SergeantVisitorTest extends VisitorTest { + + /** + * Create a new test instance for the given visitor + */ + public SergeantVisitorTest() { + super( + new SergeantVisitor(), + Optional.empty(), + Optional.of("Hello sergeant"), + Optional.empty() + ); + } + +} diff --git a/visitor/src/test/java/com/iluwatar/visitor/SoldierTest.java b/visitor/src/test/java/com/iluwatar/visitor/SoldierTest.java new file mode 100644 index 000000000..18b2f2c08 --- /dev/null +++ b/visitor/src/test/java/com/iluwatar/visitor/SoldierTest.java @@ -0,0 +1,47 @@ +/** + * The MIT License + * Copyright (c) 2014 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.visitor; + +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.verify; + +/** + * Date: 12/30/15 - 19:45 PM + * + * @author Jeroen Meulemeester + */ +public class SoldierTest extends UnitTest { + + /** + * Create a new test instance for the given {@link Soldier} + */ + public SoldierTest() { + super(Soldier::new); + } + + @Override + void verifyVisit(Soldier unit, UnitVisitor mockedVisitor) { + verify(mockedVisitor).visitSoldier(eq(unit)); + } + +} \ No newline at end of file diff --git a/visitor/src/test/java/com/iluwatar/visitor/SoldierVisitorTest.java b/visitor/src/test/java/com/iluwatar/visitor/SoldierVisitorTest.java new file mode 100644 index 000000000..c56485eb4 --- /dev/null +++ b/visitor/src/test/java/com/iluwatar/visitor/SoldierVisitorTest.java @@ -0,0 +1,46 @@ +/** + * The MIT License + * Copyright (c) 2014 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.visitor; + +import java.util.Optional; + +/** + * Date: 12/30/15 - 18:59 PM + * + * @author Jeroen Meulemeester + */ +public class SoldierVisitorTest extends VisitorTest { + + /** + * Create a new test instance for the given visitor + */ + public SoldierVisitorTest() { + super( + new SoldierVisitor(), + Optional.empty(), + Optional.empty(), + Optional.of("Greetings soldier") + ); + } + +} diff --git a/visitor/src/test/java/com/iluwatar/visitor/StdOutTest.java b/visitor/src/test/java/com/iluwatar/visitor/StdOutTest.java new file mode 100644 index 000000000..075f235f5 --- /dev/null +++ b/visitor/src/test/java/com/iluwatar/visitor/StdOutTest.java @@ -0,0 +1,75 @@ +/** + * The MIT License + * Copyright (c) 2014 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.visitor; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since some actions don't have any + * influence on accessible objects, except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +} diff --git a/visitor/src/test/java/com/iluwatar/visitor/UnitTest.java b/visitor/src/test/java/com/iluwatar/visitor/UnitTest.java new file mode 100644 index 000000000..cba91a7f6 --- /dev/null +++ b/visitor/src/test/java/com/iluwatar/visitor/UnitTest.java @@ -0,0 +1,82 @@ +/** + * The MIT License + * Copyright (c) 2014 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.visitor; + +import org.junit.Test; + +import java.util.Arrays; +import java.util.function.Function; + +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/30/15 - 18:59 PM + * + * @author Jeroen Meulemeester + */ +public abstract class UnitTest { + + /** + * Factory to create new instances of the tested unit + */ + private final Function factory; + + /** + * Create a new test instance for the given unit type {@link U} + * + * @param factory Factory to create new instances of the tested unit + */ + public UnitTest(final Function factory) { + this.factory = factory; + } + + @Test + public void testAccept() throws Exception { + final Unit[] children = new Unit[5]; + Arrays.setAll(children, (i) -> mock(Unit.class)); + + final U unit = this.factory.apply(children); + final UnitVisitor visitor = mock(UnitVisitor.class); + unit.accept(visitor); + verifyVisit(unit, visitor); + + for (final Unit child : children) { + verify(child).accept(eq(visitor)); + } + + verifyNoMoreInteractions(children); + verifyNoMoreInteractions(visitor); + } + + /** + * Verify if the correct visit method is called on the mock, depending on the tested instance + * + * @param unit The tested unit instance + * @param mockedVisitor The mocked {@link UnitVisitor} who should have gotten a visit by the unit + */ + abstract void verifyVisit(final U unit, final UnitVisitor mockedVisitor); + +} \ No newline at end of file diff --git a/visitor/src/test/java/com/iluwatar/visitor/VisitorTest.java b/visitor/src/test/java/com/iluwatar/visitor/VisitorTest.java new file mode 100644 index 000000000..4a131bbf2 --- /dev/null +++ b/visitor/src/test/java/com/iluwatar/visitor/VisitorTest.java @@ -0,0 +1,102 @@ +/** + * The MIT License + * Copyright (c) 2014 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.visitor; + +import org.junit.Test; + +import java.util.Optional; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/30/15 - 18:59 PM + * + * @author Jeroen Meulemeester + */ +public abstract class VisitorTest extends StdOutTest { + + /** + * The tested visitor instance + */ + private final V visitor; + + /** + * The optional expected response when being visited by a commander + */ + private final Optional commanderResponse; + + /** + * The optional expected response when being visited by a sergeant + */ + private final Optional sergeantResponse; + + /** + * The optional expected response when being visited by a soldier + */ + private final Optional soldierResponse; + + /** + * Create a new test instance for the given visitor + * + * @param commanderResponse The optional expected response when being visited by a commander + * @param sergeantResponse The optional expected response when being visited by a sergeant + * @param soldierResponse The optional expected response when being visited by a soldier + */ + public VisitorTest(final V visitor, final Optional commanderResponse, + final Optional sergeantResponse, final Optional soldierResponse) { + + this.visitor = visitor; + this.commanderResponse = commanderResponse; + this.sergeantResponse = sergeantResponse; + this.soldierResponse = soldierResponse; + } + + @Test + public void testVisitCommander() { + this.visitor.visitCommander(new Commander()); + if (this.commanderResponse.isPresent()) { + verify(getStdOutMock()).println(this.commanderResponse.get()); + } + verifyNoMoreInteractions(getStdOutMock()); + } + + @Test + public void testVisitSergeant() { + this.visitor.visitSergeant(new Sergeant()); + if (this.sergeantResponse.isPresent()) { + verify(getStdOutMock()).println(this.sergeantResponse.get()); + } + verifyNoMoreInteractions(getStdOutMock()); + } + + @Test + public void testVisitSoldier() { + this.visitor.visitSoldier(new Soldier()); + if (this.soldierResponse.isPresent()) { + verify(getStdOutMock()).println(this.soldierResponse.get()); + } + verifyNoMoreInteractions(getStdOutMock()); + } + +}