diff --git a/.all-contributorsrc b/.all-contributorsrc index 8ecd99005..642856aab 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -315,7 +315,8 @@ "avatar_url": "https://avatars1.githubusercontent.com/u/6295975?v=4", "profile": "https://github.com/Anurag870", "contributions": [ - "code" + "code", + "doc" ] }, { @@ -946,7 +947,8 @@ "avatar_url": "https://avatars3.githubusercontent.com/u/23739158?v=4", "profile": "https://github.com/grzesiekkedzior", "contributions": [ - "code" + "code", + "review" ] }, { @@ -1082,7 +1084,8 @@ "avatar_url": "https://avatars1.githubusercontent.com/u/10645273?v=4", "profile": "https://github.com/ravening", "contributions": [ - "code" + "code", + "review" ] }, { @@ -1132,6 +1135,322 @@ "review", "maintenance" ] + }, + { + "login": "nahteb", + "name": "Bethan Palmer", + "avatar_url": "https://avatars3.githubusercontent.com/u/13121570?v=4", + "profile": "https://github.com/nahteb", + "contributions": [ + "code" + ] + }, + { + "login": "ToxicDreamz", + "name": "Toxic Dreamz", + "avatar_url": "https://avatars0.githubusercontent.com/u/45225562?v=4", + "profile": "https://github.com/ToxicDreamz", + "contributions": [ + "code" + ] + }, + { + "login": "edycutjong", + "name": "Edy Cu Tjong", + "avatar_url": "https://avatars1.githubusercontent.com/u/1098102?v=4", + "profile": "http://www.edycutjong.com", + "contributions": [ + "doc" + ] + }, + { + "login": "mkrzywanski", + "name": "Michał Krzywański", + "avatar_url": "https://avatars0.githubusercontent.com/u/15279585?v=4", + "profile": "https://github.com/mkrzywanski", + "contributions": [ + "code" + ] + }, + { + "login": "stefanbirkner", + "name": "Stefan Birkner", + "avatar_url": "https://avatars1.githubusercontent.com/u/711349?v=4", + "profile": "https://www.stefan-birkner.de", + "contributions": [ + "code" + ] + }, + { + "login": "fedorskvorcov", + "name": "Fedor Skvorcov", + "avatar_url": "https://avatars3.githubusercontent.com/u/43882212?v=4", + "profile": "https://github.com/fedorskvorcov", + "contributions": [ + "code" + ] + }, + { + "login": "samilAyoub", + "name": "samilAyoub", + "avatar_url": "https://avatars0.githubusercontent.com/u/61546990?v=4", + "profile": "https://github.com/samilAyoub", + "contributions": [ + "code" + ] + }, + { + "login": "vdlald", + "name": "Vladislav Golubinov", + "avatar_url": "https://avatars0.githubusercontent.com/u/29997701?v=4", + "profile": "https://github.com/vdlald", + "contributions": [ + "code" + ] + }, + { + "login": "swarajsaaj", + "name": "Swaraj", + "avatar_url": "https://avatars2.githubusercontent.com/u/6285049?v=4", + "profile": "https://github.com/swarajsaaj", + "contributions": [ + "code" + ] + }, + { + "login": "ChFlick", + "name": "Christoph Flick", + "avatar_url": "https://avatars0.githubusercontent.com/u/4465376?v=4", + "profile": "http://christophflick.de", + "contributions": [ + "doc" + ] + }, + { + "login": "Ascenio", + "name": "Ascênio", + "avatar_url": "https://avatars1.githubusercontent.com/u/7662016?v=4", + "profile": "https://github.com/Ascenio", + "contributions": [ + "review" + ] + }, + { + "login": "dsibilio", + "name": "Domenico Sibilio", + "avatar_url": "https://avatars2.githubusercontent.com/u/24280982?v=4", + "profile": "https://www.linkedin.com/in/domenico-sibilio/", + "contributions": [ + "doc" + ] + }, + { + "login": "akashchandwani", + "name": "Akash Chandwani", + "avatar_url": "https://avatars2.githubusercontent.com/u/3483277?v=4", + "profile": "https://github.com/akashchandwani", + "contributions": [ + "review" + ] + }, + { + "login": "manannikov", + "name": "Pavlo Manannikov", + "avatar_url": "https://avatars2.githubusercontent.com/u/7019769?v=4", + "profile": "http://www.linkedin.com/in/manannikov", + "contributions": [ + "code" + ] + }, + { + "login": "eimanip", + "name": "Eiman", + "avatar_url": "https://avatars0.githubusercontent.com/u/20307301?v=4", + "profile": "https://github.com/eimanip", + "contributions": [ + "code" + ] + }, + { + "login": "OrangePants-R", + "name": "Rocky", + "avatar_url": "https://avatars0.githubusercontent.com/u/42976136?v=4", + "profile": "https://github.com/OrangePants-R", + "contributions": [ + "doc" + ] + }, + { + "login": "ibrahimAlii", + "name": "Ibrahim ali abdelghany", + "avatar_url": "https://avatars2.githubusercontent.com/u/21141301?v=4", + "profile": "https://ibrahimalii.github.io/", + "contributions": [ + "review" + ] + }, + { + "login": "gkulkarni2020", + "name": "Girish Kulkarni", + "avatar_url": "https://avatars3.githubusercontent.com/u/5161548?v=4", + "profile": "https://github.com/gkulkarni2020", + "contributions": [ + "doc" + ] + }, + { + "login": "omk13", + "name": "Omar Karazoun", + "avatar_url": "https://avatars0.githubusercontent.com/u/59054172?v=4", + "profile": "https://github.com/omk13", + "contributions": [ + "code" + ] + }, + { + "login": "jeff303", + "name": "Jeff Evans", + "avatar_url": "https://avatars0.githubusercontent.com/u/3521562?v=4", + "profile": "https://github.com/jeff303", + "contributions": [ + "code" + ] + }, + { + "login": "viveksb007", + "name": "Vivek Singh", + "avatar_url": "https://avatars1.githubusercontent.com/u/12713808?v=4", + "profile": "https://viveksb007.github.io", + "contributions": [ + "code" + ] + }, + { + "login": "siavashsoleymani", + "name": "siavash", + "avatar_url": "https://avatars2.githubusercontent.com/u/18074419?v=4", + "profile": "https://github.com/siavashsoleymani", + "contributions": [ + "code" + ] + }, + { + "login": "ruchpeanuts", + "name": "ruchpeanuts", + "avatar_url": "https://avatars0.githubusercontent.com/u/29301900?v=4", + "profile": "https://github.com/ruchpeanuts", + "contributions": [ + "doc" + ] + }, + { + "login": "warp125", + "name": "warp125", + "avatar_url": "https://avatars1.githubusercontent.com/u/48073115?v=4", + "profile": "https://github.com/warp125", + "contributions": [ + "translation" + ] + }, + { + "login": "tkhadir", + "name": "KHADIR Tayeb", + "avatar_url": "https://avatars1.githubusercontent.com/u/45130488?v=4", + "profile": "http://libkhadir.fr", + "contributions": [ + "translation" + ] + }, + { + "login": "ignite1771", + "name": "ignite1771", + "avatar_url": "https://avatars2.githubusercontent.com/u/59446563?v=4", + "profile": "https://github.com/ignite1771", + "contributions": [ + "code" + ] + }, + { + "login": "demirhalil", + "name": "Halil Demir", + "avatar_url": "https://avatars1.githubusercontent.com/u/22895118?v=4", + "profile": "https://github.com/demirhalil", + "contributions": [ + "translation" + ] + }, + { + "login": "rohit10000", + "name": "Rohit Singh", + "avatar_url": "https://avatars.githubusercontent.com/u/20845565?v=4", + "profile": "https://github.com/rohit10000", + "contributions": [ + "code" + ] + }, + { + "login": "byoungju94", + "name": "byoungju94", + "avatar_url": "https://avatars.githubusercontent.com/u/42516378?v=4", + "profile": "https://github.com/byoungju94", + "contributions": [ + "code" + ] + }, + { + "login": "moustafafarhat", + "name": "Moustafa Farhat", + "avatar_url": "https://avatars.githubusercontent.com/u/38836727?v=4", + "profile": "https://github.com/moustafafarhat", + "contributions": [ + "translation" + ] + }, + { + "login": "richardmr36", + "name": "Martel Richard", + "avatar_url": "https://avatars.githubusercontent.com/u/19147333?v=4", + "profile": "https://github.com/richardmr36", + "contributions": [ + "code" + ] + }, + { + "login": "va1m", + "name": "va1m", + "avatar_url": "https://avatars.githubusercontent.com/u/17025445?v=4", + "profile": "https://github.com/va1m", + "contributions": [ + "code" + ] + }, + { + "login": "noamgrinch", + "name": "Noam Greenshtain", + "avatar_url": "https://avatars.githubusercontent.com/u/31648669?v=4", + "profile": "https://github.com/noamgrinch", + "contributions": [ + "code" + ] + }, + { + "login": "qfxl", + "name": "yonghong Xu", + "avatar_url": "https://avatars.githubusercontent.com/u/14086462?v=4", + "profile": "https://xuyonghong.cn/", + "contributions": [ + "doc" + ] + }, + { + "login": "jinishavora", + "name": "jinishavora", + "avatar_url": "https://avatars.githubusercontent.com/u/40777762?v=4", + "profile": "https://www.linkedin.com/in/jinisha-vora", + "contributions": [ + "review", + "code" + ] } ], "contributorsPerLine": 4, diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 000000000..3e35aee71 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,52 @@ +# +# The MIT License +# Copyright © 2014-2021 Ilkka Seppälä +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +version: 2 + +jobs: + sonar-pr: + docker: + - image: circleci/openjdk:11-node + steps: + - checkout + - restore_cache: + key: jdp-sonar-pr-{{ checksum "pom.xml" }} + - run: | + if [ -n "${CIRCLE_PR_NUMBER}" ]; then + MAVEN_OPTS="-Xmx3000m" xvfb-run ./mvnw -B clean verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar \ + -Dsonar.pullrequest.key=${CIRCLE_PR_NUMBER} \ + -Dsonar.pullrequest.branch=${CIRCLE_BRANCH} \ + -Dsonar.pullrequest.base=master + else + echo "No Sonar PR analysis as this is not a pull request" + fi + - save_cache: + key: jdp-sonar-pr-{{ checksum "pom.xml" }} + paths: + - ~/.m2 + +workflows: + version: 2 + all: + jobs: + - sonar-pr diff --git a/.github/workflows/maven-ci.yml b/.github/workflows/maven-ci.yml new file mode 100644 index 000000000..f264831bd --- /dev/null +++ b/.github/workflows/maven-ci.yml @@ -0,0 +1,77 @@ +# +# The MIT License +# Copyright © 2014-2021 Ilkka Seppälä +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: Java CI + +on: + push: + branches: [ master ] + + +jobs: + + build: + + runs-on: ubuntu-20.04 + + steps: + + - name: Checkout Code + uses: actions/checkout@master + with: + # Disabling shallow clone for improving relevancy of SonarQube reporting + fetch-depth: 0 + + - name: Set up JDK 11 + uses: actions/setup-java@master + with: + java-version: 11 + + - name: Cache SonarCloud packages + uses: actions/cache@master + with: + path: ~/.sonar/cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + + - name: Cache Maven dependencies + uses: actions/cache@master + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + # Some tests need screen access + - name: Install xvfb + run: sudo apt-get install -y xvfb + + - name: Build with Maven and run SonarQube analysis + run: xvfb-run ./mvnw clean verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar + env: + # These two env variables are needed for sonar analysis + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.github/workflows/maven.yml b/.github/workflows/maven-pr-builder.yml similarity index 65% rename from .github/workflows/maven.yml rename to .github/workflows/maven-pr-builder.yml index d18cad280..f529920ca 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven-pr-builder.yml @@ -1,6 +1,6 @@ # # The MIT License -# Copyright © 2014-2019 Ilkka Seppälä +# Copyright © 2014-2021 Ilkka Seppälä # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -24,37 +24,38 @@ # This workflow will build a Java project with Maven # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven -name: Java CI with Maven +name: Java PR Builder on: - push: - branches: [ master ] pull_request: branches: [ master ] + types: [ opened, reopened, synchronize ] jobs: build: - runs-on: ubuntu-18.04 + runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 + - name: Checkout Code + uses: actions/checkout@master + - name: Set up JDK 11 - uses: actions/setup-java@v1 + uses: actions/setup-java@master with: java-version: 11 + + - name: Cache Maven Dependecies + uses: actions/cache@master + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + # Some tests need screen access - name: Install xvfb - run: sudo apt-get install xvfb - # SonarQube scan does not work for forked repositories - # See https://jira.sonarsource.com/browse/MMF-1371 + run: sudo apt-get install -y xvfb + - name: Build with Maven - if: github.ref != 'refs/heads/master' - run: xvfb-run mvn clean verify - - name: Build with Maven and run SonarQube analysis - if: github.ref == 'refs/heads/master' - run: xvfb-run mvn clean verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar - env: - # These two env variables are needed for sonar analysis - GITHUB_TOKEN: ${{ secrets.REPOSITORY_ACCESS_TOKEN }} - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: xvfb-run ./mvnw clean verify diff --git a/.gitignore b/.gitignore index ada1e7d10..027769b63 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,54 @@ +################## Eclipse ###################### target .metadata .settings .classpath .project *.class -# Package Files # +tmp/ +*.tmp +*.bak +*~.nib +local.properties +.loadpath +.recommenders + +####### Java annotation processor (APT) ######## +.factorypath + +################ Package Files ################## *.jar *.war *.ear -.idea -*.iml *.swp datanucleus.log /bin/ -/bin/ -/bin/ *.log event-sourcing/Journal.json + +################## Checkstyle ################### +.checkstyle + +##################### STS ####################### +.apt_generated +.springBeans +.sts4-cache + +################# IntelliJ IDEA ################# +.idea +*.iws +*.iml +*.ipr + +################### NetBeans #################### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +#################### VS Code #################### +.vscode/ diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 000000000..cfc696f01 --- /dev/null +++ b/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,125 @@ +/* + * The MIT License + * Copyright © 2014-2021 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +import java.net.*; +import java.io.*; +import java.nio.channels.*; +import java.util.Properties; + +public class MavenWrapperDownloader { + + private static final String WRAPPER_VERSION = "0.5.6"; + /** + * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. + */ + private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" + + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; + + /** + * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to + * use instead of the default one. + */ + private static final String MAVEN_WRAPPER_PROPERTIES_PATH = + ".mvn/wrapper/maven-wrapper.properties"; + + /** + * Path where the maven-wrapper.jar will be saved to. + */ + private static final String MAVEN_WRAPPER_JAR_PATH = + ".mvn/wrapper/maven-wrapper.jar"; + + /** + * Name of the property which should be used to override the default download url for the wrapper. + */ + private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; + + public static void main(String args[]) { + System.out.println("- Downloader started"); + File baseDirectory = new File(args[0]); + System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); + + // If the maven-wrapper.properties exists, read it and check if it contains a custom + // wrapperUrl parameter. + File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); + String url = DEFAULT_DOWNLOAD_URL; + if(mavenWrapperPropertyFile.exists()) { + FileInputStream mavenWrapperPropertyFileInputStream = null; + try { + mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); + Properties mavenWrapperProperties = new Properties(); + mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); + url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); + } catch (IOException e) { + System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); + } finally { + try { + if(mavenWrapperPropertyFileInputStream != null) { + mavenWrapperPropertyFileInputStream.close(); + } + } catch (IOException e) { + // Ignore ... + } + } + } + System.out.println("- Downloading from: " + url); + + File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); + if(!outputFile.getParentFile().exists()) { + if(!outputFile.getParentFile().mkdirs()) { + System.out.println( + "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); + } + } + System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); + try { + downloadFileFromURL(url, outputFile); + System.out.println("Done"); + System.exit(0); + } catch (Throwable e) { + System.out.println("- Error downloading"); + e.printStackTrace(); + System.exit(1); + } + } + + private static void downloadFileFromURL(String urlString, File destination) throws Exception { + if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { + String username = System.getenv("MVNW_USERNAME"); + char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override + protected PasswordAuthentication getPasswordAuthentication() { + return new PasswordAuthentication(username, password); + } + }); + } + URL website = new URL(urlString); + ReadableByteChannel rbc; + rbc = Channels.newChannel(website.openStream()); + FileOutputStream fos = new FileOutputStream(destination); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.close(); + rbc.close(); + } + +} diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 000000000..7af88697b --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,25 @@ +# +# The MIT License +# Copyright © 2014-2021 Ilkka Seppälä +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/LICENSE.md b/LICENSE.md index 909c618ce..ff341fcb6 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2014-2020 Ilkka Seppälä +Copyright (c) 2014-2021 Ilkka Seppälä 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 5d883dc4e..bb42f3f69 100644 --- a/README.md +++ b/README.md @@ -4,14 +4,21 @@ # Design patterns implemented in Java -![Java CI with Maven](https://github.com/iluwatar/java-design-patterns/workflows/Java%20CI%20with%20Maven/badge.svg) +![Java CI](https://github.com/iluwatar/java-design-patterns/workflows/Java%20CI/badge.svg) [![License MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/LICENSE.md) +[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=iluwatar_java-design-patterns&metric=ncloc)](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns) +[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=iluwatar_java-design-patterns&metric=coverage)](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns) [![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) -[![Sonarcloud Status](https://sonarcloud.io/api/project_badges/measure?project=iluwatar_java-design-patterns&metric=alert_status)](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns) -[![All Contributors](https://img.shields.io/badge/all_contributors-124-orange.svg?style=flat-square)](#contributors-) +[![All Contributors](https://img.shields.io/badge/all_contributors-159-orange.svg?style=flat-square)](#contributors-) +
+ +Read in different language : [![CN](/assets/flags/CN.png)**CN**](/zh/README.md), [![KR](/assets/flags/KR.png)**KR**](/ko/README.md), [![FR](/assets/flags/FR.png)**FR**](/fr/README.md), [![TR](/assets/flags/TR.png)**TR**](/tr/README.md), [![AR](/assets/flags/AR.png)**AR**](/ar/README.md) + +
+ # Introduction Design patterns are the best formalized practices a programmer can use to @@ -30,7 +37,7 @@ This site showcases Java Design Patterns. The solutions have been developed by experienced programmers and architects from the open source community. The patterns can be browsed by their high level descriptions or by looking at their source code. The source code examples are well commented and can be thought as -programming tutorials how to implement a specific pattern. We use the most +programming tutorials on how to implement a specific pattern. We use the most popular battle-proven open source Java technologies. Before you dive into the material, you should be familiar with various diff --git a/abstract-document/pom.xml b/abstract-document/pom.xml index 5a19c3c81..42ede08da 100644 --- a/abstract-document/pom.xml +++ b/abstract-document/pom.xml @@ -1,19 +1,28 @@ - + @@ -21,7 +30,7 @@ java-design-patterns com.iluwatar - 1.23.0-SNAPSHOT + 1.24.0-SNAPSHOT abstract-document diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java index 485af25b3..21be23326 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/AbstractDocument.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java index d13021e72..59ca86135 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,8 +27,7 @@ import com.iluwatar.abstractdocument.domain.Car; import com.iluwatar.abstractdocument.domain.enums.Property; import java.util.List; import java.util.Map; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; /** * The Abstract Document pattern enables handling additional, non-static properties. This pattern @@ -38,10 +37,9 @@ import org.slf4j.LoggerFactory; *

In Abstract Document pattern,({@link AbstractDocument}) fully implements {@link Document}) * interface. Traits are then defined to enable access to properties in usual, static way. */ +@Slf4j public class App { - private static final Logger LOGGER = LoggerFactory.getLogger(App.class); - /** * Program entry point. * diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java index d0eb85f34..07b1354a3 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/Document.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java index bf68d40e5..124890cc5 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Car.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java index 76d7d7431..f1d3f3465 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasModel.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java index 54f308ccf..c007072fc 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasParts.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java index a50c725c3..2f9318939 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasPrice.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java index 2722564d5..c1bdc6cf0 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/HasType.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java index 996598a92..8689c812a 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/Part.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/enums/Property.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/enums/Property.java index 640f0ed83..bcd4443cd 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/enums/Property.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/domain/enums/Property.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java index 13db318e4..c301b85eb 100644 --- a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java +++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AbstractDocumentTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,19 +23,18 @@ package com.iluwatar.abstractdocument; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; +import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; /** * AbstractDocument test class */ -public class AbstractDocumentTest { +class AbstractDocumentTest { private static final String KEY = "key"; private static final String VALUE = "value"; @@ -50,13 +49,13 @@ public class AbstractDocumentTest { private final DocumentImplementation document = new DocumentImplementation(new HashMap<>()); @Test - public void shouldPutAndGetValue() { + void shouldPutAndGetValue() { document.put(KEY, VALUE); assertEquals(VALUE, document.get(KEY)); } @Test - public void shouldRetrieveChildren() { + void shouldRetrieveChildren() { var children = List.of(Map.of(), Map.of()); document.put(KEY, children); @@ -67,14 +66,14 @@ public class AbstractDocumentTest { } @Test - public void shouldRetrieveEmptyStreamForNonExistingChildren() { + void shouldRetrieveEmptyStreamForNonExistingChildren() { var children = document.children(KEY, DocumentImplementation::new); assertNotNull(children); assertEquals(0, children.count()); } @Test - public void shouldIncludePropsInToString() { + void shouldIncludePropsInToString() { var props = Map.of(KEY, (Object) VALUE); var document = new DocumentImplementation(props); assertTrue(document.toString().contains(KEY)); diff --git a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java index aed63f303..97d730826 100644 --- a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java +++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,14 +25,23 @@ package com.iluwatar.abstractdocument; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + /** * Simple App test */ -public class AppTest { +class AppTest { + + /** + * Issue: Add at least one assertion to this test case. + * + * Solution: Inserted assertion to check whether the execution of the main method in {@link App} + * throws an exception. + */ @Test - public void shouldExecuteAppWithoutException() { - App.main(null); + void shouldExecuteAppWithoutException() { + assertDoesNotThrow(() -> 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 index 5b1311ff6..a0e129083 100644 --- a/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java +++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,19 +23,20 @@ package com.iluwatar.abstractdocument; -import static org.junit.jupiter.api.Assertions.assertEquals; - import com.iluwatar.abstractdocument.domain.Car; import com.iluwatar.abstractdocument.domain.Part; import com.iluwatar.abstractdocument.domain.enums.Property; +import org.junit.jupiter.api.Test; + import java.util.List; import java.util.Map; -import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; /** * Test for Part and Car */ -public class DomainTest { +class DomainTest { private static final String TEST_PART_TYPE = "test-part-type"; private static final String TEST_PART_MODEL = "test-part-model"; @@ -45,7 +46,7 @@ public class DomainTest { private static final long TEST_CAR_PRICE = 1L; @Test - public void shouldConstructPart() { + void shouldConstructPart() { var partProperties = Map.of( Property.TYPE.toString(), TEST_PART_TYPE, Property.MODEL.toString(), TEST_PART_MODEL, @@ -58,7 +59,7 @@ public class DomainTest { } @Test - public void shouldConstructCar() { + void shouldConstructCar() { var carProperties = Map.of( Property.MODEL.toString(), TEST_CAR_MODEL, Property.PRICE.toString(), TEST_CAR_PRICE, diff --git a/abstract-factory/README.md b/abstract-factory/README.md index 22edb81e0..4cdbd850a 100644 --- a/abstract-factory/README.md +++ b/abstract-factory/README.md @@ -213,8 +213,8 @@ Example use cases ## Related patterns -[Factory Method](https://java-design-patterns.com/patterns/factory-method/) -[Factory Kit](https://java-design-patterns.com/patterns/factory-kit/) +* [Factory Method](https://java-design-patterns.com/patterns/factory-method/) +* [Factory Kit](https://java-design-patterns.com/patterns/factory-kit/) ## Credits diff --git a/abstract-factory/pom.xml b/abstract-factory/pom.xml index 614b26232..3b82189b9 100644 --- a/abstract-factory/pom.xml +++ b/abstract-factory/pom.xml @@ -1,19 +1,28 @@ - + com.iluwatar java-design-patterns - 1.23.0-SNAPSHOT + 1.24.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 e158ece74..d208002a7 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,9 +23,7 @@ package com.iluwatar.abstractfactory; -import com.iluwatar.abstractfactory.App.FactoryMaker.KingdomType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; /** * The Abstract Factory pattern provides a way to encapsulate a group of individual factories that @@ -41,84 +39,13 @@ import org.slf4j.LoggerFactory; * 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 { +@Slf4j +public class App implements Runnable { - private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + private final Kingdom kingdom = new Kingdom(); - 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; - } - - /** - * The factory of kingdom factories. - */ - public static class FactoryMaker { - - /** - * Enumeration for the different types of Kingdoms. - */ - public enum KingdomType { - ELF, ORC - } - - /** - * The factory method to create KingdomFactory concrete objects. - */ - public static KingdomFactory makeFactory(KingdomType type) { - switch (type) { - case ELF: - return new ElfKingdomFactory(); - case ORC: - return new OrcKingdomFactory(); - default: - throw new IllegalArgumentException("KingdomType not supported."); - } - } + public Kingdom getKingdom() { + return kingdom; } /** @@ -127,19 +54,33 @@ public class App { * @param args command line args */ public static void main(String[] args) { - var app = new App(); + app.run(); + } + @Override + public void run() { LOGGER.info("Elf Kingdom"); - app.createKingdom(FactoryMaker.makeFactory(KingdomType.ELF)); - LOGGER.info(app.getArmy().getDescription()); - LOGGER.info(app.getCastle().getDescription()); - LOGGER.info(app.getKing().getDescription()); + createKingdom(Kingdom.FactoryMaker.KingdomType.ELF); + LOGGER.info(kingdom.getArmy().getDescription()); + LOGGER.info(kingdom.getCastle().getDescription()); + LOGGER.info(kingdom.getKing().getDescription()); LOGGER.info("Orc Kingdom"); - app.createKingdom(FactoryMaker.makeFactory(KingdomType.ORC)); - LOGGER.info(app.getArmy().getDescription()); - LOGGER.info(app.getCastle().getDescription()); - LOGGER.info(app.getKing().getDescription()); + createKingdom(Kingdom.FactoryMaker.KingdomType.ORC); + LOGGER.info(kingdom.getArmy().getDescription()); + LOGGER.info(kingdom.getCastle().getDescription()); + LOGGER.info(kingdom.getKing().getDescription()); + } + + /** + * Creates kingdom. + * @param kingdomType type of Kingdom + */ + public void createKingdom(final Kingdom.FactoryMaker.KingdomType kingdomType) { + final KingdomFactory kingdomFactory = Kingdom.FactoryMaker.makeFactory(kingdomType); + kingdom.setKing(kingdomFactory.createKing()); + kingdom.setCastle(kingdomFactory.createCastle()); + kingdom.setArmy(kingdomFactory.createArmy()); } } \ No newline at end of file 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 51f69a822..0ed5dd8bd 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Army.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Army.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Castle.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Castle.java index c75eb32ef..2977cc978 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Castle.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Castle.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfArmy.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfArmy.java index 6d2da97cc..ad54943aa 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfArmy.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfArmy.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfCastle.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfCastle.java index 5f2b6ed2a..9c2ca1d4b 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfCastle.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfCastle.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKing.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKing.java index f7c4c6146..aa64a489f 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKing.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKing.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKingdomFactory.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKingdomFactory.java index 4493d2d08..a727a1778 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKingdomFactory.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKingdomFactory.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/abstract-factory/src/main/java/com/iluwatar/abstractfactory/King.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/King.java index 97e5f676f..704c14679 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/King.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/King.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Kingdom.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Kingdom.java new file mode 100644 index 000000000..f78e67875 --- /dev/null +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Kingdom.java @@ -0,0 +1,63 @@ +/* + * The MIT License + * Copyright © 2014-2021 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 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 lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class Kingdom { + + private King king; + private Castle castle; + private Army army; + + /** + * The factory of kingdom factories. + */ + public static class FactoryMaker { + + /** + * Enumeration for the different types of Kingdoms. + */ + public enum KingdomType { + ELF, ORC + } + + /** + * The factory method to create KingdomFactory concrete objects. + */ + public static KingdomFactory makeFactory(KingdomType type) { + switch (type) { + case ELF: + return new ElfKingdomFactory(); + case ORC: + return new OrcKingdomFactory(); + default: + throw new IllegalArgumentException("KingdomType not supported."); + } + } + } +} 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 a72dbf78c..520e36de0 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/KingdomFactory.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/KingdomFactory.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcArmy.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcArmy.java index ae0bd5c3e..fa7bf3889 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcArmy.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcArmy.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcCastle.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcCastle.java index 458a61bf9..fea7c1866 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcCastle.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcCastle.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKing.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKing.java index f73ada9b0..9a47a9d0b 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKing.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKing.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKingdomFactory.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKingdomFactory.java index ae7c744be..eb038ca61 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKingdomFactory.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKingdomFactory.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java index f3db525a1..3136ee29f 100644 --- a/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java +++ b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,65 +23,71 @@ package com.iluwatar.abstractfactory; +import org.junit.jupiter.api.Test; + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; -import com.iluwatar.abstractfactory.App.FactoryMaker; -import com.iluwatar.abstractfactory.App.FactoryMaker.KingdomType; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; - /** * Test for abstract factory. */ -public class AbstractFactoryTest { +class AbstractFactoryTest { private final App app = new App(); - private KingdomFactory elfFactory; - private KingdomFactory orcFactory; - - @BeforeEach - public void setUp() { - elfFactory = FactoryMaker.makeFactory(KingdomType.ELF); - orcFactory = FactoryMaker.makeFactory(KingdomType.ORC); - } @Test - public void king() { - final var elfKing = app.getKing(elfFactory); + void king() { + app.createKingdom(Kingdom.FactoryMaker.KingdomType.ELF); + final var kingdom = app.getKingdom(); + + final var elfKing = kingdom.getKing(); assertTrue(elfKing instanceof ElfKing); assertEquals(ElfKing.DESCRIPTION, elfKing.getDescription()); - final var orcKing = app.getKing(orcFactory); + + app.createKingdom(Kingdom.FactoryMaker.KingdomType.ORC); + final var orcKing = kingdom.getKing(); assertTrue(orcKing instanceof OrcKing); assertEquals(OrcKing.DESCRIPTION, orcKing.getDescription()); } @Test - public void castle() { - final var elfCastle = app.getCastle(elfFactory); + void castle() { + app.createKingdom(Kingdom.FactoryMaker.KingdomType.ELF); + final var kingdom = app.getKingdom(); + + final var elfCastle = kingdom.getCastle(); assertTrue(elfCastle instanceof ElfCastle); assertEquals(ElfCastle.DESCRIPTION, elfCastle.getDescription()); - final var orcCastle = app.getCastle(orcFactory); + + app.createKingdom(Kingdom.FactoryMaker.KingdomType.ORC); + final var orcCastle = kingdom.getCastle(); assertTrue(orcCastle instanceof OrcCastle); assertEquals(OrcCastle.DESCRIPTION, orcCastle.getDescription()); } @Test - public void army() { - final var elfArmy = app.getArmy(elfFactory); + void army() { + app.createKingdom(Kingdom.FactoryMaker.KingdomType.ELF); + final var kingdom = app.getKingdom(); + + final var elfArmy = kingdom.getArmy(); assertTrue(elfArmy instanceof ElfArmy); assertEquals(ElfArmy.DESCRIPTION, elfArmy.getDescription()); - final var orcArmy = app.getArmy(orcFactory); + + app.createKingdom(Kingdom.FactoryMaker.KingdomType.ORC); + final var orcArmy = kingdom.getArmy(); assertTrue(orcArmy instanceof OrcArmy); assertEquals(OrcArmy.DESCRIPTION, orcArmy.getDescription()); } @Test - public void createElfKingdom() { - app.createKingdom(elfFactory); - final var king = app.getKing(); - final var castle = app.getCastle(); - final var army = app.getArmy(); + void createElfKingdom() { + app.createKingdom(Kingdom.FactoryMaker.KingdomType.ELF); + final var kingdom = app.getKingdom(); + + final var king = kingdom.getKing(); + final var castle = kingdom.getCastle(); + final var army = kingdom.getArmy(); assertTrue(king instanceof ElfKing); assertEquals(ElfKing.DESCRIPTION, king.getDescription()); assertTrue(castle instanceof ElfCastle); @@ -91,11 +97,13 @@ public class AbstractFactoryTest { } @Test - public void createOrcKingdom() { - app.createKingdom(orcFactory); - final var king = app.getKing(); - final var castle = app.getCastle(); - final var army = app.getArmy(); + void createOrcKingdom() { + app.createKingdom(Kingdom.FactoryMaker.KingdomType.ORC); + final var kingdom = app.getKingdom(); + + final var king = kingdom.getKing(); + final var castle = kingdom.getCastle(); + final var army = kingdom.getArmy(); assertTrue(king instanceof OrcKing); assertEquals(OrcKing.DESCRIPTION, king.getDescription()); assertTrue(castle instanceof OrcCastle); 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 4036cc9b8..c5589895d 100644 --- a/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java +++ b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,12 +25,19 @@ package com.iluwatar.abstractfactory; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + /** - * Tests that Abstract Factory example runs without errors. + * Issue: Add at least one assertion to this test case. + * + * Solution: Inserted assertion to check whether the execution of the main method in {@link App} + * throws an exception. */ -public class AppTest { +class AppTest { + @Test - public void test() { - App.main(new String[]{}); + void shouldExecuteApplicationWithoutException() { + + assertDoesNotThrow(() -> App.main(new String[]{})); } } diff --git a/active-object/README.md b/active-object/README.md new file mode 100644 index 000000000..6e26a8595 --- /dev/null +++ b/active-object/README.md @@ -0,0 +1,125 @@ +--- +layout: pattern +title: Active Object +folder: active-object +permalink: /patterns/active-object/ +categories: Concurrency +tags: + - Performance +--- + + +## Intent +The active object design pattern decouples method execution from method invocation for objects that each reside in their thread of control. The goal is to introduce concurrency, by using asynchronous method invocation and a scheduler for handling requests. + +## Explanation + +The class that implements the active object pattern will contain a self-synchronization mechanism without using 'synchronized' methods. + +Real-world example + +>The Orcs are known for their wildness and untameable soul. It seems like they have their own thread of control based on previous behavior. + +To implement a creature that has its own thread of control mechanism and expose its API only and not the execution itself, we can use the Active Object pattern. + + +**Programmatic Example** + +```java +public abstract class ActiveCreature{ + private final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName()); + + private BlockingQueue requests; + + private String name; + + private Thread thread; + + public ActiveCreature(String name) { + this.name = name; + this.requests = new LinkedBlockingQueue(); + thread = new Thread(new Runnable() { + @Override + public void run() { + while (true) { + try { + requests.take().run(); + } catch (InterruptedException e) { + logger.error(e.getMessage()); + } + } + } + } + ); + thread.start(); + } + + public void eat() throws InterruptedException { + requests.put(new Runnable() { + @Override + public void run() { + logger.info("{} is eating!",name()); + logger.info("{} has finished eating!",name()); + } + } + ); + } + + public void roam() throws InterruptedException { + requests.put(new Runnable() { + @Override + public void run() { + logger.info("{} has started to roam and the wastelands.",name()); + } + } + ); + } + + public String name() { + return this.name; + } +} +``` + +We can see that any class that will extend the ActiveCreature class will have its own thread of control to execute and invocate methods. + +For example, the Orc class: + +```java +public class Orc extends ActiveCreature { + + public Orc(String name) { + super(name); + } + +} +``` + +Now, we can create multiple creatures such as Orcs, tell them to eat and roam and they will execute it on their own thread of control: + +```java + public static void main(String[] args) { + var app = new App(); + app.run(); + } + + @Override + public void run() { + ActiveCreature creature; + try { + for (int i = 0;i < creatures;i++) { + creature = new Orc(Orc.class.getSimpleName().toString() + i); + creature.eat(); + creature.roam(); + } + Thread.sleep(1000); + } catch (InterruptedException e) { + logger.error(e.getMessage()); + } + Runtime.getRuntime().exit(1); + } +``` + +## Class diagram + +![alt text](./etc/active-object.urm.PNG "Active Object class diagram") diff --git a/active-object/etc/active-object.urm.PNG b/active-object/etc/active-object.urm.PNG new file mode 100644 index 000000000..c14f66144 Binary files /dev/null and b/active-object/etc/active-object.urm.PNG differ diff --git a/active-object/etc/active-object.urm.puml b/active-object/etc/active-object.urm.puml new file mode 100644 index 000000000..3fc3c8e1e --- /dev/null +++ b/active-object/etc/active-object.urm.puml @@ -0,0 +1,25 @@ +@startuml +package com.iluwatar.activeobject { + abstract class ActiveCreature { + - logger : Logger + - name : String + - requests : BlockingQueue + - thread : Thread + + ActiveCreature(name : String) + + eat() + + name() : String + + roam() + } + class App { + - creatures : Integer + - logger : Logger + + App() + + main(args : String[]) {static} + + run() + } + class Orc { + + Orc(name : String) + } +} +Orc --|> ActiveCreature +@enduml \ No newline at end of file diff --git a/active-object/pom.xml b/active-object/pom.xml new file mode 100644 index 000000000..3ea22d4e2 --- /dev/null +++ b/active-object/pom.xml @@ -0,0 +1,65 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.24.0-SNAPSHOT + + active-object + + + org.junit.jupiter + junit-jupiter-engine + test + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + com.iluwatar.activeobject.App + + + + + + + + + diff --git a/active-object/src/main/java/com/iluwatar/activeobject/ActiveCreature.java b/active-object/src/main/java/com/iluwatar/activeobject/ActiveCreature.java new file mode 100644 index 000000000..479dc0643 --- /dev/null +++ b/active-object/src/main/java/com/iluwatar/activeobject/ActiveCreature.java @@ -0,0 +1,118 @@ +/* + * The MIT License + * Copyright © 2014-2021 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.activeobject; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * ActiveCreature class is the base of the active object example. + * @author Noam Greenshtain + * + */ +public abstract class ActiveCreature { + + private static final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName()); + + private BlockingQueue requests; + + private String name; + + private Thread thread; // Thread of execution. + + private int status; // status of the thread of execution. + + /** + * Constructor and initialization. + */ + protected ActiveCreature(String name) { + this.name = name; + this.status = 0; + this.requests = new LinkedBlockingQueue<>(); + thread = new Thread(() -> { + boolean infinite = true; + while (infinite) { + try { + requests.take().run(); + } catch (InterruptedException e) { + if (this.status != 0) { + logger.error("Thread was interrupted. --> {}", e.getMessage()); + } + infinite = false; + Thread.currentThread().interrupt(); + } + } + }); + thread.start(); + } + + /** + * Eats the porridge. + * @throws InterruptedException due to firing a new Runnable. + */ + public void eat() throws InterruptedException { + requests.put(() -> { + logger.info("{} is eating!",name()); + logger.info("{} has finished eating!",name()); + }); + } + + /** + * Roam in the wastelands. + * @throws InterruptedException due to firing a new Runnable. + */ + public void roam() throws InterruptedException { + requests.put(() -> + logger.info("{} has started to roam in the wastelands.",name()) + ); + } + + /** + * Returns the name of the creature. + * @return the name of the creature. + */ + public String name() { + return this.name; + } + + /** + * Kills the thread of execution. + * @param status of the thread of execution. 0 == OK, the rest is logging an error. + */ + public void kill(int status) { + this.status = status; + this.thread.interrupt(); + } + + /** + * Returns the status of the thread of execution. + * @return the status of the thread of execution. + */ + public int getStatus() { + return this.status; + } +} diff --git a/active-object/src/main/java/com/iluwatar/activeobject/App.java b/active-object/src/main/java/com/iluwatar/activeobject/App.java new file mode 100644 index 000000000..d36d1023d --- /dev/null +++ b/active-object/src/main/java/com/iluwatar/activeobject/App.java @@ -0,0 +1,75 @@ +/* + * The MIT License + * Copyright © 2014-2021 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.activeobject; + +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The Active Object pattern helps to solve synchronization difficulties without using + * 'synchronized' methods. The active object will contain a thread-safe data structure + * (such as BlockingQueue) and use to synchronize method calls by moving the logic of the method + * into an invocator(usually a Runnable) and store it in the DSA. + * + *

In this example, we fire 20 threads to modify a value in the target class. + */ +public class App implements Runnable { + + private static final Logger logger = LoggerFactory.getLogger(App.class.getName()); + + private static final int NUM_CREATURES = 3; + + /** + * Program entry point. + * + * @param args command line arguments. + */ + public static void main(String[] args) { + var app = new App(); + app.run(); + } + + @Override + public void run() { + List creatures = new ArrayList<>(); + try { + for (int i = 0;i < NUM_CREATURES;i++) { + creatures.add(new Orc(Orc.class.getSimpleName() + i)); + creatures.get(i).eat(); + creatures.get(i).roam(); + } + Thread.sleep(1000); + } catch (InterruptedException e) { + logger.error(e.getMessage()); + Thread.currentThread().interrupt(); + } finally { + for (int i = 0;i < NUM_CREATURES;i++) { + creatures.get(i).kill(0); + } + } + } +} diff --git a/command/src/main/java/com/iluwatar/command/Command.java b/active-object/src/main/java/com/iluwatar/activeobject/Orc.java similarity index 81% rename from command/src/main/java/com/iluwatar/command/Command.java rename to active-object/src/main/java/com/iluwatar/activeobject/Orc.java index 83010f160..2bce5b2b0 100644 --- a/command/src/main/java/com/iluwatar/command/Command.java +++ b/active-object/src/main/java/com/iluwatar/activeobject/Orc.java @@ -1,37 +1,37 @@ -/* - * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package com.iluwatar.command; - -/** - * Interface for Commands. - */ -public interface Command { - void execute(Target target); - - void undo(); - - void redo(); - - String toString(); -} +/* + * The MIT License + * Copyright © 2014-2021 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.activeobject; + +/** + * An implementation of the ActiveCreature class. + * @author Noam Greenshtain + * + */ +public class Orc extends ActiveCreature { + + public Orc(String name) { + super(name); + } + +} diff --git a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DelayedServiceTest.java b/active-object/src/test/java/com/iluwatar/activeobject/ActiveCreatureTest.java similarity index 77% rename from circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DelayedServiceTest.java rename to active-object/src/test/java/com/iluwatar/activeobject/ActiveCreatureTest.java index af747f794..8a4296a1f 100644 --- a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DelayedServiceTest.java +++ b/active-object/src/test/java/com/iluwatar/activeobject/ActiveCreatureTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,21 +21,22 @@ * THE SOFTWARE. */ -package com.iluwatar.circuitbreaker; +package com.iluwatar.activeobject; import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; +class ActiveCreatureTest { + + @Test + void executionTest() throws InterruptedException { + ActiveCreature orc = new Orc("orc1"); + assertEquals("orc1",orc.name()); + assertEquals(0,orc.getStatus()); + orc.eat(); + orc.roam(); + orc.kill(0); + } + -/** - * Monitoring Service test - */ -public class DelayedServiceTest { - - //Improves code coverage - @Test - public void testDefaultConstructor() { - var obj = new DelayedService(); - assertEquals(obj.response(System.nanoTime()), "Delayed service is down"); - } } diff --git a/active-object/src/test/java/com/iluwatar/activeobject/AppTest.java b/active-object/src/test/java/com/iluwatar/activeobject/AppTest.java new file mode 100644 index 000000000..c7b223d67 --- /dev/null +++ b/active-object/src/test/java/com/iluwatar/activeobject/AppTest.java @@ -0,0 +1,37 @@ +/* + * The MIT License + * Copyright © 2014-2021 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.activeobject; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.Test; + + +class AppTest { + + @Test + void shouldExecuteApplicationWithoutException() { + assertDoesNotThrow(() -> App.main(new String[]{})); + } +} diff --git a/acyclic-visitor/pom.xml b/acyclic-visitor/pom.xml index 24bab933a..234cbfefc 100644 --- a/acyclic-visitor/pom.xml +++ b/acyclic-visitor/pom.xml @@ -1,18 +1,27 @@ - + @@ -21,7 +30,7 @@ com.iluwatar java-design-patterns - 1.23.0-SNAPSHOT + 1.24.0-SNAPSHOT acyclic-visitor diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/AllModemVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/AllModemVisitor.java index 354c4db74..28345c505 100644 --- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/AllModemVisitor.java +++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/AllModemVisitor.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/App.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/App.java index 866abc3b7..b151019c2 100644 --- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/App.java +++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/App.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitor.java index f9df38529..bfe7f6646 100644 --- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitor.java +++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitor.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,17 +23,15 @@ package com.iluwatar.acyclicvisitor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; /** * ConfigureForDosVisitor class implements both zoom's and hayes' visit method for Dos * manufacturer. */ +@Slf4j public class ConfigureForDosVisitor implements AllModemVisitor { - private static final Logger LOGGER = LoggerFactory.getLogger(ConfigureForDosVisitor.class); - @Override public void visit(Hayes hayes) { LOGGER.info(hayes + " used with Dos configurator."); diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitor.java index 3d14eff8f..1f465967f 100644 --- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitor.java +++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitor.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,17 +23,15 @@ package com.iluwatar.acyclicvisitor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; /** * ConfigureForUnixVisitor class implements zoom's visit method for Unix manufacturer, unlike * traditional visitor pattern, this class may selectively implement visit for other modems. */ +@Slf4j public class ConfigureForUnixVisitor implements ZoomVisitor { - private static final Logger LOGGER = LoggerFactory.getLogger(ConfigureForUnixVisitor.class); - @Override public void visit(Zoom zoom) { LOGGER.info(zoom + " used with Unix configurator."); diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Hayes.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Hayes.java index b49a6234c..3e30258f5 100644 --- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Hayes.java +++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Hayes.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,16 +23,14 @@ package com.iluwatar.acyclicvisitor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; /** * Hayes class implements its accept method. */ +@Slf4j public class Hayes extends Modem { - private static final Logger LOGGER = LoggerFactory.getLogger(ConfigureForDosVisitor.class); - /** * Accepts all visitors but honors only HayesVisitor. */ diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/HayesVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/HayesVisitor.java index 59527d57b..06ee5300d 100644 --- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/HayesVisitor.java +++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/HayesVisitor.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Modem.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Modem.java index 201712dd1..04018d543 100644 --- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Modem.java +++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Modem.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ModemVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ModemVisitor.java index b4058f237..b2181fe7c 100644 --- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ModemVisitor.java +++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ModemVisitor.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Zoom.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Zoom.java index 3fbaa38df..01e7e7aca 100644 --- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Zoom.java +++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Zoom.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,16 +23,14 @@ package com.iluwatar.acyclicvisitor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; /** * Zoom class implements its accept method. */ +@Slf4j public class Zoom extends Modem { - private static final Logger LOGGER = LoggerFactory.getLogger(ConfigureForDosVisitor.class); - /** * Accepts all visitors but honors only ZoomVisitor. */ diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ZoomVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ZoomVisitor.java index fd54d8b21..e86a64fa6 100644 --- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ZoomVisitor.java +++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ZoomVisitor.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/AppTest.java b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/AppTest.java index 4b9a7ec6c..12f0ba6ce 100644 --- a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/AppTest.java +++ b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/AppTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,13 +25,23 @@ package com.iluwatar.acyclicvisitor; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + /** * Tests that the Acyclic Visitor example runs without errors. */ -public class AppTest { +class AppTest { + + /** + * Issue: Add at least one assertion to this test case. + * + * Solution: Inserted assertion to check whether the execution of the main method in {@link App} + * throws an exception. + */ @Test - public void test() { - App.main(new String[]{}); + void shouldExecuteApplicationWithoutException() { + + assertDoesNotThrow(() -> App.main(new String[]{})); } } \ No newline at end of file diff --git a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitorTest.java b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitorTest.java index 79097a454..b41fc956c 100644 --- a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitorTest.java +++ b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitorTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -35,34 +35,34 @@ import uk.org.lidalia.slf4jtest.TestLoggerFactory; /** * ConfigureForDosVisitor test class */ -public class ConfigureForDosVisitorTest { +class ConfigureForDosVisitorTest { private final TestLogger logger = TestLoggerFactory.getTestLogger(ConfigureForDosVisitor.class); - + @Test - public void testVisitForZoom() { + void testVisitForZoom() { var conDos = new ConfigureForDosVisitor(); var zoom = new Zoom(); - + conDos.visit(zoom); - + assertThat(logger.getLoggingEvents()) .extracting("level", "message") .contains(tuple(INFO, zoom + " used with Dos configurator.")); } - + @Test - public void testVisitForHayes() { + void testVisitForHayes() { var conDos = new ConfigureForDosVisitor(); var hayes = new Hayes(); - + conDos.visit(hayes); - + assertThat(logger.getLoggingEvents()) .extracting("level", "message") .contains(tuple(INFO, hayes + " used with Dos configurator.")); } - + @AfterEach public void clearLoggers() { TestLoggerFactory.clear(); diff --git a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitorTest.java b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitorTest.java index 32067ad38..d2ea55809 100644 --- a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitorTest.java +++ b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitorTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,35 +23,34 @@ package com.iluwatar.acyclicvisitor; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import uk.org.lidalia.slf4jtest.TestLogger; +import uk.org.lidalia.slf4jtest.TestLoggerFactory; + import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.groups.Tuple.tuple; import static uk.org.lidalia.slf4jext.Level.INFO; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; - -import uk.org.lidalia.slf4jtest.TestLogger; -import uk.org.lidalia.slf4jtest.TestLoggerFactory; - /** * ConfigureForUnixVisitor test class */ -public class ConfigureForUnixVisitorTest { - +class ConfigureForUnixVisitorTest { + private static final TestLogger LOGGER = TestLoggerFactory.getTestLogger(ConfigureForUnixVisitor.class); - + @AfterEach public void clearLoggers() { TestLoggerFactory.clear(); } - + @Test - public void testVisitForZoom() { + void testVisitForZoom() { var conUnix = new ConfigureForUnixVisitor(); var zoom = new Zoom(); - + conUnix.visit(zoom); - + assertThat(LOGGER.getLoggingEvents()) .extracting("level", "message") .contains(tuple(INFO, zoom + " used with Unix configurator.")); diff --git a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/HayesTest.java b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/HayesTest.java index 308dd5879..eaf9fb152 100644 --- a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/HayesTest.java +++ b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/HayesTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,34 +23,32 @@ package com.iluwatar.acyclicvisitor; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; - import org.junit.jupiter.api.Test; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.*; + /** * Hayes test class */ -public class HayesTest { +class HayesTest { @Test - public void testAcceptForDos() { + void testAcceptForDos() { var hayes = new Hayes(); var mockVisitor = mock(ConfigureForDosVisitor.class); - + hayes.accept(mockVisitor); - verify((HayesVisitor)mockVisitor).visit(eq(hayes)); + verify((HayesVisitor) mockVisitor).visit(eq(hayes)); } - + @Test - public void testAcceptForUnix() { + void testAcceptForUnix() { var hayes = new Hayes(); var mockVisitor = mock(ConfigureForUnixVisitor.class); - + hayes.accept(mockVisitor); - + verifyZeroInteractions(mockVisitor); } } diff --git a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ZoomTest.java b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ZoomTest.java index 2dcfcfbbb..4373fe818 100644 --- a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ZoomTest.java +++ b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ZoomTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,32 +24,32 @@ package com.iluwatar.acyclicvisitor; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.mock; - import org.junit.jupiter.api.Test; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + /** * Zoom test class */ -public class ZoomTest { - +class ZoomTest { + @Test - public void testAcceptForDos() { + void testAcceptForDos() { var zoom = new Zoom(); var mockVisitor = mock(ConfigureForDosVisitor.class); - + zoom.accept(mockVisitor); - verify((ZoomVisitor)mockVisitor).visit(eq(zoom)); + verify((ZoomVisitor) mockVisitor).visit(eq(zoom)); } - + @Test - public void testAcceptForUnix() { + void testAcceptForUnix() { var zoom = new Zoom(); var mockVisitor = mock(ConfigureForUnixVisitor.class); - + zoom.accept(mockVisitor); - verify((ZoomVisitor)mockVisitor).visit(eq(zoom)); + verify((ZoomVisitor) mockVisitor).visit(eq(zoom)); } } diff --git a/adapter/README.md b/adapter/README.md index aef4cdb69..65ffa4fc6 100644 --- a/adapter/README.md +++ b/adapter/README.md @@ -42,8 +42,8 @@ public interface RowingBoat { void row(); } +@Slf4j public class FishingBoat { - private static final Logger LOGGER = LoggerFactory.getLogger(FishingBoat.class); public void sail() { LOGGER.info("The fishing boat is sailing"); } @@ -70,10 +70,9 @@ public class Captain { Now let's say the pirates are coming and our captain needs to escape but there is only fishing boat available. We need to create an adapter that allows the captain to operate the fishing boat with his rowing boat skills. ```java +@Slf4j public class FishingBoatAdapter implements RowingBoat { - private static final Logger LOGGER = LoggerFactory.getLogger(FishingBoatAdapter.class); - private final FishingBoat boat; public FishingBoatAdapter() { diff --git a/adapter/pom.xml b/adapter/pom.xml index 4c725def8..f50a7ae3d 100644 --- a/adapter/pom.xml +++ b/adapter/pom.xml @@ -1,19 +1,28 @@ - + com.iluwatar java-design-patterns - 1.23.0-SNAPSHOT + 1.24.0-SNAPSHOT adapter diff --git a/adapter/src/main/java/com/iluwatar/adapter/App.java b/adapter/src/main/java/com/iluwatar/adapter/App.java index 4e3755fb2..1375d85c7 100644 --- a/adapter/src/main/java/com/iluwatar/adapter/App.java +++ b/adapter/src/main/java/com/iluwatar/adapter/App.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/adapter/src/main/java/com/iluwatar/adapter/Captain.java b/adapter/src/main/java/com/iluwatar/adapter/Captain.java index b83b13429..036792e8c 100644 --- a/adapter/src/main/java/com/iluwatar/adapter/Captain.java +++ b/adapter/src/main/java/com/iluwatar/adapter/Captain.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,24 +23,20 @@ package com.iluwatar.adapter; +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; +import lombok.Setter; + /** * The Captain uses {@link RowingBoat} to sail.
This is the client in the pattern. */ +@Setter +@NoArgsConstructor +@AllArgsConstructor public final class Captain { private RowingBoat rowingBoat; - public Captain() { - } - - public Captain(final RowingBoat boat) { - this.rowingBoat = boat; - } - - void setRowingBoat(final RowingBoat boat) { - this.rowingBoat = boat; - } - void row() { rowingBoat.row(); } diff --git a/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java b/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java index 123006c46..38866dc33 100644 --- a/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java +++ b/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,18 +23,15 @@ package com.iluwatar.adapter; -import static org.slf4j.LoggerFactory.getLogger; - -import org.slf4j.Logger; +import lombok.extern.slf4j.Slf4j; /** * Device class (adaptee in the pattern). We want to reuse this class. Fishing boat moves by * sailing. */ +@Slf4j final class FishingBoat { - private static final Logger LOGGER = getLogger(FishingBoat.class); - void sail() { LOGGER.info("The fishing boat is sailing"); } diff --git a/adapter/src/main/java/com/iluwatar/adapter/FishingBoatAdapter.java b/adapter/src/main/java/com/iluwatar/adapter/FishingBoatAdapter.java index 39a9adab4..73c7dd60e 100644 --- a/adapter/src/main/java/com/iluwatar/adapter/FishingBoatAdapter.java +++ b/adapter/src/main/java/com/iluwatar/adapter/FishingBoatAdapter.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,11 +29,7 @@ package com.iluwatar.adapter; */ public class FishingBoatAdapter implements RowingBoat { - private final FishingBoat boat; - - public FishingBoatAdapter() { - boat = new FishingBoat(); - } + private final FishingBoat boat = new FishingBoat(); public final void row() { boat.sail(); diff --git a/adapter/src/main/java/com/iluwatar/adapter/RowingBoat.java b/adapter/src/main/java/com/iluwatar/adapter/RowingBoat.java index 908036a3f..69d39c193 100644 --- a/adapter/src/main/java/com/iluwatar/adapter/RowingBoat.java +++ b/adapter/src/main/java/com/iluwatar/adapter/RowingBoat.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/adapter/src/main/java/com/iluwatar/adapter/package-info.java b/adapter/src/main/java/com/iluwatar/adapter/package-info.java index d036d86dd..873df29c5 100644 --- a/adapter/src/main/java/com/iluwatar/adapter/package-info.java +++ b/adapter/src/main/java/com/iluwatar/adapter/package-info.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/adapter/src/test/java/com/iluwatar/adapter/AdapterPatternTest.java b/adapter/src/test/java/com/iluwatar/adapter/AdapterPatternTest.java index f87073b23..fc55cd69e 100644 --- a/adapter/src/test/java/com/iluwatar/adapter/AdapterPatternTest.java +++ b/adapter/src/test/java/com/iluwatar/adapter/AdapterPatternTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,18 +23,19 @@ package com.iluwatar.adapter; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; /** * Test class */ -public class AdapterPatternTest { +class AdapterPatternTest { private Map beans; @@ -64,7 +65,7 @@ public class AdapterPatternTest { * by the client ({@link Captain} ). */ @Test - public void testAdapter() { + void testAdapter() { var captain = (Captain) beans.get(ROWING_BEAN); // when captain moves diff --git a/adapter/src/test/java/com/iluwatar/adapter/AppTest.java b/adapter/src/test/java/com/iluwatar/adapter/AppTest.java index 3bf8e1010..8b224e451 100644 --- a/adapter/src/test/java/com/iluwatar/adapter/AppTest.java +++ b/adapter/src/test/java/com/iluwatar/adapter/AppTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,12 +25,23 @@ package com.iluwatar.adapter; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + /** * Tests that Adapter example runs without errors. */ -public class AppTest { +class AppTest { + + /** + * Issue: Add at least one assertion to this test case. + * + * Solution: Inserted assertion to check whether the execution of the main method in {@link App} + * throws an exception. + */ + @Test - public void test() { - App.main(new String[]{}); + void shouldExecuteApplicationWithoutException() { + + assertDoesNotThrow(() -> App.main(new String[]{})); } } diff --git a/aggregator-microservices/aggregator-service/pom.xml b/aggregator-microservices/aggregator-service/pom.xml index f4482d0e3..46337c0ec 100644 --- a/aggregator-microservices/aggregator-service/pom.xml +++ b/aggregator-microservices/aggregator-service/pom.xml @@ -1,26 +1,35 @@ - + aggregator-microservices com.iluwatar - 1.23.0-SNAPSHOT + 1.24.0-SNAPSHOT 4.0.0 aggregator-service 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 index f28377a1d..209187bd7 100644 --- 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 @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,8 +26,7 @@ package com.iluwatar.aggregator.microservices; import static java.util.Objects.requireNonNullElse; import javax.annotation.Resource; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** @@ -37,20 +36,18 @@ import org.springframework.web.bind.annotation.RestController; @RestController public class Aggregator { - @Resource private ProductInformationClient informationClient; @Resource private ProductInventoryClient inventoryClient; - /** * Retrieves product data. * * @return a Product. */ - @RequestMapping(path = "/product", method = RequestMethod.GET) + @GetMapping("/product") public Product getProduct() { var product = new 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 index 3c09c54be..791df87ba 100644 --- 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 @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/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 index 3c214a58a..f451728b9 100644 --- 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 @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,9 +23,14 @@ package com.iluwatar.aggregator.microservices; +import lombok.Getter; +import lombok.Setter; + /** * Encapsulates all the data for a Product that clients will request. */ +@Getter +@Setter public class Product { /** @@ -39,20 +44,4 @@ public class 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 index 47d786ec6..75d87967d 100644 --- 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 @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/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 index d19dcd829..2fcad382e 100644 --- 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 @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,18 +28,16 @@ import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; /** * An adapter to communicate with information micro-service. */ +@Slf4j @Component public class ProductInformationClientImpl implements ProductInformationClient { - private static final Logger LOGGER = LoggerFactory.getLogger(ProductInformationClientImpl.class); - @Override public String getProductTitle() { var request = HttpRequest.newBuilder() @@ -54,6 +52,7 @@ public class ProductInformationClientImpl implements ProductInformationClient { LOGGER.error("IOException Occurred", ioe); } catch (InterruptedException ie) { LOGGER.error("InterruptedException Occurred", ie); + Thread.currentThread().interrupt(); } return null; } 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 index 22369350a..1ddba57ba 100644 --- 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 @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/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 index e493c8040..cc78d2d8d 100644 --- 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 @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,18 +28,16 @@ import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; /** * An adapter to communicate with inventory micro-service. */ +@Slf4j @Component public class ProductInventoryClientImpl implements ProductInventoryClient { - private static final Logger LOGGER = LoggerFactory.getLogger(ProductInventoryClientImpl.class); - @Override public Integer getProductInventories() { var response = ""; @@ -56,6 +54,7 @@ public class ProductInventoryClientImpl implements ProductInventoryClient { LOGGER.error("IOException Occurred", ioe); } catch (InterruptedException ie) { LOGGER.error("InterruptedException Occurred", ie); + Thread.currentThread().interrupt(); } if ("".equalsIgnoreCase(response)) { return null; diff --git a/aggregator-microservices/aggregator-service/src/main/resources/application.properties b/aggregator-microservices/aggregator-service/src/main/resources/application.properties index f9e29f5a7..79c2622b2 100644 --- a/aggregator-microservices/aggregator-service/src/main/resources/application.properties +++ b/aggregator-microservices/aggregator-service/src/main/resources/application.properties @@ -1,6 +1,6 @@ # # The MIT License -# Copyright © 2014-2019 Ilkka Seppälä +# Copyright © 2014-2021 Ilkka Seppälä # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -20,4 +20,5 @@ # 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 index cb958ecf9..2ffa9cd42 100644 --- 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 @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,19 +23,19 @@ package com.iluwatar.aggregator.microservices; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.when; - import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; + /** * Test Aggregation of domain objects */ -public class AggregatorTest { +class AggregatorTest { @InjectMocks private Aggregator aggregator; @@ -55,7 +55,7 @@ public class AggregatorTest { * Tests getting the data for a desktop client */ @Test - public void testGetProduct() { + void testGetProduct() { var title = "The Product Title."; var inventories = 5; diff --git a/aggregator-microservices/information-microservice/pom.xml b/aggregator-microservices/information-microservice/pom.xml index f99d26b65..43d5ba072 100644 --- a/aggregator-microservices/information-microservice/pom.xml +++ b/aggregator-microservices/information-microservice/pom.xml @@ -1,26 +1,35 @@ - + aggregator-microservices com.iluwatar - 1.23.0-SNAPSHOT + 1.24.0-SNAPSHOT 4.0.0 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 index 3815fffc4..0db963733 100644 --- 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 @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/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 index 2accc013d..4849e4228 100644 --- 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 @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,8 +23,7 @@ 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.GetMapping; import org.springframework.web.bind.annotation.RestController; /** @@ -38,7 +37,7 @@ public class InformationController { * * @return product inventory. */ - @RequestMapping(value = "/information", method = RequestMethod.GET) + @GetMapping("/information") 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 index b953a61da..8f2a5f174 100644 --- a/aggregator-microservices/information-microservice/src/main/resources/application.properties +++ b/aggregator-microservices/information-microservice/src/main/resources/application.properties @@ -1,6 +1,6 @@ # # The MIT License -# Copyright © 2014-2019 Ilkka Seppälä +# Copyright © 2014-2021 Ilkka Seppälä # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -20,4 +20,5 @@ # 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 index 90388af1a..909392d1d 100644 --- 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 @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,17 +23,17 @@ package com.iluwatar.information.microservice; -import static org.junit.jupiter.api.Assertions.assertEquals; - import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + /** * Test for Information Rest Controller */ -public class InformationControllerTest { +class InformationControllerTest { @Test - public void shouldGetProductTitle() { + void shouldGetProductTitle() { var infoController = new InformationController(); var title = infoController.getProductTitle(); assertEquals("The Product Title.", title); diff --git a/aggregator-microservices/inventory-microservice/pom.xml b/aggregator-microservices/inventory-microservice/pom.xml index f7899aa8f..9f74cf7c9 100644 --- a/aggregator-microservices/inventory-microservice/pom.xml +++ b/aggregator-microservices/inventory-microservice/pom.xml @@ -1,26 +1,35 @@ - + aggregator-microservices com.iluwatar - 1.23.0-SNAPSHOT + 1.24.0-SNAPSHOT 4.0.0 inventory-microservice 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 index 9a49518b5..a97b609ac 100644 --- 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 @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/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 index e3c3838f8..0e40507cc 100644 --- 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 @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,8 +23,7 @@ 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.GetMapping; import org.springframework.web.bind.annotation.RestController; /** @@ -38,7 +37,7 @@ public class InventoryController { * * @return product inventory. */ - @RequestMapping(value = "/inventories", method = RequestMethod.GET) + @GetMapping("/inventories") 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 index 9d2021f44..380791d88 100644 --- a/aggregator-microservices/inventory-microservice/src/main/resources/application.properties +++ b/aggregator-microservices/inventory-microservice/src/main/resources/application.properties @@ -1,6 +1,6 @@ # # The MIT License -# Copyright © 2014-2019 Ilkka Seppälä +# Copyright © 2014-2021 Ilkka Seppälä # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -20,4 +20,5 @@ # 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 index e9961796b..9a85f1d5a 100644 --- 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 @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,16 +23,17 @@ package com.iluwatar.inventory.microservice; -import static org.junit.jupiter.api.Assertions.assertEquals; - import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + /** * Test Inventory Rest Controller */ -public class InventoryControllerTest { +class InventoryControllerTest { + @Test - public void testGetProductInventories() { + void testGetProductInventories() { var inventoryController = new InventoryController(); var numberOfInventories = inventoryController.getProductInventories(); assertEquals(5, numberOfInventories); diff --git a/aggregator-microservices/pom.xml b/aggregator-microservices/pom.xml index a63ec2f12..f5d18c2b4 100644 --- a/aggregator-microservices/pom.xml +++ b/aggregator-microservices/pom.xml @@ -2,7 +2,7 @@ + java-design-patterns com.iluwatar - 1.23.0-SNAPSHOT + 1.24.0-SNAPSHOT 4.0.0 ambassador diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/App.java b/ambassador/src/main/java/com/iluwatar/ambassador/App.java index 087fc39c0..6da5a2588 100644 --- a/ambassador/src/main/java/com/iluwatar/ambassador/App.java +++ b/ambassador/src/main/java/com/iluwatar/ambassador/App.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/ambassador/src/main/java/com/iluwatar/ambassador/Client.java b/ambassador/src/main/java/com/iluwatar/ambassador/Client.java index 70d52e799..0ec258016 100644 --- a/ambassador/src/main/java/com/iluwatar/ambassador/Client.java +++ b/ambassador/src/main/java/com/iluwatar/ambassador/Client.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,20 +23,19 @@ package com.iluwatar.ambassador; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; /** * A simple Client. */ +@Slf4j public class Client { - private static final Logger LOGGER = LoggerFactory.getLogger(Client.class); private final ServiceAmbassador serviceAmbassador = new ServiceAmbassador(); long useService(int value) { var result = serviceAmbassador.doRemoteFunction(value); - LOGGER.info("Service result: " + result); + LOGGER.info("Service result: {}", result); return result; } } diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/RemoteService.java b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteService.java index a80806851..18a2d2214 100644 --- a/ambassador/src/main/java/com/iluwatar/ambassador/RemoteService.java +++ b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteService.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,15 +26,14 @@ package com.iluwatar.ambassador; import static java.lang.Thread.sleep; import com.iluwatar.ambassador.util.RandomProvider; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; /** * A remote legacy application represented by a Singleton implementation. */ +@Slf4j public class RemoteService implements RemoteServiceInterface { private static final int THRESHOLD = 200; - private static final Logger LOGGER = LoggerFactory.getLogger(RemoteService.class); private static RemoteService service = null; private final RandomProvider randomProvider; @@ -62,7 +61,7 @@ public class RemoteService implements RemoteServiceInterface { * * @param value integer value to be multiplied. * @return if waitTime is less than {@link RemoteService#THRESHOLD}, it returns value * 10, - * otherwise {@link RemoteServiceInterface#FAILURE}. + * otherwise {@link RemoteServiceStatus#FAILURE}. */ @Override public long doRemoteFunction(int value) { @@ -73,7 +72,9 @@ public class RemoteService implements RemoteServiceInterface { sleep(waitTime); } catch (InterruptedException e) { LOGGER.error("Thread sleep state interrupted", e); + Thread.currentThread().interrupt(); } - return waitTime <= THRESHOLD ? value * 10 : FAILURE; + return waitTime <= THRESHOLD ? value * 10 + : RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue(); } } diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceInterface.java b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceInterface.java index 5b4995134..3012eea29 100644 --- a/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceInterface.java +++ b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceInterface.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,7 +27,6 @@ package com.iluwatar.ambassador; * Interface shared by ({@link RemoteService}) and ({@link ServiceAmbassador}). */ interface RemoteServiceInterface { - int FAILURE = -1; long doRemoteFunction(int value); } diff --git a/command/src/main/java/com/iluwatar/command/InvisibilitySpell.java b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceStatus.java similarity index 63% rename from command/src/main/java/com/iluwatar/command/InvisibilitySpell.java rename to ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceStatus.java index 33e053cc2..e7ee06af9 100644 --- a/command/src/main/java/com/iluwatar/command/InvisibilitySpell.java +++ b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceStatus.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,37 +21,27 @@ * THE SOFTWARE. */ -package com.iluwatar.command; +package com.iluwatar.ambassador; /** - * InvisibilitySpell is a concrete command. + * Holds information regarding the status of the Remote Service. + * + *

This Enum replaces the integer value previously + * stored in {@link RemoteServiceInterface} as SonarCloud was identifying + * it as an issue. All test cases have been checked after changes, + * without failures.

*/ -public class InvisibilitySpell implements Command { - private Target target; +public enum RemoteServiceStatus { + FAILURE(-1); - @Override - public void execute(Target target) { - target.setVisibility(Visibility.INVISIBLE); - this.target = target; + private final long remoteServiceStatusValue; + + RemoteServiceStatus(long remoteServiceStatusValue) { + this.remoteServiceStatusValue = remoteServiceStatusValue; } - @Override - public void undo() { - if (target != null) { - target.setVisibility(Visibility.VISIBLE); - } - } - - @Override - public void redo() { - if (target != null) { - target.setVisibility(Visibility.INVISIBLE); - } - } - - @Override - public String toString() { - return "Invisibility spell"; + public long getRemoteServiceStatusValue() { + return remoteServiceStatusValue; } } diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/ServiceAmbassador.java b/ambassador/src/main/java/com/iluwatar/ambassador/ServiceAmbassador.java index a9d34581c..bd8481d00 100644 --- a/ambassador/src/main/java/com/iluwatar/ambassador/ServiceAmbassador.java +++ b/ambassador/src/main/java/com/iluwatar/ambassador/ServiceAmbassador.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,19 +23,19 @@ package com.iluwatar.ambassador; +import static com.iluwatar.ambassador.RemoteServiceStatus.FAILURE; import static java.lang.Thread.sleep; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; /** * ServiceAmbassador provides an interface for a ({@link Client}) to access ({@link RemoteService}). * The interface adds logging, latency testing and usage of the service in a safe way that will not * add stress to the remote service when connectivity issues occur. */ +@Slf4j public class ServiceAmbassador implements RemoteServiceInterface { - private static final Logger LOGGER = LoggerFactory.getLogger(ServiceAmbassador.class); private static final int RETRIES = 3; private static final int DELAY_MS = 3000; @@ -52,26 +52,27 @@ public class ServiceAmbassador implements RemoteServiceInterface { var result = RemoteService.getRemoteService().doRemoteFunction(value); var timeTaken = System.currentTimeMillis() - startTime; - LOGGER.info("Time taken (ms): " + timeTaken); + LOGGER.info("Time taken (ms): {}", timeTaken); return result; } private long safeCall(int value) { var retries = 0; - var result = (long) FAILURE; + var result = FAILURE.getRemoteServiceStatusValue(); for (int i = 0; i < RETRIES; i++) { if (retries >= RETRIES) { - return FAILURE; + return FAILURE.getRemoteServiceStatusValue(); } - if ((result = checkLatency(value)) == FAILURE) { - LOGGER.info("Failed to reach remote: (" + (i + 1) + ")"); + if ((result = checkLatency(value)) == FAILURE.getRemoteServiceStatusValue()) { + LOGGER.info("Failed to reach remote: ({})", i + 1); retries++; try { sleep(DELAY_MS); } catch (InterruptedException e) { LOGGER.error("Thread sleep state interrupted", e); + Thread.currentThread().interrupt(); } } else { break; diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/util/RandomProvider.java b/ambassador/src/main/java/com/iluwatar/ambassador/util/RandomProvider.java index 5948472c0..9af9da500 100644 --- a/ambassador/src/main/java/com/iluwatar/ambassador/util/RandomProvider.java +++ b/ambassador/src/main/java/com/iluwatar/ambassador/util/RandomProvider.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/ambassador/src/test/java/com/iluwatar/ambassador/AppTest.java b/ambassador/src/test/java/com/iluwatar/ambassador/AppTest.java index c9a4d09b6..57658be39 100644 --- a/ambassador/src/test/java/com/iluwatar/ambassador/AppTest.java +++ b/ambassador/src/test/java/com/iluwatar/ambassador/AppTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,13 +25,23 @@ package com.iluwatar.ambassador; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + /** * Application test */ class AppTest { + /** + * Issue: Add at least one assertion to this test case. + * + * Solution: Inserted assertion to check whether the execution of the main method in {@link App} + * throws an exception. + */ + @Test - void test() { - App.main(new String[]{}); + void shouldExecuteApplicationWithoutException() { + + assertDoesNotThrow(() -> App.main(new String[]{})); } } diff --git a/ambassador/src/test/java/com/iluwatar/ambassador/ClientTest.java b/ambassador/src/test/java/com/iluwatar/ambassador/ClientTest.java index 12a93a1cb..93ac792ae 100644 --- a/ambassador/src/test/java/com/iluwatar/ambassador/ClientTest.java +++ b/ambassador/src/test/java/com/iluwatar/ambassador/ClientTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -37,6 +37,6 @@ class ClientTest { Client client = new Client(); var result = client.useService(10); - assertTrue(result == 100 || result == RemoteService.FAILURE); + assertTrue(result == 100 || result == RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue()); } } diff --git a/ambassador/src/test/java/com/iluwatar/ambassador/RemoteServiceTest.java b/ambassador/src/test/java/com/iluwatar/ambassador/RemoteServiceTest.java index 6c45acf66..c8c7d66ca 100644 --- a/ambassador/src/test/java/com/iluwatar/ambassador/RemoteServiceTest.java +++ b/ambassador/src/test/java/com/iluwatar/ambassador/RemoteServiceTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -37,7 +37,7 @@ class RemoteServiceTest { void testFailedCall() { var remoteService = new RemoteService(new StaticRandomProvider(0.21)); var result = remoteService.doRemoteFunction(10); - assertEquals(RemoteServiceInterface.FAILURE, result); + assertEquals(RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue(), result); } @Test diff --git a/ambassador/src/test/java/com/iluwatar/ambassador/ServiceAmbassadorTest.java b/ambassador/src/test/java/com/iluwatar/ambassador/ServiceAmbassadorTest.java index 8eb55b30a..a17e36d74 100644 --- a/ambassador/src/test/java/com/iluwatar/ambassador/ServiceAmbassadorTest.java +++ b/ambassador/src/test/java/com/iluwatar/ambassador/ServiceAmbassadorTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -35,6 +35,6 @@ class ServiceAmbassadorTest { @Test void test() { long result = new ServiceAmbassador().doRemoteFunction(10); - assertTrue(result == 100 || result == RemoteServiceInterface.FAILURE); + assertTrue(result == 100 || result == RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue()); } } diff --git a/api-gateway/api-gateway-service/pom.xml b/api-gateway/api-gateway-service/pom.xml index 744023e90..26c567d95 100644 --- a/api-gateway/api-gateway-service/pom.xml +++ b/api-gateway/api-gateway-service/pom.xml @@ -2,7 +2,7 @@ +[![All Contributors](https://img.shields.io/badge/all_contributors-148-orange.svg?style=flat-square)](#contributors-) + + + +# مقدمة + +أنماط التصميم هي أفضل التدريبات التي يمكن للمبرمج استخدامها لحل المشكلات الشائعة عند تصميم تطبيق أو نظام. +يمكن لأنماط التصميم تسريع عملية التطوير من خلال توفير نماذج تطوير مجربة ومثبتة ومفيدة. +تساعد إعادة استخدام أنماط التصميم في منع المشكلات الدقيقة التي تسبب مشكلات كبيرة ، كما تعمل على تحسين قابلية قراءة الكود للمهندسين البرمجيات الذين هم على دراية بالأنماط. + +# البدء في التعلم + +يعرض هذا الموقع أنماط تصميم جافا. تم تطوير الحلول من قبل المبرمجين والمهندسين ذوي الخبرة من مجتمع المصادر المفتوحة. يمكن تصفح الأنماط من خلال أوصافها عالية المستوى أو بالنظر إلى كود المصدر الخاص بها. تم كتابة الشرح على أمثلة الكود المصدري جيدًا ويمكن اعتبارها برامج تعليمية حول كيفية تنفيذ نمط معين. نحن نستخدم تقنيات Java مفتوحة المصدر الأكثر شيوعًا والتي أثبتت جدارتها في مشاريع الجافا. +قبل الغوص في المادة ، يجب أن تكون على دراية [بمبادئ تصميم البرامج المختلفة](https://java-design-patterns.com/principles/). + +يجب أن تكون جميع التصميمات بسيطة قدر الإمكان. يجب أن تبدأ بـ KISS و YAGNI وافعل أبسط شيء يمكن أن يعمل بهذه المبادىء. يجب إستخدام الأنماط فقط عند الحاجة إليها من أجل تمديد البرنامج بشكل عملي. + +بمجرد أن تصبح معتادًا على هذه المفاهيم ، يمكنك البدء في التعمق في [أنماط التصميم المتاحة](https://java-design-patterns.com/patterns/) من خلال أي من الأساليب التالية + +ابحث عن نمط محدد بالاسم. لا يمكنك العثور على واحد؟ الرجاء الإبلاغ عن نمط جديد [هنا](https://github.com/iluwatar/java-design-patterns/issues). +استخدام علامات مثل Performance أو Gang of Four أو الوصول إلى البيانات. +استخدام فئات الأنماط و Creational, Behavioral وغيرها. + +نأمل أن تجد الحلول الموجهة للكائنات المعروضة على هذا الموقع مفيدة في مشاريعك البرمجية وأن تستمتع بتعلمها بقدر ما قمنا بتطويرها. + + +# كيف تساهم؟ + +إذا كنت على استعداد للمساهمة في المشروع ، فستجد المعلومات ذات الصلة في [ويكي المطور الخاص بنا](https://github.com/iluwatar/java-design-patterns/wiki). سنساعدك ونجيب على أسئلتك في [غرفة الدردشة Gitter](https://gitter.im/iluwatar/java-design-patterns). + + +# الترخيص + +هذا المشروع مرخص بموجب شروط ترخيص MIT. \ No newline at end of file diff --git a/arrange-act-assert/README.md b/arrange-act-assert/README.md index 6b3cb4058..23efdd71e 100644 --- a/arrange-act-assert/README.md +++ b/arrange-act-assert/README.md @@ -81,10 +81,10 @@ Then we write our unit tests according to Arrange/Act/Assert pattern. Notice the separated steps for each unit test. ```java -public class CashAAATest { +class CashAAATest { @Test - public void testPlus() { + void testPlus() { //Arrange var cash = new Cash(3); //Act @@ -94,7 +94,7 @@ public class CashAAATest { } @Test - public void testMinus() { + void testMinus() { //Arrange var cash = new Cash(8); //Act @@ -105,7 +105,7 @@ public class CashAAATest { } @Test - public void testInsufficientMinus() { + void testInsufficientMinus() { //Arrange var cash = new Cash(1); //Act @@ -116,7 +116,7 @@ public class CashAAATest { } @Test - public void testUpdate() { + void testUpdate() { //Arrange var cash = new Cash(5); //Act diff --git a/arrange-act-assert/pom.xml b/arrange-act-assert/pom.xml index bb0387e7a..e23237197 100644 --- a/arrange-act-assert/pom.xml +++ b/arrange-act-assert/pom.xml @@ -2,7 +2,7 @@ + com.iluwatar java-design-patterns - 1.23.0-SNAPSHOT + 1.24.0-SNAPSHOT async-method-invocation @@ -36,11 +45,6 @@ mockito-core test - - junit - junit - 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 40c186704..b7d5e386b 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,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,14 +24,15 @@ package com.iluwatar.async.method.invocation; import java.util.concurrent.Callable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; /** - * 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. + * In this example, we are launching space rockets and deploying lunar rovers. + * + *

The application demonstrates the async method invocation pattern. The 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. * *

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 @@ -55,10 +56,9 @@ import org.slf4j.LoggerFactory; * @see java.util.concurrent.CompletableFuture * @see java.util.concurrent.ExecutorService */ +@Slf4j public class App { - private static final Logger LOGGER = LoggerFactory.getLogger(App.class); - /** * Program entry point. */ @@ -70,13 +70,14 @@ public class App { final var asyncResult1 = executor.startProcess(lazyval(10, 500)); final var asyncResult2 = executor.startProcess(lazyval("test", 300)); final var asyncResult3 = executor.startProcess(lazyval(50L, 700)); - final var asyncResult4 = executor.startProcess(lazyval(20, 400), callback("Callback result 4")); + final var asyncResult4 = executor.startProcess(lazyval(20, 400), + callback("Deploying lunar rover")); final var asyncResult5 = - executor.startProcess(lazyval("callback", 600), callback("Callback result 5")); + executor.startProcess(lazyval("callback", 600), callback("Deploying lunar rover")); // 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"); + Thread.sleep(350); // Oh boy, we are working hard here + log("Mission command is sipping coffee"); // wait for completion of the tasks final var result1 = executor.endProcess(asyncResult1); @@ -86,9 +87,9 @@ public class App { 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("Space rocket <" + result1 + "> launch complete"); + log("Space rocket <" + result2 + "> launch complete"); + log("Space rocket <" + result3 + "> launch complete"); } /** @@ -101,7 +102,7 @@ public class App { private static Callable lazyval(T value, long delayMillis) { return () -> { Thread.sleep(delayMillis); - log("Task completed with: " + value); + log("Space rocket <" + value + "> launched successfully"); return value; }; } @@ -117,7 +118,7 @@ public class App { if (ex.isPresent()) { log(name + " failed: " + ex.map(Exception::getMessage).orElse("")); } else { - log(name + ": " + value); + log(name + " <" + value + ">"); } }; } 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 22b36134f..e51b0f190 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,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/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 819ffd237..84cb7bec8 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,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/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 6aaf233b4..d27011a18 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,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/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 e430e9ce4..b514a4000 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,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/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 830e66a2d..db49fae85 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,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,12 +25,24 @@ package com.iluwatar.async.method.invocation; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + /** * Application test */ class AppTest { + + /** + * Issue: Add at least one assertion to this test case. + * + * Solution: Inserted assertion to check whether the execution of the main method in {@link App} + * throws an exception. + */ + @Test - void test() throws Exception { - App.main(new String[]{}); + void shouldExecuteApplicationWithoutException() { + + assertDoesNotThrow(() -> App.main(new String[]{})); + } } 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 index 1e54747fa..b644e0e23 100644 --- 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 @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/balking/README.md b/balking/README.md index 22257ac7b..9d3550ad5 100644 --- a/balking/README.md +++ b/balking/README.md @@ -9,19 +9,131 @@ tags: --- ## Intent -Balking Pattern is used to prevent an object from executing certain code if it is an -incomplete or inappropriate state + +Balking Pattern is used to prevent an object from executing a certain code if it is in an incomplete +or inappropriate state. + +## Explanation + +Real world example + +> There's a start-button in a washing machine to initiate the laundry washing. When the washing +> machine is inactive the button works as expected, but if it's already washing the button does +> nothing. + +In plain words + +> Using the balking pattern, a certain code executes only if the object is in particular state. + +Wikipedia says + +> The balking pattern is a software design pattern that only executes an action on an object when +> the object is in a particular state. For example, if an object reads ZIP files and a calling +> method invokes a get method on the object when the ZIP file is not open, the object would "balk" +> at the request. + +**Programmatic Example** + +In this example implementation, `WashingMachine` is an object that has two states in which it can +be: ENABLED and WASHING. If the machine is ENABLED, the state changes to WASHING using a thread-safe +method. On the other hand, if it already has been washing and any other thread executes `wash()` +it won't do that and returns without doing anything. + +Here are the relevant parts of the `WashingMachine` class. + +```java +@Slf4j +public class WashingMachine { + + private final DelayProvider delayProvider; + private WashingMachineState washingMachineState; + + public WashingMachine(DelayProvider delayProvider) { + this.delayProvider = delayProvider; + this.washingMachineState = WashingMachineState.ENABLED; + } + + public WashingMachineState getWashingMachineState() { + return washingMachineState; + } + + public void wash() { + synchronized (this) { + var machineState = getWashingMachineState(); + LOGGER.info("{}: Actual machine state: {}", Thread.currentThread().getName(), machineState); + if (this.washingMachineState == WashingMachineState.WASHING) { + LOGGER.error("Cannot wash if the machine has been already washing!"); + return; + } + this.washingMachineState = WashingMachineState.WASHING; + } + LOGGER.info("{}: Doing the washing", Thread.currentThread().getName()); + this.delayProvider.executeAfterDelay(50, TimeUnit.MILLISECONDS, this::endOfWashing); + } + + public synchronized void endOfWashing() { + washingMachineState = WashingMachineState.ENABLED; + LOGGER.info("{}: Washing completed.", Thread.currentThread().getId()); + } +} +``` + +Here's the simple `DelayProvider` interface used by the `WashingMachine`. + +```java +public interface DelayProvider { + void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task); +} +``` + +Now we introduce the application using the `WashingMachine`. + +```java + public static void main(String... args) { + final var washingMachine = new WashingMachine(); + var executorService = Executors.newFixedThreadPool(3); + for (int i = 0; i < 3; i++) { + executorService.execute(washingMachine::wash); + } + executorService.shutdown(); + try { + executorService.awaitTermination(10, TimeUnit.SECONDS); + } catch (InterruptedException ie) { + LOGGER.error("ERROR: Waiting on executor service shutdown!"); + Thread.currentThread().interrupt(); + } + } +``` + +Here is the console output of the program. + +``` +14:02:52.268 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-2: Actual machine state: ENABLED +14:02:52.272 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-2: Doing the washing +14:02:52.272 [pool-1-thread-3] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-3: Actual machine state: WASHING +14:02:52.273 [pool-1-thread-3] ERROR com.iluwatar.balking.WashingMachine - Cannot wash if the machine has been already washing! +14:02:52.273 [pool-1-thread-1] INFO com.iluwatar.balking.WashingMachine - pool-1-thread-1: Actual machine state: WASHING +14:02:52.273 [pool-1-thread-1] ERROR com.iluwatar.balking.WashingMachine - Cannot wash if the machine has been already washing! +14:02:52.324 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - 14: Washing completed. +``` ## Class diagram + ![alt text](./etc/balking.png "Balking") ## Applicability + Use the Balking pattern when -* you want to invoke an action on an object only when it is in a particular state -* objects are generally only in a state that is prone to balking temporarily -but for an unknown amount of time +* You want to invoke an action on an object only when it is in a particular state +* Objects are generally only in a state that is prone to balking temporarily but for an unknown + amount of time ## Related patterns -* Guarded Suspension Pattern -* Double Checked Locking Pattern + +* [Guarded Suspension Pattern](https://java-design-patterns.com/patterns/guarded-suspension/) +* [Double Checked Locking Pattern](https://java-design-patterns.com/patterns/double-checked-locking/) + +## Credits + +* [Patterns in Java: A Catalog of Reusable Design Patterns Illustrated with UML, 2nd Edition, Volume 1](https://www.amazon.com/gp/product/0471227293/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0471227293&linkId=0e39a59ffaab93fb476036fecb637b99) diff --git a/balking/pom.xml b/balking/pom.xml index 964531692..1b200663b 100644 --- a/balking/pom.xml +++ b/balking/pom.xml @@ -1,26 +1,35 @@ - + java-design-patterns com.iluwatar - 1.23.0-SNAPSHOT + 1.24.0-SNAPSHOT 4.0.0 diff --git a/balking/src/main/java/com/iluwatar/balking/App.java b/balking/src/main/java/com/iluwatar/balking/App.java index 3e72acc59..644a344f4 100644 --- a/balking/src/main/java/com/iluwatar/balking/App.java +++ b/balking/src/main/java/com/iluwatar/balking/App.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,28 +23,24 @@ package com.iluwatar.balking; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; /** * In Balking Design Pattern if an object’s method is invoked when it is in an inappropriate state, * then the method will return without doing anything. Objects that use this pattern are generally * only in a state that is prone to balking temporarily but for an unknown amount of time * - *

In this example implementation WashingMachine is an object that has two states in which it - * can be: ENABLED and WASHING. If the machine is ENABLED the state is changed into WASHING that any - * other thread can't invoke this action on this and then do the job. On the other hand if it have - * been already washing and any other thread execute wash() it can't do that once again and returns - * doing nothing. + *

In this example implementation, {@link WashingMachine} is an object that has two states in + * which it can be: ENABLED and WASHING. If the machine is ENABLED, the state changes to WASHING + * using a thread-safe method. On the other hand, if it already has been washing and any other + * thread executes {@link WashingMachine#wash()} it won't do that and returns without doing + * anything. */ - +@Slf4j public class App { - private static final Logger LOGGER = LoggerFactory.getLogger(App.class); - /** * Entry Point. * @@ -58,10 +54,12 @@ public class App { } executorService.shutdown(); try { - executorService.awaitTermination(10, TimeUnit.SECONDS); + if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) { + executorService.shutdownNow(); + } } catch (InterruptedException ie) { LOGGER.error("ERROR: Waiting on executor service shutdown!"); + Thread.currentThread().interrupt(); } } - } diff --git a/balking/src/main/java/com/iluwatar/balking/DelayProvider.java b/balking/src/main/java/com/iluwatar/balking/DelayProvider.java index ed05cd292..9b21e9574 100644 --- a/balking/src/main/java/com/iluwatar/balking/DelayProvider.java +++ b/balking/src/main/java/com/iluwatar/balking/DelayProvider.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/balking/src/main/java/com/iluwatar/balking/WashingMachine.java b/balking/src/main/java/com/iluwatar/balking/WashingMachine.java index b35bd99df..1da2b0204 100644 --- a/balking/src/main/java/com/iluwatar/balking/WashingMachine.java +++ b/balking/src/main/java/com/iluwatar/balking/WashingMachine.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,15 +24,14 @@ package com.iluwatar.balking; import java.util.concurrent.TimeUnit; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; /** * Washing machine class. */ +@Slf4j public class WashingMachine { - private static final Logger LOGGER = LoggerFactory.getLogger(WashingMachine.class); private final DelayProvider delayProvider; private WashingMachineState washingMachineState; @@ -44,7 +43,8 @@ public class WashingMachine { try { Thread.sleep(timeUnit.toMillis(interval)); } catch (InterruptedException ie) { - ie.printStackTrace(); + LOGGER.error("", ie); + Thread.currentThread().interrupt(); } task.run(); }); @@ -71,7 +71,7 @@ public class WashingMachine { var machineState = getWashingMachineState(); LOGGER.info("{}: Actual machine state: {}", Thread.currentThread().getName(), machineState); if (this.washingMachineState == WashingMachineState.WASHING) { - LOGGER.error("ERROR: Cannot wash if the machine has been already washing!"); + LOGGER.error("Cannot wash if the machine has been already washing!"); return; } this.washingMachineState = WashingMachineState.WASHING; diff --git a/balking/src/main/java/com/iluwatar/balking/WashingMachineState.java b/balking/src/main/java/com/iluwatar/balking/WashingMachineState.java index 664a4c0c9..f652a841f 100644 --- a/balking/src/main/java/com/iluwatar/balking/WashingMachineState.java +++ b/balking/src/main/java/com/iluwatar/balking/WashingMachineState.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,5 +28,6 @@ package com.iluwatar.balking; * as well as during washing. */ public enum WashingMachineState { - ENABLED, WASHING + ENABLED, + WASHING } diff --git a/balking/src/test/java/com/iluwatar/balking/AppTest.java b/balking/src/test/java/com/iluwatar/balking/AppTest.java index 8c75a1f62..9c3fa683a 100644 --- a/balking/src/test/java/com/iluwatar/balking/AppTest.java +++ b/balking/src/test/java/com/iluwatar/balking/AppTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,15 +24,26 @@ package com.iluwatar.balking; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + /** * Application test */ class AppTest { + /** + * Issue: Add at least one assertion to this test case. + * + * Solution: Inserted assertion to check whether the execution of the main method in {@link App} + * throws an exception. + */ + @Test - void main() { - App.main(); + void shouldExecuteApplicationWithoutException() { + assertDoesNotThrow((Executable) App::main); } } \ No newline at end of file diff --git a/balking/src/test/java/com/iluwatar/balking/WashingMachineTest.java b/balking/src/test/java/com/iluwatar/balking/WashingMachineTest.java index 9e218e3f0..8f41bf36e 100644 --- a/balking/src/test/java/com/iluwatar/balking/WashingMachineTest.java +++ b/balking/src/test/java/com/iluwatar/balking/WashingMachineTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/bridge/README.md b/bridge/README.md index 82c2f2735..12a6358f5 100644 --- a/bridge/README.md +++ b/bridge/README.md @@ -9,20 +9,26 @@ tags: --- ## Also known as + Handle/Body ## Intent + Decouple an abstraction from its implementation so that the two can vary independently. ## Explanation Real world example -> Consider you have a weapon with different enchantments and you are supposed to allow mixing different weapons with different enchantments. What would you do? Create multiple copies of each of the weapons for each of the enchantments or would you just create separate enchantment and set it for the weapon as needed? Bridge pattern allows you to do the second. +> Consider you have a weapon with different enchantments, and you are supposed to allow mixing +> different weapons with different enchantments. What would you do? Create multiple copies of each +> of the weapons for each of the enchantments or would you just create separate enchantment and set +> it for the weapon as needed? Bridge pattern allows you to do the second. In Plain Words -> Bridge pattern is about preferring composition over inheritance. Implementation details are pushed from a hierarchy to another object with a separate hierarchy. +> Bridge pattern is about preferring composition over inheritance. Implementation details are pushed +> from a hierarchy to another object with a separate hierarchy. Wikipedia says @@ -30,7 +36,7 @@ Wikipedia says **Programmatic Example** -Translating our weapon example from above. Here we have the `Weapon` hierarchy +Translating our weapon example from above. Here we have the `Weapon` hierarchy: ```java public interface Weapon { @@ -105,7 +111,7 @@ public class Hammer implements Weapon { } ``` -And the separate enchantment hierarchy +Here's the separate enchantment hierarchy: ```java public interface Enchantment { @@ -151,7 +157,7 @@ public class SoulEatingEnchantment implements Enchantment { } ``` -And both the hierarchies in action +Here are both hierarchies in action: ```java var enchantedSword = new Sword(new SoulEatingEnchantment()); @@ -178,18 +184,21 @@ hammer.unwield(); ``` ## Class diagram + ![alt text](./etc/bridge.urm.png "Bridge class diagram") ## 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 -* changes in the implementation of an abstraction should have no impact on clients; that is, their code should not have to be recompiled. -* 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. +* 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. +* Changes in the implementation of an abstraction should have no impact on clients; that is, their code should not have to be recompiled. +* 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. ## Tutorial + * [Bridge Pattern Tutorial](https://www.journaldev.com/1491/bridge-design-pattern-java) ## Credits diff --git a/bridge/pom.xml b/bridge/pom.xml index 0664bc9b5..7fbed70e9 100644 --- a/bridge/pom.xml +++ b/bridge/pom.xml @@ -1,19 +1,28 @@ - + com.iluwatar java-design-patterns - 1.23.0-SNAPSHOT + 1.24.0-SNAPSHOT bridge diff --git a/bridge/src/main/java/com/iluwatar/bridge/App.java b/bridge/src/main/java/com/iluwatar/bridge/App.java index 3e89ef6f6..639d53f58 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/App.java +++ b/bridge/src/main/java/com/iluwatar/bridge/App.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,8 +23,7 @@ package com.iluwatar.bridge; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; /** * Composition over inheritance. The Bridge pattern can also be thought of as two layers of @@ -39,10 +38,9 @@ import org.slf4j.LoggerFactory; * enchantments. We can easily combine any weapon with any enchantment using composition instead of * creating deep class hierarchy. */ +@Slf4j public class App { - private static final Logger LOGGER = LoggerFactory.getLogger(App.class); - /** * Program entry point. * diff --git a/bridge/src/main/java/com/iluwatar/bridge/Enchantment.java b/bridge/src/main/java/com/iluwatar/bridge/Enchantment.java index 8388fe91e..3ddc34f9f 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/Enchantment.java +++ b/bridge/src/main/java/com/iluwatar/bridge/Enchantment.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/bridge/src/main/java/com/iluwatar/bridge/FlyingEnchantment.java b/bridge/src/main/java/com/iluwatar/bridge/FlyingEnchantment.java index 772456b88..436b11a70 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/FlyingEnchantment.java +++ b/bridge/src/main/java/com/iluwatar/bridge/FlyingEnchantment.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,16 +23,14 @@ package com.iluwatar.bridge; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; /** * FlyingEnchantment. */ +@Slf4j public class FlyingEnchantment implements Enchantment { - private static final Logger LOGGER = LoggerFactory.getLogger(FlyingEnchantment.class); - @Override public void onActivate() { LOGGER.info("The item begins to glow faintly."); diff --git a/bridge/src/main/java/com/iluwatar/bridge/Hammer.java b/bridge/src/main/java/com/iluwatar/bridge/Hammer.java index ffab542cb..834a2c54a 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/Hammer.java +++ b/bridge/src/main/java/com/iluwatar/bridge/Hammer.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,22 +23,18 @@ package com.iluwatar.bridge; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; /** * Hammer. */ +@Slf4j +@AllArgsConstructor public class Hammer implements Weapon { - private static final Logger LOGGER = LoggerFactory.getLogger(Hammer.class); - private final Enchantment enchantment; - public Hammer(Enchantment enchantment) { - this.enchantment = enchantment; - } - @Override public void wield() { LOGGER.info("The hammer is wielded."); diff --git a/bridge/src/main/java/com/iluwatar/bridge/SoulEatingEnchantment.java b/bridge/src/main/java/com/iluwatar/bridge/SoulEatingEnchantment.java index ede98d2cb..746b827f6 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/SoulEatingEnchantment.java +++ b/bridge/src/main/java/com/iluwatar/bridge/SoulEatingEnchantment.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,16 +23,14 @@ package com.iluwatar.bridge; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; /** * SoulEatingEnchantment. */ +@Slf4j public class SoulEatingEnchantment implements Enchantment { - private static final Logger LOGGER = LoggerFactory.getLogger(SoulEatingEnchantment.class); - @Override public void onActivate() { LOGGER.info("The item spreads bloodlust."); diff --git a/bridge/src/main/java/com/iluwatar/bridge/Sword.java b/bridge/src/main/java/com/iluwatar/bridge/Sword.java index 71f87a55d..ef9764eca 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/Sword.java +++ b/bridge/src/main/java/com/iluwatar/bridge/Sword.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,22 +23,18 @@ package com.iluwatar.bridge; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; /** * Sword. */ +@Slf4j +@AllArgsConstructor public class Sword implements Weapon { - private static final Logger LOGGER = LoggerFactory.getLogger(Sword.class); - private final Enchantment enchantment; - public Sword(Enchantment enchantment) { - this.enchantment = enchantment; - } - @Override public void wield() { LOGGER.info("The sword is wielded."); diff --git a/bridge/src/main/java/com/iluwatar/bridge/Weapon.java b/bridge/src/main/java/com/iluwatar/bridge/Weapon.java index 76272332e..c5e1b5074 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/Weapon.java +++ b/bridge/src/main/java/com/iluwatar/bridge/Weapon.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/bridge/src/test/java/com/iluwatar/bridge/AppTest.java b/bridge/src/test/java/com/iluwatar/bridge/AppTest.java index d3edbb27c..da2019815 100644 --- a/bridge/src/test/java/com/iluwatar/bridge/AppTest.java +++ b/bridge/src/test/java/com/iluwatar/bridge/AppTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,12 +25,22 @@ package com.iluwatar.bridge; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + /** * Application test */ class AppTest { + + /** + * Issue: Add at least one assertion to this test case. + * + * Solution: Inserted assertion to check whether the execution of the main method in {@link App} + * throws an exception. + */ + @Test - void test() { - App.main(new String[]{}); + void shouldExecuteApplicationWithoutException() { + assertDoesNotThrow(() -> App.main(new String[]{})); } } diff --git a/bridge/src/test/java/com/iluwatar/bridge/HammerTest.java b/bridge/src/test/java/com/iluwatar/bridge/HammerTest.java index b91f9f402..678913c2a 100644 --- a/bridge/src/test/java/com/iluwatar/bridge/HammerTest.java +++ b/bridge/src/test/java/com/iluwatar/bridge/HammerTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/bridge/src/test/java/com/iluwatar/bridge/SwordTest.java b/bridge/src/test/java/com/iluwatar/bridge/SwordTest.java index 95a6bc3d3..7bba8599c 100644 --- a/bridge/src/test/java/com/iluwatar/bridge/SwordTest.java +++ b/bridge/src/test/java/com/iluwatar/bridge/SwordTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/bridge/src/test/java/com/iluwatar/bridge/WeaponTest.java b/bridge/src/test/java/com/iluwatar/bridge/WeaponTest.java index 620e0104b..e8c4ef277 100644 --- a/bridge/src/test/java/com/iluwatar/bridge/WeaponTest.java +++ b/bridge/src/test/java/com/iluwatar/bridge/WeaponTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/builder/README.md b/builder/README.md index bb7426e35..008854ed7 100644 --- a/builder/README.md +++ b/builder/README.md @@ -9,36 +9,47 @@ tags: --- ## Intent -Separate the construction of a complex object from its -representation so that the same construction process can create different -representations. + +Separate the construction of a complex object from its representation so that the same construction +process can create different representations. ## Explanation Real world example -> Imagine a character generator for a role playing game. The easiest option is to let computer create the character for you. But if you want to select the character details like profession, gender, hair color etc. the character generation becomes a step-by-step process that completes when all the selections are ready. +> Imagine a character generator for a role-playing game. The easiest option is to let the computer +> create the character for you. If you want to manually select the character details like +> profession, gender, hair color etc. the character generation becomes a step-by-step process that +> completes when all the selections are ready. In plain words -> Allows you to create different flavors of an object while avoiding constructor pollution. Useful when there could be several flavors of an object. Or when there are a lot of steps involved in creation of an object. +> Allows you to create different flavors of an object while avoiding constructor pollution. Useful +> when there could be several flavors of an object. Or when there are a lot of steps involved in +> creation of an object. Wikipedia says -> The builder pattern is an object creation software design pattern with the intentions of finding a solution to the telescoping constructor anti-pattern. +> The builder pattern is an object creation software design pattern with the intentions of finding +> a solution to the telescoping constructor anti-pattern. -Having said that let me add a bit about what telescoping constructor anti-pattern is. At one point or the other we have all seen a constructor like below: +Having said that let me add a bit about what telescoping constructor anti-pattern is. At one point +or the other, we have all seen a constructor like below: ```java public Hero(Profession profession, String name, HairType hairType, HairColor hairColor, Armor armor, Weapon weapon) { } ``` -As you can see the number of constructor parameters can quickly get out of hand and it might become difficult to understand the arrangement of parameters. Plus this parameter list could keep on growing if you would want to add more options in future. This is called telescoping constructor anti-pattern. +As you can see the number of constructor parameters can quickly get out of hand, and it may become +difficult to understand the arrangement of parameters. Plus this parameter list could keep on +growing if you would want to add more options in the future. This is called telescoping constructor +anti-pattern. **Programmatic Example** -The sane alternative is to use the Builder pattern. First of all we have our hero that we want to create +The sane alternative is to use the Builder pattern. First of all we have our hero that we want to +create: ```java public final class Hero { @@ -60,7 +71,7 @@ public final class Hero { } ``` -And then we have the builder +Then we have the builder: ```java public static class Builder { @@ -105,20 +116,22 @@ And then we have the builder } ``` -And then it can be used as: +Then it can be used as: ```java var mage = new Hero.Builder(Profession.MAGE, "Riobard").withHairColor(HairColor.BLACK).withWeapon(Weapon.DAGGER).build(); ``` ## Class diagram + ![alt text](./etc/builder.urm.png "Builder class diagram") ## 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 +* 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 diff --git a/builder/pom.xml b/builder/pom.xml index dab9c66a7..439ce282a 100644 --- a/builder/pom.xml +++ b/builder/pom.xml @@ -1,19 +1,28 @@ - + com.iluwatar java-design-patterns - 1.23.0-SNAPSHOT + 1.24.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 76e514749..e62dd8ceb 100644 --- a/builder/src/main/java/com/iluwatar/builder/App.java +++ b/builder/src/main/java/com/iluwatar/builder/App.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,8 +24,7 @@ package com.iluwatar.builder; import com.iluwatar.builder.Hero.Builder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; /** * The intention of the Builder pattern is to find a solution to the telescoping constructor @@ -48,10 +47,9 @@ import org.slf4j.LoggerFactory; * 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. */ +@Slf4j public class App { - private static final Logger LOGGER = LoggerFactory.getLogger(App.class); - /** * Program entry point. * @@ -76,6 +74,5 @@ public class App { .withWeapon(Weapon.BOW) .build(); LOGGER.info(thief.toString()); - } } diff --git a/builder/src/main/java/com/iluwatar/builder/Armor.java b/builder/src/main/java/com/iluwatar/builder/Armor.java index 5a20a0824..0ed7be12e 100644 --- a/builder/src/main/java/com/iluwatar/builder/Armor.java +++ b/builder/src/main/java/com/iluwatar/builder/Armor.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,19 +23,21 @@ package com.iluwatar.builder; +import lombok.AllArgsConstructor; + /** * Armor enumeration. */ +@AllArgsConstructor 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 final String title; - Armor(String title) { - this.title = 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 1beccff5e..361bd5556 100644 --- a/builder/src/main/java/com/iluwatar/builder/HairColor.java +++ b/builder/src/main/java/com/iluwatar/builder/HairColor.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,7 +28,11 @@ package com.iluwatar.builder; */ public enum HairColor { - WHITE, BLOND, RED, BROWN, BLACK; + WHITE, + BLOND, + RED, + BROWN, + BLACK; @Override public String toString() { diff --git a/builder/src/main/java/com/iluwatar/builder/HairType.java b/builder/src/main/java/com/iluwatar/builder/HairType.java index 950f02a1b..9342031f6 100644 --- a/builder/src/main/java/com/iluwatar/builder/HairType.java +++ b/builder/src/main/java/com/iluwatar/builder/HairType.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,20 +23,22 @@ package com.iluwatar.builder; +import lombok.AllArgsConstructor; + /** * HairType enumeration. */ +@AllArgsConstructor 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 final String title; - HairType(String title) { - this.title = 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 86dc11034..7d45bd3df 100644 --- a/builder/src/main/java/com/iluwatar/builder/Hero.java +++ b/builder/src/main/java/com/iluwatar/builder/Hero.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/builder/src/main/java/com/iluwatar/builder/Profession.java b/builder/src/main/java/com/iluwatar/builder/Profession.java index 23b1ac220..f4c25fe89 100644 --- a/builder/src/main/java/com/iluwatar/builder/Profession.java +++ b/builder/src/main/java/com/iluwatar/builder/Profession.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/builder/src/main/java/com/iluwatar/builder/Weapon.java b/builder/src/main/java/com/iluwatar/builder/Weapon.java index 4ca78b95f..c80349287 100644 --- a/builder/src/main/java/com/iluwatar/builder/Weapon.java +++ b/builder/src/main/java/com/iluwatar/builder/Weapon.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/builder/src/test/java/com/iluwatar/builder/AppTest.java b/builder/src/test/java/com/iluwatar/builder/AppTest.java index 941f62f75..1c9ccddfa 100644 --- a/builder/src/test/java/com/iluwatar/builder/AppTest.java +++ b/builder/src/test/java/com/iluwatar/builder/AppTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,12 +25,22 @@ package com.iluwatar.builder; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + /** * Application test */ class AppTest { + + /** + * Issue: Add at least one assertion to this test case. + * + * Solution: Inserted assertion to check whether the execution of the main method in {@link App} + * throws an exception. + */ + @Test - void test() { - App.main(new String[]{}); + void shouldExecuteApplicationWithoutException() { + assertDoesNotThrow(() -> App.main(new String[]{})); } } diff --git a/builder/src/test/java/com/iluwatar/builder/HeroTest.java b/builder/src/test/java/com/iluwatar/builder/HeroTest.java index 97c984a70..f456c5ed9 100644 --- a/builder/src/test/java/com/iluwatar/builder/HeroTest.java +++ b/builder/src/test/java/com/iluwatar/builder/HeroTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/business-delegate/README.md b/business-delegate/README.md index b00c67819..886781632 100644 --- a/business-delegate/README.md +++ b/business-delegate/README.md @@ -9,21 +9,156 @@ tags: --- ## 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. +## Explanation + +Real world example + +> A mobile phone application promises to stream any movie in existence to your phone. It captures +> the user's search string and passes this on to the business delegate. The business delegate +> selects the most suitable video streaming service and plays the video from there. + +In Plain Words + +> Business delegate adds an abstraction layer between the presentation and business tiers. + +Wikipedia says + +> Business delegate is a Java EE design pattern. This pattern is directing to reduce the coupling +> in between business services and the connected presentation tier, and to hide the implementation +> details of services (including lookup and accessibility of EJB architecture). Business delegates +> acts as an adaptor to invoke business objects from the presentation tier. + +**Programmatic Example** + +First, we have an abstraction for video streaming services and a couple of implementations. + +```java +public interface VideoStreamingService { + void doProcessing(); +} + +@Slf4j +public class NetflixService implements VideoStreamingService { + @Override + public void doProcessing() { + LOGGER.info("NetflixService is now processing"); + } +} + +@Slf4j +public class YouTubeService implements VideoStreamingService { + @Override + public void doProcessing() { + LOGGER.info("YouTubeService is now processing"); + } +} +``` + +Then we have a lookup service that decides which video streaming service is used. + +```java +@Setter +public class BusinessLookup { + + private NetflixService netflixService; + private YouTubeService youTubeService; + + public VideoStreamingService getBusinessService(String movie) { + if (movie.toLowerCase(Locale.ROOT).contains("die hard")) { + return netflixService; + } else { + return youTubeService; + } + } +} +``` + +The business delegate uses a business lookup to route movie playback requests to a suitable +video streaming service. + +```java +@Setter +public class BusinessDelegate { + + private BusinessLookup lookupService; + + public void playbackMovie(String movie) { + VideoStreamingService videoStreamingService = lookupService.getBusinessService(movie); + videoStreamingService.doProcessing(); + } +} +``` + +The mobile client utilizes business delegate to call the business tier. + +```java +public class MobileClient { + + private final BusinessDelegate businessDelegate; + + public MobileClient(BusinessDelegate businessDelegate) { + this.businessDelegate = businessDelegate; + } + + public void playbackMovie(String movie) { + businessDelegate.playbackMovie(movie); + } +} +``` + +Finally, we can show the full example in action. + +```java + public static void main(String[] args) { + + // prepare the objects + var businessDelegate = new BusinessDelegate(); + var businessLookup = new BusinessLookup(); + businessLookup.setNetflixService(new NetflixService()); + businessLookup.setYouTubeService(new YouTubeService()); + businessDelegate.setLookupService(businessLookup); + + // create the client and use the business delegate + var client = new MobileClient(businessDelegate); + client.playbackMovie("Die Hard 2"); + client.playbackMovie("Maradona: The Greatest Ever"); + } +``` + +Here is the console output. + +``` +21:15:33.790 [main] INFO com.iluwatar.business.delegate.NetflixService - NetflixService is now processing +21:15:33.794 [main] INFO com.iluwatar.business.delegate.YouTubeService - YouTubeService is now processing +``` + ## Class diagram -![alt text](./etc/business-delegate.png "Business Delegate") + +![alt text](./etc/business-delegate.urm.png "Business Delegate") + +## Related patterns + +* [Service locator pattern](https://java-design-patterns.com/patterns/service-locator/) ## 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 +* 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 + +## Tutorials + +* [Business Delegate Pattern at TutorialsPoint](https://www.tutorialspoint.com/design_pattern/business_delegate_pattern.htm) ## Credits * [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=48d37c67fb3d845b802fa9b619ad8f31) +* [Core J2EE Patterns: Best Practices and Design Strategies](https://www.amazon.com/gp/product/0130648841/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0130648841&linkId=a0100de2b28c71ede8db1757fb2b5947) diff --git a/business-delegate/etc/business-delegate.png b/business-delegate/etc/business-delegate.png deleted file mode 100644 index 928cf9346..000000000 Binary files a/business-delegate/etc/business-delegate.png and /dev/null differ diff --git a/business-delegate/etc/business-delegate.ucls b/business-delegate/etc/business-delegate.ucls deleted file mode 100644 index 668a6579e..000000000 --- a/business-delegate/etc/business-delegate.ucls +++ /dev/null @@ -1,136 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/business-delegate/etc/business-delegate.urm.png b/business-delegate/etc/business-delegate.urm.png new file mode 100644 index 000000000..4dca6c263 Binary files /dev/null and b/business-delegate/etc/business-delegate.urm.png differ diff --git a/business-delegate/etc/business-delegate.urm.puml b/business-delegate/etc/business-delegate.urm.puml index 40aa2d6f0..407e3e12d 100644 --- a/business-delegate/etc/business-delegate.urm.puml +++ b/business-delegate/etc/business-delegate.urm.puml @@ -5,53 +5,42 @@ package com.iluwatar.business.delegate { + main(args : String[]) {static} } class BusinessDelegate { - - businessService : BusinessService - lookupService : BusinessLookup - - serviceType : ServiceType + BusinessDelegate() - + doTask() - + setLookupService(businessLookup : BusinessLookup) - + setServiceType(serviceType : ServiceType) + + playbackMovie(movie : String) + + setLookupService(lookupService : BusinessLookup) } class BusinessLookup { - - ejbService : EjbService - - jmsService : JmsService + - netflixService : NetflixService + - youTubeService : YouTubeService + BusinessLookup() - + getBusinessService(serviceType : ServiceType) : BusinessService - + setEjbService(ejbService : EjbService) - + setJmsService(jmsService : JmsService) + + getBusinessService(movie : String) : VideoStreamingService + + setNetflixService(netflixService : NetflixService) + + setYouTubeService(youTubeService : YouTubeService) } - interface BusinessService { + class MobileClient { + - businessDelegate : BusinessDelegate + + MobileClient(businessDelegate : BusinessDelegate) + + playbackMovie(movie : String) + } + class NetflixService { + - LOGGER : Logger {static} + + NetflixService() + + doProcessing() + } + interface VideoStreamingService { + doProcessing() {abstract} } - class Client { - - businessDelegate : BusinessDelegate - + Client(businessDelegate : BusinessDelegate) - + doTask() - } - class EjbService { + class YouTubeService { - LOGGER : Logger {static} - + EjbService() + + YouTubeService() + doProcessing() } - class JmsService { - - LOGGER : Logger {static} - + JmsService() - + doProcessing() - } - enum ServiceType { - + EJB {static} - + JMS {static} - + valueOf(name : String) : ServiceType {static} - + values() : ServiceType[] {static} - } } -BusinessLookup --> "-ejbService" EjbService -BusinessDelegate --> "-serviceType" ServiceType -Client --> "-businessDelegate" BusinessDelegate -BusinessDelegate --> "-businessService" BusinessService +BusinessLookup --> "-netflixService" NetflixService +BusinessLookup --> "-youTubeService" YouTubeService +MobileClient --> "-businessDelegate" BusinessDelegate BusinessDelegate --> "-lookupService" BusinessLookup -BusinessLookup --> "-jmsService" JmsService -EjbService ..|> BusinessService -JmsService ..|> BusinessService +NetflixService ..|> VideoStreamingService +YouTubeService ..|> VideoStreamingService @enduml \ No newline at end of file diff --git a/business-delegate/pom.xml b/business-delegate/pom.xml index 26987c73a..aa70c99ec 100644 --- a/business-delegate/pom.xml +++ b/business-delegate/pom.xml @@ -1,19 +1,28 @@ - + com.iluwatar java-design-patterns - 1.23.0-SNAPSHOT + 1.24.0-SNAPSHOT business-delegate 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 115234df7..e87ca9c64 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,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -33,9 +33,9 @@ package com.iluwatar.business.delegate; * 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. + *

In this example the client ({@link MobileClient}) utilizes a business delegate ( + * {@link BusinessDelegate}) to search for movies in video streaming services. The Business Delegate + * then selects the appropriate service and makes the service call. */ public class App { @@ -46,18 +46,16 @@ public class App { */ public static void main(String[] args) { + // prepare the objects var businessDelegate = new BusinessDelegate(); var businessLookup = new BusinessLookup(); - businessLookup.setEjbService(new EjbService()); - businessLookup.setJmsService(new JmsService()); - + businessLookup.setNetflixService(new NetflixService()); + businessLookup.setYouTubeService(new YouTubeService()); businessDelegate.setLookupService(businessLookup); - businessDelegate.setServiceType(ServiceType.EJB); - var client = new Client(businessDelegate); - client.doTask(); - - businessDelegate.setServiceType(ServiceType.JMS); - client.doTask(); + // create the client and use the business delegate + var client = new MobileClient(businessDelegate); + client.playbackMovie("Die Hard 2"); + client.playbackMovie("Maradona: The Greatest Ever"); } } 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 c39a2ad39..6246145e7 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,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,25 +23,18 @@ package com.iluwatar.business.delegate; +import lombok.Setter; + /** * BusinessDelegate separates the presentation and business tiers. */ +@Setter public class BusinessDelegate { private BusinessLookup lookupService; - private BusinessService businessService; - private ServiceType serviceType; - 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(); + public void playbackMovie(String movie) { + VideoStreamingService videoStreamingService = lookupService.getBusinessService(movie); + videoStreamingService.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 489caa23c..0369c04e8 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,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,34 +23,30 @@ package com.iluwatar.business.delegate; +import java.util.Locale; +import lombok.Setter; + /** * Class for performing service lookups. */ +@Setter public class BusinessLookup { - private EjbService ejbService; + private NetflixService netflixService; - private JmsService jmsService; + private YouTubeService youTubeService; /** - * Gets service instance based on service type. + * Gets service instance based on given movie search string. * - * @param serviceType Type of service instance to be returned. + * @param movie Search string for the movie. * @return Service instance. */ - public BusinessService getBusinessService(ServiceType serviceType) { - if (serviceType.equals(ServiceType.EJB)) { - return ejbService; + public VideoStreamingService getBusinessService(String movie) { + if (movie.toLowerCase(Locale.ROOT).contains("die hard")) { + return netflixService; } else { - return jmsService; + return youTubeService; } } - - 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/Client.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/MobileClient.java similarity index 81% rename from business-delegate/src/main/java/com/iluwatar/business/delegate/Client.java rename to business-delegate/src/main/java/com/iluwatar/business/delegate/MobileClient.java index 2c13bc149..2cfb6f344 100644 --- a/business-delegate/src/main/java/com/iluwatar/business/delegate/Client.java +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/MobileClient.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,17 +24,17 @@ package com.iluwatar.business.delegate; /** - * Client utilizes BusinessDelegate to call the business tier. + * MobileClient utilizes BusinessDelegate to call the business tier. */ -public class Client { +public class MobileClient { private final BusinessDelegate businessDelegate; - public Client(BusinessDelegate businessDelegate) { + public MobileClient(BusinessDelegate businessDelegate) { this.businessDelegate = businessDelegate; } - public void doTask() { - businessDelegate.doTask(); + public void playbackMovie(String movie) { + businessDelegate.playbackMovie(movie); } } diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/JmsService.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/NetflixService.java similarity index 79% rename from business-delegate/src/main/java/com/iluwatar/business/delegate/JmsService.java rename to business-delegate/src/main/java/com/iluwatar/business/delegate/NetflixService.java index 2317d783a..ae9da8747 100644 --- a/business-delegate/src/main/java/com/iluwatar/business/delegate/JmsService.java +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/NetflixService.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,18 +23,16 @@ package com.iluwatar.business.delegate; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; /** - * Service JMS implementation. + * NetflixService implementation. */ -public class JmsService implements BusinessService { - - private static final Logger LOGGER = LoggerFactory.getLogger(JmsService.class); +@Slf4j +public class NetflixService implements VideoStreamingService { @Override public void doProcessing() { - LOGGER.info("JmsService is now processing"); + LOGGER.info("NetflixService is now processing"); } } diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessService.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/VideoStreamingService.java similarity index 89% rename from business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessService.java rename to business-delegate/src/main/java/com/iluwatar/business/delegate/VideoStreamingService.java index 3094d3f6e..3c8b7e3fb 100644 --- a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessService.java +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/VideoStreamingService.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,9 +24,9 @@ package com.iluwatar.business.delegate; /** - * Interface for service implementations. + * Interface for video streaming service implementations. */ -public interface BusinessService { +public interface VideoStreamingService { void doProcessing(); } diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/EjbService.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/YouTubeService.java similarity index 79% rename from business-delegate/src/main/java/com/iluwatar/business/delegate/EjbService.java rename to business-delegate/src/main/java/com/iluwatar/business/delegate/YouTubeService.java index 6f39abb1a..aa79e7309 100644 --- a/business-delegate/src/main/java/com/iluwatar/business/delegate/EjbService.java +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/YouTubeService.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,18 +23,16 @@ package com.iluwatar.business.delegate; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; /** - * Service EJB implementation. + * YouTubeService implementation. */ -public class EjbService implements BusinessService { - - private static final Logger LOGGER = LoggerFactory.getLogger(EjbService.class); +@Slf4j +public class YouTubeService implements VideoStreamingService { @Override public void doProcessing() { - LOGGER.info("EjbService is now processing"); + LOGGER.info("YouTubeService is now processing"); } } 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 48e756acb..f70b67814 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,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,13 +27,23 @@ import org.junit.jupiter.api.Test; import java.io.IOException; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + /** * Tests that Business Delegate example runs without errors. */ -public class AppTest { +class AppTest { + + /** + * Issue: Add at least one assertion to this test case. + * + * Solution: Inserted assertion to check whether the execution of the main method in {@link App} + * throws an exception. + */ + @Test - public void test() throws IOException { - String[] args = {}; - App.main(args); + void shouldExecuteApplicationWithoutException() { + + assertDoesNotThrow(() -> App.main(new String[]{})); } } 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 index 10815ad3a..8cd5e2021 100644 --- a/business-delegate/src/test/java/com/iluwatar/business/delegate/BusinessDelegateTest.java +++ b/business-delegate/src/test/java/com/iluwatar/business/delegate/BusinessDelegateTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,27 +26,20 @@ package com.iluwatar.business.delegate; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; + +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; /** - * 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. + * Tests for the {@link BusinessDelegate} */ -public class BusinessDelegateTest { +class BusinessDelegateTest { - private EjbService ejbService; + private NetflixService netflixService; - private JmsService jmsService; - - private BusinessLookup businessLookup; + private YouTubeService youTubeService; private BusinessDelegate businessDelegate; @@ -56,46 +49,40 @@ public class BusinessDelegateTest { */ @BeforeEach public void setup() { - ejbService = spy(new EjbService()); - jmsService = spy(new JmsService()); + netflixService = spy(new NetflixService()); + youTubeService = spy(new YouTubeService()); - businessLookup = spy(new BusinessLookup()); - businessLookup.setEjbService(ejbService); - businessLookup.setJmsService(jmsService); + BusinessLookup businessLookup = spy(new BusinessLookup()); + businessLookup.setNetflixService(netflixService); + businessLookup.setYouTubeService(youTubeService); businessDelegate = spy(new BusinessDelegate()); businessDelegate.setLookupService(businessLookup); } /** - * In this example the client ({@link Client}) utilizes a business delegate ( + * In this example the client ({@link MobileClient}) 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() { + void testBusinessDelegate() { // setup a client object - var client = new Client(businessDelegate); - - // set the service type - businessDelegate.setServiceType(ServiceType.EJB); + var client = new MobileClient(businessDelegate); // action - client.doTask(); + client.playbackMovie("Die hard"); - // 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); + // verifying that the businessDelegate was used by client during playbackMovie() method. + verify(businessDelegate).playbackMovie(anyString()); + verify(netflixService).doProcessing(); // action - client.doTask(); + client.playbackMovie("Maradona"); // verifying that the businessDelegate was used by client during doTask() method. - verify(businessDelegate, times(2)).doTask(); - verify(jmsService).doProcessing(); + verify(businessDelegate, times(2)).playbackMovie(anyString()); + verify(youTubeService).doProcessing(); } } diff --git a/bytecode/README.md b/bytecode/README.md index ee3f96ed8..115f0b96a 100644 --- a/bytecode/README.md +++ b/bytecode/README.md @@ -9,18 +9,234 @@ tags: --- ## Intent -Allows to encode behaviour as instructions for virtual machine. + +Allows encoding behavior as instructions for a virtual machine. + +## Explanation + +Real world example + +> A team is working on a new game where wizards battle against each other. The wizard behavior +> needs to be carefully adjusted and iterated hundreds of times through playtesting. It's not +> optimal to ask the programmer to make changes each time the game designer wants to vary the +> behavior, so the wizard behavior is implemented as a data-driven virtual machine. + +In plain words + +> Bytecode pattern enables behavior driven by data instead of code. + +[Gameprogrammingpatterns.com](https://gameprogrammingpatterns.com/bytecode.html) documentation +states: + +> An instruction set defines the low-level operations that can be performed. A series of +> instructions is encoded as a sequence of bytes. A virtual machine executes these instructions one +> at a time, using a stack for intermediate values. By combining instructions, complex high-level +> behavior can be defined. + +**Programmatic Example** + +One of the most important game objects is the `Wizard` class. + +```java +@AllArgsConstructor +@Setter +@Getter +@Slf4j +public class Wizard { + + private int health; + private int agility; + private int wisdom; + private int numberOfPlayedSounds; + private int numberOfSpawnedParticles; + + public void playSound() { + LOGGER.info("Playing sound"); + numberOfPlayedSounds++; + } + + public void spawnParticles() { + LOGGER.info("Spawning particles"); + numberOfSpawnedParticles++; + } +} +``` + +Next, we show the available instructions for our virtual machine. Each of the instructions has its +own semantics on how it operates with the stack data. For example, the ADD instruction takes the top +two items from the stack, adds them together and pushes the result to the stack. + +```java +@AllArgsConstructor +@Getter +public enum Instruction { + + LITERAL(1), // e.g. "LITERAL 0", push 0 to stack + SET_HEALTH(2), // e.g. "SET_HEALTH", pop health and wizard number, call set health + SET_WISDOM(3), // e.g. "SET_WISDOM", pop wisdom and wizard number, call set wisdom + SET_AGILITY(4), // e.g. "SET_AGILITY", pop agility and wizard number, call set agility + PLAY_SOUND(5), // e.g. "PLAY_SOUND", pop value as wizard number, call play sound + SPAWN_PARTICLES(6), // e.g. "SPAWN_PARTICLES", pop value as wizard number, call spawn particles + GET_HEALTH(7), // e.g. "GET_HEALTH", pop value as wizard number, push wizard's health + GET_AGILITY(8), // e.g. "GET_AGILITY", pop value as wizard number, push wizard's agility + GET_WISDOM(9), // e.g. "GET_WISDOM", pop value as wizard number, push wizard's wisdom + ADD(10), // e.g. "ADD", pop 2 values, push their sum + DIVIDE(11); // e.g. "DIVIDE", pop 2 values, push their division + // ... +} +``` + +At the heart of our example is the `VirtualMachine` class. It takes instructions as input and +executes them to provide the game object behavior. + +```java +@Getter +@Slf4j +public class VirtualMachine { + + private final Stack stack = new Stack<>(); + + private final Wizard[] wizards = new Wizard[2]; + + public VirtualMachine() { + wizards[0] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32), + 0, 0); + wizards[1] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32), + 0, 0); + } + + public VirtualMachine(Wizard wizard1, Wizard wizard2) { + wizards[0] = wizard1; + wizards[1] = wizard2; + } + + public void execute(int[] bytecode) { + for (var i = 0; i < bytecode.length; i++) { + Instruction instruction = Instruction.getInstruction(bytecode[i]); + switch (instruction) { + case LITERAL: + // Read the next byte from the bytecode. + int value = bytecode[++i]; + // Push the next value to stack + stack.push(value); + break; + case SET_AGILITY: + var amount = stack.pop(); + var wizard = stack.pop(); + setAgility(wizard, amount); + break; + case SET_WISDOM: + amount = stack.pop(); + wizard = stack.pop(); + setWisdom(wizard, amount); + break; + case SET_HEALTH: + amount = stack.pop(); + wizard = stack.pop(); + setHealth(wizard, amount); + break; + case GET_HEALTH: + wizard = stack.pop(); + stack.push(getHealth(wizard)); + break; + case GET_AGILITY: + wizard = stack.pop(); + stack.push(getAgility(wizard)); + break; + case GET_WISDOM: + wizard = stack.pop(); + stack.push(getWisdom(wizard)); + break; + case ADD: + var a = stack.pop(); + var b = stack.pop(); + stack.push(a + b); + break; + case DIVIDE: + a = stack.pop(); + b = stack.pop(); + stack.push(b / a); + break; + case PLAY_SOUND: + wizard = stack.pop(); + getWizards()[wizard].playSound(); + break; + case SPAWN_PARTICLES: + wizard = stack.pop(); + getWizards()[wizard].spawnParticles(); + break; + default: + throw new IllegalArgumentException("Invalid instruction value"); + } + LOGGER.info("Executed " + instruction.name() + ", Stack contains " + getStack()); + } + } + + public void setHealth(int wizard, int amount) { + wizards[wizard].setHealth(amount); + } + // other setters -> + // ... +} +``` + +Now we can show the full example utilizing the virtual machine. + +```java + public static void main(String[] args) { + + var vm = new VirtualMachine( + new Wizard(45, 7, 11, 0, 0), + new Wizard(36, 18, 8, 0, 0)); + + vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); + vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); + vm.execute(InstructionConverterUtil.convertToByteCode("GET_HEALTH")); + vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); + vm.execute(InstructionConverterUtil.convertToByteCode("GET_AGILITY")); + vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); + vm.execute(InstructionConverterUtil.convertToByteCode("GET_WISDOM")); + vm.execute(InstructionConverterUtil.convertToByteCode("ADD")); + vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 2")); + vm.execute(InstructionConverterUtil.convertToByteCode("DIVIDE")); + vm.execute(InstructionConverterUtil.convertToByteCode("ADD")); + vm.execute(InstructionConverterUtil.convertToByteCode("SET_HEALTH")); + } +``` + +Here is the console output. + +``` +16:20:10.193 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0] +16:20:10.196 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 0] +16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed GET_HEALTH, Stack contains [0, 45] +16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 45, 0] +16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed GET_AGILITY, Stack contains [0, 45, 7] +16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 45, 7, 0] +16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed GET_WISDOM, Stack contains [0, 45, 7, 11] +16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed ADD, Stack contains [0, 45, 18] +16:20:10.197 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed LITERAL, Stack contains [0, 45, 18, 2] +16:20:10.198 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed DIVIDE, Stack contains [0, 45, 9] +16:20:10.198 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed ADD, Stack contains [0, 54] +16:20:10.198 [main] INFO com.iluwatar.bytecode.VirtualMachine - Executed SET_HEALTH, Stack contains [] +``` ## Class diagram + ![alt text](./etc/bytecode.urm.png "Bytecode class diagram") ## Applicability + Use the Bytecode pattern when you have a lot of behavior you need to define and your game’s implementation language isn’t a good fit because: -* it’s too low-level, making it tedious or error-prone to program in. -* iterating on it takes too long due to slow compile times or other tooling issues. -* it has too much trust. If you want to ensure the behavior being defined can’t break the game, you need to sandbox it from the rest of the codebase. +* It’s too low-level, making it tedious or error-prone to program in. +* Iterating on it takes too long due to slow compile times or other tooling issues. +* It has too much trust. If you want to ensure the behavior being defined can’t break the game, you need to sandbox it from the rest of the codebase. + +## Related patterns + +* [Interpreter](https://java-design-patterns.com/patterns/interpreter/) ## Credits diff --git a/bytecode/etc/bytecode.png b/bytecode/etc/bytecode.png deleted file mode 100644 index 31b6bc6ed..000000000 Binary files a/bytecode/etc/bytecode.png and /dev/null differ diff --git a/bytecode/etc/bytecode.ucls b/bytecode/etc/bytecode.ucls deleted file mode 100644 index 3ec390458..000000000 --- a/bytecode/etc/bytecode.ucls +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/bytecode/etc/bytecode.urm.png b/bytecode/etc/bytecode.urm.png index 82036a78a..51335fa0a 100644 Binary files a/bytecode/etc/bytecode.urm.png and b/bytecode/etc/bytecode.urm.png differ diff --git a/bytecode/etc/bytecode.urm.puml b/bytecode/etc/bytecode.urm.puml index d675ae398..224e909ef 100644 --- a/bytecode/etc/bytecode.urm.puml +++ b/bytecode/etc/bytecode.urm.puml @@ -3,7 +3,6 @@ package com.iluwatar.bytecode { class App { - LOGGER : Logger {static} + App() - - interpretInstruction(instruction : String, vm : VirtualMachine) {static} + main(args : String[]) {static} } enum Instruction { @@ -18,22 +17,25 @@ package com.iluwatar.bytecode { + SET_HEALTH {static} + SET_WISDOM {static} + SPAWN_PARTICLES {static} - - value : int + - intValue : int + getInstruction(value : int) : Instruction {static} + getIntValue() : int + valueOf(name : String) : Instruction {static} + values() : Instruction[] {static} } class VirtualMachine { + - LOGGER : Logger {static} - stack : Stack - wizards : Wizard[] + VirtualMachine() + + VirtualMachine(wizard1 : Wizard, wizard2 : Wizard) + execute(bytecode : int[]) + getAgility(wizard : int) : int + getHealth(wizard : int) : int + getStack() : Stack + getWisdom(wizard : int) : int + getWizards() : Wizard[] + - randomInt(min : int, max : int) : int + setAgility(wizard : int, amount : int) + setHealth(wizard : int, amount : int) + setWisdom(wizard : int, amount : int) @@ -45,7 +47,7 @@ package com.iluwatar.bytecode { - numberOfPlayedSounds : int - numberOfSpawnedParticles : int - wisdom : int - + Wizard() + + Wizard(health : int, agility : int, wisdom : int, numberOfPlayedSounds : int, numberOfSpawnedParticles : int) + getAgility() : int + getHealth() : int + getNumberOfPlayedSounds() : int @@ -54,6 +56,8 @@ package com.iluwatar.bytecode { + playSound() + setAgility(agility : int) + setHealth(health : int) + + setNumberOfPlayedSounds(numberOfPlayedSounds : int) + + setNumberOfSpawnedParticles(numberOfSpawnedParticles : int) + setWisdom(wisdom : int) + spawnParticles() } diff --git a/bytecode/pom.xml b/bytecode/pom.xml index f6be69cee..3655ebbb4 100644 --- a/bytecode/pom.xml +++ b/bytecode/pom.xml @@ -1,26 +1,35 @@ - + java-design-patterns com.iluwatar - 1.23.0-SNAPSHOT + 1.24.0-SNAPSHOT 4.0.0 diff --git a/bytecode/src/main/java/com/iluwatar/bytecode/App.java b/bytecode/src/main/java/com/iluwatar/bytecode/App.java index 04f473cee..f76a8e6a4 100644 --- a/bytecode/src/main/java/com/iluwatar/bytecode/App.java +++ b/bytecode/src/main/java/com/iluwatar/bytecode/App.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,8 +24,7 @@ package com.iluwatar.bytecode; import com.iluwatar.bytecode.util.InstructionConverterUtil; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; /** * The intention of Bytecode pattern is to give behavior the flexibility of data by encoding it as @@ -40,8 +39,8 @@ import org.slf4j.LoggerFactory; * ensure the behavior being defined can’t break the game, you need to sandbox it from the rest of * the codebase. */ +@Slf4j public class App { - private static final Logger LOGGER = LoggerFactory.getLogger(App.class); /** * Main app method. @@ -50,31 +49,21 @@ public class App { */ public static void main(String[] args) { - var wizard = new Wizard(); - wizard.setHealth(45); - wizard.setAgility(7); - wizard.setWisdom(11); + var vm = new VirtualMachine( + new Wizard(45, 7, 11, 0, 0), + new Wizard(36, 18, 8, 0, 0)); - var vm = new VirtualMachine(); - vm.getWizards()[0] = wizard; - - interpretInstruction("LITERAL 0", vm); - interpretInstruction("LITERAL 0", vm); - interpretInstruction("GET_HEALTH", vm); - interpretInstruction("LITERAL 0", vm); - interpretInstruction("GET_AGILITY", vm); - interpretInstruction("LITERAL 0", vm); - interpretInstruction("GET_WISDOM ", vm); - interpretInstruction("ADD", vm); - interpretInstruction("LITERAL 2", vm); - interpretInstruction("DIVIDE", vm); - interpretInstruction("ADD", vm); - interpretInstruction("SET_HEALTH", vm); - } - - private static void interpretInstruction(String instruction, VirtualMachine vm) { - vm.execute(InstructionConverterUtil.convertToByteCode(instruction)); - var stack = vm.getStack(); - LOGGER.info(instruction + String.format("%" + (12 - instruction.length()) + "s", "") + stack); + vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); + vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); + vm.execute(InstructionConverterUtil.convertToByteCode("GET_HEALTH")); + vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); + vm.execute(InstructionConverterUtil.convertToByteCode("GET_AGILITY")); + vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0")); + vm.execute(InstructionConverterUtil.convertToByteCode("GET_WISDOM")); + vm.execute(InstructionConverterUtil.convertToByteCode("ADD")); + vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 2")); + vm.execute(InstructionConverterUtil.convertToByteCode("DIVIDE")); + vm.execute(InstructionConverterUtil.convertToByteCode("ADD")); + vm.execute(InstructionConverterUtil.convertToByteCode("SET_HEALTH")); } } diff --git a/bytecode/src/main/java/com/iluwatar/bytecode/Instruction.java b/bytecode/src/main/java/com/iluwatar/bytecode/Instruction.java index eeaf16846..ad16fb7f2 100644 --- a/bytecode/src/main/java/com/iluwatar/bytecode/Instruction.java +++ b/bytecode/src/main/java/com/iluwatar/bytecode/Instruction.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,32 +23,29 @@ package com.iluwatar.bytecode; +import lombok.AllArgsConstructor; +import lombok.Getter; + /** * Representation of instructions understandable by virtual machine. */ +@AllArgsConstructor +@Getter public enum Instruction { - LITERAL(1), - SET_HEALTH(2), - SET_WISDOM(3), - SET_AGILITY(4), - PLAY_SOUND(5), - SPAWN_PARTICLES(6), - GET_HEALTH(7), - GET_AGILITY(8), - GET_WISDOM(9), - ADD(10), - DIVIDE(11); + LITERAL(1), // e.g. "LITERAL 0", push 0 to stack + SET_HEALTH(2), // e.g. "SET_HEALTH", pop health and wizard number, call set health + SET_WISDOM(3), // e.g. "SET_WISDOM", pop wisdom and wizard number, call set wisdom + SET_AGILITY(4), // e.g. "SET_AGILITY", pop agility and wizard number, call set agility + PLAY_SOUND(5), // e.g. "PLAY_SOUND", pop value as wizard number, call play sound + SPAWN_PARTICLES(6), // e.g. "SPAWN_PARTICLES", pop value as wizard number, call spawn particles + GET_HEALTH(7), // e.g. "GET_HEALTH", pop value as wizard number, push wizard's health + GET_AGILITY(8), // e.g. "GET_AGILITY", pop value as wizard number, push wizard's agility + GET_WISDOM(9), // e.g. "GET_WISDOM", pop value as wizard number, push wizard's wisdom + ADD(10), // e.g. "ADD", pop 2 values, push their sum + DIVIDE(11); // e.g. "DIVIDE", pop 2 values, push their division - private final int value; - - Instruction(int value) { - this.value = value; - } - - public int getIntValue() { - return value; - } + private final int intValue; /** * Converts integer value to Instruction. diff --git a/bytecode/src/main/java/com/iluwatar/bytecode/VirtualMachine.java b/bytecode/src/main/java/com/iluwatar/bytecode/VirtualMachine.java index c45301c29..ee223b5d8 100644 --- a/bytecode/src/main/java/com/iluwatar/bytecode/VirtualMachine.java +++ b/bytecode/src/main/java/com/iluwatar/bytecode/VirtualMachine.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,10 +24,15 @@ package com.iluwatar.bytecode; import java.util.Stack; +import java.util.concurrent.ThreadLocalRandom; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; /** * Implementation of virtual machine. */ +@Getter +@Slf4j public class VirtualMachine { private final Stack stack = new Stack<>(); @@ -35,12 +40,21 @@ public class VirtualMachine { private final Wizard[] wizards = new Wizard[2]; /** - * Constructor. + * No-args constructor. */ public VirtualMachine() { - for (var i = 0; i < wizards.length; i++) { - wizards[i] = new Wizard(); - } + wizards[0] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32), + 0, 0); + wizards[1] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32), + 0, 0); + } + + /** + * Constructor taking the wizards as arguments. + */ + public VirtualMachine(Wizard wizard1, Wizard wizard2) { + wizards[0] = wizard1; + wizards[1] = wizard2; } /** @@ -55,6 +69,7 @@ public class VirtualMachine { case LITERAL: // Read the next byte from the bytecode. int value = bytecode[++i]; + // Push the next value to stack stack.push(value); break; case SET_AGILITY: @@ -105,13 +120,10 @@ public class VirtualMachine { default: throw new IllegalArgumentException("Invalid instruction value"); } + LOGGER.info("Executed " + instruction.name() + ", Stack contains " + getStack()); } } - public Stack getStack() { - return stack; - } - public void setHealth(int wizard, int amount) { wizards[wizard].setHealth(amount); } @@ -136,7 +148,7 @@ public class VirtualMachine { return wizards[wizard].getAgility(); } - public Wizard[] getWizards() { - return wizards; + private int randomInt(int min, int max) { + return ThreadLocalRandom.current().nextInt(min, max + 1); } } diff --git a/bytecode/src/main/java/com/iluwatar/bytecode/Wizard.java b/bytecode/src/main/java/com/iluwatar/bytecode/Wizard.java index 5153969d9..ce62b276a 100644 --- a/bytecode/src/main/java/com/iluwatar/bytecode/Wizard.java +++ b/bytecode/src/main/java/com/iluwatar/bytecode/Wizard.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,48 +23,27 @@ package com.iluwatar.bytecode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; /** * This class represent game objects which properties can be changed by instructions interpreted by * virtual machine. */ +@AllArgsConstructor +@Setter +@Getter +@Slf4j public class Wizard { - private static final Logger LOGGER = LoggerFactory.getLogger(Wizard.class); private int health; - private int agility; private int wisdom; - private int numberOfPlayedSounds; private int numberOfSpawnedParticles; - public int getHealth() { - return health; - } - - public void setHealth(int health) { - this.health = health; - } - - public int getAgility() { - return agility; - } - - public void setAgility(int agility) { - this.agility = agility; - } - - public int getWisdom() { - return wisdom; - } - - public void setWisdom(int wisdom) { - this.wisdom = wisdom; - } - public void playSound() { LOGGER.info("Playing sound"); numberOfPlayedSounds++; @@ -74,12 +53,4 @@ public class Wizard { LOGGER.info("Spawning particles"); numberOfSpawnedParticles++; } - - public int getNumberOfPlayedSounds() { - return numberOfPlayedSounds; - } - - public int getNumberOfSpawnedParticles() { - return numberOfSpawnedParticles; - } } diff --git a/bytecode/src/main/java/com/iluwatar/bytecode/util/InstructionConverterUtil.java b/bytecode/src/main/java/com/iluwatar/bytecode/util/InstructionConverterUtil.java index b0baf326e..ab7643129 100644 --- a/bytecode/src/main/java/com/iluwatar/bytecode/util/InstructionConverterUtil.java +++ b/bytecode/src/main/java/com/iluwatar/bytecode/util/InstructionConverterUtil.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -73,6 +73,4 @@ public class InstructionConverterUtil { return false; } } - - } diff --git a/bytecode/src/test/java/com/iluwatar/bytecode/AppTest.java b/bytecode/src/test/java/com/iluwatar/bytecode/AppTest.java index 59962d39e..a035c797e 100644 --- a/bytecode/src/test/java/com/iluwatar/bytecode/AppTest.java +++ b/bytecode/src/test/java/com/iluwatar/bytecode/AppTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,13 +25,22 @@ package com.iluwatar.bytecode; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + /** * Application test */ -public class AppTest { +class AppTest { + + /** + * Issue: Add at least one assertion to this test case. + * + * Solution: Inserted assertion to check whether the execution of the main method in {@link App} + * throws an exception. + */ @Test - public void test() { - App.main(new String[]{}); + void shouldExecuteApplicationWithoutException() { + assertDoesNotThrow(() -> App.main(new String[]{})); } } diff --git a/bytecode/src/test/java/com/iluwatar/bytecode/VirtualMachineTest.java b/bytecode/src/test/java/com/iluwatar/bytecode/VirtualMachineTest.java index 4518ca310..1fbe72014 100644 --- a/bytecode/src/test/java/com/iluwatar/bytecode/VirtualMachineTest.java +++ b/bytecode/src/test/java/com/iluwatar/bytecode/VirtualMachineTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,19 +23,19 @@ package com.iluwatar.bytecode; -import org.junit.jupiter.api.Test; - import static com.iluwatar.bytecode.Instruction.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import org.junit.jupiter.api.Test; + /** - * Test for {@Link VirtualMachine} + * Test for {@link VirtualMachine} */ -public class VirtualMachineTest { +class VirtualMachineTest { @Test - public void testLiteral() { + void testLiteral() { var bytecode = new int[2]; bytecode[0] = LITERAL.getIntValue(); bytecode[1] = 10; @@ -48,7 +48,7 @@ public class VirtualMachineTest { } @Test - public void testSetHealth() { + void testSetHealth() { var wizardNumber = 0; var bytecode = new int[5]; bytecode[0] = LITERAL.getIntValue(); @@ -64,7 +64,7 @@ public class VirtualMachineTest { } @Test - public void testSetAgility() { + void testSetAgility() { var wizardNumber = 0; var bytecode = new int[5]; bytecode[0] = LITERAL.getIntValue(); @@ -80,7 +80,7 @@ public class VirtualMachineTest { } @Test - public void testSetWisdom() { + void testSetWisdom() { var wizardNumber = 0; var bytecode = new int[5]; bytecode[0] = LITERAL.getIntValue(); @@ -96,7 +96,7 @@ public class VirtualMachineTest { } @Test - public void testGetHealth() { + void testGetHealth() { var wizardNumber = 0; var bytecode = new int[8]; bytecode[0] = LITERAL.getIntValue(); @@ -115,7 +115,7 @@ public class VirtualMachineTest { } @Test - public void testPlaySound() { + void testPlaySound() { var wizardNumber = 0; var bytecode = new int[3]; bytecode[0] = LITERAL.getIntValue(); @@ -130,7 +130,7 @@ public class VirtualMachineTest { } @Test - public void testSpawnParticles() { + void testSpawnParticles() { var wizardNumber = 0; var bytecode = new int[3]; bytecode[0] = LITERAL.getIntValue(); @@ -145,7 +145,7 @@ public class VirtualMachineTest { } @Test - public void testInvalidInstruction() { + void testInvalidInstruction() { var bytecode = new int[1]; bytecode[0] = 999; var vm = new VirtualMachine(); diff --git a/bytecode/src/test/java/com/iluwatar/bytecode/util/InstructionConverterUtilTest.java b/bytecode/src/test/java/com/iluwatar/bytecode/util/InstructionConverterUtilTest.java index e7438fce1..89486a8f7 100644 --- a/bytecode/src/test/java/com/iluwatar/bytecode/util/InstructionConverterUtilTest.java +++ b/bytecode/src/test/java/com/iluwatar/bytecode/util/InstructionConverterUtilTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,16 +24,16 @@ package com.iluwatar.bytecode.util; import com.iluwatar.bytecode.Instruction; -import com.iluwatar.bytecode.util.InstructionConverterUtil; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** - * Test for {@Link InstructionConverterUtil} + * Test for {@link InstructionConverterUtil} */ -public class InstructionConverterUtilTest { +class InstructionConverterUtilTest { + @Test - public void testEmptyInstruction() { + void testEmptyInstruction() { var instruction = ""; var bytecode = InstructionConverterUtil.convertToByteCode(instruction); @@ -42,7 +42,7 @@ public class InstructionConverterUtilTest { } @Test - public void testInstructions() { + void testInstructions() { var instructions = "LITERAL 35 SET_HEALTH SET_WISDOM SET_AGILITY PLAY_SOUND" + " SPAWN_PARTICLES GET_HEALTH ADD DIVIDE"; diff --git a/caching/README.md b/caching/README.md index 912f1d218..89487fc42 100644 --- a/caching/README.md +++ b/caching/README.md @@ -10,20 +10,326 @@ tags: --- ## 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. + +The caching pattern avoids expensive re-acquisition of resources by not releasing them immediately +after use. The resources retain their identity, are kept in some fast-access storage, and are +re-used to avoid having to acquire them again. + +## Explanation + +Real world example + +> A team is working on a website that provides new homes for abandoned cats. People can post their +> cats on the website after registering, but all the new posts require approval from one of the +> site moderators. The user accounts of the site moderators contain a specific flag and the data +> is stored in a MongoDB database. Checking for the moderator flag each time a post is viewed +> becomes expensive and it's a good idea to utilize caching here. + +In plain words + +> Caching pattern keeps frequently needed data in fast-access storage to improve performance. + +Wikipedia says: + +> In computing, a cache is a hardware or software component that stores data so that future +> requests for that data can be served faster; the data stored in a cache might be the result of +> an earlier computation or a copy of data stored elsewhere. A cache hit occurs when the requested +> data can be found in a cache, while a cache miss occurs when it cannot. Cache hits are served by +> reading data from the cache, which is faster than recomputing a result or reading from a slower +> data store; thus, the more requests that can be served from the cache, the faster the system +> performs. + +**Programmatic Example** + +Let's first look at the data layer of our application. The interesting classes are `UserAccount` +which is a simple Java object containing the user account details, and `DbManager` which handles +reading and writing of these objects to/from MongoDB database. + +```java +@Setter +@Getter +@AllArgsConstructor +@ToString +public class UserAccount { + private String userId; + private String userName; + private String additionalInfo; +} + +@Slf4j +public final class DbManager { + + private static MongoClient mongoClient; + private static MongoDatabase db; + + private DbManager() { /*...*/ } + + public static void createVirtualDb() { /*...*/ } + + public static void connect() throws ParseException { /*...*/ } + + public static UserAccount readFromDb(String userId) { /*...*/ } + + public static void writeToDb(UserAccount userAccount) { /*...*/ } + + public static void updateDb(UserAccount userAccount) { /*...*/ } + + public static void upsertDb(UserAccount userAccount) { /*...*/ } +} +``` + +In the example, we are demonstrating various different caching policies + +* Write-through writes data to the cache and DB in a single transaction +* Write-around writes data immediately into the DB instead of the cache +* Write-behind writes data into the cache initially whilst the data is only written into the DB + when the cache is full +* Cache-aside pushes the responsibility of keeping the data synchronized in both data sources to + the application itself +* Read-through strategy is also included in the aforementioned strategies and it returns data from + the cache to the caller if it exists, otherwise queries from DB and stores it into the cache for + future use. + +The cache implementation in `LruCache` is a hash table accompanied by a doubly +linked-list. The linked-list helps in capturing and maintaining the LRU data in the cache. When +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. + +```java +@Slf4j +public class LruCache { + + static class Node { + String userId; + UserAccount userAccount; + Node previous; + Node next; + + public Node(String userId, UserAccount userAccount) { + this.userId = userId; + this.userAccount = userAccount; + } + } + + /* ... omitted details ... */ + + public LruCache(int capacity) { + this.capacity = capacity; + } + + public UserAccount get(String userId) { + if (cache.containsKey(userId)) { + var node = cache.get(userId); + remove(node); + setHead(node); + return node.userAccount; + } + return null; + } + + public void set(String userId, UserAccount userAccount) { + if (cache.containsKey(userId)) { + var old = cache.get(userId); + old.userAccount = userAccount; + remove(old); + setHead(old); + } else { + var newNode = new Node(userId, userAccount); + if (cache.size() >= capacity) { + LOGGER.info("# Cache is FULL! Removing {} from cache...", end.userId); + 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 remove(Node node) { /* ... */ } + public void setHead(Node node) { /* ... */ } + public void invalidate(String userId) { /* ... */ } + public boolean isFull() { /* ... */ } + public UserAccount getLruData() { /* ... */ } + public void clear() { /* ... */ } + public List getCacheDataInListForm() { /* ... */ } + public void setCapacity(int newCapacity) { /* ... */ } +} +``` + +The next layer we are going to look at is `CacheStore` which implements the different caching +strategies. + +```java +@Slf4j +public class CacheStore { + + private static LruCache cache; + + /* ... details omitted ... */ + + public static UserAccount readThrough(String userId) { + if (cache.contains(userId)) { + LOGGER.info("# Cache Hit!"); + return cache.get(userId); + } + LOGGER.info("# Cache Miss!"); + UserAccount userAccount = DbManager.readFromDb(userId); + cache.set(userId, userAccount); + return userAccount; + } + + public static void writeThrough(UserAccount userAccount) { + if (cache.contains(userAccount.getUserId())) { + DbManager.updateDb(userAccount); + } else { + DbManager.writeToDb(userAccount); + } + cache.set(userAccount.getUserId(), userAccount); + } + + public static void clearCache() { + if (cache != null) { + cache.clear(); + } + } + + public static void flushCache() { + LOGGER.info("# flushCache..."); + Optional.ofNullable(cache) + .map(LruCache::getCacheDataInListForm) + .orElse(List.of()) + .forEach(DbManager::updateDb); + } + + /* ... omitted the implementation of other caching strategies ... */ + +} +``` + +`AppManager` helps to bridge the gap in communication between the main class and the application's +back-end. DB connection is initialized through this class. The chosen caching strategy/policy is +also initialized here. Before the cache can be used, the size of the cache has to be set. Depending +on the chosen caching policy, `AppManager` will call the appropriate function in the `CacheStore` +class. + +```java +@Slf4j +public final class AppManager { + + private static CachingPolicy cachingPolicy; + + private AppManager() { + } + + public static void initDb(boolean useMongoDb) { /* ... */ } + + public static void initCachingPolicy(CachingPolicy policy) { /* ... */ } + + public static void initCacheCapacity(int capacity) { /* ... */ } + + public static UserAccount find(String userId) { + if (cachingPolicy == CachingPolicy.THROUGH || cachingPolicy == CachingPolicy.AROUND) { + return CacheStore.readThrough(userId); + } else if (cachingPolicy == CachingPolicy.BEHIND) { + return CacheStore.readThroughWithWriteBackPolicy(userId); + } else if (cachingPolicy == CachingPolicy.ASIDE) { + return findAside(userId); + } + return null; + } + + public static void save(UserAccount userAccount) { + if (cachingPolicy == CachingPolicy.THROUGH) { + CacheStore.writeThrough(userAccount); + } else if (cachingPolicy == CachingPolicy.AROUND) { + CacheStore.writeAround(userAccount); + } else if (cachingPolicy == CachingPolicy.BEHIND) { + CacheStore.writeBehind(userAccount); + } else if (cachingPolicy == CachingPolicy.ASIDE) { + saveAside(userAccount); + } + } + + public static String printCacheContent() { + return CacheStore.print(); + } + + /* ... details omitted ... */ +} +``` + +Here is what we do in the main class of the application. + +```java +@Slf4j +public class App { + + public static void main(String[] args) { + AppManager.initDb(false); + AppManager.initCacheCapacity(3); + var app = new App(); + app.useReadAndWriteThroughStrategy(); + app.useReadThroughAndWriteAroundStrategy(); + app.useReadThroughAndWriteBehindStrategy(); + app.useCacheAsideStategy(); + } + + public void useReadAndWriteThroughStrategy() { + LOGGER.info("# CachingPolicy.THROUGH"); + AppManager.initCachingPolicy(CachingPolicy.THROUGH); + var userAccount1 = new UserAccount("001", "John", "He is a boy."); + AppManager.save(userAccount1); + LOGGER.info(AppManager.printCacheContent()); + AppManager.find("001"); + AppManager.find("001"); + } + + public void useReadThroughAndWriteAroundStrategy() { /* ... */ } + + public void useReadThroughAndWriteBehindStrategy() { /* ... */ } + + public void useCacheAsideStategy() { /* ... */ } +} +``` + +Finally, here is some of the console output from the program. + +``` +12:32:53.845 [main] INFO com.iluwatar.caching.App - # CachingPolicy.THROUGH +12:32:53.900 [main] INFO com.iluwatar.caching.App - +--CACHE CONTENT-- +UserAccount(userId=001, userName=John, additionalInfo=He is a boy.) +---- +``` ## Class diagram + ![alt text](./etc/caching.png "Caching") ## Applicability + Use the Caching pattern(s) when -* Repetitious acquisition, initialization, and release of the same resource causes unnecessary performance overhead. +* Repetitious acquisition, initialization, and release of the same resource cause unnecessary + performance overhead. + +## Related patterns + +* [Proxy](https://java-design-patterns.com/patterns/proxy/) ## 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) * [Cache-Aside pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/cache-aside) +* [Java EE 8 High Performance: Master techniques such as memory optimization, caching, concurrency, and multithreading to achieve maximum performance from your enterprise applications](https://www.amazon.com/gp/product/178847306X/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=178847306X&linkId=e948720055599f248cdac47da9125ff4) +* [Java Performance: In-Depth Advice for Tuning and Programming Java 8, 11, and Beyond](https://www.amazon.com/gp/product/1492056111/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1492056111&linkId=7e553581559b9ec04221259e52004b08) +* [Effective Java](https://www.amazon.com/gp/product/B078H61SCH/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=B078H61SCH&linkId=f06607a0b48c76541ef19c5b8b9e7882) +* [Java Performance: The Definitive Guide: Getting the Most Out of Your Code](https://www.amazon.com/gp/product/1449358454/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1449358454&linkId=475c18363e350630cc0b39ab681b2687) diff --git a/caching/pom.xml b/caching/pom.xml index 704dd78d0..f15334c88 100644 --- a/caching/pom.xml +++ b/caching/pom.xml @@ -2,7 +2,7 @@ "-state" State +DefaultCircuitBreaker --> "-state" State +MonitoringService --> "-delayedService" CircuitBreaker +DefaultCircuitBreaker --> "-service" RemoteService +DefaultCircuitBreaker ..|> CircuitBreaker +DelayedRemoteService ..|> RemoteService +QuickRemoteService ..|> RemoteService @enduml \ No newline at end of file diff --git a/circuit-breaker/pom.xml b/circuit-breaker/pom.xml index fd9f85675..160537144 100644 --- a/circuit-breaker/pom.xml +++ b/circuit-breaker/pom.xml @@ -1,7 +1,7 @@ "-size" Size -Wizard --> "-undoStack" Command -ShrinkSpell --> "-oldSize" Size -InvisibilitySpell --> "-target" Target -ShrinkSpell --> "-target" Target +Wizard --> "-changeSize" Goblin +Wizard --> "-changeVisibility" Goblin Target --> "-visibility" Visibility Goblin --|> Target -InvisibilitySpell ..|> Command -ShrinkSpell ..|> Command +App --> "castSpell" Wizard @enduml diff --git a/command/pom.xml b/command/pom.xml index 50a14c45f..c0bc15810 100644 --- a/command/pom.xml +++ b/command/pom.xml @@ -2,7 +2,7 @@ + java-design-patterns com.iluwatar - 1.23.0-SNAPSHOT + 1.24.0-SNAPSHOT converter 4.0.0 diff --git a/converter/src/main/java/com/iluwatar/converter/App.java b/converter/src/main/java/com/iluwatar/converter/App.java index 2d4ec0429..dc3eab53a 100644 --- a/converter/src/main/java/com/iluwatar/converter/App.java +++ b/converter/src/main/java/com/iluwatar/converter/App.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,8 +24,7 @@ package com.iluwatar.converter; import java.util.List; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; /** * The Converter pattern is a behavioral design pattern which allows a common way of bidirectional @@ -33,10 +32,9 @@ import org.slf4j.LoggerFactory; * isomorphic types). Moreover, the pattern introduces a common way of converting a collection of * objects between types. */ +@Slf4j public class App { - private static final Logger LOGGER = LoggerFactory.getLogger(App.class); - /** * Program entry point. * @@ -47,7 +45,7 @@ public class App { UserDto dtoUser = new UserDto("John", "Doe", true, "whatever[at]wherever.com"); User user = userConverter.convertFromDto(dtoUser); - LOGGER.info("Entity converted from DTO:" + user); + LOGGER.info("Entity converted from DTO: {}", user); var users = List.of( new User("Camile", "Tough", false, "124sad"), diff --git a/converter/src/main/java/com/iluwatar/converter/Converter.java b/converter/src/main/java/com/iluwatar/converter/Converter.java index 10425e1ca..a40650faf 100644 --- a/converter/src/main/java/com/iluwatar/converter/Converter.java +++ b/converter/src/main/java/com/iluwatar/converter/Converter.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,6 +27,7 @@ import java.util.Collection; import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; /** * Generic converter, thanks to Java8 features not only provides a way of generic bidirectional @@ -36,22 +37,12 @@ import java.util.stream.Collectors; * @param DTO representation's type * @param Domain representation's type */ +@RequiredArgsConstructor public class Converter { private final Function fromDto; private final Function fromEntity; - /** - * Constructor. - * - * @param fromDto Function that converts given dto entity into the domain entity. - * @param fromEntity Function that converts given domain entity into the dto entity. - */ - public Converter(final Function fromDto, final Function fromEntity) { - this.fromDto = fromDto; - this.fromEntity = fromEntity; - } - /** * Converts DTO to Entity. * diff --git a/converter/src/main/java/com/iluwatar/converter/User.java b/converter/src/main/java/com/iluwatar/converter/User.java index 2c1ba9ff0..f8b528a56 100644 --- a/converter/src/main/java/com/iluwatar/converter/User.java +++ b/converter/src/main/java/com/iluwatar/converter/User.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,69 +23,21 @@ package com.iluwatar.converter; -import java.util.Objects; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.ToString; /** * User class. */ +@ToString +@EqualsAndHashCode +@Getter +@RequiredArgsConstructor public class User { private final String firstName; private final String lastName; - private final boolean isActive; + private final boolean active; private final String userId; - - /** - * Constructor. - * - * @param firstName user's first name - * @param lastName user's last name - * @param isActive flag indicating whether the user is active - * @param userId user's identificator - */ - public User(String firstName, String lastName, boolean isActive, String userId) { - this.firstName = firstName; - this.lastName = lastName; - this.isActive = isActive; - this.userId = userId; - } - - public String getFirstName() { - return firstName; - } - - public String getLastName() { - return lastName; - } - - public boolean isActive() { - return isActive; - } - - public String getUserId() { - return userId; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - var user = (User) o; - return isActive == user.isActive && Objects.equals(firstName, user.firstName) && Objects - .equals(lastName, user.lastName) && Objects.equals(userId, user.userId); - } - - @Override - public int hashCode() { - return Objects.hash(firstName, lastName, isActive, userId); - } - - @Override - public String toString() { - return "User{" + "firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' - + ", isActive=" + isActive + ", userId='" + userId + '\'' + '}'; - } } diff --git a/converter/src/main/java/com/iluwatar/converter/UserConverter.java b/converter/src/main/java/com/iluwatar/converter/UserConverter.java index c2341695e..7f5cabd82 100644 --- a/converter/src/main/java/com/iluwatar/converter/UserConverter.java +++ b/converter/src/main/java/com/iluwatar/converter/UserConverter.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/converter/src/main/java/com/iluwatar/converter/UserDto.java b/converter/src/main/java/com/iluwatar/converter/UserDto.java index 67a886087..0717c4952 100644 --- a/converter/src/main/java/com/iluwatar/converter/UserDto.java +++ b/converter/src/main/java/com/iluwatar/converter/UserDto.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,70 +23,23 @@ package com.iluwatar.converter; -import java.util.Objects; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.ToString; /** * User DTO class. */ +@RequiredArgsConstructor +@Getter +@EqualsAndHashCode +@ToString public class UserDto { private final String firstName; private final String lastName; - private final boolean isActive; + private final boolean active; private final String email; - /** - * Constructor. - * - * @param firstName user's first name - * @param lastName user's last name - * @param isActive flag indicating whether the user is active - * @param email user's email address - */ - public UserDto(String firstName, String lastName, boolean isActive, String email) { - this.firstName = firstName; - this.lastName = lastName; - this.isActive = isActive; - this.email = email; - } - - public String getFirstName() { - return firstName; - } - - public String getLastName() { - return lastName; - } - - public boolean isActive() { - return isActive; - } - - public String getEmail() { - return email; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - var userDto = (UserDto) o; - return isActive == userDto.isActive && Objects.equals(firstName, userDto.firstName) && Objects - .equals(lastName, userDto.lastName) && Objects.equals(email, userDto.email); - } - - @Override - public int hashCode() { - return Objects.hash(firstName, lastName, isActive, email); - } - - @Override - public String toString() { - return "UserDto{" + "firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' - + ", isActive=" + isActive + ", email='" + email + '\'' + '}'; - } } diff --git a/converter/src/test/java/com/iluwatar/converter/AppTest.java b/converter/src/test/java/com/iluwatar/converter/AppTest.java index ed53c6863..55b9a2258 100644 --- a/converter/src/test/java/com/iluwatar/converter/AppTest.java +++ b/converter/src/test/java/com/iluwatar/converter/AppTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,14 +25,24 @@ package com.iluwatar.converter; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + /** * App running test */ -public class AppTest { +class AppTest { + + /** + * Issue: Add at least one assertion to this test case. + * + * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])} + * throws an exception. + */ @Test - public void testMain() { - App.main(new String[]{}); + void shouldExecuteApplicationWithoutException() { + + assertDoesNotThrow(() -> App.main(new String[]{})); } } diff --git a/converter/src/test/java/com/iluwatar/converter/ConverterTest.java b/converter/src/test/java/com/iluwatar/converter/ConverterTest.java index 46aca82a7..d7358d8a4 100644 --- a/converter/src/test/java/com/iluwatar/converter/ConverterTest.java +++ b/converter/src/test/java/com/iluwatar/converter/ConverterTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -32,7 +32,7 @@ import org.junit.jupiter.api.Test; /** * Tests for {@link Converter} */ -public class ConverterTest { +class ConverterTest { private final UserConverter userConverter = new UserConverter(); @@ -40,7 +40,7 @@ public class ConverterTest { * Tests whether a converter created of opposite functions holds equality as a bijection. */ @Test - public void testConversionsStartingFromDomain() { + void testConversionsStartingFromDomain() { var u1 = new User("Tom", "Hanks", true, "tom@hanks.com"); var u2 = userConverter.convertFromDto(userConverter.convertFromEntity(u1)); assertEquals(u1, u2); @@ -50,7 +50,7 @@ public class ConverterTest { * Tests whether a converter created of opposite functions holds equality as a bijection. */ @Test - public void testConversionsStartingFromDto() { + void testConversionsStartingFromDto() { var u1 = new UserDto("Tom", "Hanks", true, "tom@hanks.com"); var u2 = userConverter.convertFromEntity(userConverter.convertFromDto(u1)); assertEquals(u1, u2); @@ -61,7 +61,7 @@ public class ConverterTest { * instantiated allowing various different conversion strategies to be implemented. */ @Test - public void testCustomConverter() { + void testCustomConverter() { var converter = new Converter( userDto -> new User( userDto.getFirstName(), @@ -85,7 +85,7 @@ public class ConverterTest { * domain users returns an equal collection. */ @Test - public void testCollectionConversion() { + void testCollectionConversion() { var users = List.of( new User("Camile", "Tough", false, "124sad"), new User("Marti", "Luther", true, "42309fd"), diff --git a/cqrs/pom.xml b/cqrs/pom.xml index b3a0303e6..7d2d5aca9 100644 --- a/cqrs/pom.xml +++ b/cqrs/pom.xml @@ -2,7 +2,7 @@ - - com.github.sbrannen - spring-test-junit5 - test - org.junit.jupiter junit-jupiter-engine diff --git a/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/App.java b/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/App.java index 34fa6e307..d2ac10912 100644 --- a/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/App.java +++ b/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/App.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/routes/AggregatorRoute.java b/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/routes/AggregatorRoute.java index 2465fd960..ae0880c90 100644 --- a/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/routes/AggregatorRoute.java +++ b/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/routes/AggregatorRoute.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/routes/MessageAggregationStrategy.java b/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/routes/MessageAggregationStrategy.java index 9fd4c933a..365a0b45a 100644 --- a/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/routes/MessageAggregationStrategy.java +++ b/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/routes/MessageAggregationStrategy.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/eip-aggregator/src/main/resources/application.properties b/eip-aggregator/src/main/resources/application.properties index 34febce1a..ca07b459e 100644 --- a/eip-aggregator/src/main/resources/application.properties +++ b/eip-aggregator/src/main/resources/application.properties @@ -1,6 +1,6 @@ # # The MIT License -# Copyright © 2014-2019 Ilkka Seppälä +# Copyright © 2014-2021 Ilkka Seppälä # # 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/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/AppTest.java b/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/AppTest.java index ed604e8c2..08e03441c 100644 --- a/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/AppTest.java +++ b/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/AppTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,13 +25,22 @@ package com.iluwatar.eip.aggregator; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + /** * Test for App class */ -public class AppTest { +class AppTest { + + /** + * Issue: Add at least one assertion to this test case. + * + * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])} + * throws an exception. + */ @Test - public void testMain() throws Exception { - App.main(new String[]{}); + void shouldExecuteApplicationWithoutException() { + assertDoesNotThrow(() -> App.main(new String[]{})); } } diff --git a/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/routes/AggregatorRouteTest.java b/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/routes/AggregatorRouteTest.java index e1c6f5b2b..305201694 100644 --- a/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/routes/AggregatorRouteTest.java +++ b/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/routes/AggregatorRouteTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -49,7 +49,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; @ActiveProfiles("test") @EnableAutoConfiguration @ComponentScan -public class AggregatorRouteTest { +class AggregatorRouteTest { @EndpointInject(uri = "{{entry}}") private ProducerTemplate entry; @@ -64,7 +64,7 @@ public class AggregatorRouteTest { */ @Test @DirtiesContext - public void testSplitter() throws Exception { + void testSplitter() throws Exception { // Three items in one entry message entry.sendBody("TEST1"); diff --git a/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/routes/MessageAggregationStrategyTest.java b/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/routes/MessageAggregationStrategyTest.java index 8325e7255..40c15b370 100644 --- a/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/routes/MessageAggregationStrategyTest.java +++ b/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/routes/MessageAggregationStrategyTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -32,10 +32,10 @@ import org.junit.jupiter.api.Test; /** * Tests MessageAggregationStrategy */ -public class MessageAggregationStrategyTest { +class MessageAggregationStrategyTest { @Test - public void testAggregate() { + void testAggregate() { var mas = new MessageAggregationStrategy(); var oldExchange = new DefaultExchange((CamelContext) null); oldExchange.getIn().setBody("TEST1"); @@ -49,7 +49,7 @@ public class MessageAggregationStrategyTest { } @Test - public void testAggregateOldNull() { + void testAggregateOldNull() { var mas = new MessageAggregationStrategy(); var newExchange = new DefaultExchange((CamelContext) null); diff --git a/eip-aggregator/src/test/resources/application-test.properties b/eip-aggregator/src/test/resources/application-test.properties index 0cab1156b..843005704 100644 --- a/eip-aggregator/src/test/resources/application-test.properties +++ b/eip-aggregator/src/test/resources/application-test.properties @@ -1,6 +1,6 @@ # # The MIT License -# Copyright © 2014-2019 Ilkka Seppälä +# Copyright © 2014-2021 Ilkka Seppälä # # 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/eip-message-channel/pom.xml b/eip-message-channel/pom.xml index bea72b1f9..33ee56c05 100644 --- a/eip-message-channel/pom.xml +++ b/eip-message-channel/pom.xml @@ -2,7 +2,7 @@ - - com.github.sbrannen - spring-test-junit5 - test - org.junit.jupiter junit-jupiter-engine diff --git a/eip-splitter/src/main/java/com/iluwatar/eip/splitter/App.java b/eip-splitter/src/main/java/com/iluwatar/eip/splitter/App.java index 2792e0be4..263d9fab5 100644 --- a/eip-splitter/src/main/java/com/iluwatar/eip/splitter/App.java +++ b/eip-splitter/src/main/java/com/iluwatar/eip/splitter/App.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/eip-splitter/src/main/java/com/iluwatar/eip/splitter/routes/SplitterRoute.java b/eip-splitter/src/main/java/com/iluwatar/eip/splitter/routes/SplitterRoute.java index 4d2cb3efb..4fc467960 100644 --- a/eip-splitter/src/main/java/com/iluwatar/eip/splitter/routes/SplitterRoute.java +++ b/eip-splitter/src/main/java/com/iluwatar/eip/splitter/routes/SplitterRoute.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/eip-splitter/src/main/resources/application.properties b/eip-splitter/src/main/resources/application.properties index 34febce1a..ca07b459e 100644 --- a/eip-splitter/src/main/resources/application.properties +++ b/eip-splitter/src/main/resources/application.properties @@ -1,6 +1,6 @@ # # The MIT License -# Copyright © 2014-2019 Ilkka Seppälä +# Copyright © 2014-2021 Ilkka Seppälä # # 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/eip-splitter/src/test/java/com/iluwatar/eip/splitter/AppTest.java b/eip-splitter/src/test/java/com/iluwatar/eip/splitter/AppTest.java index 1a7dfcb0a..c9ba7d17b 100644 --- a/eip-splitter/src/test/java/com/iluwatar/eip/splitter/AppTest.java +++ b/eip-splitter/src/test/java/com/iluwatar/eip/splitter/AppTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,13 +25,22 @@ package com.iluwatar.eip.splitter; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + /** * Test for App class */ -public class AppTest { +class AppTest { + + /** + * Issue: Add at least one assertion to this test case. + * + * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])} + * throws an exception. + */ @Test - public void testMain() throws Exception { - App.main(new String[]{}); + void shouldExecuteApplicationWithoutException() { + assertDoesNotThrow(() -> App.main(new String[]{})); } } diff --git a/eip-splitter/src/test/java/com/iluwatar/eip/splitter/routes/SplitterRouteTest.java b/eip-splitter/src/test/java/com/iluwatar/eip/splitter/routes/SplitterRouteTest.java index 037954609..bc48e9ab4 100644 --- a/eip-splitter/src/test/java/com/iluwatar/eip/splitter/routes/SplitterRouteTest.java +++ b/eip-splitter/src/test/java/com/iluwatar/eip/splitter/routes/SplitterRouteTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -47,7 +47,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; @ActiveProfiles("test") @EnableAutoConfiguration @ComponentScan -public class SplitterRouteTest { +class SplitterRouteTest { @EndpointInject(uri = "{{entry}}") private ProducerTemplate entry; @@ -62,7 +62,7 @@ public class SplitterRouteTest { */ @Test @DirtiesContext - public void testSplitter() throws Exception { + void testSplitter() throws Exception { // Three items in one entry message entry.sendBody(new String[]{"TEST1", "TEST2", "TEST3"}); diff --git a/eip-splitter/src/test/resources/application-test.properties b/eip-splitter/src/test/resources/application-test.properties index 0cab1156b..843005704 100644 --- a/eip-splitter/src/test/resources/application-test.properties +++ b/eip-splitter/src/test/resources/application-test.properties @@ -1,6 +1,6 @@ # # The MIT License -# Copyright © 2014-2019 Ilkka Seppälä +# Copyright © 2014-2021 Ilkka Seppälä # # 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/eip-wire-tap/pom.xml b/eip-wire-tap/pom.xml index 06cbc33db..d7c2d1568 100644 --- a/eip-wire-tap/pom.xml +++ b/eip-wire-tap/pom.xml @@ -2,7 +2,7 @@ - - com.github.sbrannen - spring-test-junit5 - test - org.junit.jupiter junit-jupiter-engine diff --git a/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/App.java b/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/App.java index f3b5c11c1..7940d3e5e 100644 --- a/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/App.java +++ b/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/App.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/routes/WireTapRoute.java b/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/routes/WireTapRoute.java index 8eea7adbd..39d89e738 100644 --- a/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/routes/WireTapRoute.java +++ b/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/routes/WireTapRoute.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * 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/eip-wire-tap/src/main/resources/application.properties b/eip-wire-tap/src/main/resources/application.properties index 13e13b959..cb77786ed 100644 --- a/eip-wire-tap/src/main/resources/application.properties +++ b/eip-wire-tap/src/main/resources/application.properties @@ -1,6 +1,6 @@ # # The MIT License -# Copyright © 2014-2019 Ilkka Seppälä +# Copyright © 2014-2021 Ilkka Seppälä # # 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/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/AppTest.java b/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/AppTest.java index 31154043c..20fc2a8bc 100644 --- a/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/AppTest.java +++ b/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/AppTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,13 +25,22 @@ package com.iluwatar.eip.wiretap; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + /** * Test for App class */ -public class AppTest { +class AppTest { + + /** + * Issue: Add at least one assertion to this test case. + * + * Solution: Inserted assertion to check whether the execution of the main method in {@link App#main(String[])} + * throws an exception. + */ @Test - public void testMain() throws Exception { - App.main(new String[]{}); + void shouldExecuteApplicationWithoutException() { + assertDoesNotThrow(() -> App.main(new String[]{})); } } diff --git a/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/routes/WireTapRouteTest.java b/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/routes/WireTapRouteTest.java index 0d13374dc..49bb043c4 100644 --- a/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/routes/WireTapRouteTest.java +++ b/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/routes/WireTapRouteTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -49,7 +49,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; @ActiveProfiles("test") @EnableAutoConfiguration @ComponentScan -public class WireTapRouteTest { +class WireTapRouteTest { @EndpointInject(uri = "{{entry}}") private ProducerTemplate entry; @@ -67,7 +67,7 @@ public class WireTapRouteTest { */ @Test @DirtiesContext - public void testWireTap() throws Exception { + void testWireTap() throws Exception { entry.sendBody("TEST"); endpoint.expectedMessageCount(1); diff --git a/eip-wire-tap/src/test/resources/application-test.properties b/eip-wire-tap/src/test/resources/application-test.properties index a64f4b316..801b7c928 100644 --- a/eip-wire-tap/src/test/resources/application-test.properties +++ b/eip-wire-tap/src/test/resources/application-test.properties @@ -1,6 +1,6 @@ # # The MIT License -# Copyright © 2014-2019 Ilkka Seppälä +# Copyright © 2014-2021 Ilkka Seppälä # # 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/event-aggregator/pom.xml b/event-aggregator/pom.xml index 5553de2e3..e9b712f37 100644 --- a/event-aggregator/pom.xml +++ b/event-aggregator/pom.xml @@ -1,7 +1,7 @@ + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.24.0-SNAPSHOT + + factory + + + org.junit.jupiter + junit-jupiter-engine + test + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + com.iluwatar.factory.App + + + + + + + + + \ No newline at end of file diff --git a/mutex/src/main/java/com/iluwatar/mutex/App.java b/factory/src/main/java/com/iluwatar/factory/App.java similarity index 58% rename from mutex/src/main/java/com/iluwatar/mutex/App.java rename to factory/src/main/java/com/iluwatar/factory/App.java index c50acc65a..732f5458f 100644 --- a/mutex/src/main/java/com/iluwatar/mutex/App.java +++ b/factory/src/main/java/com/iluwatar/factory/App.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,29 +21,29 @@ * THE SOFTWARE. */ -package com.iluwatar.mutex; +package com.iluwatar.factory; + +import lombok.extern.slf4j.Slf4j; /** - * A Mutex prevents multiple threads from accessing a resource simultaneously. + * Factory is an object for creating other objects, it providing Providing a static method to + * create and return objects of varying classes, in order to hide the implementation logic + * and makes client code focus on usage rather then objects initialization and management. * - *

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

In this example the CarFactory is the factory class and it provides a static method to + * create different cars. */ + +@Slf4j public class App { /** - * main method. + * Program main entry point. */ public static void main(String[] args) { - var mutex = new Mutex(); - var jar = new Jar(1000, mutex); - var peter = new Thief("Peter", jar); - var john = new Thief("John", jar); - peter.start(); - john.start(); + var car1 = CarsFactory.getCar(CarType.FORD); + var car2 = CarsFactory.getCar(CarType.FERRARI); + LOGGER.info(car1.getDescription()); + LOGGER.info(car2.getDescription()); } - } diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/ServiceType.java b/factory/src/main/java/com/iluwatar/factory/Car.java similarity index 87% rename from business-delegate/src/main/java/com/iluwatar/business/delegate/ServiceType.java rename to factory/src/main/java/com/iluwatar/factory/Car.java index c0f02b5e3..e1e248fb4 100644 --- a/business-delegate/src/main/java/com/iluwatar/business/delegate/ServiceType.java +++ b/factory/src/main/java/com/iluwatar/factory/Car.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,12 +21,13 @@ * THE SOFTWARE. */ -package com.iluwatar.business.delegate; +package com.iluwatar.factory; /** - * Enumeration for service types. + * Car interface. */ -public enum ServiceType { +public interface Car { + + String getDescription(); - EJB, JMS } diff --git a/factory/src/main/java/com/iluwatar/factory/CarType.java b/factory/src/main/java/com/iluwatar/factory/CarType.java new file mode 100644 index 000000000..074ea32bc --- /dev/null +++ b/factory/src/main/java/com/iluwatar/factory/CarType.java @@ -0,0 +1,42 @@ +/* + * The MIT License + * Copyright © 2014-2021 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 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; + +import java.util.function.Supplier; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +/** + * Enumeration for different types of cars. + */ +@RequiredArgsConstructor +@Getter +public enum CarType { + + FORD(Ford::new), + FERRARI(Ferrari::new); + + private final Supplier constructor; + +} diff --git a/factory/src/main/java/com/iluwatar/factory/CarsFactory.java b/factory/src/main/java/com/iluwatar/factory/CarsFactory.java new file mode 100644 index 000000000..941552065 --- /dev/null +++ b/factory/src/main/java/com/iluwatar/factory/CarsFactory.java @@ -0,0 +1,37 @@ +/* + * The MIT License + * Copyright © 2014-2021 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 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; + +/** + * Factory of cars. + */ +public class CarsFactory { + + /** + * Factory method takes as parameter a car type and initiate the appropriate class. + */ + public static Car getCar(CarType type) { + return type.getConstructor().get(); + } +} diff --git a/factory/src/main/java/com/iluwatar/factory/Ferrari.java b/factory/src/main/java/com/iluwatar/factory/Ferrari.java new file mode 100644 index 000000000..c11ee3679 --- /dev/null +++ b/factory/src/main/java/com/iluwatar/factory/Ferrari.java @@ -0,0 +1,37 @@ +/* + * The MIT License + * Copyright © 2014-2021 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 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; + +/** + * Ferrari implementation. + */ +public class Ferrari implements Car { + + static final String DESCRIPTION = "This is Ferrari."; + + @Override + public String getDescription() { + return DESCRIPTION; + } +} diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/Lock.java b/factory/src/main/java/com/iluwatar/factory/Ford.java similarity index 81% rename from semaphore/src/main/java/com/iluwatar/semaphore/Lock.java rename to factory/src/main/java/com/iluwatar/factory/Ford.java index 5679e2a05..d1091a00b 100644 --- a/semaphore/src/main/java/com/iluwatar/semaphore/Lock.java +++ b/factory/src/main/java/com/iluwatar/factory/Ford.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,15 +21,17 @@ * THE SOFTWARE. */ -package com.iluwatar.semaphore; +package com.iluwatar.factory; /** - * Lock is an interface for a lock which can be acquired and released. + * Ford implementation. */ -public interface Lock { +public class Ford implements Car { - void acquire() throws InterruptedException; - - void release(); + static final String DESCRIPTION = "This is Ford."; + @Override + public String getDescription() { + return DESCRIPTION; + } } diff --git a/semaphore/src/test/java/com/iluwatar/semaphore/AppTest.java b/factory/src/test/java/com/iluwatar/factory/AppTest.java similarity index 82% rename from semaphore/src/test/java/com/iluwatar/semaphore/AppTest.java rename to factory/src/test/java/com/iluwatar/factory/AppTest.java index f450c0593..9bb244500 100644 --- a/semaphore/src/test/java/com/iluwatar/semaphore/AppTest.java +++ b/factory/src/test/java/com/iluwatar/factory/AppTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,16 +21,17 @@ * THE SOFTWARE. */ -package com.iluwatar.semaphore; +package com.iluwatar.factory; + +import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.Test; -/** - * Application Test Entrypoint - */ -public class AppTest { +class AppTest { + @Test - public void test() { - App.main(new String[]{}); + void shouldExecuteWithoutExceptions() { + assertDoesNotThrow(() -> App.main(new String[]{})); } + } diff --git a/factory/src/test/java/com/iluwatar/factory/CarsFactoryTest.java b/factory/src/test/java/com/iluwatar/factory/CarsFactoryTest.java new file mode 100644 index 000000000..c29cbbeb4 --- /dev/null +++ b/factory/src/test/java/com/iluwatar/factory/CarsFactoryTest.java @@ -0,0 +1,38 @@ +/* + * The MIT License + * Copyright © 2014-2021 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 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; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.Test; + +class CarsFactoryTest { + + @Test + void shouldReturnFerrariInstance() { + final var ferrari = CarsFactory.getCar(CarType.FERRARI); + assertTrue(ferrari instanceof Ferrari); + } + +} diff --git a/feature-toggle/pom.xml b/feature-toggle/pom.xml index 13f646b80..b20d05afa 100644 --- a/feature-toggle/pom.xml +++ b/feature-toggle/pom.xml @@ -2,7 +2,7 @@ "-issues" Threat +SimpleThreat --> "-threatType" ThreatType +SimpleProbabilisticThreatAwareSystem --> "-threats" ProbableThreat +ProbabilisticThreatAwareSystem --|> ThreatAwareSystem +ProbableThreat --|> Threat +SimpleProbabilisticThreatAwareSystem ..|> ProbabilisticThreatAwareSystem +SimpleProbableThreat ..|> ProbableThreat +SimpleProbableThreat --|> SimpleThreat +SimpleThreat ..|> Threat +SimpleThreatAwareSystem ..|> ThreatAwareSystem +@enduml \ No newline at end of file diff --git a/filterer/pom.xml b/filterer/pom.xml new file mode 100644 index 000000000..19500b859 --- /dev/null +++ b/filterer/pom.xml @@ -0,0 +1,73 @@ + + + + + java-design-patterns + com.iluwatar + 1.24.0-SNAPSHOT + + 4.0.0 + + filterer + + + + com.google.guava + guava + + + org.junit.jupiter + junit-jupiter-api + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + com.iluwatar.filterer.App + + + + + + + + + \ No newline at end of file diff --git a/filterer/src/main/java/com/iluwatar/filterer/App.java b/filterer/src/main/java/com/iluwatar/filterer/App.java new file mode 100644 index 000000000..d25c73a55 --- /dev/null +++ b/filterer/src/main/java/com/iluwatar/filterer/App.java @@ -0,0 +1,104 @@ +/* + * The MIT License + * Copyright © 2014-2021 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.filterer; + +import com.iluwatar.filterer.threat.ProbableThreat; +import com.iluwatar.filterer.threat.SimpleProbabilisticThreatAwareSystem; +import com.iluwatar.filterer.threat.SimpleProbableThreat; +import com.iluwatar.filterer.threat.SimpleThreat; +import com.iluwatar.filterer.threat.SimpleThreatAwareSystem; +import com.iluwatar.filterer.threat.Threat; +import com.iluwatar.filterer.threat.ThreatAwareSystem; +import com.iluwatar.filterer.threat.ThreatType; +import java.util.List; +import java.util.function.Predicate; +import lombok.extern.slf4j.Slf4j; + +/** + * This demo class represent how {@link com.iluwatar.filterer.domain.Filterer} pattern is used to + * filter container-like objects to return filtered versions of themselves. The container like + * objects are systems that are aware of threats that they can be vulnerable to. We would like + * to have a way to create copy of different system objects but with filtered threats. + * The thing is to keep it simple if we add new subtype of {@link Threat} + * (for example {@link ProbableThreat}) - we still need to be able to filter by it's properties. + */ +@Slf4j +public class App { + + public static void main(String[] args) { + filteringSimpleThreats(); + filteringSimpleProbableThreats(); + } + + /** + * Demonstrates how to filter {@link com.iluwatar.filterer.threat.ProbabilisticThreatAwareSystem} + * based on probability property. The @{@link com.iluwatar.filterer.domain.Filterer#by(Predicate)} + * method is able to use {@link com.iluwatar.filterer.threat.ProbableThreat} + * as predicate argument. + */ + private static void filteringSimpleProbableThreats() { + LOGGER.info("### Filtering ProbabilisticThreatAwareSystem by probability ###"); + + var trojanArcBomb = new SimpleProbableThreat("Trojan-ArcBomb", 1, ThreatType.TROJAN, 0.99); + var rootkit = new SimpleProbableThreat("Rootkit-Kernel", 2, ThreatType.ROOTKIT, 0.8); + + List probableThreats = List.of(trojanArcBomb, rootkit); + + var probabilisticThreatAwareSystem = + new SimpleProbabilisticThreatAwareSystem("Sys-1", probableThreats); + + LOGGER.info("Filtering ProbabilisticThreatAwareSystem. Initial : " + + probabilisticThreatAwareSystem); + + //Filtering using filterer + var filteredThreatAwareSystem = probabilisticThreatAwareSystem.filtered() + .by(probableThreat -> Double.compare(probableThreat.probability(), 0.99) == 0); + + LOGGER.info("Filtered by probability = 0.99 : " + filteredThreatAwareSystem); + } + + /** + * Demonstrates how to filter {@link ThreatAwareSystem} based on startingOffset property + * of {@link SimpleThreat}. The @{@link com.iluwatar.filterer.domain.Filterer#by(Predicate)} + * method is able to use {@link Threat} as predicate argument. + */ + private static void filteringSimpleThreats() { + LOGGER.info("### Filtering ThreatAwareSystem by ThreatType ###"); + + var rootkit = new SimpleThreat(ThreatType.ROOTKIT, 1, "Simple-Rootkit"); + var trojan = new SimpleThreat(ThreatType.TROJAN, 2, "Simple-Trojan"); + List threats = List.of(rootkit, trojan); + + var threatAwareSystem = new SimpleThreatAwareSystem("Sys-1", threats); + + LOGGER.info("Filtering ThreatAwareSystem. Initial : " + threatAwareSystem); + + //Filtering using Filterer + var rootkitThreatAwareSystem = threatAwareSystem.filtered() + .by(threat -> threat.type() == ThreatType.ROOTKIT); + + LOGGER.info("Filtered by threatType = ROOTKIT : " + rootkitThreatAwareSystem); + } + +} diff --git a/filterer/src/main/java/com/iluwatar/filterer/domain/Filterer.java b/filterer/src/main/java/com/iluwatar/filterer/domain/Filterer.java new file mode 100644 index 000000000..a9d3f9dc6 --- /dev/null +++ b/filterer/src/main/java/com/iluwatar/filterer/domain/Filterer.java @@ -0,0 +1,36 @@ +/* + * The MIT License + * Copyright © 2014-2021 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.filterer.domain; + +import java.util.function.Predicate; + +/** + * Filterer helper interface. + * @param type of the container-like object. + * @param type of the elements contained within this container-like object. + */ +@FunctionalInterface +public interface Filterer { + G by(Predicate predicate); +} \ No newline at end of file diff --git a/filterer/src/main/java/com/iluwatar/filterer/threat/ProbabilisticThreatAwareSystem.java b/filterer/src/main/java/com/iluwatar/filterer/threat/ProbabilisticThreatAwareSystem.java new file mode 100644 index 000000000..9d6a9ce5e --- /dev/null +++ b/filterer/src/main/java/com/iluwatar/filterer/threat/ProbabilisticThreatAwareSystem.java @@ -0,0 +1,49 @@ +/* + * The MIT License + * Copyright © 2014-2021 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.filterer.threat; + +import com.iluwatar.filterer.domain.Filterer; + +import java.util.List; + +/** + * Represents system that is aware of it's threats with given probability of their occurrence. + */ +public interface ProbabilisticThreatAwareSystem extends ThreatAwareSystem { + + /** + * {@inheritDoc} + * @return + */ + @Override + List threats(); + + /** + * {@inheritDoc} + * @return + */ + @Override + Filterer filtered(); +} + diff --git a/filterer/src/main/java/com/iluwatar/filterer/threat/ProbableThreat.java b/filterer/src/main/java/com/iluwatar/filterer/threat/ProbableThreat.java new file mode 100644 index 000000000..dec119f84 --- /dev/null +++ b/filterer/src/main/java/com/iluwatar/filterer/threat/ProbableThreat.java @@ -0,0 +1,35 @@ +/* + * The MIT License + * Copyright © 2014-2021 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.filterer.threat; + +/** + * Represents threat that might be a threat with given probability. + */ +public interface ProbableThreat extends Threat { + /** + * Returns probability of occurrence of given threat. + * @return probability of occurrence of given threat. + */ + double probability(); +} \ No newline at end of file diff --git a/filterer/src/main/java/com/iluwatar/filterer/threat/SimpleProbabilisticThreatAwareSystem.java b/filterer/src/main/java/com/iluwatar/filterer/threat/SimpleProbabilisticThreatAwareSystem.java new file mode 100644 index 000000000..ac16e6614 --- /dev/null +++ b/filterer/src/main/java/com/iluwatar/filterer/threat/SimpleProbabilisticThreatAwareSystem.java @@ -0,0 +1,81 @@ +/* + * The MIT License + * Copyright © 2014-2021 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.filterer.threat; + +import com.iluwatar.filterer.domain.Filterer; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import lombok.EqualsAndHashCode; +import lombok.RequiredArgsConstructor; +import lombok.ToString; + +/** + * {@inheritDoc} + */ +@ToString +@EqualsAndHashCode +@RequiredArgsConstructor +public class SimpleProbabilisticThreatAwareSystem implements ProbabilisticThreatAwareSystem { + + private final String systemId; + private final List threats; + + /** + * {@inheritDoc} + */ + @Override + public String systemId() { + return systemId; + } + + /** + * {@inheritDoc} + */ + @Override + public List threats() { + return threats; + } + + /** + * {@inheritDoc} + */ + @Override + public Filterer filtered() { + return this::filteredGroup; + } + + private ProbabilisticThreatAwareSystem filteredGroup( + final Predicate predicate) { + return new SimpleProbabilisticThreatAwareSystem(this.systemId, filteredItems(predicate)); + } + + private List filteredItems( + final Predicate predicate) { + return this.threats.stream() + .filter(predicate) + .collect(Collectors.toUnmodifiableList()); + } + +} diff --git a/filterer/src/main/java/com/iluwatar/filterer/threat/SimpleProbableThreat.java b/filterer/src/main/java/com/iluwatar/filterer/threat/SimpleProbableThreat.java new file mode 100644 index 000000000..307e6d41b --- /dev/null +++ b/filterer/src/main/java/com/iluwatar/filterer/threat/SimpleProbableThreat.java @@ -0,0 +1,57 @@ +/* + * The MIT License + * Copyright © 2014-2021 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.filterer.threat; + +import lombok.EqualsAndHashCode; + +/** + * {@inheritDoc} + */ +@EqualsAndHashCode(callSuper = false) +public class SimpleProbableThreat extends SimpleThreat implements ProbableThreat { + + private final double probability; + + public SimpleProbableThreat(final String name, final int id, final ThreatType threatType, + final double probability) { + super(threatType, id, name); + this.probability = probability; + } + + /** + * {@inheritDoc} + */ + @Override + public double probability() { + return probability; + } + + @Override + public String toString() { + return "SimpleProbableThreat{" + + "probability=" + probability + + "} " + + super.toString(); + } +} diff --git a/command/src/main/java/com/iluwatar/command/ShrinkSpell.java b/filterer/src/main/java/com/iluwatar/filterer/threat/SimpleThreat.java similarity index 65% rename from command/src/main/java/com/iluwatar/command/ShrinkSpell.java rename to filterer/src/main/java/com/iluwatar/filterer/threat/SimpleThreat.java index 3f21fc7c1..44294b1db 100644 --- a/command/src/main/java/com/iluwatar/command/ShrinkSpell.java +++ b/filterer/src/main/java/com/iluwatar/filterer/threat/SimpleThreat.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,39 +21,46 @@ * THE SOFTWARE. */ -package com.iluwatar.command; +package com.iluwatar.filterer.threat; + +import lombok.EqualsAndHashCode; +import lombok.RequiredArgsConstructor; +import lombok.ToString; /** - * ShrinkSpell is a concrete command. + * Represents a simple threat. */ -public class ShrinkSpell implements Command { +@ToString +@EqualsAndHashCode +@RequiredArgsConstructor +public class SimpleThreat implements Threat { - private Size oldSize; - private Target target; + private final ThreatType threatType; + private final int id; + private final String name; + /** + * {@inheritDoc} + */ @Override - public void execute(Target target) { - oldSize = target.getSize(); - target.setSize(Size.SMALL); - this.target = target; + public String name() { + return name; } + /** + * {@inheritDoc} + */ @Override - public void undo() { - if (oldSize != null && target != null) { - var temp = target.getSize(); - target.setSize(oldSize); - oldSize = temp; - } + public int id() { + return id; } + /** + * {@inheritDoc} + */ @Override - public void redo() { - undo(); + public ThreatType type() { + return threatType; } - @Override - public String toString() { - return "Shrink spell"; - } } diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/FruitBowl.java b/filterer/src/main/java/com/iluwatar/filterer/threat/SimpleThreatAwareSystem.java similarity index 50% rename from semaphore/src/main/java/com/iluwatar/semaphore/FruitBowl.java rename to filterer/src/main/java/com/iluwatar/filterer/threat/SimpleThreatAwareSystem.java index 5c2901efe..7a165c3f3 100644 --- a/semaphore/src/main/java/com/iluwatar/semaphore/FruitBowl.java +++ b/filterer/src/main/java/com/iluwatar/filterer/threat/SimpleThreatAwareSystem.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,72 +21,60 @@ * THE SOFTWARE. */ -package com.iluwatar.semaphore; +package com.iluwatar.filterer.threat; +import com.iluwatar.filterer.domain.Filterer; import java.util.ArrayList; import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import lombok.EqualsAndHashCode; +import lombok.RequiredArgsConstructor; +import lombok.ToString; /** - * A FruitBowl contains Fruit. + * {@inheritDoc} */ -public class FruitBowl { +@ToString +@EqualsAndHashCode +@RequiredArgsConstructor +public class SimpleThreatAwareSystem implements ThreatAwareSystem { - private final List fruit = new ArrayList<>(); + private final String systemId; + private final List issues; /** - * Returns the amount of fruits left in bowl. - * - * @return The amount of Fruit left in the bowl. + * {@inheritDoc} */ - public int countFruit() { - return fruit.size(); + @Override + public String systemId() { + return systemId; } /** - * Put an item of Fruit into the bowl. - * - * @param f fruit + * {@inheritDoc} */ - public void put(Fruit f) { - fruit.add(f); + @Override + public List threats() { + return new ArrayList<>(issues); } /** - * Take an item of Fruit out of the bowl. - * - * @return The Fruit taken out of the bowl, or null if empty. + * {@inheritDoc} */ - public Fruit take() { - if (fruit.isEmpty()) { - return null; - } else { - return fruit.remove(0); - } + @Override + public Filterer filtered() { + return this::filteredGroup; } - /** - * toString method. - */ - public String toString() { - var apples = 0; - var oranges = 0; - var lemons = 0; - - for (var 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"; + private ThreatAwareSystem filteredGroup(Predicate predicate) { + return new SimpleThreatAwareSystem(this.systemId, filteredItems(predicate)); } + + private List filteredItems(Predicate predicate) { + return this.issues.stream() + .filter(predicate) + .collect(Collectors.toUnmodifiableList()); + } + } diff --git a/filterer/src/main/java/com/iluwatar/filterer/threat/Threat.java b/filterer/src/main/java/com/iluwatar/filterer/threat/Threat.java new file mode 100644 index 000000000..7eeaa6be2 --- /dev/null +++ b/filterer/src/main/java/com/iluwatar/filterer/threat/Threat.java @@ -0,0 +1,49 @@ +/* + * The MIT License + * Copyright © 2014-2021 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.filterer.threat; + +/** + * Represents a threat that can be detected in given system. + */ +public interface Threat { + /** + * Returns name of the threat. + * + * @return value representing name of the threat. + */ + String name(); + + /** + * Returns unique id of the threat. + * + * @return value representing threat id. + */ + int id(); + + /** + * Returns threat type. + * @return {@link ThreatType} + */ + ThreatType type(); +} diff --git a/filterer/src/main/java/com/iluwatar/filterer/threat/ThreatAwareSystem.java b/filterer/src/main/java/com/iluwatar/filterer/threat/ThreatAwareSystem.java new file mode 100644 index 000000000..4e34c99dd --- /dev/null +++ b/filterer/src/main/java/com/iluwatar/filterer/threat/ThreatAwareSystem.java @@ -0,0 +1,55 @@ +/* + * The MIT License + * Copyright © 2014-2021 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.filterer.threat; + +import com.iluwatar.filterer.domain.Filterer; + +import java.util.List; + +/** + * Represents system that is aware of threats that are present in it. + */ +public interface ThreatAwareSystem { + + /** + * Returns the system id. + * + * @return system id. + */ + String systemId(); + + /** + * Returns list of threats for this system. + * @return list of threats for this system. + */ + List threats(); + + /** + * Returns the instance of {@link Filterer} helper interface that allows to covariantly + * specify lower bound for predicate that we want to filter by. + * @return an instance of {@link Filterer} helper interface. + */ + Filterer filtered(); + +} diff --git a/filterer/src/main/java/com/iluwatar/filterer/threat/ThreatType.java b/filterer/src/main/java/com/iluwatar/filterer/threat/ThreatType.java new file mode 100644 index 000000000..417db3e0c --- /dev/null +++ b/filterer/src/main/java/com/iluwatar/filterer/threat/ThreatType.java @@ -0,0 +1,30 @@ +/* + * The MIT License + * Copyright © 2014-2021 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.filterer.threat; + +public enum ThreatType { + TROJAN, + WORM, + ROOTKIT +} diff --git a/mutex/src/test/java/com/iluwatar/mutex/AppTest.java b/filterer/src/test/java/com/iluwatar/filterer/AppTest.java similarity index 88% rename from mutex/src/test/java/com/iluwatar/mutex/AppTest.java rename to filterer/src/test/java/com/iluwatar/filterer/AppTest.java index 0bee249a6..9ba98dd23 100644 --- a/mutex/src/test/java/com/iluwatar/mutex/AppTest.java +++ b/filterer/src/test/java/com/iluwatar/filterer/AppTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,16 +21,13 @@ * THE SOFTWARE. */ -package com.iluwatar.mutex; +package com.iluwatar.filterer; import org.junit.jupiter.api.Test; -/** - * Application Test Entrypoint - */ -public class AppTest { +class AppTest { @Test - public void test() { + void shouldLaunchApp() { App.main(new String[]{}); } -} +} \ No newline at end of file diff --git a/semaphore/src/test/java/com/iluwatar/semaphore/SemaphoreTest.java b/filterer/src/test/java/com/iluwatar/filterer/threat/SimpleProbabilisticThreatAwareSystemTest.java similarity index 58% rename from semaphore/src/test/java/com/iluwatar/semaphore/SemaphoreTest.java rename to filterer/src/test/java/com/iluwatar/filterer/threat/SimpleProbabilisticThreatAwareSystemTest.java index 6d64066fb..50fca774c 100644 --- a/semaphore/src/test/java/com/iluwatar/semaphore/SemaphoreTest.java +++ b/filterer/src/test/java/com/iluwatar/filterer/threat/SimpleProbabilisticThreatAwareSystemTest.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,39 +21,31 @@ * THE SOFTWARE. */ -package com.iluwatar.semaphore; +package com.iluwatar.filterer.threat; import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; +import java.util.List; import org.junit.jupiter.api.Test; -/** - * Test case for acquiring and releasing a Semaphore - */ -public class SemaphoreTest { +class SimpleProbabilisticThreatAwareSystemTest { @Test - public void acquireReleaseTest() { - var sphore = new Semaphore(3); + void shouldFilterByProbability() { + //given + var trojan = new SimpleProbableThreat("Troyan-ArcBomb", 1, ThreatType.TROJAN, 0.99); + var rootkit = new SimpleProbableThreat("Rootkit-System", 2, ThreatType.ROOTKIT, 0.8); + List probableThreats = List.of(trojan, rootkit); - assertEquals(3, sphore.getAvailableLicenses()); + var simpleProbabilisticThreatAwareSystem = + new SimpleProbabilisticThreatAwareSystem("System-1", probableThreats); - for (var i = 2; i >= 0; i--) { - try { - sphore.acquire(); - assertEquals(i, sphore.getAvailableLicenses()); - } catch (InterruptedException e) { - fail(e.toString()); - } - } + //when + var filtered = simpleProbabilisticThreatAwareSystem.filtered() + .by(probableThreat -> Double.compare(probableThreat.probability(), 0.99) == 0); - for (var i = 1; i <= 3; i++) { - sphore.release(); - assertEquals(i, sphore.getAvailableLicenses()); - } - - sphore.release(); - assertEquals(3, sphore.getAvailableLicenses()); + //then + assertEquals(filtered.threats().size(), 1); + assertEquals(filtered.threats().get(0), trojan); } -} +} \ No newline at end of file diff --git a/filterer/src/test/java/com/iluwatar/filterer/threat/SimpleThreatAwareSystemTest.java b/filterer/src/test/java/com/iluwatar/filterer/threat/SimpleThreatAwareSystemTest.java new file mode 100644 index 000000000..d8b28e18a --- /dev/null +++ b/filterer/src/test/java/com/iluwatar/filterer/threat/SimpleThreatAwareSystemTest.java @@ -0,0 +1,50 @@ +/* + * The MIT License + * Copyright © 2014-2021 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.filterer.threat; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class SimpleThreatAwareSystemTest { + @Test + void shouldFilterByThreatType() { + //given + var rootkit = new SimpleThreat(ThreatType.ROOTKIT, 1, "Simple-Rootkit"); + var trojan = new SimpleThreat(ThreatType.TROJAN, 2, "Simple-Trojan"); + List threats = List.of(rootkit, trojan); + + var threatAwareSystem = new SimpleThreatAwareSystem("System-1", threats); + + //when + var rootkitThreatAwareSystem = threatAwareSystem.filtered() + .by(threat -> threat.type() == ThreatType.ROOTKIT); + + //then + assertEquals(rootkitThreatAwareSystem.threats().size(), 1); + assertEquals(rootkitThreatAwareSystem.threats().get(0), rootkit); + } +} \ No newline at end of file diff --git a/fluentinterface/README.md b/fluentinterface/README.md index 61c5f2eb5..8b3f80fae 100644 --- a/fluentinterface/README.md +++ b/fluentinterface/README.md @@ -9,23 +9,25 @@ tags: --- ## 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. + +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. ## Explanation -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. A fluent interface can be implemented using any of - * Method Chaining - calling a method returns some object on which further methods can be called. - * Static Factory Methods and Imports + * Method chaining - calling a method returns some object on which further methods can be called. + * Static factory methods and imports. * Named parameters - can be simulated in Java using static factory methods. Real world example -> We need to select numbers based on different criteria from the list. It's a great chance to utilize fluent interface pattern to provide readable easy-to-use developer experience. +> We need to select numbers based on different criteria from the list. It's a great chance to +> utilize fluent interface pattern to provide readable easy-to-use developer experience. In plain words @@ -33,7 +35,9 @@ In plain words Wikipedia says -> In software engineering, a fluent interface is an object-oriented API whose design relies extensively on method chaining. Its goal is to increase code legibility by creating a domain-specific language (DSL). +> In software engineering, a fluent interface is an object-oriented API whose design relies +> extensively on method chaining. Its goal is to increase code legibility by creating a +> domain-specific language (DSL). **Programmatic Example** @@ -134,29 +138,35 @@ result is printed afterwards. .first(2) .last() .ifPresent(number -> LOGGER.info("Last amongst first two negatives: {}", number)); - - // The initial list contains: 1, -61, 14, -22, 18, -87, 6, 64, -82, 26, -98, 97, 45, 23, 2, -68. - // The first three negative values are: -61, -22, -87. - // The last two positive values are: 23, 2. - // The first even number is: 14 - // A string-mapped list of negative numbers contains: String[-61], String[-22], String[-87], String[-82], String[-98], String[-68]. - // The lazy list contains the last two of the first four positive numbers mapped to Strings: String[18], String[6]. - // Last amongst first two negatives: -22 +``` + +Program output: + +```java +The initial list contains: 1, -61, 14, -22, 18, -87, 6, 64, -82, 26, -98, 97, 45, 23, 2, -68. +The first three negative values are: -61, -22, -87. +The last two positive values are: 23, 2. +The first even number is: 14 +A string-mapped list of negative numbers contains: String[-61], String[-22], String[-87], String[-82], String[-98], String[-68]. +The lazy list contains the last two of the first four positive numbers mapped to Strings: String[18], String[6]. +Last amongst first two negatives: -22 ``` ## Class diagram + ![Fluent Interface](./etc/fluentinterface.png "Fluent Interface") ## 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 +* You provide an API that would benefit from a DSL-like usage. +* You have objects that are difficult to configure or use. ## Known uses * [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) +* [Google Guava FluentIterable](https://github.com/google/guava/wiki/FunctionalExplained) * [JOOQ](http://www.jooq.org/doc/3.0/manual/getting-started/use-cases/jooq-as-a-standalone-sql-builder/) * [Mockito](http://mockito.org/) * [Java Hamcrest](http://code.google.com/p/hamcrest/wiki/Tutorial) diff --git a/fluentinterface/pom.xml b/fluentinterface/pom.xml index 9eb063c13..8f5e8e175 100644 --- a/fluentinterface/pom.xml +++ b/fluentinterface/pom.xml @@ -2,7 +2,7 @@ + +# Les patrons de conception implémentés en Java + +![Java CI with Maven](https://github.com/iluwatar/java-design-patterns/workflows/Java%20CI%20with%20Maven/badge.svg) +[![License MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/LICENSE.md) +[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=iluwatar_java-design-patterns&metric=ncloc)](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns) +[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=iluwatar_java-design-patterns&metric=coverage)](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns) +[![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) + +[![All Contributors](https://img.shields.io/badge/all_contributors-148-orange.svg?style=flat-square)](#contributors-) + + +# Introduction + +Le patron de conception est la meilleure formatalisation qu'un programmeur +peut utiliser pour résoudre un problème lors d'une conception d'une application/system. + +Le patron de conception (design pattern) est là pour accélérer le processus de +développement en fournissant des paradigmes éprouvés. + +La réutilisation de patron de conception aide à prévenir des problèmes subtiles mais +qui sont à l'origine de problèmes majeures, comme cette pratique augmente la lisibilitée +du code par les développeurs/architectes familiers avec l'utilisation de ces concepts. + +# Commencer + +Ce site présente des modèles de conception Java. Les solutions ont été développées par des +développeurs et architectes expérimentés de la communauté open source. Les modèles peuvent être parcourus par leurs descriptions de haut niveau ou en regardant leur code source. Les exemples de code source sont bien commentés et peuvent être considérés comme tutoriels de programmation sur la façon d'implémenter un modèle spécifique. Nous utilisons le plus technologies Java open source éprouvées au combat. + +Avant de plonger dans le matériau, vous devez vous familiariser avec divers +[Principes de conception de logiciels](https://java-design-patterns.com/principles/). + +Toutes les conceptions doivent être aussi simples que possible. Vous devriez commencer par KISS, YAGNI, +et faire la chose la plus simple qui pourrait éventuellement fonctionner principes. Complexité et +les modèles ne devraient être introduits que lorsqu'ils sont nécessaires pour +extensibilité. + +Une fois que vous êtes familiarisé avec ces concepts, vous pouvez commencer à explorer +[modèles de conception disponibles](https://java-design-patterns.com/patterns/) par tout +des approches suivantes + + - Recherchez un modèle spécifique par son nom. Vous n'en trouvez pas? Veuillez signaler un nouveau modèle [ici](https://github.com/iluwatar/java-design-patterns/issues). + - Utilisation de balises telles que `Performance`, `Gang of Four` ou `Data access`. + - Utilisation des catégories de modèles, `Créatif`, `Comportemental` et autres. + +J'espère que les solutions orientées objet présentées sur ce site vous seront utiles +dans vos architectures et ayez autant de plaisir à les apprendre que nous en avons eu à les développer. + +# Comment contribuer + +Si vous souhaitez contribuer au projet, vous trouverez les informations pertinentes dans +notre [wiki développeur](https://github.com/iluwatar/java-design-patterns/wiki). Nous aiderons +vous et répondez à vos questions dans le [Gitter chatroom](https://gitter.im/iluwatar/java-design-patterns). + +# Licence + +Ce projet est concédé sous les termes de la licence MIT. diff --git a/front-controller/pom.xml b/front-controller/pom.xml index 34dabc182..55009eeb7 100644 --- a/front-controller/pom.xml +++ b/front-controller/pom.xml @@ -2,7 +2,7 @@ + +# 자바로 구현된 디자인 패턴 + +![Java CI with Maven](https://github.com/iluwatar/java-design-patterns/workflows/Java%20CI%20with%20Maven/badge.svg)[](https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/LICENSE.md)![License MIT](https://img.shields.io/badge/license-MIT-blue.svg)[](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns)![Join the chat at https://gitter.im/iluwatar/java-design-patterns](https://badges.gitter.im/Join%20Chat.svg) + + + +[](#contributors-)![All Contributors](https://img.shields.io/badge/all_contributors-133-orange.svg?style=flat-square) + + + +# 소개 + +디자인 패턴은 프로그래머가 응용 프로그램이나 시스템을 디자인 할 때 일반적인 문제를 해결하는 데 사용할 수있는 가장 공식화 된 방법입니다. + +디자인 패턴은 테스트되고 입증 된 개발 패러다임을 제공하여 개발 프로세스 속도를 높일 수 있습니다. + +디자인 패턴을 재사용하면 주요 문제를 유발하는 미묘한 이슈들을 방지하는데 도움이 되며 또한 패턴에 익숙한 코더와 아키텍트의 코드 가독성도 향상됩니다. + +# 시작하기 + +이 사이트는 Java 디자인 패턴을 보여줍니다. 이 솔루션은 오픈 소스 커뮤니티의 경험이 많은 프로그래머와 설계자가 개발했습니다. 패턴은 높은 수준의 설명이나 소스 코드를 통해 찾아 볼 수 있습니다. 소스 코드 예제는 잘 설명되어 있으며 특정 패턴을 구현하는 방법을 알려주는 프로그래밍 튜토리얼로 생각할 수 있습니다. 우리는 가장 널리 알려지고 실무에서 입증된 오픈 소스 Java 기술을 사용합니다. + +자료를 살펴보기 전에 다양한 [소프트웨어 설계 원칙](https://java-design-patterns.com/principles/)을 숙지해야합니다. + +모든 디자인은 가능한 한 단순해야합니다. 당신은 KISS, YAGNI로 시작해야하며, 원칙을 작동 할 수 있는 가장 단순한 일을 해야합니다. 복잡성과 패턴은 실용적인 확장성을 위해 필요할 때만 도입되어야합니다. + +이러한 개념에 익숙해지면 다음 접근 방식 중 하나를 이용하여 [사용 가능한 디자인 패턴](https://java-design-patterns.com/patterns/)으로 드릴다운 할 수 있습니다. + +- 이름으로 특정 패턴을 검색합니다. 찾을 수 없습니까? [여기](https://github.com/iluwatar/java-design-patterns/issues)에서 새 패턴을 보고하십시오. +- `Performance`, `Gang of Four` 또는 `Data access` 태그 사용. +- 패턴 카테고리, `Creational`, `Behavioral` 및 기타 사용 + +이 사이트에 제시된 객체 지향 솔루션이 여러분의 아키텍처에서 유용하고 우리가 개발 한 것만큼 재미있게 배우기를 바랍니다. + +# 기여하는 방법 + +프로젝트에 기여할 의향이 있다면 [developer wiki](https://github.com/iluwatar/java-design-patterns/wiki)에서 관련 정보를 찾을 수 있습니다. [Gitter chatroom](https://gitter.im/iluwatar/java-design-patterns)에서 귀하를 돕고 질문에 답변 해 드리겠습니다. + +# 특허 + +이 프로젝트는 MIT 라이센스 조건에 따라 라이센스가 부여됩니다. + +# 기여자 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Ilkka Seppälä

amit1307

Narendra Pathai

Jeroen Meulemeester

Joseph McCarthy

Thomas

Anurag Agarwal

Markus Moser

Sabiq Ihab

Amit Dixit

Piyush Kailash Chaudhari

joshzambales

Kamil Pietruszka

Zafar Khaydarov

Paul Campbell

Argyro Sioziou

TylerMcConville

saksham93

nikhilbarar

Colin But

Ruslan

Juho Kang

Dheeraj Mummareddy

Bernardo Sulzbach

Aleksandar Dudukovic

Yusuf Aytaş

Mihály Kuprivecz

Stanislav Kapinus

GVSharma

Srđan Paunović

Petros G. Sideris

Pramod Gupta

Amarnath Chandana

Anurag870

Wes Gilleland

Harshraj Thakor

Martin Vandenbussche

Alexandru Somai

Artur Mogozov

anthony

Christian Cygnus

Dima Gubin

Joshua Jimenez

Kai Winter

lbroman

Przemek

Prafful Agarwal

Sanket Panhale

staillebois

Krisztián Nagy

Alexander Ivanov

Yosfik Alqadri

Agustí Becerra Milà

Juan Manuel Suárez

Luigi Cortese

Katarzyna Rzepecka

adamski.pro

Shengli Bai

Boris

Dmitry Avershin

靳阳

hoangnam2261

Arpit Jain

Jón Ingi Sveinbjörnsson

Kirill Vlasov

Mitchell Irvin

Ranjeet

PhoenixYip

M Saif Asif

kanwarpreet25

Leon Mak

Per Wramdemark

Evan Sia Wai Suan

AnaghaSasikumar

Christoffer Hamberg

Dominik Gruntz

Hannes

Leo Gutiérrez Ramírez

Zhang WH

Christopher O'Connell

George Mavroeidis

Hemant Bothra

Kevin Peters

George Aristy

Mahendran Mookkiah

Azureyjt

gans

Matt

Gopinath Langote

Hoswey

Amit Pandey

gwildor28

田浩

Stamatis Pitsios

qza

Rodolfo Forte

Ankur Kaushal

Ovidijus Okinskas

Robert Kasperczyk

Tapio Rautonen

Yuri Orlov

Varun Upadhyay

Aditya Pal

grzesiekkedzior

Sivasubramani M

Sami Airaksinen

Janne Sinivirta

Boris-Chengbiao Zhou

Jacob Hein

Richard Jones

Rachel M. Carmena

Zaerald Denze Lungos

Lars Kappert

Mike Liu

Matt Dolan

Manan

Nishant Arora

Peeyush

Rakesh

Wei Seng

Ashish Trivedi

洪月阳

xdvrx1

Subhrodip Mohanta

Bethan Palmer

Toxic Dreamz

Edy Cu Tjong

Michał Krzywański

Stefan Birkner

Fedor Skvorcov

samilAyoub

Vladislav Golubinov

Swaraj
+ + + + + + diff --git a/ko/adapter/README.md b/ko/adapter/README.md new file mode 100644 index 000000000..8ef291972 --- /dev/null +++ b/ko/adapter/README.md @@ -0,0 +1,134 @@ +--- +layout: pattern +title: Adapter +folder: adapter +permalink: "/patterns/adapter/" +categories: Structural +tags: +- Gang of Four +--- + +## 또한 ~으로 알려진 + +Wrapper + +## 의도 + +클래스의 인터페이스를 클라이언트가 기대하는 다른 인터페이스로 변환합니다. adapter를 사용하면 호환되지 않는 인터페이스로 인해 같이 쓸 수 없는 클래스를 함께 작동 할 수 있습니다. + +## 설명 + +예시 + +> 메모리 카드에 몇 장의 사진이 있고 컴퓨터로 전송해야한다고 생각하십시오. 이들을 전송하려면 컴퓨터에 메모리 카드를 연결할 수 있도록 컴퓨터 포트와 호환되는 어댑터가 필요합니다. 이 경우 카드 리더는 어댑터입니다. 또 다른 예는 유명한 전원 어댑터입니다. 세 갈래 플러그는 두 갈래 콘센트에 연결할 수 없습니다. 두 갈래 콘센트와 호환되는 전원 어댑터를 사용해야합니다. 또 다른 예는 한 사람이 말한 단어를 다른 사람에게 번역하는 번역가입니다. + +평범하게 말하자면 + +> adapter 패턴을 사용하면 호환되지 않는 개체를 adapter에 연결하여 다른 클래스와 호환되도록 할 수 있습니다. + +Wikipedia 말에 의하면 + +> 소프트웨어 엔지니어링에서 adapter 패턴은 기존 클래스의 인터페이스를 다른 인터페이스로 사용할 수 있도록 하는 소프트웨어 디자인 패턴입니다. 소스 코드를 수정하지 않고 기존 클래스가 다른 클래스와 함께 작동하도록 만드는 데 자주 사용됩니다. + +**프로그램 코드 예제** + +조정 보트만 사용할 수 있고 전혀 항해할 수 없는 선장을 생각해보십시오. + +먼저 `RowingBoat` 및 `FishingBoat` 인터페이스가 있습니다. + +```java +public interface RowingBoat { + void row(); +} + +public class FishingBoat { + private static final Logger LOGGER = LoggerFactory.getLogger(FishingBoat.class); + public void sail() { + LOGGER.info("The fishing boat is sailing"); + } +} +``` + +그리고 선장은 `RowingBoat` 인터페이스를 이동할 수 있게 구현했습니다. + +```java +public class Captain { + + private final RowingBoat rowingBoat; + // default constructor and setter for rowingBoat + public Captain(RowingBoat rowingBoat) { + this.rowingBoat = rowingBoat; + } + + public void row() { + rowingBoat.row(); + } +} +``` + +이제 해적이오고 있고 우리 선장이 탈출해야하는데 어선만 있습니다. 선장이 조정 보트 기술로 어선을 조작 할 수있는 adapter를 만들어야합니다. + +```java +public class FishingBoatAdapter implements RowingBoat { + + private static final Logger LOGGER = LoggerFactory.getLogger(FishingBoatAdapter.class); + + private final FishingBoat boat; + + public FishingBoatAdapter() { + boat = new FishingBoat(); + } + + @Override + public void row() { + boat.sail(); + } +} +``` + +이제 `Captain` 은 `FishingBoat` 를 사용하여 해적을 탈출 할 수 있습니다. + +```java +var captain = new Captain(new FishingBoatAdapter()); +captain.row(); +``` + +## 클레스 다이어그램 + +![alt text](https://github.com/warp125/java-design-patterns/blob/master/adapter/etc/adapter.urm.png?raw=true) + +## 적용 가능성 + +다음과 같은 경우 adapter 패턴을 사용합니다. + +- 기존 클래스를 사용 하려는데 해당 인터페이스가 필요한 클래스와 일치하지 않습니다. +- 관련이 없거나 예상치 못한 클래스, 즉 호환되는 인터페이스가 반드시 필요하지 않은 클래스와 협력하는 재사용 가능한 클래스를 만들고 싶습니다. +- 기존의 여러 하위 클래스를 사용해야하지만 모든 하위 클래스를 하위 클래스로 지정하여 인터페이스를 조정하는 것은 비현실적입니다. 개체 adapter는 부모 클래스의 인터페이스를 조정할 수 있습니다. +- 타사 라이브러리를 사용하는 대부분의 응용 프로그램은 adapter를 응용 프로그램과 타사 라이브러리 사이의 중간 계층으로 사용하여 라이브러리에서 응용 프로그램을 분리합니다. 다른 라이브러리를 사용해야하는 경우 애플리케이션 코드를 변경할 필요없이 새 라이브러리 용 adapter만 필요합니다. + +## 결과 : + +클래스 및 개체 adapter에는 서로 다른 장단점이 있습니다.
클래스 adapter + +- 구체적인 Adaptee 클래스를 커밋하여 Adaptee를 Target에 적용합니다. 결과적으로 클래스와 모든 하위 클래스를 조정하려는 경우 클래스 adapter가 작동하지 않습니다. +- adapter는 Adaptee의 하위 클래스이기 때문에 Adaptee의 일부 동작을 오버라이드합니다. +- 하나의 객체만 생성하고 adaptee를 얻기위해 위해 추가 포인터 간접 지정이 필요하지 않습니다. + +개체 adapter + +- 하나의 adapter가 많은 Adaptees, 즉 Adaptee 자체와 모든 하위 클래스 (있는 경우)와 함께 작동하도록합시다. adapter는 한 번에 모든 어댑터에 기능을 추가 할 수도 있습니다. +- Adaptee 동작을 오버라이드하기가 더 어렵습니다. Adaptee를 서브 클래싱하고 어댑터가 Adaptee 자체가 아닌 서브 클래스를 참조하도록 해야합니다. + +## 실제 사례 + +- [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29) +- [java.util.Collections#list()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#list-java.util.Enumeration-) +- [java.util.Collections#enumeration()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#enumeration-java.util.Collection-) +- [javax.xml.bind.annotation.adapters.XMLAdapter](http://docs.oracle.com/javase/8/docs/api/javax/xml/bind/annotation/adapters/XmlAdapter.html#marshal-BoundType-) + +## 크레딧 + +- [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +- [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=48d37c67fb3d845b802fa9b619ad8f31) +- [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) +- [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7) diff --git a/ko/factory/README.md b/ko/factory/README.md new file mode 100644 index 000000000..d020a9edd --- /dev/null +++ b/ko/factory/README.md @@ -0,0 +1,128 @@ +--- +layout: pattern +title: Factory +folder: factory +permalink: "/patterns/factory/" +categories: Creational +tags: +- Gang of Four +--- + +## 또한 ~으로 알려진 + +- Simple Factory +- Static Factory Method + +## 의도 + +구현 논리를 숨기고 클라이언트 코드가 새 객체를 초기화하는 대신 사용에 집중하도록하기 위해 factory라는 클래스에 캡슐화 된 정적 메서드를 제공합니다. + +## 설명 + +예시 + +> SQLServer에 연결된 웹 응용 프로그램이 있지만 이제 Oracle로 전환하려고 합니다. 기존 소스 코드를 수정하지 않고 이를 수행하려면 주어진 데이터베이스에 대한 연결을 생성하기 위해 정적 메서드를 호출 할 수 있는 Simple Factory 패턴을 구현해야합니다. + +Wikipedia 말에 의하면 + +> factory는 다른 객체를 생성하기위한 객체입니다. 공식적으로 factory는 다양한 프로토 타입 또는 클래스의 객체를 반환하는 함수 또는 메서드입니다. + +**프로그램 코드 예제** + +우리는 인터페이스 `Car` 와 두 가지 구현 `Ford` 와 `Ferrari`을 가지고 있습니다. + +```java +public interface Car { + String getDescription(); +} + +public class Ford implements Car { + + static final String DESCRIPTION = "This is Ford."; + + @Override + public String getDescription() { + return DESCRIPTION; + } +} + +public class Ferrari implements Car { + + static final String DESCRIPTION = "This is Ferrari."; + + @Override + public String getDescription() { + return DESCRIPTION; + } +} +``` + +열거형은 우리가 지원하는 자동차 유형을 나타냅니다 ( `Ford` 및 `Ferrari` ). + +```java +public enum CarType { + + FORD(Ford::new), + FERRARI(Ferrari::new); + + private final Supplier constructor; + + CarType(Supplier constructor) { + this.constructor = constructor; + } + + public Supplier getConstructor() { + return this.constructor; + } +} +``` + +그런 다음 factory 클래스 `CarsFactory` 캡슐화 된 자동차 객체를 만드는 정적 메서드 `getCar` 가 있습니다. + +```java +public class CarsFactory { + + public static Car getCar(CarType type) { + return type.getConstructor().get(); + } +} +``` + +이제 클라이언트 코드에서 factory 클래스를 사용하여 다양한 유형의 자동차를 만들 수 있습니다. + +```java +var car1 = CarsFactory.getCar(CarType.FORD); +var car2 = CarsFactory.getCar(CarType.FERRARI); +LOGGER.info(car1.getDescription()); +LOGGER.info(car2.getDescription());; +``` + +프로그램 출력 : + +```java +This is Ford. +This Ferrari. +``` + +## 클래스 다이어그램 + +![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/factory/etc/factory.urm.png) + +## 적용 가능성 + +객체 생성 및 관리 방법이 아닌 객체 생성에만 관심이있을 때 Simple Factory 패턴을 사용합니다. + +장점 + +- 모든 객체 생성을 한곳에 유지하고 코드베이스에 '새'키 값이 확산되는 것을 방지합니다. +- 느슨하게 결합 된 코드를 작성할 수 있습니다. 주요 장점 중 일부는 더 나은 테스트 가능성, 이해하기 쉬운 코드, 교체 가능한 구성 요소, 확장성 및 격리된 기능을 포함합니다. + +단점 + +- 코드는 생각보다 복잡해집니다. + +## 관련 패턴 + +- [Factory Method](https://java-design-patterns.com/patterns/factory-method/) +- [Factory Kit](https://java-design-patterns.com/patterns/factory-kit/) +- [Abstract Factory](https://java-design-patterns.com/patterns/abstract-factory/) diff --git a/ko/observer/README.md b/ko/observer/README.md new file mode 100644 index 000000000..7f27e3fbf --- /dev/null +++ b/ko/observer/README.md @@ -0,0 +1,165 @@ +--- +layout: pattern +title: Observer +folder: observer +permalink: "/patterns/observer/" +categories: Behavioral +tags: +- Gang Of Four +- Reactive +--- + +## 또한 ~으로 알려진 + +Dependents, Publish-Subscribe + +## 의도 + +하나의 개체가 상태를 변경하면 모든 종속 항목에 알림이 전송되고 자동으로 업데이트 되도록 개체간의 일대 다 종속성을 정의합니다. + +## 설명 + +예시 + +> 멀리 떨어진 땅에는 호빗과 오크 종족이 살고 있습니다. 둘 다 대부분 야외에 있으므로 날씨 변화를 면밀히 따릅니다. 끊임없이 날씨를 관찰하고 있다고 말할 수 있습니다. + +평범하게 말하자면 + +> observer로 등록하여 개체의 상태 변경을 수신합니다. + +Wikipedia 말에 의하면 + +> observer 패턴은 주제라고하는 객체가 observer라고 하는 종속 항목 목록을 유지하고 일반적으로 메서드 중 하나를 호출하여 상태 변경을 자동으로 알리는 소프트웨어 디자인 패턴입니다. + +**프로그램 코드 예제** + +먼저 `WeatherObserver` 인터페이스와 우리의 종족 `Orcs` 와 `Hobbits` 소개하겠습니다. + +```java +public interface WeatherObserver { + + void update(WeatherType currentWeather); +} + +public class Orcs implements WeatherObserver { + + private static final Logger LOGGER = LoggerFactory.getLogger(Orcs.class); + + @Override + public void update(WeatherType currentWeather) { + LOGGER.info("The orcs are facing " + currentWeather.getDescription() + " weather now"); + } +} + +public class Hobbits implements WeatherObserver { + + private static final Logger LOGGER = LoggerFactory.getLogger(Hobbits.class); + + @Override + public void update(WeatherType currentWeather) { + switch (currentWeather) { + LOGGER.info("The hobbits are facing " + currentWeather.getDescription() + " weather now"); + } + } +} +``` + +그리고 끊임없이 변화하는 `Weather` 가 있습니다. + +```java +public class Weather { + + private static final Logger LOGGER = LoggerFactory.getLogger(Weather.class); + + private WeatherType currentWeather; + private final List observers; + + public Weather() { + observers = new ArrayList<>(); + currentWeather = WeatherType.SUNNY; + } + + public void addObserver(WeatherObserver obs) { + observers.add(obs); + } + + public void removeObserver(WeatherObserver obs) { + observers.remove(obs); + } + + /** + * Makes time pass for weather. + */ + public void timePasses() { + var enumValues = WeatherType.values(); + currentWeather = enumValues[(currentWeather.ordinal() + 1) % enumValues.length]; + LOGGER.info("The weather changed to {}.", currentWeather); + notifyObservers(); + } + + private void notifyObservers() { + for (var obs : observers) { + obs.update(currentWeather); + } + } +} +``` + +여기에 전체 예제가 있습니다. + +```java + var weather = new Weather(); + weather.addObserver(new Orcs()); + weather.addObserver(new Hobbits()); + weather.timePasses(); + weather.timePasses(); + weather.timePasses(); + weather.timePasses(); +``` + +프로그램 출력 : + +``` +The weather changed to rainy. +The orcs are facing rainy weather now +The hobbits are facing rainy weather now +The weather changed to windy. +The orcs are facing windy weather now +The hobbits are facing windy weather now +The weather changed to cold. +The orcs are facing cold weather now +The hobbits are facing cold weather now +The weather changed to sunny. +The orcs are facing sunny weather now +The hobbits are facing sunny weather now +``` + +## 클래스 다이어그램 + +![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/observer/etc/observer.png) + +## 적용 가능성 + +다음 상황에서 관찰자 패턴을 사용하십시오. + +- 추상화에 두 가지 측면이 있을 때 하나는 다른 하나에 종속됩니다. 이러한 측면을 별도의 개체에 캡슐화하면 독립적으로 변경하고 재사용 할 수 있습니다. +- 한 개체를 변경하려면 얼마나 다른 개체를 변경해야 하는지 알 수 없는 경우입니다. +- 개체가 다른 개체가 누구인지 가정하지 않고 알릴 수 있어야하는 경우. 즉, 개체가 단단히 결합되는 것을 원하지 않습니다. + +## 일반적인 사용 사례 + +- 한 개체를 변경하면 다른 개체도 변경됩니다. + +## 실제 사례 + +- [java.util.Observer](http://docs.oracle.com/javase/8/docs/api/java/util/Observer.html) +- [java.util.EventListener](http://docs.oracle.com/javase/8/docs/api/java/util/EventListener.html) +- [javax.servlet.http.HttpSessionBindingListener](http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpSessionBindingListener.html) +- [RxJava](https://github.com/ReactiveX/RxJava) + +## 크레딧 + +- [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +- [Java Generics and Collections](https://www.amazon.com/gp/product/0596527756/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596527756&linkCode=as2&tag=javadesignpat-20&linkId=246e5e2c26fe1c3ada6a70b15afcb195) +- [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) +- [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7) diff --git a/ko/prototype/README.md b/ko/prototype/README.md new file mode 100644 index 000000000..d15e6daaf --- /dev/null +++ b/ko/prototype/README.md @@ -0,0 +1,87 @@ +--- +layout: pattern +title: Prototype +folder: prototype +permalink: "/patterns/prototype/" +categories: Creational +tags: +- Gang Of Four +- Instantiation +--- + +## 의도 + +prototype 인스턴스를 사용하여 만들 개체의 종류를 지정하고 prototype을 복사하여 새 개체를 만듭니다. + +## 설명 + +먼저 성능 이점을 얻기 위해 prototype 패턴이 사용되지 않는다는 점에 유의해야합니다. 오직 prototype 인스턴스에서 새 개체를 만드는 데만 사용됩니다. + +예시 + +> 복제된 양인 돌리에 대해 기억나십니까? 자세한 내용은 다루지 않겠습니다만 여기서 핵심은 복제에 관한 것입니다. + +평범한 말하자면 + +> 복제를 통해 기존 개체를 기반으로 개체를 만듭니다. + +Wikipedia 말에 의하면 + +> prototype 패턴은 소프트웨어 개발에서 생성 디자인 패턴입니다. 생성 할 개체의 유형이 새 개체를 생성하기 위해 복제되는 prototype 인스턴스에 의해 결정될 때 사용됩니다. + +간단히 말해, 객체를 처음부터 만들고 설정하는 문제를 겪는 대신 기존 객체의 복사본을 만들어 필요에 맞게 수정할 수 있습니다. + +**프로그램 코드 예제** + +Java에서는 `Cloneable` 을 구현하고 `Object` 에서 `clone` 을 오버라이딩하여 쉽게 수행 할 수 있습니다. + +```java +class Sheep implements Cloneable { + private String name; + public Sheep(String name) { this.name = name; } + public void setName(String name) { this.name = name; } + public String getName() { return name; } + @Override + public Sheep clone() { + try { + return (Sheep)super.clone(); + } catch(CloneNotSuportedException) { + throw new InternalError(); + } + } +} +``` + +그런 다음 아래와 같이 복제할 수 있습니다. + +```java +var original = new Sheep("Jolly"); +System.out.println(original.getName()); // Jolly + +// Clone and modify what is required +var cloned = original.clone(); +cloned.setName("Dolly"); +System.out.println(cloned.getName()); // Dolly +``` + +## 클래스 다이어그램 + +![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/prototype/etc/prototype.urm.png) + +## 적용 가능성 + +시스템이 제품의 생성, 구성, 표현 및 표현 방식에 독립적이어야 할 때 prototype 패턴을 사용 + +- 인스턴스화할 클래스가 런타임에 지정되는 경우 (예 : 동적 로딩) +- 제품의 클래스 계층 구조와 유사한 팩토리의 클래스 계층 구조 구축을 방지해야할 경우 +- 클래스의 인스턴스가 몇 가지 다른 상태 조합 중 하나만 가질 수 있는 경우. 적절한 상태로 매번 클래스를 수동으로 인스턴스화하는 것보다 해당하는 수의 prototype을 설치하고 복제하는 것이 더 편리 할 수 있습니다. +- 복제에 비해 개체 생성 비용이 많이 드는 경우. + +## 실제 사례 + +- [java.lang.Object#clone()](https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#clone%28%29) + +## 크레딧 + +- [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +- [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) diff --git a/ko/singleton/README.md b/ko/singleton/README.md new file mode 100644 index 000000000..8875e56eb --- /dev/null +++ b/ko/singleton/README.md @@ -0,0 +1,84 @@ +--- +layout: pattern +title: Singleton +folder: singleton +permalink: "/patterns/singleton/" +categories: Creational +tags: +- Gang of Four +--- + +## 의도 + +클래스에 인스턴스가 하나만 있는지 확인하고 이에 대한 전역 access point을 제공합니다. + +## 설명 + +예시 + +> 마법사들이 마법을 연구하는 상아탑은 단 하나뿐입니다. 마법사는 항상 동일한 마법의 상아탑을 사용합니다. 여기서 상아탑은 singleton입니다. + +평범하게 말하자면 + +> 특정 클래스의 개체가 하나만 생성되도록합니다. + +Wikipedia 말에 의하면 + +> 소프트웨어 엔지니어링에서 singleton 패턴은 클래스의 인스턴스화를 하나의 객체로 제한하는 소프트웨어 디자인 패턴입니다. 이는 시스템 전체에서 작업을 조정하는 데 정확히 하나의 개체가 필요할 때 유용합니다. + +**프로그램 코드 예제** + +Joshua Bloch, Effective Java 2nd Edition p.18 + +> 단일 요소 열거형은 singleton을 구현하는 가장 좋은 방법입니다. + +```java +public enum EnumIvoryTower { + INSTANCE +} +``` + +그런 다음 사용하려면 : + +```java +var enumIvoryTower1 = EnumIvoryTower.INSTANCE; +var enumIvoryTower2 = EnumIvoryTower.INSTANCE; +assertEquals(enumIvoryTower1, enumIvoryTower2); // true +``` + +## 클래스 다이어그램 + +![alt text](https://github.com/iluwatar/java-design-patterns/blob/master/singleton/etc/singleton.urm.png) + +## 적용 가능성 + +다음과 같은 경우 Singleton 패턴을 사용합니다. + +- 정확히 하나의 클래스 인스턴스가 있어야하며 잘 알려진 access point에서 클라이언트에 접근할 수 있어야합니다. +- 단일 인스턴스가 서브 클래싱으로 확장 가능해야하고 클라이언트가 코드를 수정하지 않고 확장 인스턴스를 사용할 수 있어야 하는 경우 + +## 일반적인 사용 사례 + +- 로깅 클래스 +- 데이터베이스에 대한 연결 관리 +- 파일 관리자 + +## 실제 사례 + +- [java.lang.Runtime#getRuntime()](http://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#getRuntime%28%29) +- [java.awt.Desktop#getDesktop()](http://docs.oracle.com/javase/8/docs/api/java/awt/Desktop.html#getDesktop--) +- [java.lang.System#getSecurityManager()](http://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getSecurityManager--) + +## 결과 + +- 자체 생성 및 수명주기를 제어하여 SRP (Single Responsibility Principle)를 위반합니다. +- 이 개체가 사용하는 개체와 리소스가 할당 해제되는 것을 방지하는 전역 공유 인스턴스를 사용하도록 권장합니다. +- 밀접하게 연결된 코드를 만듭니다. Singleton의 클라이언트는 테스트하기가 어려워집니다. +- Singleton의 하위 클래스를 만드는 것이 거의 불가능합니다. + +## 크레딧 + +- [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59) +- [Effective Java](https://www.amazon.com/gp/product/0134685997/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0134685997&linkCode=as2&tag=javadesignpat-20&linkId=4e349f4b3ff8c50123f8147c828e53eb) +- [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b) +- [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7) diff --git a/layers/README.md b/layers/README.md index 1e309f92b..1c8a695a0 100644 --- a/layers/README.md +++ b/layers/README.md @@ -10,26 +10,33 @@ tags: --- ## Intent -Layers is an architectural pattern where software responsibilities are - divided among the different layers of the application. + +Layers is an architectural pattern where software responsibilities are divided among the different +layers of the application. ## Explanation Real world example -> Consider a web site displaying decorated cakes for weddings and such. Instead of the web page directly reaching into the database, it relies on a service to deliver this information. The service then queries the data layer to assimilate the needed information. +> Consider a web site displaying decorated cakes for weddings and such. Instead of the web page +> directly reaching into the database, it relies on a service to deliver this information. The +> service then queries the data layer to assimilate the needed information. -In Plain Words +In plain words -> With Layers architectural pattern different concerns reside on separate layers. View layer is interested only in rendering, service layer assembles the requested data from various sources, and data layer gets the bits from the data storage. +> With Layers architectural pattern different concerns reside on separate layers. View layer is +> interested only in rendering, service layer assembles the requested data from various sources, and +> data layer gets the bits from the data storage. Wikipedia says -> In software engineering, multitier architecture (often referred to as n-tier architecture) or multilayered architecture is a client–server architecture in which presentation, application processing, and data management functions are physically separated. +> In software engineering, multitier architecture (often referred to as n-tier architecture) or +> multilayered architecture is a client–server architecture in which presentation, application +> processing, and data management functions are physically separated. **Programmatic Example** -On the data layer, we keep our cake building blocks. Cakes consist of layers and topping. +On the data layer, we keep our cake building blocks. `Cake` consist of layers and topping. ```java @Entity @@ -47,7 +54,7 @@ public class Cake { } ``` -The service layer offers CakeBakingService for easy access to different aspects of cakes. +The service layer offers `CakeBakingService` for easy access to different aspects of cakes. ```java public interface CakeBakingService { @@ -66,7 +73,7 @@ public interface CakeBakingService { } ``` -On the top we have our view responsible of rendering the cakes. +On the top we have our `View` responsible of rendering the cakes. ```java public interface View { @@ -75,10 +82,9 @@ public interface View { } +@Slf4j public class CakeViewImpl implements View { - private static final Logger LOGGER = LoggerFactory.getLogger(CakeViewImpl.class); - private final CakeBakingService cakeBakingService; public CakeViewImpl(CakeBakingService cakeBakingService) { @@ -92,14 +98,16 @@ public class CakeViewImpl implements View { ``` ## Class diagram + ![alt text](./etc/layers.png "Layers") ## Applicability + Use the Layers architecture when -* you want clearly divide software responsibilities into different 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 +* You want clearly divide software responsibilities into different 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 diff --git a/layers/pom.xml b/layers/pom.xml index 2ebace18b..05392f78e 100644 --- a/layers/pom.xml +++ b/layers/pom.xml @@ -2,7 +2,7 @@ "-bookService" BookService +BookServiceImpl --> "-designPatternBooks" Book +BookViewModel --> "-bookList" Book +BookViewModel --> "-selectedBook" Book +BookServiceImpl ..|> BookService +@enduml \ No newline at end of file diff --git a/model-view-viewmodel/lombok.config b/model-view-viewmodel/lombok.config new file mode 100644 index 000000000..a23edb413 --- /dev/null +++ b/model-view-viewmodel/lombok.config @@ -0,0 +1,2 @@ +config.stopBubbling = true +lombok.addLombokGeneratedAnnotation = true \ No newline at end of file diff --git a/model-view-viewmodel/pom.xml b/model-view-viewmodel/pom.xml new file mode 100644 index 000000000..971ae59f7 --- /dev/null +++ b/model-view-viewmodel/pom.xml @@ -0,0 +1,115 @@ + + + 4.0.0 + + java-design-patterns + com.iluwatar + 1.24.0-SNAPSHOT + + com.iluwatar + model-view-viewmodel + 1.24.0-SNAPSHOT + + 9.0.0 + 19.0 + 9.4.28.v20200408 + 2.1.1 + 2.2 + yyyy-MM-dd + -${project.version}-FL-${maven.build.timestamp} + + war + model-view-viewmodel + model-view-viewmodel + + + GNU LESSER GENERAL PUBLIC LICENSE, Version 3 + https://www.gnu.org/licenses/lgpl.html + repo + + + + + ZK CE + ZK CE Repository + https://mavensync.zkoss.org/maven2 + + + ZK EVAL + ZK Evaluation Repository + https://mavensync.zkoss.org/eval + + + + + zkmaven + ZK Maven Plugin Repository + https://mavensync.zkoss.org/maven2/ + + + + + org.zkoss.zk + zkbind + ${zk.version} + + + org.junit.jupiter + junit-jupiter-engine + test + + + com.google.guava + guava-testlib + ${guava.version} + test + + + + ${project.artifactId} + + + + org.eclipse.jetty + jetty-maven-plugin + ${jetty-maven-plugin.version} + + + /${project.artifactId} + true + + 5 + + + + + maven-war-plugin + org.apache.maven.plugins + ${maven-war-plugin.version} + + + + maven-assembly-plugin + ${maven-assembly-plugin.version} + + + webapp + package + + single + + + model-view-viewmodel${packname} + false + + src/main/assembly/webapp.xml + + + + + + + + diff --git a/model-view-viewmodel/src/main/assembly/webapp.xml b/model-view-viewmodel/src/main/assembly/webapp.xml new file mode 100644 index 000000000..153904efc --- /dev/null +++ b/model-view-viewmodel/src/main/assembly/webapp.xml @@ -0,0 +1,25 @@ + + webapp + + zip + + + + ${project.basedir}/src/main/java + /${project.artifactId}/src + + + ${project.basedir}/src/main/webapp + /${project.artifactId}/WebContent + + + + + ${project.build.directory}/${project.artifactId}.war + / + + + diff --git a/mutex/src/main/java/com/iluwatar/mutex/Lock.java b/model-view-viewmodel/src/main/java/com/iluwatar/model/view/viewmodel/Book.java similarity index 80% rename from mutex/src/main/java/com/iluwatar/mutex/Lock.java rename to model-view-viewmodel/src/main/java/com/iluwatar/model/view/viewmodel/Book.java index bd28c3c08..b2a924152 100644 --- a/mutex/src/main/java/com/iluwatar/mutex/Lock.java +++ b/model-view-viewmodel/src/main/java/com/iluwatar/model/view/viewmodel/Book.java @@ -1,6 +1,6 @@ /* * The MIT License - * Copyright © 2014-2019 Ilkka Seppälä + * Copyright © 2014-2021 Ilkka Seppälä * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,15 +21,17 @@ * THE SOFTWARE. */ -package com.iluwatar.mutex; +package com.iluwatar.model.view.viewmodel; -/** - * Lock is an interface for a lock which can be acquired and released. - */ -public interface Lock { +import lombok.AllArgsConstructor; +import lombok.Data; - void acquire() throws InterruptedException; +@AllArgsConstructor +@Data +public class Book { - void release(); + private String name; + private String author; + private String description; } diff --git a/model-view-viewmodel/src/main/java/com/iluwatar/model/view/viewmodel/BookService.java b/model-view-viewmodel/src/main/java/com/iluwatar/model/view/viewmodel/BookService.java new file mode 100644 index 000000000..f01066960 --- /dev/null +++ b/model-view-viewmodel/src/main/java/com/iluwatar/model/view/viewmodel/BookService.java @@ -0,0 +1,10 @@ +package com.iluwatar.model.view.viewmodel; + +import java.util.List; + +public interface BookService { + /* List all books + * @return all books + */ + public List load(); +} diff --git a/model-view-viewmodel/src/main/java/com/iluwatar/model/view/viewmodel/BookServiceImpl.java b/model-view-viewmodel/src/main/java/com/iluwatar/model/view/viewmodel/BookServiceImpl.java new file mode 100644 index 000000000..e9b440843 --- /dev/null +++ b/model-view-viewmodel/src/main/java/com/iluwatar/model/view/viewmodel/BookServiceImpl.java @@ -0,0 +1,38 @@ +package com.iluwatar.model.view.viewmodel; + +import java.util.ArrayList; +import java.util.List; + +public class BookServiceImpl implements BookService { + private List designPatternBooks = new ArrayList<>(); + + /** Initializes Book Data. + * To be used and passed along in load method + * In this case, list design pattern books are initialized to be loaded. + */ + public BookServiceImpl() { + designPatternBooks.add(new Book( + "Head First Design Patterns: A Brain-Friendly Guide", + "Eric Freeman, Bert Bates, Kathy Sierra, Elisabeth Robson", + "Head First Design Patterns Description")); + designPatternBooks.add(new Book( + "Design Patterns: Elements of Reusable Object-Oriented Software", + "Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides", + "Design Patterns Description")); + designPatternBooks.add(new Book( + "Patterns of Enterprise Application Architecture", "Martin Fowler", + "Patterns of Enterprise Application Architecture Description")); + designPatternBooks.add(new Book( + "Design Patterns Explained", "Alan Shalloway, James Trott", + "Design Patterns Explained Description")); + designPatternBooks.add(new Book( + "Applying UML and Patterns: An Introduction to " + + "Object-Oriented Analysis and Design and Iterative Development", + "Craig Larman", "Applying UML and Patterns Description")); + } + + public List load() { + return designPatternBooks; + } + +} diff --git a/model-view-viewmodel/src/main/java/com/iluwatar/model/view/viewmodel/BookViewModel.java b/model-view-viewmodel/src/main/java/com/iluwatar/model/view/viewmodel/BookViewModel.java new file mode 100644 index 000000000..6563df723 --- /dev/null +++ b/model-view-viewmodel/src/main/java/com/iluwatar/model/view/viewmodel/BookViewModel.java @@ -0,0 +1,43 @@ +package com.iluwatar.model.view.viewmodel; + +import java.util.List; + +import org.zkoss.bind.annotation.Command; +import org.zkoss.bind.annotation.NotifyChange; +import org.zkoss.zk.ui.select.annotation.WireVariable; + +public class BookViewModel { + + @WireVariable + private List bookList; + private Book selectedBook; + private BookService bookService = new BookServiceImpl(); + + public Book getSelectedBook() { + return selectedBook; + } + + @NotifyChange("selectedBook") + public void setSelectedBook(Book selectedBook) { + this.selectedBook = selectedBook; + } + + public List getBookList() { + return bookService.load(); + } + + /** Deleting a book. + * When event is triggered on click of Delete button, + * this method will be notified with the selected entry that will be referenced + * and used to delete the selected book from the list of books. + */ + @Command + @NotifyChange({"selectedBook","bookList"}) + public void deleteBook() { + if (selectedBook != null) { + getBookList().remove(selectedBook); + selectedBook = null; + } + } + +} diff --git a/model-view-viewmodel/src/main/webapp/META-INF/MANIFEST.MF b/model-view-viewmodel/src/main/webapp/META-INF/MANIFEST.MF new file mode 100644 index 000000000..4e0a32ad6 --- /dev/null +++ b/model-view-viewmodel/src/main/webapp/META-INF/MANIFEST.MF @@ -0,0 +1,8 @@ +Manifest-Version: 1.0 +Specification-Title: ZK Application +Specification-Version: 1.0 +Specification-Vendor: +Implementation-Title: +Implementation-URL: http://your-website/ +Implementation-Version: 1.0 +Implementation-Vendor: diff --git a/model-view-viewmodel/src/main/webapp/WEB-INF/web.xml b/model-view-viewmodel/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 000000000..24af77bbb --- /dev/null +++ b/model-view-viewmodel/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,114 @@ + + + + + + model-view-viewmodel + + + + ZK listener for session cleanup + org.zkoss.zk.ui.http.HttpSessionListener + + + ZK loader for ZUML pages + zkLoader + org.zkoss.zk.ui.http.DHtmlLayoutServlet + + + + update-uri + /zkau + + 1 + + + zkLoader + *.zul + + + zkLoader + *.zhtml + + + The asynchronous update engine for ZK + auEngine + org.zkoss.zk.au.http.DHtmlUpdateServlet + + + auEngine + /zkau/* + + + 60 + + + + + doc + application/vnd.ms-word + + + gif + image/gif + + + htm + text/html + + + html + text/html + + + jpeg + image/jpeg + + + jpg + image/jpeg + + + js + text/javascript + + + pdf + application/pdf + + + png + image/png + + + txt + text/plain + + + xls + application/vnd.ms-excel + + + xml + text/xml + + + zhtml + text/html + + + zul + text/html + + + + index.zul + index.zhtml + index.html + index.htm + + diff --git a/model-view-viewmodel/src/main/webapp/index.zul b/model-view-viewmodel/src/main/webapp/index.zul new file mode 100644 index 000000000..9cdab7144 --- /dev/null +++ b/model-view-viewmodel/src/main/webapp/index.zul @@ -0,0 +1,34 @@ + + + + + + + + + + + + +