From 60fe3edae23fe2deeb2381fbbdc406ee542f0a9d Mon Sep 17 00:00:00 2001 From: Belinda Date: Thu, 11 Jun 2026 11:25:29 +0300 Subject: [PATCH] Assignment : ADT design with inheritance, polymorphism, and exception handling --- .../BaseTransaction.java | 38 ++++- .../DepositTrasaction.java | 21 ++- .../InsufficientFundsException.java | 38 +++++ .../WithdrawalTransaction.java | 114 ++++++++++++-- src/Main.java | 148 +++++++++++++++++- 5 files changed, 340 insertions(+), 19 deletions(-) create mode 100644 src/Lecture4_interfaces_abstract_classes/InsufficientFundsException.java diff --git a/src/Lecture4_interfaces_abstract_classes/BaseTransaction.java b/src/Lecture4_interfaces_abstract_classes/BaseTransaction.java index ed81eb8..23e4afe 100644 --- a/src/Lecture4_interfaces_abstract_classes/BaseTransaction.java +++ b/src/Lecture4_interfaces_abstract_classes/BaseTransaction.java @@ -4,7 +4,13 @@ import java.util.Calendar; -public abstract class BaseTransaction implements TransactionInterface { +/** + * Concrete class that implements the TransactionInterface. + * This class provides a base implementation for all transaction types. + * Subclasses (DepositTransaction, WithdrawalTransaction) override the apply() method + * to provide specific transaction behavior (polymorphism via late binding). + */ +public class BaseTransaction implements TransactionInterface { private final int amount; private final Calendar date; private final String transactionID; @@ -26,7 +32,7 @@ public BaseTransaction(int amount, @NotNull Calendar date) { /** * getAmount() - * @return integer + * @return double - the transaction amount */ public double getAmount() { return amount; // Because we are dealing with Value types we need not worry about what we return @@ -45,7 +51,29 @@ public Calendar getDate() { public String getTransactionID(){ return transactionID; } - // Method to print a transaction receipt or details - public abstract void printTransactionDetails(); - public abstract void apply(BankAccount ba); + + /** + * printTransactionDetails() + * Prints out the details of this transaction including the amount, date, and transaction ID. + */ + public void printTransactionDetails(){ + System.out.println("Transaction Details:"); + System.out.println("Transaction ID: \t" + transactionID); + System.out.println("Transaction Amount: \t" + amount); + System.out.println("Transaction Date: \t" + date.getTime()); + } + + /** + * apply(BankAccount ba) + * Base implementation of apply - simply prints the transaction details. + * This differs from DepositTransaction (which adds to balance) and + * WithdrawalTransaction (which subtracts from balance). + * Subclasses override this method to provide specific behavior (late binding). + * @param ba the BankAccount object to apply the transaction on + */ + public void apply(BankAccount ba) throws InsufficientFundsException { + System.out.println("Base Transaction Applied:"); + printTransactionDetails(); + System.out.println("Current Balance After Base Apply: \t" + ba.getBalance()); + } } diff --git a/src/Lecture4_interfaces_abstract_classes/DepositTrasaction.java b/src/Lecture4_interfaces_abstract_classes/DepositTrasaction.java index 81afab5..25a2708 100644 --- a/src/Lecture4_interfaces_abstract_classes/DepositTrasaction.java +++ b/src/Lecture4_interfaces_abstract_classes/DepositTrasaction.java @@ -4,6 +4,11 @@ import java.util.Calendar; +/** + * DepositTransaction extends BaseTransaction. + * Deposits are irreversible - once applied, they cannot be undone. + * Overrides the apply() method to add the deposit amount to the bank account balance. + */ public class DepositTrasaction extends BaseTransaction { public DepositTrasaction(int amount, @NotNull Calendar date){ super(amount, date); @@ -19,12 +24,24 @@ private boolean checkDepositAmount(int amt){ // Method to print a transaction receipt or details public void printTransactionDetails(){ - System.out.println("Deposit Trasaction: "+this.toString()); + System.out.println("Deposit Transaction: " + this.toString()); + System.out.println("Amount Deposited: \t" + getAmount()); + System.out.println("Transaction Date: \t" + getDate().getTime()); + System.out.println("Transaction ID: \t" + getTransactionID()); } - public void apply(BankAccount ba){ + /** + * apply(BankAccount ba) + * Overrides BaseTransaction.apply() to add the deposit amount to the account balance. + * This implementation differs from BaseTransaction (which only prints details) and + * WithdrawalTransaction (which subtracts from balance). + * @param ba the BankAccount object to apply the deposit on + */ + @Override + public void apply(BankAccount ba) throws InsufficientFundsException { double curr_balance = ba.getBalance(); double new_balance = curr_balance + getAmount(); ba.setBalance(new_balance); + System.out.println("Deposit Applied Successfully. New Balance: " + ba.getBalance()); } } diff --git a/src/Lecture4_interfaces_abstract_classes/InsufficientFundsException.java b/src/Lecture4_interfaces_abstract_classes/InsufficientFundsException.java new file mode 100644 index 0000000..137e6a9 --- /dev/null +++ b/src/Lecture4_interfaces_abstract_classes/InsufficientFundsException.java @@ -0,0 +1,38 @@ +package Lecture4_interfaces_abstract_classes; + +/** + * Custom Exception class to handle insufficient funds during withdrawal transactions. + * Extends Exception to make it a checked exception, enforcing proper error handling. + */ +public class InsufficientFundsException extends Exception { + + private final double amount; + private final double balance; + + /** + * Constructor for InsufficientFundsException + * @param amount the withdrawal amount that was attempted + * @param balance the current balance in the account + */ + public InsufficientFundsException(double amount, double balance) { + super("Insufficient Funds: Attempted to withdraw " + amount + " but only " + balance + " available."); + this.amount = amount; + this.balance = balance; + } + + /** + * getAmount() + * @return the amount that was attempted to be withdrawn + */ + public double getAmount() { + return amount; + } + + /** + * getBalance() + * @return the balance at the time of the failed transaction + */ + public double getBalance() { + return balance; + } +} diff --git a/src/Lecture4_interfaces_abstract_classes/WithdrawalTransaction.java b/src/Lecture4_interfaces_abstract_classes/WithdrawalTransaction.java index face5b6..0f88b0c 100644 --- a/src/Lecture4_interfaces_abstract_classes/WithdrawalTransaction.java +++ b/src/Lecture4_interfaces_abstract_classes/WithdrawalTransaction.java @@ -4,12 +4,26 @@ import java.util.Calendar; +/** + * WithdrawalTransaction extends BaseTransaction. + * Withdrawals can be reversed - the reverse() method restores the bank account balance. + * Overrides the apply() method to subtract the withdrawal amount from the bank account balance. + * Implements exception handling for insufficient funds. + */ public class WithdrawalTransaction extends BaseTransaction { + + private double amountNotWithdrawn; // Keeps a record of the amount not withdrawn + private boolean applied; // Tracks whether this transaction has been applied + private BankAccount targetAccount; // Reference to the account the transaction was applied on + public WithdrawalTransaction(int amount, @NotNull Calendar date) { super(amount, date); + this.amountNotWithdrawn = 0; + this.applied = false; + this.targetAccount = null; } - private boolean checkDepositAmount(int amt) { + private boolean checkWithdrawalAmount(int amt) { if (amt < 0) { return false; } else { @@ -17,29 +31,107 @@ private boolean checkDepositAmount(int amt) { } } - // Method to reverse the transaction + /** + * reverse() + * Reverses the withdrawal transaction by restoring the balance to the bank account. + * Only succeeds if the transaction was previously applied. + * @return true if reversal was successful, false otherwise + */ public boolean reverse() { + if (!applied || targetAccount == null) { + System.out.println("Withdrawal Transaction has not been applied yet. Cannot reverse."); + return false; + } + + // Restore the withdrawn amount (which is getAmount() - amountNotWithdrawn) + double withdrawnAmount = getAmount() - amountNotWithdrawn; + double currentBalance = targetAccount.getBalance(); + targetAccount.setBalance(currentBalance + withdrawnAmount); + System.out.println("Withdrawal Reversed Successfully. New Balance: " + targetAccount.getBalance()); + applied = false; return true; - } // return true if reversal was successful + } // Method to print a transaction receipt or details public void printTransactionDetails() { - System.out.println("Deposit Trasaction: " + this.toString()); + System.out.println("Withdrawal Transaction: " + this.toString()); + System.out.println("Amount Withdrawn: \t" + getAmount()); + System.out.println("Transaction Date: \t" + getDate().getTime()); + System.out.println("Transaction ID: \t" + getTransactionID()); + if (amountNotWithdrawn > 0) { + System.out.println("Amount Not Withdrawn: \t" + amountNotWithdrawn); + } + } + + /** + * apply(BankAccount ba) + * Overrides BaseTransaction.apply() to subtract the withdrawal amount from the account balance. + * Uses the throws keyword to handle InsufficientFundsException when the balance + * is less than the withdrawal amount. + * @param ba the BankAccount object to apply the withdrawal on + * @throws InsufficientFundsException if the balance is less than the withdrawal amount + */ + @Override + public void apply(BankAccount ba) throws InsufficientFundsException { + double curr_balance = ba.getBalance(); + if (curr_balance < getAmount()) { + throw new InsufficientFundsException(getAmount(), curr_balance); + } + double new_balance = curr_balance - getAmount(); + ba.setBalance(new_balance); + this.applied = true; + this.targetAccount = ba; + System.out.println("Withdrawal Applied Successfully. New Balance: " + ba.getBalance()); } - /* - Oportunity for assignment: implementing different form of withdrawal + /** + * Overloaded apply() method (Q3) + * Not only checks if the balance covers the withdrawal amount but also checks + * if the balance is greater than 0. In the case when 0 < balance < withdrawal amount, + * it withdraws all the balance and keeps a record of the amount not withdrawn. + * Implements exception handling using try{...} catch{...} finally{...} block. + * @param ba the BankAccount object to apply the withdrawal on + * @param checkAvailableBalance flag to indicate that this overloaded version should be used */ - public void apply(BankAccount ba) { + public void apply(BankAccount ba, boolean checkAvailableBalance) { double curr_balance = ba.getBalance(); - if (curr_balance > getAmount()) { + + try { + // Check if the balance covers the withdrawal amount + if (curr_balance < getAmount()) { + throw new InsufficientFundsException(getAmount(), curr_balance); + } + + // Sufficient funds - withdraw the full amount double new_balance = curr_balance - getAmount(); ba.setBalance(new_balance); + this.amountNotWithdrawn = 0; + System.out.println("Withdrawal Applied Successfully. New Balance: " + ba.getBalance()); + + } catch (InsufficientFundsException e) { + // If the balance is greater than 0 but less than the withdrawal amount + // Withdraw all the available balance and keep a record of the amount not withdrawn + if (curr_balance > 0) { + this.amountNotWithdrawn = getAmount() - curr_balance; + ba.setBalance(0); + System.out.println("Partial Withdrawal: Only " + curr_balance + " was available."); + System.out.println("Amount Not Withdrawn: " + amountNotWithdrawn); + } else { + this.amountNotWithdrawn = getAmount(); + System.out.println("Error: " + e.getMessage()); + } + } finally { + this.applied = true; + this.targetAccount = ba; + System.out.println("Transaction Complete. Final Balance: " + ba.getBalance()); } } - /* - Assignment 1 Q3: Write the Reverse method - a method unique to the WithdrawalTransaction Class + /** + * getAmountNotWithdrawn() + * @return the amount that could not be withdrawn due to insufficient funds */ + public double getAmountNotWithdrawn() { + return amountNotWithdrawn; + } } - diff --git a/src/Main.java b/src/Main.java index 584a048..de006de 100644 --- a/src/Main.java +++ b/src/Main.java @@ -1,4 +1,5 @@ import Lecture1_adt.*; // Import all classes from Lecture1_adt package to be used in this client code +import Lecture4_interfaces_abstract_classes.*; import java.util.Calendar; import java.util.GregorianCalendar; @@ -8,7 +9,7 @@ //TIP To Run code, press or // click the icon in the gutter. /* -* Client Code for accessing the Lecture1_adt.TransactionInterface.java module + * Client Code for accessing the Lecture1_adt.TransactionInterface.java module */ public class Main { @@ -144,6 +145,144 @@ public static void testTransaction4() { } + /* + * ==================================================================== + * Assignment 1 - Question 4: Client Code to test Assignment classes + * ==================================================================== + */ + + /** + * Test the DepositTransaction class + * Creates a DepositTransaction, applies it to a BankAccount, and prints details. + */ + public static void testDepositTransaction() { + System.out.println("============================================="); + System.out.println(" Testing DepositTransaction"); + System.out.println("============================================="); + + // Create a BankAccount with initial balance of 1000 + BankAccount account = new BankAccount(1000); + System.out.println("Initial Balance: \t" + account.getBalance()); + + // Create a DepositTransaction of 500 + Calendar date = new GregorianCalendar(); + DepositTrasaction deposit = new DepositTrasaction(500, date); + + // Print the transaction details + deposit.printTransactionDetails(); + + // Apply the deposit to the bank account + try { + deposit.apply(account); + } catch (InsufficientFundsException e) { + System.out.println("Error: " + e.getMessage()); + } + System.out.println("Balance After Deposit: \t" + account.getBalance()); + + System.out.println("\n--- Testing Polymorphism: Type casting DepositTransaction to BaseTransaction ---"); + + // Type casting: Assign the subtype object to a base type reference (early vs late binding) + BaseTransaction baseRef = deposit; // Upcasting - subtype to base type + System.out.println("\nCalling apply() through BaseTransaction reference (late binding):"); + try { + baseRef.apply(new BankAccount(2000)); // Late binding: calls DepositTrasaction.apply() + } catch (InsufficientFundsException e) { + System.out.println("Error: " + e.getMessage()); + } + } + + /** + * Test the WithdrawalTransaction class + * Tests normal withdrawal, insufficient funds exception, partial withdrawal, and reversal. + */ + public static void testWithdrawalTransaction() { + System.out.println("\n\n============================================="); + System.out.println(" Testing WithdrawalTransaction"); + System.out.println("============================================="); + + // Create a BankAccount with initial balance of 1000 + BankAccount account = new BankAccount(1000); + System.out.println("Initial Balance: \t" + account.getBalance()); + + // --- Test 1: Normal Withdrawal --- + System.out.println("\n--- Test 1: Normal Withdrawal of 300 ---"); + Calendar date1 = new GregorianCalendar(); + WithdrawalTransaction withdrawal1 = new WithdrawalTransaction(300, date1); + withdrawal1.printTransactionDetails(); + try { + withdrawal1.apply(account); + } catch (InsufficientFundsException e) { + System.out.println("Error: " + e.getMessage()); + } + System.out.println("Balance After Withdrawal: \t" + account.getBalance()); + + // --- Test 2: Reversal of the Withdrawal --- + System.out.println("\n--- Test 2: Reversing the Withdrawal of 300 ---"); + boolean reversed = withdrawal1.reverse(); + System.out.println("Reversal Successful: \t" + reversed); + System.out.println("Balance After Reversal: \t" + account.getBalance()); + + // --- Test 3: Withdrawal with Insufficient Funds (throws exception) --- + System.out.println("\n--- Test 3: Withdrawal of 5000 with Insufficient Funds (throws) ---"); + Calendar date2 = new GregorianCalendar(); + WithdrawalTransaction withdrawal2 = new WithdrawalTransaction(5000, date2); + try { + withdrawal2.apply(account); + } catch (InsufficientFundsException e) { + System.out.println("Exception Caught: " + e.getMessage()); + } + System.out.println("Balance After Failed Withdrawal: \t" + account.getBalance()); + + // --- Test 4: Partial Withdrawal using overloaded apply() with try-catch-finally --- + System.out.println("\n--- Test 4: Partial Withdrawal of 5000 using overloaded apply() ---"); + Calendar date3 = new GregorianCalendar(); + WithdrawalTransaction withdrawal3 = new WithdrawalTransaction(5000, date3); + withdrawal3.apply(account, true); + System.out.println("Amount Not Withdrawn: \t" + withdrawal3.getAmountNotWithdrawn()); + + // --- Test 5: Polymorphism - Type casting to BaseTransaction --- + System.out.println("\n--- Test 5: Polymorphism - Type Casting WithdrawalTransaction to BaseTransaction ---"); + BankAccount account2 = new BankAccount(2000); + Calendar date4 = new GregorianCalendar(); + WithdrawalTransaction withdrawal4 = new WithdrawalTransaction(500, date4); + + // Upcasting: subtype object to base type reference + BaseTransaction baseRef = withdrawal4; + System.out.println("Calling apply() through BaseTransaction reference (late binding):"); + try { + baseRef.apply(account2); // Late binding: calls WithdrawalTransaction.apply() + } catch (InsufficientFundsException e) { + System.out.println("Exception Caught: " + e.getMessage()); + } + System.out.println("Balance After Polymorphic Withdrawal: \t" + account2.getBalance()); + } + + /** + * Test the BaseTransaction class directly + * Shows how the BaseTransaction.apply() differs from subclass implementations. + */ + public static void testBaseTransaction() { + System.out.println("\n\n============================================="); + System.out.println(" Testing BaseTransaction (directly)"); + System.out.println("============================================="); + + BankAccount account = new BankAccount(1000); + Calendar date = new GregorianCalendar(); + BaseTransaction baseTransaction = new BaseTransaction(100, date); + + System.out.println("Initial Balance: \t" + account.getBalance()); + baseTransaction.printTransactionDetails(); + + try { + baseTransaction.apply(account); + } catch (InsufficientFundsException e) { + System.out.println("Error: " + e.getMessage()); + } + System.out.println("Balance After Base Apply: \t" + account.getBalance()); + // Note: BaseTransaction.apply() does NOT change the balance - it only prints details + } + + public static void main(String[] args) { // This is the client code // Uncomment the following lines to test the class which you would like to test @@ -152,5 +291,12 @@ public static void main(String[] args) { // testTransaction2() // testTransaction3() // testTransaction4() + + /* + * Assignment 1 - Question 4: Test the Assignment classes + */ + testBaseTransaction(); + testDepositTransaction(); + testWithdrawalTransaction(); } } \ No newline at end of file