Skip to content

Commit cdb78d7

Browse files
feat: Money pattern (iluwatar#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>
1 parent ac53960 commit cdb78d7

File tree

8 files changed

+529
-1
lines changed

8 files changed

+529
-1
lines changed

money/README.md

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

money/pom.xml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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+
41+
<dependencies>
42+
<dependency>
43+
<groupId>org.junit.jupiter</groupId>
44+
<artifactId>junit-jupiter-api</artifactId>
45+
<scope>test</scope>
46+
</dependency>
47+
</dependencies>
48+
49+
</project>
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.iluwatar;
2+
3+
import java.util.logging.Level;
4+
import java.util.logging.Logger;
5+
/**
6+
* The `App` class demonstrates the functionality of the {@link Money} class, which encapsulates
7+
* monetary values and their associated currencies. It showcases operations like addition,
8+
* subtraction, multiplication, and currency conversion, while ensuring validation and immutability.
9+
*
10+
* <p>Through this example, the handling of invalid operations (e.g., mismatched currencies or
11+
* invalid inputs) is demonstrated using custom exceptions. Logging is used for transparency.
12+
*
13+
* <p>This highlights the practical application of object-oriented principles such as encapsulation
14+
* and validation in a financial context.
15+
*/
16+
public class App {
17+
18+
// Initialize the logger
19+
private static final Logger logger = Logger.getLogger(App.class.getName());
20+
/**
21+
* Program entry point.
22+
*
23+
* @param args command line args
24+
*/
25+
public static void main(String[] args) {
26+
// Create instances of Money
27+
Money usdAmount1 = new Money(50.00, "USD");
28+
Money usdAmount2 = new Money(20.00, "USD");
29+
30+
// Demonstrate addition
31+
try {
32+
usdAmount1.addMoney(usdAmount2);
33+
logger.log(Level.INFO, "Sum in USD: {0}", usdAmount1.getAmount());
34+
} catch (CannotAddTwoCurrienciesException e) {
35+
logger.log(Level.SEVERE, "Error adding money: {0}", e.getMessage());
36+
}
37+
38+
// Demonstrate subtraction
39+
try {
40+
usdAmount1.subtractMoney(usdAmount2);
41+
logger.log(Level.INFO, "Difference in USD: {0}", usdAmount1.getAmount());
42+
} catch (CannotSubtractException e) {
43+
logger.log(Level.SEVERE, "Error subtracting money: {0}", e.getMessage());
44+
}
45+
46+
// Demonstrate multiplication
47+
try {
48+
usdAmount1.multiply(2);
49+
logger.log(Level.INFO, "Multiplied Amount in USD: {0}", usdAmount1.getAmount());
50+
} catch (IllegalArgumentException e) {
51+
logger.log(Level.SEVERE, "Error multiplying money: {0}", e.getMessage());
52+
}
53+
54+
// Demonstrate currency conversion
55+
try {
56+
double exchangeRateUsdToEur = 0.85; // Example exchange rate
57+
usdAmount1.exchangeCurrency("EUR", exchangeRateUsdToEur);
58+
logger.log(Level.INFO, "USD converted to EUR: {0} {1}", new Object[]{usdAmount1.getAmount(), usdAmount1.getCurrency()});
59+
} catch (IllegalArgumentException e) {
60+
logger.log(Level.SEVERE, "Error converting currency: {0}", e.getMessage());
61+
}
62+
63+
}
64+
}
65+
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+
}

0 commit comments

Comments
 (0)