diff --git a/src/BankAccount.java b/src/BankAccount.java new file mode 100644 index 0000000..e440b34 --- /dev/null +++ b/src/BankAccount.java @@ -0,0 +1,19 @@ +public class BankAccount { + private double balance; + + public BankAccount(double initialBalance) { + this.balance = initialBalance; + } + + public double getBalance() { + return balance; + } + + public void deposit(double amount) { + this.balance += amount; + } + + public void withdraw(double amount) { + this.balance -= amount; + } +} diff --git a/src/BaseTransaction.java b/src/BaseTransaction.java new file mode 100644 index 0000000..21e72c0 --- /dev/null +++ b/src/BaseTransaction.java @@ -0,0 +1,137 @@ +import java.util.Calendar; +import java.util.UUID; + +// QUESTION 1 - Extending Interface in Concrete Class (Base Class) + +public class BaseTransaction implements TransactionInterface { + protected double amount; + protected Calendar date; + protected String transactionID; + protected boolean isApplied = false; + + public BaseTransaction(double amount) { + this.amount = amount; + this.date = Calendar.getInstance(); + this.transactionID = UUID.randomUUID().toString().substring(0, 8); // Generates a unique short ID + } + + @Override + public double getAmount() { return this.amount; } [cite: 25] + + @Override + public Calendar getDate() { return this.date; } [cite: 26] + + @Override + public String getTransactionID() { return this.transactionID; } [cite: 27] + + @Override + public void printTransactionDetails() { [cite: 29] + System.out.println("--- Transaction Details ---"); + System.out.println("ID: " + transactionID); + System.out.println("Date: " + date.getTime()); + System.out.println("Amount: $" + amount); + System.out.println("Type: Generic Base Transaction"); + } + + @Override + public void apply(BankAccount ba) throws InsufficientFundsException { [cite: 30] + // This base implementation differs substantially from the subclasses as required + System.out.println("[BaseTransaction Log] Inspecting account. Current balance: $" + ba.getBalance()); [cite: 31] + this.isApplied = true; + } +} + +// QUESTION 1 & 2 - DepositTransaction Subclass + +class DepositTransaction extends BaseTransaction { + + public DepositTransaction(double amount) { + super(amount); + } + + // Question 1: Method Overriding + @Override + public void apply(BankAccount ba) { [cite: 33] + ba.deposit(this.amount); + this.isApplied = true; + System.out.println("Successfully deposited $" + this.amount); + } + + @Override + public void printTransactionDetails() { + super.printTransactionDetails(); + System.out.println("Type: Deposit (Irreversible)"); [cite: 37] + System.out.println("---------------------------"); + } +} + + +// QUESTION 1, 2, & 3 - WithdrawalTransaction Subclass + +class WithdrawalTransaction extends BaseTransaction { + private BankAccount associatedAccount; // Used for tracking the reversal + private double shortfallAmount = 0.0; // Track unpaid deficit if partial withdrawal happens + + public WithdrawalTransaction(double amount) { + super(amount); + } + + // Question 1 & Question 3 (throws keyword requirement) + @Override + public void apply(BankAccount ba) throws InsufficientFundsException { [cite: 33, 44] + this.associatedAccount = ba; + if (ba.getBalance() < this.amount) { + throw new InsufficientFundsException("Insufficient funds! Transaction amount: $" + this.amount + " exceeds Balance: $" + ba.getBalance()); [cite: 44] + } + ba.withdraw(this.amount); + this.isApplied = true; + System.out.println("Successfully withdrew $" + this.amount); + } + + // Question 3: Overloaded apply() method with try...catch...finally block + + public void apply(BankAccount ba, boolean partialWithdrawalAllowed) { [cite: 45, 47] + this.associatedAccount = ba; + try { [cite: 47] + if (ba.getBalance() <= 0) { + throw new InsufficientFundsException("Account balance is zero or negative. Cannot withdraw."); + } else if (ba.getBalance() < this.amount) { + // If balance is greater than 0 but less than the withdrawal amount, withdraw everything left + this.shortfallAmount = this.amount - ba.getBalance(); [cite: 46] + this.amount = ba.getBalance(); // Update transaction amount to what was actually pulled [cite: 46] + ba.withdraw(this.amount); [cite: 46] + this.isApplied = true; + System.out.println("Partial Withdrawal Executed! Withdrew available balance: $" + this.amount); + } else { + // Execute standard withdrawal logic + apply(ba); + } + } catch (InsufficientFundsException e) { [cite: 47] + System.out.println("[Caught Exception inside Overloaded apply]: " + e.getMessage()); + } finally { [cite: 47] + System.out.println("[Finally Block Executed] Shortfall remaining to be paid: $" + shortfallAmount); + } + } + + // Question 2: Reversal logic for withdrawals + public boolean reverse() { [cite: 38] + if (this.isApplied && this.associatedAccount != null) { + this.associatedAccount.deposit(this.amount); // Restores the balance to its original amount [cite: 39] + this.isApplied = false; + System.out.println("Withdrawal transaction " + this.transactionID + " reversed successfully."); + return true; + } + System.out.println("Reversal failed. Transaction was not applied."); + return false; + } + + @Override + public void printTransactionDetails() { + super.printTransactionDetails(); + System.out.println("Type: Withdrawal (Reversible)"); [cite: 37] + if (shortfallAmount > 0) { + System.out.println("Shortfall Notice: Unpaid shortage of $" + shortfallAmount); [cite: 46] + } + System.out.println("---------------------------"); + } +} diff --git a/src/InsufficientFundsException.java b/src/InsufficientFundsException.java new file mode 100644 index 0000000..c62d7aa --- /dev/null +++ b/src/InsufficientFundsException.java @@ -0,0 +1,6 @@ +// QUESTION 3 - Custom Exception Class +public class InsufficientFundsException extends Exception { + public InsufficientFundsException(String message) { + super(message); + } +} diff --git a/src/Main.java b/src/Main.java index 584a048..c8b5e86 100644 --- a/src/Main.java +++ b/src/Main.java @@ -1,156 +1,74 @@ -import Lecture1_adt.*; // Import all classes from Lecture1_adt package to be used in this client code +// Name: Fatuma Bobba +// Registration No: SCT212-0072/2024 -import java.util.Calendar; -import java.util.GregorianCalendar; -import java.util.ArrayList; -import java.util.List; +// QUESTION 4 - Client Testing Code -//TIP To Run code, press or -// click the icon in the gutter. -/* -* Client Code for accessing the Lecture1_adt.TransactionInterface.java module - */ public class Main { - - public static void testTransaction1() { - Calendar d1 = new GregorianCalendar(); // d1 is an Object [Objects are Reference types] - Lecture1_adt.Transaction1 t1 = new Lecture1_adt.Transaction1(1000, d1); // amount and d1 are arguments - - System.out.println(t1.toString()); - System.out.println("Lecture1_adt.TransactionInterface Amount: \t " + t1.amount); - System.out.println("Lecture1_adt.TransactionInterface Date: \t " + t1.date); - - // Please note that the Client Codes can access the data in the class directly through the dot operator - // This kind of exposure is a threat to both the Representation Independence and Preservation of Invariants - } - - - /** @return a transaction of same amount as t, one month later - * This is a PRODUCER of the class Lecture1_adt.Transaction2 - * This code will help demostrate the Design exposures still present in transaction2 class - * */ - - public static Transaction2 makeNextPayment(Transaction2 t) { - Calendar d = t.getDate(); - d.add(Calendar.MONTH, 1); - return new Transaction2(t.getAmount(), d); - } - - /* - Testing Transaction2 class - */ - public static void testTransaction2() { - - Calendar d1 = new GregorianCalendar(); - - Lecture1_adt.Transaction2 t = new Lecture1_adt.Transaction2(1000, d1); - - Lecture1_adt.Transaction2 modified_t = makeNextPayment(t); - - System.out.println("\n\nState of the Object T1 After Client Code Tried to Change the Amount"); - System.out.println("Lecture1_adt.TransactionInterface Amount: \t "+modified_t.getAmount()); - System.out.println("Lecture1_adt.TransactionInterface Date: \t "+modified_t.getDate().getTime()); - - System.out.println("\n\nHow does T2 Look Like?????"); - System.out.println("Lecture1_adt.TransactionInterface Amount: \t "+modified_t.getAmount()); - System.out.println("Lecture1_adt.TransactionInterface Date: \t "+modified_t.getDate().getTime()); - - /* Please note that Although we have solved the problem of Transaction1 - * And client code can no longer use the dot (.) operator to directly access the data - * There is still some exposure especially if we pass an object of a previous Transaction2 to create a new Transaction2 object - */ - - } - - - /** @return a list of 12 monthly payments of identical amounts - * This code will help demostrate the Design exposures still present in transaction3 class - * */ - public static List makeYearOfPayments (int amount) throws NullPointerException { - - List listOfTransaction3s = new ArrayList(); - Calendar date = new GregorianCalendar(2024, Calendar.JANUARY, 3); - - - for (int i = 0; i < 12; i++) { - listOfTransaction3s.add(new Transaction3(amount, date)); - date.add(Calendar.MONTH, 1); + public static void main(String[] args) { + System.out.println("=== STARTING BANK TRANSACTION SYSTEM TESTS ===\n"); + + // Set up test account + BankAccount myAccount = new BankAccount(500.0); + System.out.println("Initial Bank Account Balance: $" + myAccount.getBalance()); + System.out.println("------------------------------------------------"); + + // 1. Test Deposit Transaction + System.out.println("\n--- Testing Deposit Transaction ---"); + BaseTransaction deposit = new DepositTransaction(200.0); [cite: 52] + try { + deposit.apply(myAccount); [cite: 52] + deposit.printTransactionDetails(); + System.out.println("New Account Balance: $" + myAccount.getBalance()); + } catch (InsufficientFundsException e) { + System.out.println(e.getMessage()); } - return listOfTransaction3s; - } - - /* - Testing Transaction3 class - */ - public static void testTransaction3() { - List allPaymentsIn2024 = makeYearOfPayments(1000); - - for (Transaction3 t3 : allPaymentsIn2024) { - - // Display all the 12 Transactions - for (Transaction3 transact : allPaymentsIn2024) { - System.out.println("\n\n ::::::::::::::::::::::::::::::::::::::::::::\n"); - System.out.println("Lecture1_adt.TransactionInterface Amount: \t "+transact.getAmount()); - System.out.println("Lecture1_adt.TransactionInterface Date: \t "+transact.getDate().getTime()); - } + // 2. Test Successful Withdrawal Transaction + System.out.println("\n--- Testing Successful Withdrawal Transaction ---"); + WithdrawalTransaction withdrawal1 = new WithdrawalTransaction(150.0); [cite: 52] + try { + withdrawal1.apply(myAccount); [cite: 52] + withdrawal1.printTransactionDetails(); + System.out.println("New Account Balance: $" + myAccount.getBalance()); + } catch (InsufficientFundsException e) { + System.out.println(e.getMessage()); } - /* Please Check all the 12 transactions displayed and hwo their dates look like - * Note that Although Transaction3 class resolves to an extent the exposure in Transaction2 class - * There is still some exposure especially if we pass an object of a previous Transaction3 to create a - * new Transaction3 object - */ - } - - - /** @return a list of 12 monthly payments of identical amounts - * This code Show that by judicious copying and defensive programming we eliminate the exposure in Transaction3 - * As defined in the constructor of Transaction4 class - * */ - - public static List makeYearOfPaymentsFinal (int amount) throws NullPointerException { - - List listOfTransaction4s = new ArrayList(); - Calendar date = new GregorianCalendar(2024, Calendar.JANUARY, 3); - - - for (int i = 0; i < 12; i++) { - listOfTransaction4s.add(new Transaction4(amount, date)); - date.add(Calendar.MONTH, 1); + // 3. Test Question 2: Withdrawal Reversal + System.out.println("\n--- Testing Question 2: Reversal of Withdrawal ---"); + withdrawal1.reverse(); + System.out.println("Account Balance after reversal: $" + myAccount.getBalance()); + + // 4. Test Question 3: Exception Handling (Standard apply throwing Exception) + System.out.println("\n--- Testing Question 3: Exception Throwing (Insufficient Funds) ---"); + WithdrawalTransaction expensiveWithdrawal = new WithdrawalTransaction(2000.0); + try { + expensiveWithdrawal.apply(myAccount); // This will fail and throw the exception + } catch (InsufficientFundsException e) { + System.out.println("Successfully caught expected exception: " + e.getMessage()); } - return listOfTransaction4s; - } - - /* - Testing Transaction3 class - */ - public static void testTransaction4() { - /* - * Call the function to make all the Twelve transaction in a year of our business - */ - - List transactionsIn2024 = makeYearOfPaymentsFinal(1200); - - // Display all the 12 Transactions - for (Transaction4 transact : transactionsIn2024) { - System.out.println("\n\n ::::::::::::::::::::::::::::::::::::::::::::\n"); - System.out.println("Lecture1_adt.TransactionInterface Amount: \t "+transact.getAmount()); - System.out.println("Lecture1_adt.TransactionInterface Date: \t "+transact.getDate().getTime()); + // 5. Test Question 3: Overloaded apply() with Partial Withdrawal (Try/Catch/Finally) + System.out.println("\n--- Testing Question 3: Overloaded Partial Withdrawal Method ---"); + // Balance is currently $700. Attempting to withdraw $1000. + WithdrawalTransaction partialWithdrawal = new WithdrawalTransaction(1000.0); + partialWithdrawal.apply(myAccount, true); // True activates the partial withdrawal catch block logic + partialWithdrawal.printTransactionDetails(); + System.out.println("Final Account Balance: $" + myAccount.getBalance()); + + // 6. Test Hint requirement: Polymorphism & Type Casting + System.out.println("\n--- Testing Question 4 Hint: Type Casting to Base Object ---"); + WithdrawalTransaction basicW = new WithdrawalTransaction(50.0); [cite: 52] + + // Polymorphically mapping subtype object to base type object via type casting + BaseTransaction baseRef = (BaseTransaction) basicW; [cite: 53] + + try { + // This runs the overriding apply method due to late binding polymorphism + baseRef.apply(myAccount); [cite: 54] + baseRef.printTransactionDetails(); + } catch (InsufficientFundsException e) { + System.out.println(e.getMessage()); } - - // Please Take a look at all the 12 transaction now and compare with the outputs of the Transaction3 class - } - - - 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 - - // testTransaction1() - // testTransaction2() - // testTransaction3() - // testTransaction4() } -} \ No newline at end of file +} diff --git a/src/TransactionInterface.java b/src/TransactionInterface.java new file mode 100644 index 0000000..5edfee9 --- /dev/null +++ b/src/TransactionInterface.java @@ -0,0 +1,9 @@ +import java.util.Calendar; + +public interface TransactionInterface { + double getAmount(); + Calendar getDate(); + String getTransactionID(); + void printTransactionDetails(); + void apply(BankAccount ba) throws InsufficientFundsException; +}