Skip to content

Commit 9823c3a

Browse files
committed
feat: implements Tyran's Lesson 17 Functional Enhancements
1 parent 1c93014 commit 9823c3a

File tree

9 files changed

+764
-127
lines changed

9 files changed

+764
-127
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package com.codedifferently.lesson17.bank;
2+
3+
import com.codedifferently.lesson17.bank.exceptions.InsufficientFundsException;
4+
import java.util.Set;
5+
6+
/**
7+
* Abstract representation of a bank account, encapsulating common properties and behaviors. This
8+
* class serves as a base for specific account types like CheckingAccount and SavingsAccount. It
9+
* manages account ownership, balance, activation status, and provides methods for deposits,
10+
* withdrawals, and account closure.
11+
*/
12+
public abstract class Account {
13+
14+
protected Set<Customer> owners;
15+
protected String accountNumber;
16+
protected double balance;
17+
protected boolean isActive;
18+
19+
/**
20+
* Gets the account number.
21+
*
22+
* @return The account number.
23+
*/
24+
public String getAccountNumber() {
25+
return accountNumber;
26+
}
27+
28+
/**
29+
* Gets the owners of the account.
30+
*
31+
* @return The owners of the account.
32+
*/
33+
public Set<Customer> getOwners() {
34+
return owners;
35+
}
36+
37+
/**
38+
* Deposits funds into the account.
39+
*
40+
* @param amount The amount to deposit.
41+
*/
42+
public void deposit(double amount) throws IllegalStateException {
43+
if (isClosed()) {
44+
throw new IllegalStateException("Cannot deposit to a closed account");
45+
}
46+
if (amount <= 0) {
47+
throw new IllegalArgumentException("Deposit amount must be positive");
48+
}
49+
balance += amount;
50+
}
51+
52+
/**
53+
* Withdraws funds from the account.
54+
*
55+
* @param amount
56+
* @throws InsufficientFundsException
57+
*/
58+
public void withdraw(double amount) throws InsufficientFundsException {
59+
if (isClosed()) {
60+
throw new IllegalStateException("Cannot withdraw from a closed account");
61+
}
62+
if (amount <= 0) {
63+
throw new IllegalStateException("Withdrawal amount must be positive");
64+
}
65+
if (balance < amount) {
66+
throw new InsufficientFundsException("Account does not have enough funds for withdrawal");
67+
}
68+
balance -= amount;
69+
}
70+
71+
/**
72+
* Gets the balance of the account.
73+
*
74+
* @return The balance of the account.
75+
*/
76+
public double getBalance() {
77+
return balance;
78+
}
79+
80+
/** Closes the account. */
81+
public void closeAccount() throws IllegalStateException {
82+
if (balance > 0) {
83+
throw new IllegalStateException("Cannot close account with a positive balance");
84+
}
85+
isActive = false;
86+
}
87+
88+
/**
89+
* Checks if the account is closed.
90+
*
91+
* @return True if the account is closed, otherwise false.
92+
*/
93+
public boolean isClosed() {
94+
return !isActive;
95+
}
96+
97+
@Override
98+
public int hashCode() {
99+
return accountNumber.hashCode();
100+
}
101+
102+
@Override
103+
public boolean equals(Object obj) {
104+
if (obj instanceof CheckingAccount other) {
105+
return accountNumber.equals(other.accountNumber);
106+
}
107+
return false;
108+
}
109+
110+
@Override
111+
public String toString() {
112+
return "CheckingAccount{"
113+
+ "accountNumber='"
114+
+ accountNumber
115+
+ '\''
116+
+ ", balance="
117+
+ balance
118+
+ ", isActive="
119+
+ isActive
120+
+ '}';
121+
}
122+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package com.codedifferently.lesson17.bank;
2+
3+
import java.util.ArrayList;
4+
import java.util.HashMap;
5+
import java.util.List;
6+
import java.util.Map;
7+
import java.util.UUID;
8+
9+
/**
10+
* An audit log system that tracks and stores transaction records for bank customers. This class
11+
* maintains a mapping of customer IDs to their respective transaction histories, allowing for
12+
* transaction logging, retrieval, and management operations.
13+
*
14+
* <p>The audit log is thread-safe for basic operations but may require external synchronization for
15+
* complex multi-threaded scenarios.
16+
*
17+
* @author Code Differently
18+
* @version 1.0
19+
* @since 1.0
20+
*/
21+
public class AuditLog {
22+
private final Map<UUID, List<String>> logByCustomerId = new HashMap<>();
23+
24+
/**
25+
* Logs a transaction for a specific customer. If this is the first transaction for the customer,
26+
* a new transaction list is created.
27+
*
28+
* @param customerId the unique identifier of the customer performing the transaction
29+
* @param transaction a descriptive string of the transaction that occurred
30+
*/
31+
public void logTransaction(UUID customerId, String transaction) {
32+
logByCustomerId.putIfAbsent(customerId, new ArrayList<>());
33+
logByCustomerId.get(customerId).add(transaction);
34+
}
35+
36+
/**
37+
* Retrieves all transactions for a specific customer in chronological order.
38+
*
39+
* @param customerId the unique identifier of the customer whose transactions to retrieve
40+
* @return an immutable list of transaction strings for the customer, or an empty list if no
41+
* transactions exist for the customer
42+
*/
43+
public List<String> getTransactions(UUID customerId) {
44+
return logByCustomerId.getOrDefault(customerId, List.of());
45+
}
46+
47+
/**
48+
* Retrieves a specific transaction by its index position for a given customer. Transactions are
49+
* indexed in chronological order starting from 0.
50+
*
51+
* @param customerId the unique identifier of the customer
52+
* @param index the zero-based index of the transaction to retrieve
53+
* @return the transaction string at the specified index
54+
* @throws IllegalArgumentException if the index is negative
55+
* @throws IndexOutOfBoundsException if the customer has no transactions or the index is greater
56+
* than or equal to the number of transactions for the customer
57+
*/
58+
public String getTransactionsByNumber(UUID customerId, int index) {
59+
if (index < 0) {
60+
throw new IllegalArgumentException("Index must be non-negative");
61+
}
62+
63+
if (!logByCustomerId.containsKey(customerId)
64+
|| index >= logByCustomerId.get(customerId).size()) {
65+
throw new IndexOutOfBoundsException("Index out of bounds for customer transactions");
66+
}
67+
68+
return logByCustomerId.get(customerId).get(index);
69+
}
70+
71+
/**
72+
* Clears all transaction records for all customers from the audit log. This operation cannot be
73+
* undone.
74+
*/
75+
public void clearLog() {
76+
logByCustomerId.clear();
77+
}
78+
79+
/**
80+
* Clears all transaction records for a specific customer from the audit log. If the customer has
81+
* no existing records, this operation has no effect. This operation cannot be undone.
82+
*
83+
* @param customerId the unique identifier of the customer whose transactions should be cleared
84+
*/
85+
public void clearLogForCustomer(UUID customerId) {
86+
logByCustomerId.remove(customerId);
87+
}
88+
}

lesson_17/bank/bank_app/src/main/java/com/codedifferently/lesson17/bank/BankAtm.java

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,25 @@
1010
public class BankAtm {
1111

1212
private final Map<UUID, Customer> customerById = new HashMap<>();
13-
private final Map<String, CheckingAccount> accountByNumber = new HashMap<>();
13+
private final Map<String, Account> accountByNumber = new HashMap<>();
14+
public AuditLog auditLog = new AuditLog();
1415

1516
/**
1617
* Adds a checking account to the bank.
1718
*
1819
* @param account The account to add.
1920
*/
20-
public void addAccount(CheckingAccount account) {
21+
public void addAccount(Account account) {
2122
accountByNumber.put(account.getAccountNumber(), account);
2223
account
2324
.getOwners()
2425
.forEach(
2526
owner -> {
2627
customerById.put(owner.getId(), owner);
2728
});
29+
auditLog.logTransaction(
30+
account.getOwners().iterator().next().getId(),
31+
"Added account " + account.getAccountNumber());
2832
}
2933

3034
/**
@@ -33,7 +37,7 @@ public void addAccount(CheckingAccount account) {
3337
* @param customerId The ID of the customer.
3438
* @return The unique set of accounts owned by the customer.
3539
*/
36-
public Set<CheckingAccount> findAccountsByCustomerId(UUID customerId) {
40+
public Set<Account> findAccountsByCustomerId(UUID customerId) {
3741
return customerById.containsKey(customerId)
3842
? customerById.get(customerId).getAccounts()
3943
: Set.of();
@@ -46,19 +50,30 @@ public Set<CheckingAccount> findAccountsByCustomerId(UUID customerId) {
4650
* @param amount The amount to deposit.
4751
*/
4852
public void depositFunds(String accountNumber, double amount) {
49-
CheckingAccount account = getAccountOrThrow(accountNumber);
53+
Account account = getAccountOrThrow(accountNumber);
5054
account.deposit(amount);
55+
auditLog.logTransaction(
56+
account.getOwners().iterator().next().getId(),
57+
"Deposited " + amount + " to account " + accountNumber);
5158
}
5259

5360
/**
54-
* Deposits funds into an account using a check.
61+
* Deposits funds into an account using a check. Funds can only be deposited into checking
62+
* accounts.
5563
*
5664
* @param accountNumber The account number.
5765
* @param check The check to deposit.
5866
*/
5967
public void depositFunds(String accountNumber, Check check) {
60-
CheckingAccount account = getAccountOrThrow(accountNumber);
61-
check.depositFunds(account);
68+
Account account = getAccountOrThrow(accountNumber);
69+
if (account.getClass() != CheckingAccount.class) {
70+
throw new IllegalArgumentException("Can only deposit checks into checking accounts");
71+
}
72+
73+
check.depositFunds((CheckingAccount) account);
74+
auditLog.logTransaction(
75+
account.getOwners().iterator().next().getId(),
76+
"Deposited check of " + check + " to account " + accountNumber);
6277
}
6378

6479
/**
@@ -68,8 +83,11 @@ public void depositFunds(String accountNumber, Check check) {
6883
* @param amount
6984
*/
7085
public void withdrawFunds(String accountNumber, double amount) {
71-
CheckingAccount account = getAccountOrThrow(accountNumber);
86+
Account account = getAccountOrThrow(accountNumber);
7287
account.withdraw(amount);
88+
auditLog.logTransaction(
89+
account.getOwners().iterator().next().getId(),
90+
"Withdrew " + amount + " from account " + accountNumber);
7391
}
7492

7593
/**
@@ -78,8 +96,8 @@ public void withdrawFunds(String accountNumber, double amount) {
7896
* @param accountNumber The account number.
7997
* @return The account.
8098
*/
81-
private CheckingAccount getAccountOrThrow(String accountNumber) {
82-
CheckingAccount account = accountByNumber.get(accountNumber);
99+
private Account getAccountOrThrow(String accountNumber) {
100+
Account account = accountByNumber.get(accountNumber);
83101
if (account == null || account.isClosed()) {
84102
throw new AccountNotFoundException("Account not found");
85103
}

0 commit comments

Comments
 (0)