From f6942cf18dab21b354be69a3853c12f6d21b7a60 Mon Sep 17 00:00:00 2001 From: swarajsaaj Date: Thu, 10 Sep 2020 02:57:56 +0530 Subject: [PATCH] #1313 Add Separated Interface design pattern --- separated-interface/README.md | 119 ++++++++++++++++++ separated-interface/etc/class_diagram.png | Bin 0 -> 32651 bytes .../etc/separated-interface.urm.puml | 36 ++++++ separated-interface/pom.xml | 71 +++++++++++ .../com/iluwatar/separatedinterface/App.java | 61 +++++++++ .../invoice/InvoiceGenerator.java | 28 +++++ .../invoice/TaxCalculator.java | 30 +++++ .../separatedinterface/taxes/DomesticTax.java | 40 ++++++ .../separatedinterface/taxes/ForeignTax.java | 40 ++++++ .../iluwatar/separatedinterface/AppTest.java | 41 ++++++ .../invoice/InvoiceGeneratorTest.java | 25 ++++ .../taxes/DomesticTaxTest.java | 18 +++ .../taxes/ForeignTaxTest.java | 18 +++ 13 files changed, 527 insertions(+) create mode 100644 separated-interface/README.md create mode 100644 separated-interface/etc/class_diagram.png create mode 100644 separated-interface/etc/separated-interface.urm.puml create mode 100644 separated-interface/pom.xml create mode 100644 separated-interface/src/main/java/com/iluwatar/separatedinterface/App.java create mode 100644 separated-interface/src/main/java/com/iluwatar/separatedinterface/invoice/InvoiceGenerator.java create mode 100644 separated-interface/src/main/java/com/iluwatar/separatedinterface/invoice/TaxCalculator.java create mode 100644 separated-interface/src/main/java/com/iluwatar/separatedinterface/taxes/DomesticTax.java create mode 100644 separated-interface/src/main/java/com/iluwatar/separatedinterface/taxes/ForeignTax.java create mode 100644 separated-interface/src/test/java/com/iluwatar/separatedinterface/AppTest.java create mode 100644 separated-interface/src/test/java/com/iluwatar/separatedinterface/invoice/InvoiceGeneratorTest.java create mode 100644 separated-interface/src/test/java/com/iluwatar/separatedinterface/taxes/DomesticTaxTest.java create mode 100644 separated-interface/src/test/java/com/iluwatar/separatedinterface/taxes/ForeignTaxTest.java diff --git a/separated-interface/README.md b/separated-interface/README.md new file mode 100644 index 000000000..11ad4a978 --- /dev/null +++ b/separated-interface/README.md @@ -0,0 +1,119 @@ +--- +layout: pattern +title: Separated Interface +folder: separated-interface +permalink: /patterns/separated-interface/ +categories: Architectural +tags: + - Decoupling +--- + + +## Intent +Separate the interface definition and implementation in different packages. This allows the client to be completly unaware of the implementation. + +## Explanation + +Real world example + +> An Invoice generator may be created with ability to use different Tax calculators that may be added in the invoice depending upon type of purchase, region etc. + +In plain words + +> Separated interface pattern encourages to keep the implementations of an interface decoupled from the client and its definition, so the client is not dependent on the implementation. + +A client code may abstract some specific functionality to an interface, and define the definition of the interface as an SPI. Another package may implement this interface definition with a concrete logic, which will be injected into the client code at runtime (with a third class, injecting the implementation in the client) or at compile time (using Plugin pattern with some configurable file). + +**Programmatic Example** + +**Client** An Invoice generator class accepts the cost of the product and calculates the total amount payable inclusive of tax + +```java +public class InvoiceGenerator { + + private final TaxCalculator taxCalculator; + + private final double amount; + + public InvoiceGenerator(double amount, TaxCalculator taxCalculator) { + this.amount = amount; + this.taxCalculator = taxCalculator; + } + + public double getAmountWithTax() { + return amount + taxCalculator.calculate(amount); + } + +} +``` +The tax calculation logic is delegated to the ```TaxCalculator``` interface + +```java + +public interface TaxCalculator { + + double calculate(double amount); + +} + +``` + +**Implementation package** +In another package (which the client is completely unaware of) there exist multiple implementations of the ```TaxCalculator``` interface +```ForeignTax``` which levies 60% tax for international products. +```java +public class ForeignTax implements TaxCalculator { + + public static final double TAX_PERCENTAGE = 60; + + @Override + public double calculate(double amount) { + return amount * TAX_PERCENTAGE / 100.0; + } + +} +``` + +```DomesticTax``` which levies 20% tax for international products. +```java +public class DomesticTax implements TaxCalculator { + + public static final double TAX_PERCENTAGE = 20; + + @Override + public double calculate(double amount) { + return amount * TAX_PERCENTAGE / 100.0; + } + +} +``` + +These both implementations are instantiated and injected in the client class by the ```App.java``` class + +```java + var internationalProductInvoice = new InvoiceGenerator(PRODUCT_COST, new ForeignTax()); + + LOGGER.info("Foreign Tax applied: {}", "" + internationalProductInvoice.getAmountWithTax()); + + var domesticProductInvoice = new InvoiceGenerator(PRODUCT_COST, new DomesticTax()); + + LOGGER.info("Domestic Tax applied: {}", "" + domesticProductInvoice.getAmountWithTax()); +``` + +## Class diagram +![alt text](./etc/class_diagram.png "Separated Interface") + +## Applicability +Use the Separated interface pattern when + +* You are developing a framework package, and your framework needs to call some application code through interfaces. +* You have separate packages implementing the functionalities which may be plugged in your client code at runtime or compile-time. +* Your code resides in a layer that is not allowed to call the interface implementation layer by rule. For example, a domain layer needs to call a data mapper. + +## Tutorial + +* [Separated Interface Tutorial](https://www.youtube.com/watch?v=d3k-hOA7k2Y) + +## Credits + +* [Martin Fowler](https://www.martinfowler.com/eaaCatalog/separatedInterface.html) diff --git a/separated-interface/etc/class_diagram.png b/separated-interface/etc/class_diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..a0edbac8dac3cd5a0f1dd174fd1cfaa0bbc78e72 GIT binary patch literal 32651 zcma&NWmuNo)-{Yl2!aR*N+aD3(jX$zNOv~~(hUOA-O}CN4N42rE#2K*bbJfE)qC$_ zzt8dc2jPOX&NbJJF~*#yucWvjB0MHM6ciMq(7QKMP*BihP*C^q;qHTfmeH;~Q96ePpMl{b-_TYGwVwght!!!&5poY_P($ zp^Spn@1H~61IyUOCrGOrPx2x=@$9)O91w*H6Q!u160}x4tNK zuEa)ecKUab^+2bCsT&-pDJR*Ec zEmrex`JSILyXiBX>~aql^5=MAvg~Ka)6oG6^xQ^$(?`j>H|%Di>pot5NQXG)1Q3hE z2mXeBu`7#D^V6v#20m^iCB!G3X6>Sf?FBN9+5Vi`gp^vgF@|)dQ^wC*J@h1}-Yk~k zj8KYb9Vx7Ce%2hotfPW48j2dI3g@wba=$@pbWPL)1KUNoWy&cc6_w*BO~sR!P*BdO zLT~tF?9{drV80BvUUc-mWvh({C&5|Ac^diBkBMCCo0m^=ax$6f8+NkoPf73;?o8_+ zKOQ*@r{R)qKk;$5NkczrP84N;LEP|GW$@qwSJ-T)B~;@cRmstL!*#opt9P zA3oGPO!L@R@aYf_{(bOavnPW6e|@M$vX9H{RDIrn4E2fr1%uA%oX3-Y79c>nUEyK# z*e7k0{miCI7^qL&W{qchGBPq+S}&V!H~IJo?*2VEO5{cuZ16lp979GO+nG7Hs zBa>BTJa&6?ZT=W%!r)gwdGH~_@hZ6OG0#;q?CUkLul1*RtWbR9KI+rDVXib9bEP97 zm|xFI5|5;aJl6^H=#Hk%5{)S}ovtk@DH*ok{#mhkv_9lS4A%Pk4Il2|J%TBBUgmE# z-w+#Lj9N6VGn$ zo>A6^$;uNm3H0_L9FdNV>AI8Q9O-B(#Zu$3!mlo>-u9?a$e0@VigUck4=EIQvXkcJ zLiP{ysnlGa!?^7b*R+g%P4jm+o%fc` z6je-r7ux82G1KU*Je`Rtx6`MF=X^B4v0KkSHeRMLAy=r^5rk7=G}0A9@Sx#~Vws-v zPR*)Zp~`m_)jFGPf7-_6=ukCL_SOzOSEuzs;!58NQD?!;(Sr51bR1YD?Cf&Gh5XA2 z0YpmHSN!hczTyEi1*#|;I1>yfv}xTbjvgEOT@k*Ii^^Lnb2XP?1ocO%jq95N>+GCW ztL1M@Szoa)f~PJQxjKiCip8Q~Vb$3muKY5!N6&ra`?tmxT|j=A`%|W+>uLglPs0wz z4Sq}ETE!$!uE0Qgf1xeXd2=LBkeIf9@4hl*ofhJVn%T#BdlfB43t~5)zzs?hOs8cC zM^Z>8^R{w5$yaNLz4k8`zL^Un7Aa+o%9Y7!hUw5gc{n}FX0eov8&$CKJ)Bg(H-1U9 zvR1PNW|`M$T&_oL#17J*!fz5;5zpmlqeLTW`X6AI6I{ zO;Fhax5bMX9ea|Tj}?PE7G)d0u!U>np^}JdaKyf%TvR=xnVoyQt}{+EjCs{DlrGdF zH?sQms(WoECAO=lSg!}Oa-?-Y(Rl}xd6K^IqetEJ+#0+D&lOXJ5N%t&Vp%7S+f{AQ zni$k4k#D@EUt@fpV0v`Cbmujtt@OjEGx6YsF&;|t&!~$ikm?o9)9*{v^-6s>zM0e% zk*2_TsDlXNC(rlfu#ZQAIEF#@XPN#J%85p24&`jQ0;M-$9W*M{ym@JF1GP@R)!QG| zpX~U$LnEjG2kPUwktxD`_3=}|@#fg>jQNhohmkE==fahqxM(@pC%8X?UX!FZWr-)e zR3ErMU6=n8n0{n;jmcy@q;jPaCo6YwqYAl=}Ic5)vT1ZZ>vROl$Tn@8? zxq;?#SL9Qn;Lo2wKUBK9I+;~8-rDprVROB{91BnGjg_O6%kRXbHR_@wnpQnV`dvRusLP15F zJ~I!Ysw2RY3EsLRXDe*rjP`&(zT~+7_DipqMj#jDrC>Z{sP-t8ZSdDMYjrwXjxN6( zlHd^_nl6zU`Y$W8I*#S)c1PXF)(YzAq&uKazu20cNE3Xr+u)dLy*WBV^dwuJ3-^ig z_IOEJZo`d+GQ>f4aReA_$Ou@tJrGL}LABN@rKwD>$KiNFpD?Tu(wd9IVYAh6GDBF# zb-CA)@9C3%fNeIbaLLZs~!WsCuITb2{7)RB(-&Ws-+4UIv) zQEl>xk$Jb?)=)Qnx&|*8gq;H{Li1TY+}=eP<

@OZmh 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 "-taxCalculator" TaxCalculator +DomesticTax ..|> TaxCalculator +ForeignTax ..|> TaxCalculator +@enduml \ No newline at end of file diff --git a/separated-interface/pom.xml b/separated-interface/pom.xml new file mode 100644 index 000000000..da7584a93 --- /dev/null +++ b/separated-interface/pom.xml @@ -0,0 +1,71 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.24.0-SNAPSHOT + + separated-interface + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.jupiter + junit-jupiter-params + test + + + org.mockito + mockito-core + test + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + com.iluwatar.separatedinterface.App + + + + + + + + + diff --git a/separated-interface/src/main/java/com/iluwatar/separatedinterface/App.java b/separated-interface/src/main/java/com/iluwatar/separatedinterface/App.java new file mode 100644 index 000000000..e0a385c9e --- /dev/null +++ b/separated-interface/src/main/java/com/iluwatar/separatedinterface/App.java @@ -0,0 +1,61 @@ +/* + * The MIT License + * Copyright © 2014-2019 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.iluwatar.separatedinterface; + +import com.iluwatar.separatedinterface.invoice.InvoiceGenerator; +import com.iluwatar.separatedinterface.taxes.DomesticTax; +import com.iluwatar.separatedinterface.taxes.ForeignTax; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + *

The Strategy pattern (also known as the policy pattern) is a software design pattern that + * enables an algorithm's behavior to be selected at runtime.

+ * + *

Before Java 8 the Strategies needed to be separate classes forcing the developer + * to write lots of boilerplate code. With modern Java it is easy to pass behavior with method + * references and lambdas making the code shorter and more readable.

+ * + */ +public class App { + + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + + public static final double PRODUCT_COST = 50.0; + + /** + * Program entry point. + * + * @param args command line args + */ + public static void main(String[] args) { + //Create the invoice generator with product cost as 50 and foreign product tax + var internationalProductInvoice = new InvoiceGenerator(PRODUCT_COST, new ForeignTax()); + LOGGER.info("Foreign Tax applied: {}", "" + internationalProductInvoice.getAmountWithTax()); + + //Create the invoice generator with product cost as 50 and domestic product tax + var domesticProductInvoice = new InvoiceGenerator(PRODUCT_COST, new DomesticTax()); + LOGGER.info("Domestic Tax applied: {}", "" + domesticProductInvoice.getAmountWithTax()); + } +} diff --git a/separated-interface/src/main/java/com/iluwatar/separatedinterface/invoice/InvoiceGenerator.java b/separated-interface/src/main/java/com/iluwatar/separatedinterface/invoice/InvoiceGenerator.java new file mode 100644 index 000000000..f73ee32d8 --- /dev/null +++ b/separated-interface/src/main/java/com/iluwatar/separatedinterface/invoice/InvoiceGenerator.java @@ -0,0 +1,28 @@ +package com.iluwatar.separatedinterface.invoice; + +/** + * InvoiceGenerator class generates an invoice, accepting the product cost and calculating the total + * price payable inclusive tax (calculated by {@link TaxCalculator}) + */ +public class InvoiceGenerator { + + /** + * The TaxCalculator interface to calculate the payable tax. + */ + private final TaxCalculator taxCalculator; + + /** + * The base product amount without tax. + */ + private final double amount; + + public InvoiceGenerator(double amount, TaxCalculator taxCalculator) { + this.amount = amount; + this.taxCalculator = taxCalculator; + } + + public double getAmountWithTax() { + return amount + taxCalculator.calculate(amount); + } + +} \ No newline at end of file diff --git a/separated-interface/src/main/java/com/iluwatar/separatedinterface/invoice/TaxCalculator.java b/separated-interface/src/main/java/com/iluwatar/separatedinterface/invoice/TaxCalculator.java new file mode 100644 index 000000000..aa4b81a84 --- /dev/null +++ b/separated-interface/src/main/java/com/iluwatar/separatedinterface/invoice/TaxCalculator.java @@ -0,0 +1,30 @@ +/* + * 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.separatedinterface.invoice; + +public interface TaxCalculator { + + double calculate(double amount); + +} diff --git a/separated-interface/src/main/java/com/iluwatar/separatedinterface/taxes/DomesticTax.java b/separated-interface/src/main/java/com/iluwatar/separatedinterface/taxes/DomesticTax.java new file mode 100644 index 000000000..ebe0e306e --- /dev/null +++ b/separated-interface/src/main/java/com/iluwatar/separatedinterface/taxes/DomesticTax.java @@ -0,0 +1,40 @@ +/* + * 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.separatedinterface.taxes; + +import com.iluwatar.separatedinterface.invoice.TaxCalculator; + +/** + * TaxCalculator for Domestic goods with 20% tax. + */ +public class DomesticTax implements TaxCalculator { + + public static final double TAX_PERCENTAGE = 20; + + @Override + public double calculate(double amount) { + return amount * TAX_PERCENTAGE / 100.0; + } + +} diff --git a/separated-interface/src/main/java/com/iluwatar/separatedinterface/taxes/ForeignTax.java b/separated-interface/src/main/java/com/iluwatar/separatedinterface/taxes/ForeignTax.java new file mode 100644 index 000000000..583f70d3b --- /dev/null +++ b/separated-interface/src/main/java/com/iluwatar/separatedinterface/taxes/ForeignTax.java @@ -0,0 +1,40 @@ +/* + * 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.separatedinterface.taxes; + +import com.iluwatar.separatedinterface.invoice.TaxCalculator; + +/** + * TaxCalculator for foreign goods with 60% tax. + */ +public class ForeignTax implements TaxCalculator { + + public static final double TAX_PERCENTAGE = 60; + + @Override + public double calculate(double amount) { + return amount * TAX_PERCENTAGE / 100.0; + } + +} diff --git a/separated-interface/src/test/java/com/iluwatar/separatedinterface/AppTest.java b/separated-interface/src/test/java/com/iluwatar/separatedinterface/AppTest.java new file mode 100644 index 000000000..4114f9bb7 --- /dev/null +++ b/separated-interface/src/test/java/com/iluwatar/separatedinterface/AppTest.java @@ -0,0 +1,41 @@ +/* + * 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.separatedinterface; + +import org.junit.jupiter.api.Test; + +import com.iluwatar.separatedinterface.App; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +/** + * Application test. + */ +class AppTest { + + @Test + void shouldExecuteWithoutException() { + assertDoesNotThrow(() -> App.main(new String[]{})); + } +} diff --git a/separated-interface/src/test/java/com/iluwatar/separatedinterface/invoice/InvoiceGeneratorTest.java b/separated-interface/src/test/java/com/iluwatar/separatedinterface/invoice/InvoiceGeneratorTest.java new file mode 100644 index 000000000..669fb1452 --- /dev/null +++ b/separated-interface/src/test/java/com/iluwatar/separatedinterface/invoice/InvoiceGeneratorTest.java @@ -0,0 +1,25 @@ +package com.iluwatar.separatedinterface.invoice; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import static org.mockito.Mockito.*; + +public class InvoiceGeneratorTest { + + private InvoiceGenerator target; + + @Test + public void testGenerateTax() { + var productCost = 50.0; + var tax = 10.0; + TaxCalculator taxCalculatorMock = mock(TaxCalculator.class); + doReturn(tax).when(taxCalculatorMock).calculate(productCost); + + target = new InvoiceGenerator(productCost, taxCalculatorMock); + + Assertions.assertEquals(target.getAmountWithTax(), productCost + tax); + verify(taxCalculatorMock, times(1)).calculate(productCost); + } + +} diff --git a/separated-interface/src/test/java/com/iluwatar/separatedinterface/taxes/DomesticTaxTest.java b/separated-interface/src/test/java/com/iluwatar/separatedinterface/taxes/DomesticTaxTest.java new file mode 100644 index 000000000..e562586c2 --- /dev/null +++ b/separated-interface/src/test/java/com/iluwatar/separatedinterface/taxes/DomesticTaxTest.java @@ -0,0 +1,18 @@ +package com.iluwatar.separatedinterface.taxes; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class DomesticTaxTest { + + private DomesticTax target; + + @Test + public void testTaxCaluclation(){ + target = new DomesticTax(); + + var tax=target.calculate(100.0); + Assertions.assertEquals(tax,20.0); + } + +} diff --git a/separated-interface/src/test/java/com/iluwatar/separatedinterface/taxes/ForeignTaxTest.java b/separated-interface/src/test/java/com/iluwatar/separatedinterface/taxes/ForeignTaxTest.java new file mode 100644 index 000000000..86bf39e80 --- /dev/null +++ b/separated-interface/src/test/java/com/iluwatar/separatedinterface/taxes/ForeignTaxTest.java @@ -0,0 +1,18 @@ +package com.iluwatar.separatedinterface.taxes; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ForeignTaxTest { + + private ForeignTax target; + + @Test + public void testTaxCaluclation(){ + target = new ForeignTax(); + + var tax=target.calculate(100.0); + Assertions.assertEquals(tax,60.0); + } + +}