mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-09-06 02:18:46 +00:00
feat: Money pattern (#3109)
* Money pattern the implementation of the money patter and the tests along with the read me file * Update money/README.md Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com> * added App file anf modified README file and pom.xml file * modified README and pom.xml * added comments * Added a test for App.java --------- Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>
This commit is contained in:
co-authored by
Ilkka Seppälä
parent
ac539606b1
commit
cdb78d7fb6
@@ -0,0 +1,65 @@
|
||||
package com.iluwatar;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
/**
|
||||
* The `App` class demonstrates the functionality of the {@link Money} class, which encapsulates
|
||||
* monetary values and their associated currencies. It showcases operations like addition,
|
||||
* subtraction, multiplication, and currency conversion, while ensuring validation and immutability.
|
||||
*
|
||||
* <p>Through this example, the handling of invalid operations (e.g., mismatched currencies or
|
||||
* invalid inputs) is demonstrated using custom exceptions. Logging is used for transparency.
|
||||
*
|
||||
* <p>This highlights the practical application of object-oriented principles such as encapsulation
|
||||
* and validation in a financial context.
|
||||
*/
|
||||
public class App {
|
||||
|
||||
// Initialize the logger
|
||||
private static final Logger logger = Logger.getLogger(App.class.getName());
|
||||
/**
|
||||
* Program entry point.
|
||||
*
|
||||
* @param args command line args
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
// Create instances of Money
|
||||
Money usdAmount1 = new Money(50.00, "USD");
|
||||
Money usdAmount2 = new Money(20.00, "USD");
|
||||
|
||||
// Demonstrate addition
|
||||
try {
|
||||
usdAmount1.addMoney(usdAmount2);
|
||||
logger.log(Level.INFO, "Sum in USD: {0}", usdAmount1.getAmount());
|
||||
} catch (CannotAddTwoCurrienciesException e) {
|
||||
logger.log(Level.SEVERE, "Error adding money: {0}", e.getMessage());
|
||||
}
|
||||
|
||||
// Demonstrate subtraction
|
||||
try {
|
||||
usdAmount1.subtractMoney(usdAmount2);
|
||||
logger.log(Level.INFO, "Difference in USD: {0}", usdAmount1.getAmount());
|
||||
} catch (CannotSubtractException e) {
|
||||
logger.log(Level.SEVERE, "Error subtracting money: {0}", e.getMessage());
|
||||
}
|
||||
|
||||
// Demonstrate multiplication
|
||||
try {
|
||||
usdAmount1.multiply(2);
|
||||
logger.log(Level.INFO, "Multiplied Amount in USD: {0}", usdAmount1.getAmount());
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.log(Level.SEVERE, "Error multiplying money: {0}", e.getMessage());
|
||||
}
|
||||
|
||||
// Demonstrate currency conversion
|
||||
try {
|
||||
double exchangeRateUsdToEur = 0.85; // Example exchange rate
|
||||
usdAmount1.exchangeCurrency("EUR", exchangeRateUsdToEur);
|
||||
logger.log(Level.INFO, "USD converted to EUR: {0} {1}", new Object[]{usdAmount1.getAmount(), usdAmount1.getCurrency()});
|
||||
} catch (IllegalArgumentException e) {
|
||||
logger.log(Level.SEVERE, "Error converting currency: {0}", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.iluwatar;
|
||||
/**
|
||||
* An exception for when the user tries to add two diffrent currencies.
|
||||
*/
|
||||
public class CannotAddTwoCurrienciesException extends Exception {
|
||||
/**
|
||||
* Constructs an exception with the specified message.
|
||||
*
|
||||
* @param message the message shown in the terminal (as a String).
|
||||
*/
|
||||
public CannotAddTwoCurrienciesException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.iluwatar;
|
||||
/**
|
||||
* An exception for when the user tries to subtract two diffrent currencies or subtract an amount he doesn't have.
|
||||
*/
|
||||
public class CannotSubtractException extends Exception {
|
||||
/**
|
||||
* Constructs an exception with the specified message.
|
||||
*
|
||||
* @param message the message shown in the terminal (as a String).
|
||||
*/
|
||||
public CannotSubtractException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.iluwatar;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Represents a monetary value with an associated currency.
|
||||
* Provides operations for basic arithmetic (addition, subtraction, multiplication),
|
||||
* as well as currency conversion while ensuring proper rounding.
|
||||
*/
|
||||
@Getter
|
||||
public class Money {
|
||||
private @Getter double amount;
|
||||
private @Getter String currency;
|
||||
|
||||
/**
|
||||
* Constructs a Money object with the specified amount and currency.
|
||||
*
|
||||
* @param amnt the amount of money (as a double).
|
||||
* @param curr the currency code (e.g., "USD", "EUR").
|
||||
*/
|
||||
public Money(double amnt, String curr) {
|
||||
this.amount = amnt;
|
||||
this.currency = curr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds the given value to two decimal places.
|
||||
*
|
||||
* @param value the value to round.
|
||||
* @return the rounded value, up to two decimal places.
|
||||
*/
|
||||
private double roundToTwoDecimals(double value) {
|
||||
return Math.round(value * 100.0) / 100.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds another Money object to the current instance.
|
||||
*
|
||||
* @param moneyToBeAdded the Money object to add.
|
||||
* @throws CannotAddTwoCurrienciesException if the currencies do not match.
|
||||
*/
|
||||
public void addMoney(Money moneyToBeAdded) throws CannotAddTwoCurrienciesException {
|
||||
if (!moneyToBeAdded.getCurrency().equals(this.currency)) {
|
||||
throw new CannotAddTwoCurrienciesException("You are trying to add two different currencies");
|
||||
}
|
||||
this.amount = roundToTwoDecimals(this.amount + moneyToBeAdded.getAmount());
|
||||
}
|
||||
|
||||
/**
|
||||
* Subtracts another Money object from the current instance.
|
||||
*
|
||||
* @param moneyToBeSubtracted the Money object to subtract.
|
||||
* @throws CannotSubtractException if the currencies do not match or if the amount to subtract is larger than the current amount.
|
||||
*/
|
||||
public void subtractMoney(Money moneyToBeSubtracted) throws CannotSubtractException {
|
||||
if (!moneyToBeSubtracted.getCurrency().equals(this.currency)) {
|
||||
throw new CannotSubtractException("You are trying to subtract two different currencies");
|
||||
} else if (moneyToBeSubtracted.getAmount() > this.amount) {
|
||||
throw new CannotSubtractException("The amount you are trying to subtract is larger than the amount you have");
|
||||
}
|
||||
this.amount = roundToTwoDecimals(this.amount - moneyToBeSubtracted.getAmount());
|
||||
}
|
||||
|
||||
/**
|
||||
* Multiplies the current amount of money by a factor.
|
||||
*
|
||||
* @param factor the factor to multiply by.
|
||||
* @throws IllegalArgumentException if the factor is negative.
|
||||
*/
|
||||
public void multiply(int factor) {
|
||||
if (factor < 0) {
|
||||
throw new IllegalArgumentException("Factor must be non-negative");
|
||||
}
|
||||
this.amount = roundToTwoDecimals(this.amount * factor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the current amount of money to another currency using the provided exchange rate.
|
||||
*
|
||||
* @param currencyToChangeTo the new currency to convert to.
|
||||
* @param exchangeRate the exchange rate to convert from the current currency to the new currency.
|
||||
* @throws IllegalArgumentException if the exchange rate is negative.
|
||||
*/
|
||||
public void exchangeCurrency(String currencyToChangeTo, double exchangeRate) {
|
||||
if (exchangeRate < 0) {
|
||||
throw new IllegalArgumentException("Exchange rate must be non-negative");
|
||||
}
|
||||
this.amount = roundToTwoDecimals(this.amount * exchangeRate);
|
||||
this.currency = currencyToChangeTo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.iluwater.money;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import com.iluwatar.CannotAddTwoCurrienciesException;
|
||||
import com.iluwatar.CannotSubtractException;
|
||||
import com.iluwatar.Money;
|
||||
import com.iluwatar.App;
|
||||
|
||||
|
||||
class MoneyTest {
|
||||
|
||||
@Test
|
||||
void testConstructor() {
|
||||
// Test the constructor
|
||||
Money money = new Money(100.00, "USD");
|
||||
assertEquals(100.00, money.getAmount());
|
||||
assertEquals("USD", money.getCurrency());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddMoney_SameCurrency() throws CannotAddTwoCurrienciesException {
|
||||
// Test adding two Money objects with the same currency
|
||||
Money money1 = new Money(100.00, "USD");
|
||||
Money money2 = new Money(50.25, "USD");
|
||||
|
||||
money1.addMoney(money2);
|
||||
|
||||
assertEquals(150.25, money1.getAmount(), "Amount after addition should be 150.25");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddMoney_DifferentCurrency() {
|
||||
// Test adding two Money objects with different currencies
|
||||
Money money1 = new Money(100.00, "USD");
|
||||
Money money2 = new Money(50.25, "EUR");
|
||||
|
||||
assertThrows(CannotAddTwoCurrienciesException.class, () -> {
|
||||
money1.addMoney(money2);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSubtractMoney_SameCurrency() throws CannotSubtractException {
|
||||
// Test subtracting two Money objects with the same currency
|
||||
Money money1 = new Money(100.00, "USD");
|
||||
Money money2 = new Money(50.25, "USD");
|
||||
|
||||
money1.subtractMoney(money2);
|
||||
|
||||
assertEquals(49.75, money1.getAmount(), "Amount after subtraction should be 49.75");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSubtractMoney_DifferentCurrency() {
|
||||
// Test subtracting two Money objects with different currencies
|
||||
Money money1 = new Money(100.00, "USD");
|
||||
Money money2 = new Money(50.25, "EUR");
|
||||
|
||||
assertThrows(CannotSubtractException.class, () -> {
|
||||
money1.subtractMoney(money2);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSubtractMoney_AmountTooLarge() {
|
||||
// Test subtracting an amount larger than the current amount
|
||||
Money money1 = new Money(50.00, "USD");
|
||||
Money money2 = new Money(60.00, "USD");
|
||||
|
||||
assertThrows(CannotSubtractException.class, () -> {
|
||||
money1.subtractMoney(money2);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultiply() {
|
||||
// Test multiplying the money amount by a factor
|
||||
Money money = new Money(100.00, "USD");
|
||||
|
||||
money.multiply(3);
|
||||
|
||||
assertEquals(300.00, money.getAmount(), "Amount after multiplication should be 300.00");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultiply_NegativeFactor() {
|
||||
// Test multiplying by a negative factor
|
||||
Money money = new Money(100.00, "USD");
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
money.multiply(-2);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExchangeCurrency() {
|
||||
// Test converting currency using an exchange rate
|
||||
Money money = new Money(100.00, "USD");
|
||||
|
||||
money.exchangeCurrency("EUR", 0.85);
|
||||
|
||||
assertEquals("EUR", money.getCurrency(), "Currency after conversion should be EUR");
|
||||
assertEquals(85.00, money.getAmount(), "Amount after conversion should be 85.00");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testExchangeCurrency_NegativeExchangeRate() {
|
||||
// Test converting currency with a negative exchange rate
|
||||
Money money = new Money(100.00, "USD");
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
money.exchangeCurrency("EUR", -0.85);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void testAppExecution() {
|
||||
assertDoesNotThrow(() -> {
|
||||
App.main(new String[]{});
|
||||
}, "App execution should not throw any exceptions");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user