Compare commits

...

47 Commits

Author SHA1 Message Date
allcontributors[bot]
6f592f5e8a docs: update .all-contributorsrc [skip ci] 2020-09-13 14:23:53 +00:00
allcontributors[bot]
74b968942f docs: update README.md [skip ci] 2020-09-13 14:23:52 +00:00
Ilkka Seppälä
ebc0e8b3cd Merge pull request #1522 from swarajsaaj/master
#1313 Add Separated Interface pattern
2020-09-13 17:21:28 +03:00
swarajsaaj
9088ac51f6 #1313 Rename DomesticTax,ForeignTax and review fixes 2020-09-12 22:35:40 +05:30
Ilkka Seppälä
c8f7a8f0e6 Merge pull request #1523 from iluwatar/all-contributors/add-vdlald
docs: add vdlald as a contributor
2020-09-12 20:02:32 +03:00
allcontributors[bot]
c63af2ccbf docs: update .all-contributorsrc [skip ci] 2020-09-12 17:00:59 +00:00
allcontributors[bot]
9f3f5322d2 docs: update README.md [skip ci] 2020-09-12 17:00:58 +00:00
Ilkka Seppälä
5607a4974c Merge pull request #1517 from vdlald/847
847
2020-09-12 19:57:29 +03:00
swarajsaaj
a2967c5a40 #1313 Add documentation and license header 2020-09-10 03:22:00 +05:30
swarajsaaj
7fd7735527 #1313 Add separated-interface module to parent pom 2020-09-10 03:10:58 +05:30
swarajsaaj
f6942cf18d #1313 Add Separated Interface design pattern 2020-09-10 02:57:56 +05:30
Ilkka Seppälä
ef326ee77e Update README.md 2020-09-06 19:56:07 +03:00
Ilkka Seppälä
8b5f532a50 Update README.md 2020-09-06 19:55:38 +03:00
Ilkka Seppälä
a1da1e4973 Cleanup factory 2020-09-06 19:46:13 +03:00
Ilkka Seppälä
9d21dff855 Merge pull request #1520 from iluwatar/all-contributors/add-ravening
docs: add ravening as a contributor
2020-09-06 19:39:08 +03:00
Ilkka Seppälä
9d75592e8b Merge branch 'master' into all-contributors/add-ravening 2020-09-06 19:38:44 +03:00
allcontributors[bot]
8d6738b729 docs: update .all-contributorsrc [skip ci] 2020-09-06 16:37:04 +00:00
allcontributors[bot]
3205dc2cf0 docs: update README.md [skip ci] 2020-09-06 16:37:03 +00:00
Ilkka Seppälä
16e1863ae7 Merge pull request #1519 from iluwatar/all-contributors/add-samilAyoub
docs: add samilAyoub as a contributor
2020-09-06 19:36:35 +03:00
allcontributors[bot]
b9db3c4763 docs: update .all-contributorsrc [skip ci] 2020-09-06 16:35:58 +00:00
allcontributors[bot]
d72206ba72 docs: update README.md [skip ci] 2020-09-06 16:35:57 +00:00
Ilkka Seppälä
922c699e49 Merge pull request #1516 from samilAyoub/add-simple-factory
Add Simple Factory Pattern implementation
2020-09-06 19:33:03 +03:00
Vladislav Golubinov
bab48efd7c fix style 2020-09-06 12:01:48 +03:00
Vladislav Golubinov
29eecfd048 forgot to run the App 2020-09-06 11:52:16 +03:00
Vladislav Golubinov
87cf6b791c refactor 2020-09-06 11:48:40 +03:00
Vladislav Golubinov
2e36a11e24 remove lombok, related to #1503 2020-09-06 11:42:39 +03:00
Samil Ayoub
bf41b1d9c9 Updates README.md:
- Adding class diagram
- Adding Pros and Cons
- replace "" with ''
2020-09-05 18:39:28 +01:00
Samil Ayoub
b3ef214cd6 Change tabs to spaces in pom.xml 2020-09-04 22:02:19 +01:00
Samil Ayoub
6caf78e4e5 updates :
- Using lambda expression to create cars
- Using spaces instead of tabs in pom.xml
2020-09-04 21:21:51 +01:00
Vladislav Golubinov
bd48d6ce10 refactor 2020-09-04 17:31:50 +03:00
Samil Ayoub
8b26452c75 bug fixing 2020-09-03 22:58:15 +01:00
Samil Ayoub
2bb252e08f Clean the code 2020-09-03 22:41:55 +01:00
Samil Ayoub
a023cfbb1a Merge branch 'master' into add-simple-factory 2020-09-03 22:26:49 +01:00
Samil Ayoub
badf0c6b8c - README.md is added
- Change the name to factory is done
- Local variable type inference is used
2020-09-03 22:08:54 +01:00
Samil Ayoub
c9718a5227 add README file 2020-09-03 21:08:28 +01:00
Vladislav Golubinov
e89042a782 remove boilerplate code 2020-09-03 20:04:47 +03:00
Vladislav Golubinov
fb890e80dd refactor 2020-09-03 20:02:52 +03:00
Samil Ayoub
b423fd30d4 Fix bugs, clean the code and add unit tests. 2020-09-02 18:12:42 +01:00
Ilkka Seppälä
3df8472bf8 Merge pull request #1515 from fedorskvorcov/fix-typo
Remove unnecessary word from text
2020-09-02 19:59:07 +03:00
Samil Ayoub
ac98b31b68 Add Maven Assembly plugin to pom.xml 2020-09-02 14:09:44 +01:00
Samil Ayoub
46b23f322f Add Simple Factory Pattern implementation
Java source code demonstrate simple factory design pattern
2020-09-02 13:46:53 +01:00
Fedor
e231cd8d1a Remove unnecessary word from text 2020-09-02 12:32:02 +03:00
Ilkka Seppälä
19378f3fdd Update README.md 2020-09-01 20:25:39 +03:00
Ilkka Seppälä
3f4d637510 Update README.md 2020-09-01 20:18:10 +03:00
Ilkka Seppälä
25cca3547d Update README.md 2020-09-01 20:06:47 +03:00
Ilkka Seppälä
b9b6777d15 Update README.md 2020-09-01 19:55:22 +03:00
Ilkka Seppälä
2a5b8c977a Merge pull request #1514 from iluwatar/all-contributors/add-fedorskvorcov
docs: add fedorskvorcov as a contributor
2020-09-01 16:40:23 +03:00
44 changed files with 1380 additions and 382 deletions

View File

@@ -1082,7 +1082,8 @@
"avatar_url": "https://avatars1.githubusercontent.com/u/10645273?v=4",
"profile": "https://github.com/ravening",
"contributions": [
"code"
"code",
"review"
]
},
{
@@ -1185,6 +1186,33 @@
"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"
]
}
],
"contributorsPerLine": 4,

View File

@@ -10,7 +10,7 @@
[![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-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-130-orange.svg?style=flat-square)](#contributors-)
[![All Contributors](https://img.shields.io/badge/all_contributors-133-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
# Introduction
@@ -246,7 +246,7 @@ This project is licensed under the terms of the MIT license.
<tr>
<td align="center"><a href="https://github.com/nishant"><img src="https://avatars2.githubusercontent.com/u/15331971?v=4" width="100px;" alt=""/><br /><sub><b>Nishant Arora</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=nishant" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/raja-peeyush-kumar-singh"><img src="https://avatars0.githubusercontent.com/u/5496024?v=4" width="100px;" alt=""/><br /><sub><b>Peeyush</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=raja-peeyush-kumar-singh" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/ravening"><img src="https://avatars1.githubusercontent.com/u/10645273?v=4" width="100px;" alt=""/><br /><sub><b>Rakesh</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ravening" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/ravening"><img src="https://avatars1.githubusercontent.com/u/10645273?v=4" width="100px;" alt=""/><br /><sub><b>Rakesh</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ravening" title="Code">💻</a> <a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Aravening" title="Reviewed Pull Requests">👀</a></td>
<td align="center"><a href="https://github.com/vINCENT8888801"><img src="https://avatars0.githubusercontent.com/u/8037883?v=4" width="100px;" alt=""/><br /><sub><b>Wei Seng</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=vINCENT8888801" title="Code">💻</a></td>
</tr>
<tr>
@@ -264,6 +264,11 @@ This project is licensed under the terms of the MIT license.
<tr>
<td align="center"><a href="https://www.stefan-birkner.de"><img src="https://avatars1.githubusercontent.com/u/711349?v=4" width="100px;" alt=""/><br /><sub><b>Stefan Birkner</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=stefanbirkner" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/fedorskvorcov"><img src="https://avatars3.githubusercontent.com/u/43882212?v=4" width="100px;" alt=""/><br /><sub><b>Fedor Skvorcov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=fedorskvorcov" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/samilAyoub"><img src="https://avatars0.githubusercontent.com/u/61546990?v=4" width="100px;" alt=""/><br /><sub><b>samilAyoub</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=samilAyoub" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/vdlald"><img src="https://avatars0.githubusercontent.com/u/29997701?v=4" width="100px;" alt=""/><br /><sub><b>Vladislav Golubinov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=vdlald" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/swarajsaaj"><img src="https://avatars2.githubusercontent.com/u/6285049?v=4" width="100px;" alt=""/><br /><sub><b>Swaraj</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=swarajsaaj" title="Code">💻</a></td>
</tr>
</table>

View File

@@ -23,7 +23,6 @@
package com.iluwatar.abstractfactory;
import com.iluwatar.abstractfactory.App.FactoryMaker.KingdomType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -41,84 +40,14 @@ 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 {
public class App implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
private static Logger log = LoggerFactory.getLogger(App.class);
private King king;
private Castle castle;
private Army army;
private final Kingdom kingdom = new Kingdom();
/**
* 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 +56,33 @@ public class App {
* @param args command line args
*/
public static void main(String[] args) {
var app = new App();
app.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());
@Override
public void run() {
log.info("Elf Kingdom");
createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
log.info(kingdom.getArmy().getDescription());
log.info(kingdom.getCastle().getDescription());
log.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());
log.info("Orc Kingdom");
createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
log.info(kingdom.getArmy().getDescription());
log.info(kingdom.getCastle().getDescription());
log.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());
}
}

View File

@@ -0,0 +1,59 @@
package com.iluwatar.abstractfactory;
public class Kingdom {
private King king;
private Castle castle;
private Army army;
public King getKing() {
return king;
}
public Castle getCastle() {
return castle;
}
public Army getArmy() {
return army;
}
public void setKing(King king) {
this.king = king;
}
public void setCastle(Castle castle) {
this.castle = castle;
}
public void setArmy(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.");
}
}
}
}

View File

@@ -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 {
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);
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);
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);
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();
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);
@@ -92,10 +98,12 @@ 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();
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);

133
factory/README.md Normal file
View File

@@ -0,0 +1,133 @@
---
layout: pattern
title: Factory
folder: factory
permalink: /patterns/factory/
categories: Creational
tags:
- Gang of Four
---
## Also known as
* Simple Factory
* Static Factory Method
## Intent
Providing a static method encapsulated in a class called factory, in order to hide the
implementation logic and makes client code focus on usage rather then initialization new objects.
## Explanation
Real world example
> Lets say we have a web application connected to SQLServer, but now we want to switch to Oracle. To
> do so without modifying existing source code, we need to implements Simple Factory pattern, in
> which a static method can be invoked to create connection to a given database.
Wikipedia says
> Factory is an object for creating other objects formally a factory is a function or method that
> returns objects of a varying prototype or class.
**Programmatic Example**
We have an interface `Car` and two implementations `Ford` and `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;
}
}
```
Enumeration above represents types of cars that we support (`Ford` and `Ferrari`).
```java
public enum CarType {
FORD(Ford::new),
FERRARI(Ferrari::new);
private final Supplier<Car> constructor;
CarType(Supplier<Car> constructor) {
this.constructor = constructor;
}
public Supplier<Car> getConstructor() {
return this.constructor;
}
}
```
Then we have the static method `getCar` to create car objects encapsulated in the factory class
`CarSimpleFactory`.
```java
public class CarsFactory {
public static Car getCar(CarType type) {
return type.getConstructor().get();
}
}
```
Now on the client code we can create different types of cars using the factory class.
```java
var car1 = CarsFactory.getCar(CarType.FORD);
var car2 = CarsFactory.getCar(CarType.FERRARI);
LOGGER.info(car1.getDescription());
LOGGER.info(car2.getDescription());;
```
Program output:
```java
This is Ford.
This Ferrari.
```
## Class Diagram
![alt text](./etc/factory.urm.png "Factory pattern class diagram")
## Applicability
Use the Simple Factory pattern when you only care about the creation of a object, not how to create
and manage it.
Pros
* Allows keeping all objects creation in one place and avoid of spreading 'new' key value across codebase.
* Allows to writs loosely coupled code. Some of its main advantages include better testability, easy-to-understand code, swappable components, scalability and isolated features.
Cons
* The code becomes more complicated than it should be.
## Related patterns
* [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/)

BIN
factory/etc/factory.urm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -0,0 +1,35 @@
@startuml
package com.iluwatar.factory {
class App {
- LOGGER : Logger {static}
+ App()
+ main(args : String[]) {static}
}
interface Car {
+ getDescription() : String {abstract}
}
class CarsFactory {
+ CarsFactory()
+ getCar(type : CarType) : Car {static}
}
~enum CarType {
+ FERRARI {static}
+ FORD {static}
+ valueOf(name : String) : CarType {static}
+ values() : CarType[] {static}
}
class Ferrari {
~ DESCRIPTION : String {static}
+ Ferrari()
+ getDescription() : String
}
class Ford {
~ DESCRIPTION : String {static}
+ Ford()
+ getDescription() : String
}
}
CarType ..+ CarsFactory
Ferrari ..|> Car
Ford ..|> Car
@enduml

43
factory/pom.xml Normal file
View File

@@ -0,0 +1,43 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.24.0-SNAPSHOT</version>
</parent>
<artifactId>factory</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Maven assembly plugin is invoked with default setting which we have
in parent pom and specifying the class having main method -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.factory.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,51 @@
/*
* The MIT License
* Copyright © 2014-2019 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 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.
*
* <p>In this example the CarFactory is the factory class and it provides a static method to
* create different cars.
*/
public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
/**
* Program main entry point.
*/
public static void main(String[] args) {
var car1 = CarsFactory.getCar(CarType.FORD);
var car2 = CarsFactory.getCar(CarType.FERRARI);
LOGGER.info(car1.getDescription());
LOGGER.info(car2.getDescription());
}
}

View File

@@ -0,0 +1,10 @@
package com.iluwatar.factory;
/**
* Car interface.
*/
public interface Car {
String getDescription();
}

View File

@@ -0,0 +1,22 @@
package com.iluwatar.factory;
import java.util.function.Supplier;
public enum CarType {
/**
* Enumeration for different types of cars.
*/
FORD(Ford::new),
FERRARI(Ferrari::new);
private final Supplier<Car> constructor;
CarType(Supplier<Car> constructor) {
this.constructor = constructor;
}
public Supplier<Car> getConstructor() {
return this.constructor;
}
}

View File

@@ -0,0 +1,14 @@
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();
}
}

View File

@@ -0,0 +1,14 @@
package com.iluwatar.factory;
/**
* Ferrari implementation.
*/
public class Ferrari implements Car {
static final String DESCRIPTION = "This is Ferrari.";
@Override
public String getDescription() {
return DESCRIPTION;
}
}

View File

@@ -0,0 +1,14 @@
package com.iluwatar.factory;
/**
* Ford implementation.
*/
public class Ford implements Car {
static final String DESCRIPTION = "This is Ford.";
@Override
public String getDescription() {
return DESCRIPTION;
}
}

View File

@@ -0,0 +1,14 @@
package com.iluwatar.factory;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
class AppTest {
@Test
void shouldExecuteWithoutExceptions() {
assertDoesNotThrow(() -> App.main(new String[]{}));
}
}

View File

@@ -0,0 +1,15 @@
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);
}
}

View File

@@ -195,6 +195,8 @@
<module>arrange-act-assert</module>
<module>transaction-script</module>
<module>filterer</module>
<module>factory</module>
<module>separated-interface</module>
</modules>
<repositories>

View File

@@ -9,6 +9,7 @@ tags:
---
## Intent
Private Class Data design pattern seeks to reduce exposure of attributes by limiting their
visibility. It reduces the number of class attributes by encapsulating them in single Data object.
@@ -124,9 +125,11 @@ immutableStew.mix(); // Mixing the immutable stew we find: 2 potatoes, 4 carrot
```
## Class diagram
![alt text](./etc/private-class-data.png "Private Class Data")
## Applicability
Use the Private Class Data pattern when
* You want to prevent write access to class data members
* You want to prevent write access to class data members.

View File

@@ -14,23 +14,25 @@ CompletableFuture
## Intent
A Promise represents a proxy for a value not necessarily known when the promise is created. It allows you to associate
dependent promises to an asynchronous action's eventual success value or failure reason. Promises are a way to write
async code that still appears as though it is executing in a synchronous way.
A Promise represents a proxy for a value not necessarily known when the promise is created. It
allows you to associate dependent promises to an asynchronous action's eventual success value or
failure reason. Promises are a way to write async code that still appears as though it is executing
in a synchronous way.
## Explanation
The Promise object is used for asynchronous computations. A Promise represents an operation that hasn't completed yet,
but is expected in the future.
The Promise object is used for asynchronous computations. A Promise represents an operation that
hasn't completed yet, but is expected in the future.
Promises provide a few advantages over callback objects:
* Functional composition and error handling
* Prevents callback hell and provides callback aggregation
* Functional composition and error handling.
* Prevents callback hell and provides callback aggregation.
Real world example
> We are developing a software solution that downloads files and calculates the number of lines and character
frequencies in those files. Promise is an ideal solution to make the code concise and easy to understand.
> We are developing a software solution that downloads files and calculates the number of lines and
> character frequencies in those files. Promise is an ideal solution to make the code concise and
> easy to understand.
In plain words
@@ -38,14 +40,15 @@ In plain words
Wikipedia says
> In computer science, future, promise, delay, and deferred refer to constructs used for synchronizing program
execution in some concurrent programming languages. They describe an object that acts as a proxy for a result that is
initially unknown, usually because the computation of its value is not yet complete.
> In computer science, future, promise, delay, and deferred refer to constructs used for
> synchronizing program execution in some concurrent programming languages. They describe an object
> that acts as a proxy for a result that is initially unknown, usually because the computation of
> its value is not yet complete.
**Programmatic Example**
In the example a file is downloaded and its line count is calculated. The calculated line count is then consumed and
printed on console.
In the example a file is downloaded and its line count is calculated. The calculated line count is
then consumed and printed on console.
Let's first introduce a support class we need for implementation. Here's `PromiseSupport`.
@@ -195,7 +198,7 @@ public class Promise<T> extends PromiseSupport<T> {
public <V> Promise<V> thenApply(Function<? super T, V> func) {
Promise<V> dest = new Promise<>();
fulfillmentAction = new TransformAction<V>(this, dest, func);
fulfillmentAction = new TransformAction<>(this, dest, func);
return dest;
}
@@ -246,8 +249,8 @@ public class Promise<T> extends PromiseSupport<T> {
}
```
Now we can show the full example in action. Here's how to download and count the number of lines in a file using
`Promise`.
Now we can show the full example in action. Here's how to download and count the number of lines in
a file using `Promise`.
```java
countLines().thenAccept(
@@ -280,8 +283,8 @@ Now we can show the full example in action. Here's how to download and count the
## Applicability
Promise pattern is applicable in concurrent programming when some work needs to be done asynchronously
and:
Promise pattern is applicable in concurrent programming when some work needs to be done
asynchronously and:
* Code maintainability and readability suffers due to callback hell.
* You need to compose promises and need better error handling for asynchronous tasks.

View File

@@ -81,7 +81,7 @@ public class App {
* @throws InterruptedException if main thread is interrupted.
* @throws ExecutionException if an execution error occurs.
*/
public static void main(String[] args) throws InterruptedException, ExecutionException {
public static void main(String[] args) throws InterruptedException {
var app = new App();
try {
app.promiseUsage();

View File

@@ -141,7 +141,7 @@ public class Promise<T> extends PromiseSupport<T> {
*/
public <V> Promise<V> thenApply(Function<? super T, V> func) {
Promise<V> dest = new Promise<>();
fulfillmentAction = new TransformAction<V>(this, dest, func);
fulfillmentAction = new TransformAction<>(this, dest, func);
return dest;
}

View File

@@ -26,7 +26,6 @@ package com.iluwatar.promise;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -114,4 +113,4 @@ class PromiseSupport<T> implements Future<T> {
}
throw new ExecutionException(exception);
}
}
}

View File

@@ -10,16 +10,19 @@ tags:
---
## Intent
Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
Specify the kinds of objects to create using a prototypical instance, and create new objects by
copying this prototype.
## Explanation
First it should be noted that Prototype pattern is not used to gain performance benefits. It's only used for creating
new objects from prototype instance.
First it should be noted that Prototype pattern is not used to gain performance benefits. It's only
used for creating new objects from prototype instance.
Real world example
> Remember Dolly? The sheep that was cloned! Lets not get into the details but the key point here is that it is all about cloning.
> Remember Dolly? The sheep that was cloned! Lets not get into the details but the key point here is
> that it is all about cloning.
In plain words
@@ -27,9 +30,12 @@ In plain words
Wikipedia says
> The prototype pattern is a creational design pattern in software development. It is used when the type of objects to create is determined by a prototypical instance, which is cloned to produce new objects.
> The prototype pattern is a creational design pattern in software development. It is used when the
> type of objects to create is determined by a prototypical instance, which is cloned to produce new
> objects.
In short, it allows you to create a copy of an existing object and modify it to your needs, instead of going through the trouble of creating an object from scratch and setting it up.
In short, it allows you to create a copy of an existing object and modify it to your needs, instead
of going through the trouble of creating an object from scratch and setting it up.
**Programmatic Example**
@@ -52,7 +58,7 @@ class Sheep implements Cloneable {
}
```
Then it can be cloned like below
Then it can be cloned like below:
```java
var original = new Sheep("Jolly");
@@ -65,15 +71,20 @@ System.out.println(cloned.getName()); // Dolly
```
## Class diagram
![alt text](./etc/prototype.urm.png "Prototype pattern class diagram")
## Applicability
Use the Prototype pattern when a system should be independent of how its products are created, composed and represented; and
* When the classes to instantiate are specified at run-time, for example, by dynamic loading
* To avoid building a class hierarchy of factories that parallels the class hierarchy of products
* When instances of a class can have one of only a few different combinations of state. It may be more convenient to install a corresponding number of prototypes and clone them rather than instantiating the class manually, each time with the appropriate state
* When object creation is expensive compared to cloning
Use the Prototype pattern when a system should be independent of how its products are created,
composed, represented and
* When the classes to instantiate are specified at run-time, for example, by dynamic loading.
* To avoid building a class hierarchy of factories that parallels the class hierarchy of products.
* When instances of a class can have one of only a few different combinations of state. It may be
more convenient to install a corresponding number of prototypes and clone them rather than
instantiating the class manually, each time with the appropriate state.
* When object creation is expensive compared to cloning.
## Real world examples

View File

@@ -10,16 +10,20 @@ tags:
---
## Also known as
Surrogate
## Intent
Provide a surrogate or placeholder for another object to control
access to it.
Provide a surrogate or placeholder for another object to control access to it.
## Explanation
Real world example
> Imagine a tower where the local wizards go to study their spells. The ivory tower can only be accessed through a proxy which ensures that only the first three wizards can enter. Here the proxy represents the functionality of the tower and adds access control to it.
> Imagine a tower where the local wizards go to study their spells. The ivory tower can only be
> accessed through a proxy which ensures that only the first three wizards can enter. Here the proxy
> represents the functionality of the tower and adds access control to it.
In plain words
@@ -27,11 +31,17 @@ In plain words
Wikipedia says
> A proxy, in its most general form, is a class functioning as an interface to something else. A proxy is a wrapper or agent object that is being called by the client to access the real serving object behind the scenes. Use of the proxy can simply be forwarding to the real object, or can provide additional logic. In the proxy extra functionality can be provided, for example caching when operations on the real object are resource intensive, or checking preconditions before operations on the real object are invoked.
> A proxy, in its most general form, is a class functioning as an interface to something else.
> A proxy is a wrapper or agent object that is being called by the client to access the real serving
> object behind the scenes. Use of the proxy can simply be forwarding to the real object, or can
> provide additional logic. In the proxy extra functionality can be provided, for example caching
> when operations on the real object are resource intensive, or checking preconditions before
> operations on the real object are invoked.
**Programmatic Example**
Taking our wizard tower example from above. Firstly we have the wizard tower interface and the ivory tower class
Taking our wizard tower example from above. Firstly we have the `WizardTower` interface and the
`IvoryTower` class.
```java
public interface WizardTower {
@@ -50,7 +60,7 @@ public class IvoryTower implements WizardTower {
}
```
Then a simple wizard class
Then a simple `Wizard` class.
```java
public class Wizard {
@@ -68,7 +78,7 @@ public class Wizard {
}
```
Then we have the proxy to add access control to wizard tower
Then we have the `WizardTowerProxy` to add access control to `WizardTower`.
```java
public class WizardTowerProxy implements WizardTower {
@@ -97,28 +107,41 @@ public class WizardTowerProxy implements WizardTower {
}
```
And here is tower entering scenario
And here is the tower entering scenario.
```java
var proxy = new WizardTowerProxy(new IvoryTower());
proxy.enter(new Wizard("Red wizard")); // Red wizard enters the tower.
proxy.enter(new Wizard("White wizard")); // White wizard enters the tower.
proxy.enter(new Wizard("Black wizard")); // Black wizard enters the tower.
proxy.enter(new Wizard("Green wizard")); // Green wizard is not allowed to enter!
proxy.enter(new Wizard("Brown wizard")); // Brown wizard is not allowed to enter!
proxy.enter(new Wizard("Red wizard"));
proxy.enter(new Wizard("White wizard"));
proxy.enter(new Wizard("Black wizard"));
proxy.enter(new Wizard("Green wizard"));
proxy.enter(new Wizard("Brown wizard"));
```
Program output:
```
Red wizard enters the tower.
White wizard enters the tower.
Black wizard enters the tower.
Green wizard is not allowed to enter!
Brown wizard is not allowed to enter!
```
## Class diagram
![alt text](./etc/proxy.urm.png "Proxy pattern class diagram")
## Applicability
Proxy is applicable whenever there is a need for a more
versatile or sophisticated reference to an object than a simple pointer. Here
are several common situations in which the Proxy pattern is applicable
Proxy is applicable whenever there is a need for a more versatile or sophisticated reference to an
object than a simple pointer. Here are several common situations in which the Proxy pattern is
applicable.
* Remote proxy provides a local representative for an object in a different address space.
* Virtual proxy creates expensive objects on demand.
* Protection proxy controls access to the original object. Protection proxies are useful when objects should have different access rights.
* Protection proxy controls access to the original object. Protection proxies are useful when
objects should have different access rights.
## Typical Use Case
@@ -136,7 +159,8 @@ are several common situations in which the Proxy pattern is applicable
* [java.lang.reflect.Proxy](http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Proxy.html)
* [Apache Commons Proxy](https://commons.apache.org/proper/commons-proxy/)
* Mocking frameworks Mockito, Powermock, EasyMock
* Mocking frameworks [Mockito](https://site.mockito.org/),
[Powermock](https://powermock.github.io/), [EasyMock](https://easymock.org/)
## Related patterns

View File

@@ -1,40 +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.proxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The object to be proxyed.
*/
public class IvoryTower implements WizardTower {
private static final Logger LOGGER = LoggerFactory.getLogger(IvoryTower.class);
public void enter(Wizard wizard) {
LOGGER.info("{} enters the tower.", wizard);
}
}
/*
* 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.proxy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The object to be proxied.
*/
public class IvoryTower implements WizardTower {
private static final Logger LOGGER = LoggerFactory.getLogger(IvoryTower.class);
public void enter(Wizard wizard) {
LOGGER.info("{} enters the tower.", wizard);
}
}

View File

@@ -9,26 +9,33 @@ tags:
---
## Intent
Repository layer is added between the domain and data mapping layers to isolate domain objects from details of the
database access code and to minimize scattering and duplication of query code. The Repository pattern is especially
useful in systems where number of domain classes is large or heavy querying is utilized.
Repository layer is added between the domain and data mapping layers to isolate domain objects from
details of the database access code and to minimize scattering and duplication of query code. The
Repository pattern is especially useful in systems where number of domain classes is large or heavy
querying is utilized.
## Explanation
Real world example
> Let's say we need a persistent data store for persons. Adding new persons and searching for them according to different criteria must be easy.
> Let's say we need a persistent data store for persons. Adding new persons and searching for them
> according to different criteria must be easy.
In plain words
> Repository architectural pattern creates a uniform layer of data repositories that can be used for CRUD operations.
> Repository architectural pattern creates a uniform layer of data repositories that can be used for
> CRUD operations.
[Microsoft documentation](https://docs.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/infrastructure-persistence-layer-design) says
> Repositories are classes or components that encapsulate the logic required to access data sources. They centralize common data access functionality, providing better maintainability and decoupling the infrastructure or technology used to access databases from the domain model layer.
> Repositories are classes or components that encapsulate the logic required to access data sources.
> They centralize common data access functionality, providing better maintainability and decoupling
> the infrastructure or technology used to access databases from the domain model layer.
**Programmatic Example**
Let's first look at the person class that we need to persist.
Let's first look at the person entity that we need to persist.
```java
@Entity
@@ -39,107 +46,23 @@ public class Person {
private Long id;
private String name;
private String surname;
private int age;
public Person() {
}
/**
* Constructor.
*/
public Person(String name, String surname, int age) {
this.name = name;
this.surname = surname;
this.age = age;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", surname=" + surname + ", age=" + age + "]";
}
@Override
public int hashCode() {
final var prime = 31;
var result = 1;
result = prime * result + age;
result = prime * result + (id == null ? 0 : id.hashCode());
result = prime * result + (name == null ? 0 : name.hashCode());
result = prime * result + (surname == null ? 0 : surname.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
var other = (Person) obj;
if (age != other.age) {
return false;
}
if (id == null) {
if (other.id != null) {
return false;
}
} else if (!id.equals(other.id)) {
return false;
}
if (name == null) {
if (other.name != null) {
return false;
}
} else if (!name.equals(other.name)) {
return false;
}
if (surname == null) {
return other.surname == null;
}
return surname.equals(other.surname);
}
// getters and setters ->
...
}
```
We are using Spring Data to create the repository so it becomes really simple.
We are using Spring Data to create the `PersonRepository` so it becomes really simple.
```java
@Repository
@@ -150,7 +73,7 @@ public interface PersonRepository
}
```
Additionally we define a helper class for specification queries.
Additionally we define a helper class `PersonSpecifications` for specification queries.
```java
public class PersonSpecifications {
@@ -189,7 +112,7 @@ public class PersonSpecifications {
}
```
And here's the repository in action.
And here's the repository example in action.
```java
var peter = new Person("Peter", "Sagan", 17);
@@ -226,30 +149,36 @@ And here's the repository in action.
persons.stream().map(Person::toString).forEach(LOGGER::info);
repository.deleteAll();
// Count Person records: 4
// Person [id=1, name=Peter, surname=Sagan, age=17]
// Person [id=2, name=Nasta, surname=Kuzminova, age=25]
// Person [id=3, name=John, surname=lawrence, age=35]
// Person [id=4, name=Terry, surname=Law, age=36]
// Find by id 2: Person [id=2, name=Barbora, surname=Spotakova, age=25]
// Count Person records: 3
// Find by John is Person [id=3, name=John, surname=lawrence, age=35]
// Find Person with age between 20,40:
// Person [id=3, name=John, surname=lawrence, age=35]
// Person [id=4, name=Terry, surname=Law, age=36]
```
Program output:
```
Count Person records: 4
Person [id=1, name=Peter, surname=Sagan, age=17]
Person [id=2, name=Nasta, surname=Kuzminova, age=25]
Person [id=3, name=John, surname=lawrence, age=35]
Person [id=4, name=Terry, surname=Law, age=36]
Find by id 2: Person [id=2, name=Barbora, surname=Spotakova, age=25]
Count Person records: 3
Find by John is Person [id=3, name=John, surname=lawrence, age=35]
Find Person with age between 20,40:
Person [id=3, name=John, surname=lawrence, age=35]
Person [id=4, name=Terry, surname=Law, age=36]
```
## Class diagram
![alt text](./etc/repository.png "Repository")
## Applicability
Use the Repository pattern when
* The number of domain objects is large
* You want to avoid duplication of query code
* You want to keep the database querying code in single place
* You have multiple data sources
* The number of domain objects is large.
* You want to avoid duplication of query code.
* You want to keep the database querying code in single place.
* You have multiple data sources.
## Real world examples

View File

@@ -23,7 +23,6 @@
package com.iluwatar.repository;
import java.sql.SQLException;
import java.util.List;
import java.util.Properties;
import javax.sql.DataSource;
@@ -89,7 +88,7 @@ public class AppConfig {
* Get transaction manager.
*/
@Bean
public JpaTransactionManager transactionManager() throws SQLException {
public JpaTransactionManager transactionManager() {
var transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;

View File

@@ -60,7 +60,7 @@ public class PersonSpecifications {
*/
public static class NameEqualSpec implements Specification<Person> {
public String name;
public final String name;
public NameEqualSpec(String name) {
this.name = name;

View File

@@ -10,39 +10,40 @@ tags:
---
## Intent
Transparently retry certain operations that involve communication with external resources, particularly over the
network, isolating calling code from the retry implementation details.
Transparently retry certain operations that involve communication with external resources,
particularly over the network, isolating calling code from the retry implementation details.
## Explanation
Retry pattern consists retrying operations on remote resources over the
network a set number of times. It closely depends on both business and technical
requirements: how much time will the business allow the end user to wait while
the operation finishes? What are the performance characteristics of the
remote resource during peak loads as well as our application as more threads
are waiting for the remote resource's availability? Among the errors returned
by the remote service, which can be safely ignored in order to retry? Is the
operation [idempotent](https://en.wikipedia.org/wiki/Idempotence)?
Another concern is the impact on the calling code by implementing the retry
mechanism. The retry mechanics should ideally be completely transparent to the
calling code (service interface remains unaltered). There are two general
approaches to this problem: from an enterprise architecture standpoint
(strategic), and a shared library standpoint (tactical).
Retry pattern consists retrying operations on remote resources over the network a set number of
times. It closely depends on both business and technical requirements: How much time will the
business allow the end user to wait while the operation finishes? What are the performance
characteristics of the remote resource during peak loads as well as our application as more threads
are waiting for the remote resource's availability? Among the errors returned by the remote service,
which can be safely ignored in order to retry? Is the operation
[idempotent](https://en.wikipedia.org/wiki/Idempotence)?
From a strategic point of view, this would be solved by having requests
be redirected to a separate intermediary system, traditionally an
[ESB](https://en.wikipedia.org/wiki/Enterprise_service_bus), but more recently
a [Service Mesh](https://medium.com/microservices-in-practice/service-mesh-for-microservices-2953109a3c9a).
Another concern is the impact on the calling code by implementing the retry mechanism. The retry
mechanics should ideally be completely transparent to the calling code (service interface remains
unaltered). There are two general approaches to this problem: From an enterprise architecture
standpoint (strategic), and a shared library standpoint (tactical).
From a tactical point of view, this would be solved by reusing shared libraries
like [Hystrix](https://github.com/Netflix/Hystrix) (please note that *Hystrix* is a complete implementation of
the [Circuit Breaker](https://java-design-patterns.com/patterns/circuit-breaker/) pattern, of which the Retry pattern
can be considered a subset of.). This is the type of solution showcased in the simple example that accompanies this
*README*.
From a strategic point of view, this would be solved by having requests redirected to a separate
intermediary system, traditionally an [ESB](https://en.wikipedia.org/wiki/Enterprise_service_bus),
but more recently a [Service Mesh](https://medium.com/microservices-in-practice/service-mesh-for-microservices-2953109a3c9a).
From a tactical point of view, this would be solved by reusing shared libraries like
[Hystrix](https://github.com/Netflix/Hystrix) (please note that Hystrix is a complete implementation
of the [Circuit Breaker](https://java-design-patterns.com/patterns/circuit-breaker/) pattern, of
which the Retry pattern can be considered a subset of). This is the type of solution showcased in
the simple example that accompanies this `README.md`.
Real world example
> Our application uses a service providing customer information. Once in a while the service seems to be flaky and can return errors or sometimes it just times out. To circumvent these problems we apply the retry pattern.
> Our application uses a service providing customer information. Once in a while the service seems
> to be flaky and can return errors or sometimes it just times out. To circumvent these problems we
> apply the retry pattern.
In plain words
@@ -50,11 +51,14 @@ In plain words
[Microsoft documentation](https://docs.microsoft.com/en-us/azure/architecture/patterns/retry) says
> Enable an application to handle transient failures when it tries to connect to a service or network resource, by transparently retrying a failed operation. This can improve the stability of the application.
> Enable an application to handle transient failures when it tries to connect to a service or
> network resource, by transparently retrying a failed operation. This can improve the stability of
> the application.
**Programmatic Example**
In our hypothetical application, we have a generic interface for all operations on remote interfaces.
In our hypothetical application, we have a generic interface for all operations on remote
interfaces.
```java
public interface BusinessOperation<T> {
@@ -73,16 +77,14 @@ public final class FindCustomer implements BusinessOperation<String> {
}
```
Our `FindCustomer` implementation can be configured to throw
`BusinessException`s before returning the customer's ID, thereby simulating a
'flaky' service that intermittently fails. Some exceptions, like the
`CustomerNotFoundException`, are deemed to be recoverable after some
hypothetical analysis because the root cause of the error stems from "some
database locking issue". However, the `DatabaseNotAvailableException` is
considered to be a definite showstopper - the application should not attempt
to recover from this error.
Our `FindCustomer` implementation can be configured to throw `BusinessException`s before returning
the customer's ID, thereby simulating a flaky service that intermittently fails. Some exceptions,
like the `CustomerNotFoundException`, are deemed to be recoverable after some hypothetical analysis
because the root cause of the error stems from "some database locking issue". However, the
`DatabaseNotAvailableException` is considered to be a definite showstopper - the application should
not attempt to recover from this error.
We can model a 'recoverable' scenario by instantiating `FindCustomer` like this:
We can model a recoverable scenario by instantiating `FindCustomer` like this:
```java
final var op = new FindCustomer(
@@ -93,15 +95,12 @@ final var op = new FindCustomer(
);
```
In this configuration, `FindCustomer` will throw `CustomerNotFoundException`
three times, after which it will consistently return the customer's ID
(`12345`).
In this configuration, `FindCustomer` will throw `CustomerNotFoundException` three times, after
which it will consistently return the customer's ID (`12345`).
In our hypothetical scenario, our analysts indicate that this operation
typically fails 2-4 times for a given input during peak hours, and that each
worker thread in the database subsystem typically needs 50ms to
"recover from an error". Applying these policies would yield something like
this:
In our hypothetical scenario, our analysts indicate that this operation typically fails 2-4 times
for a given input during peak hours, and that each worker thread in the database subsystem typically
needs 50ms to "recover from an error". Applying these policies would yield something like this:
```java
final var op = new Retry<>(
@@ -117,26 +116,27 @@ final var op = new Retry<>(
);
```
Executing `op` *once* would automatically trigger at most 5 retry attempts,
with a 100 millisecond delay between attempts, ignoring any
`CustomerNotFoundException` thrown while trying. In this particular scenario,
due to the configuration for `FindCustomer`, there will be 1 initial attempt
Executing `op` once would automatically trigger at most 5 retry attempts, with a 100 millisecond
delay between attempts, ignoring any `CustomerNotFoundException` thrown while trying. In this
particular scenario, due to the configuration for `FindCustomer`, there will be 1 initial attempt
and 3 additional retries before finally returning the desired result `12345`.
If our `FindCustomer` operation were instead to throw a fatal
`DatabaseNotFoundException`, which we were instructed not to ignore, but
more importantly we did *not* instruct our `Retry` to ignore, then the operation
would have failed immediately upon receiving the error, not matter how many
attempts were left.
If our `FindCustomer` operation were instead to throw a fatal `DatabaseNotFoundException`, which we
were instructed not to ignore, but more importantly we did not instruct our `Retry` to ignore, then
the operation would have failed immediately upon receiving the error, not matter how many attempts
were left.
## Class diagram
![alt text](./etc/retry.png "Retry")
## Applicability
Whenever an application needs to communicate with an external resource, particularly in a cloud environment, and if
the business requirements allow it.
Whenever an application needs to communicate with an external resource, particularly in a cloud
environment, and if the business requirements allow it.
## Consequences
**Pros:**
* Resiliency

View File

@@ -0,0 +1,119 @@
---
layout: pattern
title: Separated Interface
folder: separated-interface
permalink: /patterns/separated-interface/
categories: Structural
tags:
- Decoupling
---
## Intent
Separate the interface definition and implementation in different packages. This allows the client to be completely 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 ([Service Programming Interface](https://en.wikipedia.org/wiki/Service_provider_interface) is an API intended and open to be implemented or extended by a third party). 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
```ForeignTaxCalculator``` which levies 60% tax for international products.
```java
public class ForeignTaxCalculator implements TaxCalculator {
public static final double TAX_PERCENTAGE = 60;
@Override
public double calculate(double amount) {
return amount * TAX_PERCENTAGE / 100.0;
}
}
```
```DomesticTaxCalculator``` which levies 20% tax for international products.
```java
public class DomesticTaxCalculator 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 ForeignTaxCalculator());
LOGGER.info("Foreign Tax applied: {}", "" + internationalProductInvoice.getAmountWithTax());
var domesticProductInvoice = new InvoiceGenerator(PRODUCT_COST, new DomesticTaxCalculator());
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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -0,0 +1,36 @@
@startuml
package com.iluwatar.separatedinterface {
class App {
- LOGGER : Logger {static}
+ PRODUCT_COST : double {static}
+ App()
+ main(args : String[]) {static}
}
}
package com.iluwatar.separatedinterface.taxes {
class DomesticTaxCalculator {
+ TAX_PERCENTAGE : double {static}
+ DomesticTaxCalculator()
+ calculate(amount : double) : double
}
class ForeignTaxCalculator {
+ TAX_PERCENTAGE : double {static}
+ ForeignTaxCalculator()
+ calculate(amount : double) : double
}
}
package com.iluwatar.separatedinterface.invoice {
class InvoiceGenerator {
- amount : double
- taxCalculator : TaxCalculator
+ InvoiceGenerator(amount : double, taxCalculator : TaxCalculator)
+ getAmountWithTax() : double
}
interface TaxCalculator {
+ calculate(double) : double {abstract}
}
}
InvoiceGenerator --> "-taxCalculator" TaxCalculator
DomesticTaxCalculator ..|> TaxCalculator
ForeignTaxCalculator ..|> TaxCalculator
@enduml

View File

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

View File

@@ -0,0 +1,62 @@
/*
* The MIT License
* Copyright © 2014-2019 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.separatedinterface;
import com.iluwatar.separatedinterface.invoice.InvoiceGenerator;
import com.iluwatar.separatedinterface.taxes.DomesticTaxCalculator;
import com.iluwatar.separatedinterface.taxes.ForeignTaxCalculator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* <p>The Separated Interface pattern encourages to separate the interface definition and
* implementation in different packages. This allows the client to be completely unaware of the
* implementation.</p>
*
* <p>In this class the {@link InvoiceGenerator} class is injected with different instances of
* {@link com.iluwatar.separatedinterface.invoice.TaxCalculator} implementations located in separate
* packages, to receive different responses for both of the implementations.</p>
*/
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 ForeignTaxCalculator());
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 DomesticTaxCalculator());
LOGGER.info("Domestic Tax applied: {}", "" + domesticProductInvoice.getAmountWithTax());
}
}

View File

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

View File

@@ -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);
}

View File

@@ -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 DomesticTaxCalculator implements TaxCalculator {
public static final double TAX_PERCENTAGE = 20;
@Override
public double calculate(double amount) {
return amount * TAX_PERCENTAGE / 100.0;
}
}

View File

@@ -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 ForeignTaxCalculator implements TaxCalculator {
public static final double TAX_PERCENTAGE = 60;
@Override
public double calculate(double amount) {
return amount * TAX_PERCENTAGE / 100.0;
}
}

View File

@@ -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[]{}));
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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;
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);
}
}

View File

@@ -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.taxes;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class DomesticTaxCalculatorTest {
private DomesticTaxCalculator target;
@Test
public void testTaxCalculation(){
target = new DomesticTaxCalculator();
var tax=target.calculate(100.0);
Assertions.assertEquals(tax,20.0);
}
}

View File

@@ -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.taxes;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class ForeignTaxCalculatorTest {
private ForeignTaxCalculator target;
@Test
public void testTaxCalculation(){
target = new ForeignTaxCalculator();
var tax=target.calculate(100.0);
Assertions.assertEquals(tax,60.0);
}
}

View File

@@ -33,7 +33,7 @@ import org.slf4j.LoggerFactory;
* <p>One of the risks of this pattern is that bugs resulting from setting a singleton up in a
* distributed environment can be tricky to debug, since it will work fine if you debug with a
* single classloader. Additionally, these problems can crop up a while after the implementation of
* a singleton, since they may start out synchronous and only become async with time, so you it may
* a singleton, since they may start out synchronous and only become async with time, so it may
* not be clear why you are seeing certain changes in behaviour.</p>
*
* <p>There are many ways to implement the Singleton. The first one is the eagerly initialized