-
-
Notifications
You must be signed in to change notification settings - Fork 26.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Money Pattern Implementation - Issue #1305 #1673
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<parent> | ||
<artifactId>java-design-patterns</artifactId> | ||
<groupId>com.iluwatar</groupId> | ||
<version>1.24.0-SNAPSHOT</version> | ||
</parent> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<artifactId>money</artifactId> | ||
|
||
<properties> | ||
<maven.compiler.source>15</maven.compiler.source> | ||
<maven.compiler.target>15</maven.compiler.target> | ||
</properties> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>org.junit.jupiter</groupId> | ||
<artifactId>junit-jupiter-engine</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
</dependencies> | ||
|
||
</project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
package com.iluwatar.money; | ||
|
||
import com.iluwatar.money.exception.BalanceDoesNotExistForAccountException; | ||
import com.iluwatar.money.exception.CurrencyCannotBeExchangedException; | ||
import com.iluwatar.money.exception.InsufficientFundsException; | ||
import com.iluwatar.money.exception.SubtractionCannotOccurException; | ||
import java.util.Arrays; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
|
||
/** | ||
* This class represents a User Account, which holds all the {@link Balance}s | ||
* that this user has created. | ||
*/ | ||
public class Account { | ||
|
||
private final int accountId; | ||
private final Set<Balance> accountBalances = new HashSet<>(); | ||
|
||
public Account(int accountId) { | ||
this.accountId = accountId; | ||
} | ||
|
||
/** | ||
* Creates a new Balance object for the account. | ||
* | ||
* @param money Contains the information required to create the balance. | ||
*/ | ||
public Balance createBalance(Money money) { | ||
Balance createdBalance = new Balance(this, money); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're able to use the |
||
this.accountBalances.add(createdBalance); | ||
return createdBalance; | ||
} | ||
|
||
/** | ||
* Deposits funds into the corresponding {@link Balance} within the {@link Account}. | ||
* | ||
* @param amountToDeposit The {@link Money} object containing the amount to deposit | ||
* and the {@link Currency}. | ||
*/ | ||
public void depositFundsIntoBalance(Balance balance, Money amountToDeposit) { | ||
balance.getMoney().addMoney(amountToDeposit); | ||
} | ||
|
||
/** | ||
* Withdraws funds from an accounts balance. | ||
* | ||
* @param moneyToWithdraw The {@link Money} containing the amount and {@link Currency} | ||
* representation to be withdrawn. | ||
* | ||
* @throws InsufficientFundsException If the {@link Account} doesn't have sufficient funds. | ||
*/ | ||
public void withdrawFromBalance(Balance balance, Money moneyToWithdraw) | ||
throws InsufficientFundsException { | ||
|
||
|
||
if (balance.getMoney().getAmount() > moneyToWithdraw.getAmount()) { | ||
|
||
try { | ||
balance.getMoney().subtractMoneyBy(moneyToWithdraw); | ||
} catch (SubtractionCannotOccurException e) { | ||
System.err.println("Insufficient funds in balance."); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use a logger for output. See Lombok There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check this throughout the example. |
||
} | ||
|
||
} else { | ||
|
||
try { | ||
Balance combinedBalance = combineAllAccountBalances(moneyToWithdraw.getCurrency()); | ||
|
||
if (combinedBalance.getMoney().getAmount() > moneyToWithdraw.getAmount()) { | ||
balance.getMoney().subtractMoneyBy(moneyToWithdraw); | ||
} else { | ||
throw new InsufficientFundsException("Not enough funds on the account."); | ||
} | ||
} catch (SubtractionCannotOccurException e) { | ||
System.err.println("Error while trying to withdraw currency."); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Combines all of the account balances and adds them to the balance that is to be withdrawn from. | ||
* | ||
* @param currencyToBeCombinedInto The currency that the accounts balances should | ||
* be combined into. | ||
* @return The combined {@link Balance} that contains all the converted funds. | ||
*/ | ||
private Balance combineAllAccountBalances(Currency currencyToBeCombinedInto) { | ||
Balance requestedBalance = null; | ||
try { | ||
requestedBalance = retrieveRequestedBalance(currencyToBeCombinedInto); | ||
} catch (BalanceDoesNotExistForAccountException e) { | ||
System.err.println(currencyToBeCombinedInto + "Balance non-existent"); | ||
} | ||
|
||
Balance finalRequestedBalance = requestedBalance; | ||
this.getAccountBalances().forEach(balance -> { | ||
|
||
if (balance.getMoney().getCurrency() != currencyToBeCombinedInto) { | ||
try { | ||
Money convertedCurrency = CurrencyExchange.convertCurrency(balance.getMoney(), | ||
ExchangeMethod.assignExchangeMethodBasedOnInput( | ||
finalRequestedBalance.getMoney().getCurrency())); | ||
|
||
finalRequestedBalance.getMoney().addMoney(convertedCurrency); | ||
} catch (CurrencyCannotBeExchangedException e) { | ||
System.err.println("Currency must not be the same to convert."); | ||
} | ||
} | ||
}); | ||
|
||
return requestedBalance; | ||
} | ||
|
||
/** | ||
* Retrieves the {@link Balance} corresponding with the {@link Currency}. | ||
* | ||
* @param currency The {@link Currency} of the {@link Balance} which is to be retrieved. | ||
* | ||
* @return The {@link Balance} that corresponds to the {@link Currency}. | ||
* | ||
* @throws BalanceDoesNotExistForAccountException If the {@link Balance} which was requested | ||
* doesn't exist. | ||
*/ | ||
private Balance retrieveRequestedBalance(Currency currency) | ||
throws BalanceDoesNotExistForAccountException { | ||
|
||
return this.getAccountBalances().stream() | ||
.filter(balance -> balance.getMoney().getCurrency() == currency).findFirst() | ||
.orElseThrow(BalanceDoesNotExistForAccountException::new); | ||
} | ||
|
||
public void addExistingBalancesToAccount(Balance... balances) { | ||
this.accountBalances.addAll(Arrays.asList(balances)); | ||
} | ||
|
||
public int getAccountId() { | ||
return accountId; | ||
} | ||
|
||
public Set<Balance> getAccountBalances() { | ||
return accountBalances; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
package com.iluwatar.money; | ||
|
||
public class Balance { | ||
|
||
private final Money money; | ||
private final Account account; | ||
private boolean isPrimaryBalance; | ||
|
||
/** | ||
* Constructs a {@link Balance} and links it to the account. | ||
* | ||
* @param account The account this balance belongs to. | ||
* @param money The money that this balance has. | ||
*/ | ||
public Balance(Account account, Money money) { | ||
this.money = money; | ||
this.account = account; | ||
|
||
account.addExistingBalancesToAccount(this); | ||
} | ||
|
||
/** | ||
* Constructs a {@link Balance} and links it to the account. | ||
* Also sets the balance to the accounts primary balance. | ||
* | ||
* @param account The account this balance blongs to. | ||
* @param money The money that this balance has. | ||
* @param primaryBalance Is this balance the accounts primary balance. | ||
*/ | ||
public Balance(Account account, Money money, boolean primaryBalance) { | ||
this.isPrimaryBalance = primaryBalance; | ||
this.account = account; | ||
this.money = money; | ||
|
||
account.addExistingBalancesToAccount(this); | ||
} | ||
|
||
public Money getMoney() { | ||
return money; | ||
} | ||
|
||
public Account getAccount() { | ||
return account; | ||
} | ||
|
||
public boolean isPrimaryBalance() { | ||
return isPrimaryBalance; | ||
} | ||
Comment on lines
+38
to
+48
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To replace boilerplate such as these getters you're able to use Lombok annotations. |
||
|
||
/** | ||
* Converts the {@link Balance} that contains the type of {@link Currency} | ||
* provided in the method parameters, into a primary balance. | ||
* | ||
* @param currency The type of {@link Currency} to assign primary. | ||
*/ | ||
public void convertBalanceToPrimary(Currency currency) { | ||
|
||
account.getAccountBalances().stream().filter(balance -> | ||
balance.isPrimaryBalance && balance.getMoney().getCurrency() != currency) | ||
.findFirst().ifPresent(alreadyExistingPrimaryBalance -> | ||
alreadyExistingPrimaryBalance.isPrimaryBalance = false); | ||
|
||
this.isPrimaryBalance = true; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.iluwatar.money; | ||
|
||
public enum Currency { | ||
USD(100, "USD"), | ||
EUR(100, "USD"); | ||
|
||
private int centFactor; | ||
private String stringRepresentation; | ||
|
||
Currency(int centFactor, String stringRepresentation) { | ||
this.centFactor = centFactor; | ||
this.stringRepresentation = stringRepresentation; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package com.iluwatar.money; | ||
|
||
import com.iluwatar.money.exception.CurrencyCannotBeExchangedException; | ||
|
||
public class CurrencyExchange { | ||
|
||
/** | ||
* Converts the given {@link Money} objects {@link Currency} to another Currency. | ||
* | ||
* @param moneyToExchangeCurrency The {@link Money} object which is to be converted. | ||
* @param exchangeMethod The {@link ExchangeMethod} which is to be used | ||
* in the conversion. | ||
* @return A new {@link Money} object with the converted amount and {@link Currency}. | ||
* @throws CurrencyCannotBeExchangedException If both {@link Currency} objects are the same. | ||
*/ | ||
public static Money convertCurrency(Money moneyToExchangeCurrency, ExchangeMethod exchangeMethod) | ||
throws CurrencyCannotBeExchangedException { | ||
if (moneyToExchangeCurrency.getCurrency() != exchangeMethod.getExchangedCurrency()) { | ||
return new Money(moneyToExchangeCurrency.multiplyMoneyBy(exchangeMethod.getExchangeRatio()), | ||
exchangeMethod.getExchangedCurrency()); | ||
} else { | ||
throw new CurrencyCannotBeExchangedException( | ||
"Currency cannot be the same in order to exchange."); | ||
} | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package com.iluwatar.money; | ||
|
||
import java.util.Arrays; | ||
|
||
public enum ExchangeMethod { | ||
USD_TO_EUR(0.67, Currency.EUR), | ||
EUR_TO_USD(1.5, Currency.USD); | ||
|
||
private final double exchangeRatio; | ||
private final Currency exchangedCurrency; | ||
|
||
ExchangeMethod(double exchangeRatio, Currency resultingCurrency) { | ||
this.exchangeRatio = exchangeRatio; | ||
this.exchangedCurrency = resultingCurrency; | ||
} | ||
|
||
public Currency getExchangedCurrency() { | ||
return exchangedCurrency; | ||
} | ||
|
||
public double getExchangeRatio() { | ||
return exchangeRatio; | ||
} | ||
|
||
public static ExchangeMethod assignExchangeMethodBasedOnInput(Currency currencyToExchangeTo) { | ||
return Arrays.stream(values()).filter(value -> | ||
value.getExchangedCurrency() == currencyToExchangeTo).findFirst().get(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package com.iluwatar.money; | ||
|
||
import com.iluwatar.money.exception.SubtractionCannotOccurException; | ||
|
||
public class Money { | ||
|
||
private long amount; | ||
private final Currency currency; | ||
|
||
|
||
public Money(long amount, Currency currency) { | ||
this.amount = amount; | ||
this.currency = currency; | ||
} | ||
|
||
/** | ||
* Adds both {@link Money} objects {@code amount} to each other and returns a new | ||
* {@link Money} object with the sum of both amounts. | ||
* | ||
* @param moneyToAdd The {@link Money} object which is the be added to this object. | ||
* @return The sum of the amounts of both of the {@link Money} objects. | ||
*/ | ||
public long addMoney(Money moneyToAdd) { | ||
this.amount = this.amount + moneyToAdd.getAmount(); | ||
return this.amount; | ||
} | ||
|
||
/** | ||
* Subtracts from this objects amount with the {@link Money} | ||
* objects amount provided in the method params. | ||
* | ||
* @param moneyToSubtractBy The {@link Money} object containing the amount | ||
* that is to be subtracted from this object. | ||
* @return The difference between the amounts of the {@link Money} objects. | ||
* @throws SubtractionCannotOccurException If this objects amount is less than the {@link Money} | ||
* objects amount which this object | ||
* is to be subtracted by. | ||
*/ | ||
public long subtractMoneyBy(Money moneyToSubtractBy) throws SubtractionCannotOccurException { | ||
if (this.amount < moneyToSubtractBy.amount) { | ||
throw new SubtractionCannotOccurException( | ||
"The amount to subtract is more than what we currently have."); | ||
} | ||
this.amount = this.amount - moneyToSubtractBy.amount; | ||
return this.amount; | ||
} | ||
|
||
/** | ||
* Multiplies this object with the multiplier provided in the method parameter. | ||
* | ||
* @param multiplier Represents the multiplier of which this objects amount | ||
* is to be multiplied by. | ||
* @return A new {@link Money} object with the product of {@code this.amount * multiplier}. | ||
*/ | ||
public long multiplyMoneyBy(double multiplier) { | ||
return Math.round(this.amount * multiplier); | ||
} | ||
|
||
public long getAmount() { | ||
return amount; | ||
} | ||
|
||
public Currency getCurrency() { | ||
return currency; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add the maven-assembly-plugin as in the other patterns, so that the example can be easily and independently executed on the command line.