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:
Ahmed-Taha-981
2025-01-06 19:19:22 +02:00
committed by GitHub
co-authored by Ilkka Seppälä
parent ac539606b1
commit cdb78d7fb6
8 changed files with 529 additions and 1 deletions
+65
View File
@@ -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");
}
}