Skip to content

Commit fea63d0

Browse files
Money pattern
the implementation of the money patter and the tests along with the read me file
1 parent f9945c9 commit fea63d0

File tree

7 files changed

+456
-0
lines changed

7 files changed

+456
-0
lines changed

money/README.md

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
---
2+
title: "Money Pattern in Java: Encapsulating Monetary Values with Currency Consistency"
3+
shortTitle: Money
4+
description: "Learn how the Money design pattern in Java ensures currency safety, precision handling, and maintainable financial operations. Explore examples, applicability, and benefits of the pattern."
5+
category: Finance
6+
language: en
7+
tag:
8+
- Encapsulation
9+
- Precision handling
10+
- Currency safety
11+
- Value Object
12+
- Financial operations
13+
---
14+
15+
## Also known as
16+
17+
* Value Object
18+
19+
## Intent of Money Design Pattern
20+
21+
The Money design pattern provides a robust way to encapsulate monetary values and their associated currencies. It ensures precise calculations, currency consistency, and maintainability of financial logic in Java applications.
22+
23+
## Detailed Explanation of Money Pattern with Real-World Examples
24+
25+
### Real-world example
26+
27+
> Imagine an e-commerce platform where customers shop in their local currencies. The platform needs to calculate order totals, taxes, and discounts accurately while handling multiple currencies seamlessly.
28+
29+
In this example:
30+
- Each monetary value (like a product price or tax amount) is encapsulated in a `Money` object.
31+
- The `Money` class ensures that only values in the same currency are combined and supports safe currency conversion for global operations.
32+
33+
### In plain words
34+
35+
> The Money pattern encapsulates both an amount and its currency, ensuring financial operations are precise, consistent, and maintainable.
36+
37+
### Wikipedia says
38+
39+
> "The Money design pattern encapsulates a monetary value and its currency, allowing for safe arithmetic operations and conversions while preserving accuracy and consistency in financial calculations."
40+
41+
## Programmatic Example of Money Pattern in Java
42+
43+
### Money Class
44+
45+
```java
46+
package com.iluwatar;
47+
48+
import lombok.Getter;
49+
50+
/**
51+
* Represents a monetary value with an associated currency.
52+
* Provides operations for basic arithmetic (addition, subtraction, multiplication),
53+
* as well as currency conversion while ensuring proper rounding.
54+
*/
55+
@Getter
56+
public class Money {
57+
private @Getter double amount;
58+
private @Getter String currency;
59+
60+
public Money(double amnt, String curr) {
61+
this.amount = amnt;
62+
this.currency = curr;
63+
}
64+
65+
private double roundToTwoDecimals(double value) {
66+
return Math.round(value * 100.0) / 100.0;
67+
}
68+
69+
public void addMoney(Money moneyToBeAdded) throws CannotAddTwoCurrienciesException {
70+
if (!moneyToBeAdded.getCurrency().equals(this.currency)) {
71+
throw new CannotAddTwoCurrienciesException("You are trying to add two different currencies");
72+
}
73+
this.amount = roundToTwoDecimals(this.amount + moneyToBeAdded.getAmount());
74+
}
75+
76+
public void subtractMoney(Money moneyToBeSubtracted) throws CannotSubtractException {
77+
if (!moneyToBeSubtracted.getCurrency().equals(this.currency)) {
78+
throw new CannotSubtractException("You are trying to subtract two different currencies");
79+
} else if (moneyToBeSubtracted.getAmount() > this.amount) {
80+
throw new CannotSubtractException("The amount you are trying to subtract is larger than the amount you have");
81+
}
82+
this.amount = roundToTwoDecimals(this.amount - moneyToBeSubtracted.getAmount());
83+
}
84+
85+
public void multiply(int factor) {
86+
if (factor < 0) {
87+
throw new IllegalArgumentException("Factor must be non-negative");
88+
}
89+
this.amount = roundToTwoDecimals(this.amount * factor);
90+
}
91+
92+
public void exchangeCurrency(String currencyToChangeTo, double exchangeRate) {
93+
if (exchangeRate < 0) {
94+
throw new IllegalArgumentException("Exchange rate must be non-negative");
95+
}
96+
this.amount = roundToTwoDecimals(this.amount * exchangeRate);
97+
this.currency = currencyToChangeTo;
98+
}
99+
}
100+
101+
## When to Use the Money Pattern
102+
103+
The Money pattern should be used in scenarios where:
104+
105+
1. **Currency-safe arithmetic operations**
106+
To ensure that arithmetic operations like addition, subtraction, and multiplication are performed only between amounts in the same currency, preventing inconsistencies or errors in calculations.
107+
108+
2. **Accurate rounding for financial calculations**
109+
Precise rounding to two decimal places is critical to maintain accuracy and consistency in financial systems.
110+
111+
3. **Consistent currency conversion**
112+
When handling international transactions or displaying monetary values in different currencies, the Money pattern facilitates easy and reliable conversion using exchange rates.
113+
114+
4. **Encapsulation of monetary logic**
115+
By encapsulating all monetary operations within a dedicated class, the Money pattern improves maintainability and reduces the likelihood of errors.
116+
117+
5. **Preventing errors in financial operations**
118+
Strict validation ensures that operations like subtraction or multiplication are only performed when conditions are met, safeguarding against misuse or logical errors.
119+
120+
6. **Handling diverse scenarios in financial systems**
121+
Useful in complex systems like e-commerce, banking, and payroll applications where precise and consistent monetary value handling is crucial.
122+
123+
---
124+
## Benefits and Trade-offs of Money Pattern
125+
126+
### Benefits
127+
1. **Precision and Accuracy**
128+
The Money pattern ensures precise handling of monetary values, reducing the risk of rounding errors.
129+
130+
2. **Encapsulation of Business Logic**
131+
By encapsulating monetary operations, the pattern enhances maintainability and reduces redundancy in financial systems.
132+
133+
3. **Currency Safety**
134+
It ensures operations are performed only between amounts of the same currency, avoiding logical errors.
135+
136+
4. **Improved Readability**
137+
By abstracting monetary logic into a dedicated class, the code becomes easier to read and maintain.
138+
139+
5. **Ease of Extension**
140+
Adding new operations, handling different currencies, or incorporating additional business rules is straightforward.
141+
142+
### Trade-offs
143+
1. **Increased Complexity**
144+
Introducing a dedicated `Money` class can add some overhead, especially for small or simple projects.
145+
146+
2. **Potential for Misuse**
147+
Without proper validation and handling, incorrect usage of the Money pattern may introduce subtle bugs.
148+
149+
3. **Performance Overhead**
150+
Precision and encapsulation might slightly affect performance in systems with extremely high transaction volumes.
151+
152+
---
153+
154+
## Related Design Patterns
155+
156+
1. **Value Object**
157+
Money is a classic example of the Value Object pattern, where objects are immutable and define equality based on their value.
158+
159+
2. **Factory Method**
160+
Factories can be employed to handle creation logic, such as applying default exchange rates or rounding rules.
161+
162+
---
163+
164+
## References and Credits
165+
166+
- [Patterns of Enterprise Application Architecture](https://martinfowler.com/eaaCatalog/money.html) by Martin Fowler
167+
- [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)

money/pom.xml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
5+
6+
The MIT License
7+
Copyright © 2014-2022 Ilkka Seppälä
8+
9+
Permission is hereby granted, free of charge, to any person obtaining a copy
10+
of this software and associated documentation files (the "Software"), to deal
11+
in the Software without restriction, including without limitation the rights
12+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
copies of the Software, and to permit persons to whom the Software is
14+
furnished to do so, subject to the following conditions:
15+
16+
The above copyright notice and this permission notice shall be included in
17+
all copies or substantial portions of the Software.
18+
19+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
THE SOFTWARE.
26+
27+
-->
28+
<project xmlns="http://maven.apache.org/POM/4.0.0"
29+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
30+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
31+
<modelVersion>4.0.0</modelVersion>
32+
<parent>
33+
<groupId>com.iluwatar</groupId>
34+
<artifactId>java-design-patterns</artifactId>
35+
<version>1.26.0-SNAPSHOT</version>
36+
</parent>
37+
38+
<artifactId>money</artifactId>
39+
40+
<properties>
41+
<maven.compiler.source>17</maven.compiler.source>
42+
<maven.compiler.target>17</maven.compiler.target>
43+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
44+
</properties>
45+
<dependencies>
46+
<dependency>
47+
<groupId>org.junit.jupiter</groupId>
48+
<artifactId>junit-jupiter-api</artifactId>
49+
<scope>test</scope>
50+
</dependency>
51+
</dependencies>
52+
53+
</project>
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.iluwatar;
2+
/**
3+
* An exception for when the user tries to add two diffrent currencies.
4+
*/
5+
public class CannotAddTwoCurrienciesException extends Exception {
6+
/**
7+
* Constructs an exception with the specified message.
8+
*
9+
* @param message the message shown in the terminal (as a String).
10+
*/
11+
public CannotAddTwoCurrienciesException(String message) {
12+
super(message);
13+
}
14+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.iluwatar;
2+
/**
3+
* An exception for when the user tries to subtract two diffrent currencies or subtract an amount he doesn't have.
4+
*/
5+
public class CannotSubtractException extends Exception {
6+
/**
7+
* Constructs an exception with the specified message.
8+
*
9+
* @param message the message shown in the terminal (as a String).
10+
*/
11+
public CannotSubtractException(String message) {
12+
super(message);
13+
}
14+
15+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package com.iluwatar;
2+
3+
import lombok.Getter;
4+
5+
/**
6+
* Represents a monetary value with an associated currency.
7+
* Provides operations for basic arithmetic (addition, subtraction, multiplication),
8+
* as well as currency conversion while ensuring proper rounding.
9+
*/
10+
@Getter
11+
public class Money {
12+
private @Getter double amount;
13+
private @Getter String currency;
14+
15+
/**
16+
* Constructs a Money object with the specified amount and currency.
17+
*
18+
* @param amnt the amount of money (as a double).
19+
* @param curr the currency code (e.g., "USD", "EUR").
20+
*/
21+
public Money(double amnt, String curr) {
22+
this.amount = amnt;
23+
this.currency = curr;
24+
}
25+
26+
/**
27+
* Rounds the given value to two decimal places.
28+
*
29+
* @param value the value to round.
30+
* @return the rounded value, up to two decimal places.
31+
*/
32+
private double roundToTwoDecimals(double value) {
33+
return Math.round(value * 100.0) / 100.0;
34+
}
35+
36+
/**
37+
* Adds another Money object to the current instance.
38+
*
39+
* @param moneyToBeAdded the Money object to add.
40+
* @throws CannotAddTwoCurrienciesException if the currencies do not match.
41+
*/
42+
public void addMoney(Money moneyToBeAdded) throws CannotAddTwoCurrienciesException {
43+
if (!moneyToBeAdded.getCurrency().equals(this.currency)) {
44+
throw new CannotAddTwoCurrienciesException("You are trying to add two different currencies");
45+
}
46+
this.amount = roundToTwoDecimals(this.amount + moneyToBeAdded.getAmount());
47+
}
48+
49+
/**
50+
* Subtracts another Money object from the current instance.
51+
*
52+
* @param moneyToBeSubtracted the Money object to subtract.
53+
* @throws CannotSubtractException if the currencies do not match or if the amount to subtract is larger than the current amount.
54+
*/
55+
public void subtractMoney(Money moneyToBeSubtracted) throws CannotSubtractException {
56+
if (!moneyToBeSubtracted.getCurrency().equals(this.currency)) {
57+
throw new CannotSubtractException("You are trying to subtract two different currencies");
58+
} else if (moneyToBeSubtracted.getAmount() > this.amount) {
59+
throw new CannotSubtractException("The amount you are trying to subtract is larger than the amount you have");
60+
}
61+
this.amount = roundToTwoDecimals(this.amount - moneyToBeSubtracted.getAmount());
62+
}
63+
64+
/**
65+
* Multiplies the current amount of money by a factor.
66+
*
67+
* @param factor the factor to multiply by.
68+
* @throws IllegalArgumentException if the factor is negative.
69+
*/
70+
public void multiply(int factor) {
71+
if (factor < 0) {
72+
throw new IllegalArgumentException("Factor must be non-negative");
73+
}
74+
this.amount = roundToTwoDecimals(this.amount * factor);
75+
}
76+
77+
/**
78+
* Converts the current amount of money to another currency using the provided exchange rate.
79+
*
80+
* @param currencyToChangeTo the new currency to convert to.
81+
* @param exchangeRate the exchange rate to convert from the current currency to the new currency.
82+
* @throws IllegalArgumentException if the exchange rate is negative.
83+
*/
84+
public void exchangeCurrency(String currencyToChangeTo, double exchangeRate) {
85+
if (exchangeRate < 0) {
86+
throw new IllegalArgumentException("Exchange rate must be non-negative");
87+
}
88+
this.amount = roundToTwoDecimals(this.amount * exchangeRate);
89+
this.currency = currencyToChangeTo;
90+
}
91+
}

0 commit comments

Comments
 (0)