Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 52 additions & 22 deletions src/Lecture4_interfaces_abstract_classes/BaseTransaction.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,78 @@

import java.util.Calendar;

public abstract class BaseTransaction implements TransactionInterface {
private final int amount;
/**
* BaseTransaction - A concrete class that implements the TransactionInterface.
* This class provides base implementations for all transaction-related methods.
* Subclasses (DepositTransaction, WithdrawalTransaction) override the apply() method
* to provide specific transaction behavior (polymorphism / late binding).
*/
public class BaseTransaction implements TransactionInterface {
private final double amount;
private final Calendar date;
private final String transactionID;

/**
* Lecture1_adt.TransactionInterface Constructor
* @param amount in an integer
* @param date: Not null, and must be a Calendar object
* @return void
* Instialises the field, attributes of a transaction
* Creates a object of this
* BaseTransaction Constructor
* @param amount the transaction amount as an integer
* @param date Not null, must be a Calendar object
* Initialises the fields/attributes of a transaction
*/
public BaseTransaction(int amount, @NotNull Calendar date) {
this.amount = amount;
this.date = (Calendar) date.clone();
int uniq = (int) Math.random()*10000;
transactionID = date.toString()+uniq;
int uniq = (int) (Math.random() * 10000);
transactionID = date.getTimeInMillis() + "-" + uniq;
}

/**
* getAmount()
* @return integer
* getAmount() - Method to get the transaction amount
* @return the transaction amount as a double
*/
public double getAmount() {
return amount; // Because we are dealing with Value types we need not worry about what we return
return amount; // Value type, safe to return directly
}

/**
* getDate()
* @return Calendar Object
* getDate() - Method to get the transaction date
* @return a defensive copy of the Calendar date object
*/
public Calendar getDate() {
// return date; // Because we are dealing with Reference types we need to judiciously copy what our getters return
return (Calendar) date.clone(); // Defensive copying or Judicious Copying
return (Calendar) date.clone(); // Defensive copying to preserve invariants
}

// Method to get a unique identifier for the transaction
/**
* getTransactionID() - Method to get a unique identifier for the transaction
* @return the unique transaction ID string
*/
public String getTransactionID(){
return transactionID;
return transactionID;
}

/**
* printTransactionDetails() - Prints the details of this transaction.
* Displays the transaction ID, amount, and date.
*/
public void printTransactionDetails(){
System.out.println("-------- Transaction Details --------");
System.out.println("Transaction ID: \t" + getTransactionID());
System.out.println("Transaction Amount: \t" + getAmount());
System.out.println("Transaction Date: \t" + getDate().getTime());
System.out.println("-------------------------------------");
}

/**
* apply() - Applies this transaction on a BankAccount object.
* The BaseTransaction implementation simply prints the transaction details
* without modifying the bank account balance. This differs substantially from
* the DepositTransaction (which adds to balance) and WithdrawalTransaction
* (which subtracts from balance) implementations.
* @param ba the BankAccount to apply the transaction to
*/
public void apply(BankAccount ba) throws InsufficientFundsException {
System.out.println("Base Transaction Applied.");
printTransactionDetails();
System.out.println("Account Balance: \t" + ba.getBalance());
// BaseTransaction does NOT modify the balance — subclasses override this
}
// Method to print a transaction receipt or details
public abstract void printTransactionDetails();
public abstract void apply(BankAccount ba);
}
39 changes: 37 additions & 2 deletions src/Lecture4_interfaces_abstract_classes/DepositTrasaction.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,27 @@

import java.util.Calendar;

/**
* DepositTransaction - A subclass of BaseTransaction that handles deposit operations.
* Deposits are irreversible (no reverse() method provided).
* Overrides the apply() method to add the transaction amount to the bank account balance.
*/
public class DepositTrasaction extends BaseTransaction {

/**
* DepositTransaction Constructor
* @param amount the deposit amount as an integer
* @param date Not null, the date of the transaction
*/
public DepositTrasaction(int amount, @NotNull Calendar date){
super(amount, date);
}

/**
* Validates the deposit amount.
* @param amt the amount to check
* @return true if the amount is non-negative, false otherwise
*/
private boolean checkDepositAmount(int amt){
if (amt < 0){
return false;
Expand All @@ -17,14 +34,32 @@ private boolean checkDepositAmount(int amt){
}
}

// Method to print a transaction receipt or details
/**
* printTransactionDetails() - Overrides BaseTransaction to print deposit-specific details.
* Displays the type of transaction (Deposit) along with the transaction information.
*/
@Override
public void printTransactionDetails(){
System.out.println("Deposit Trasaction: "+this.toString());
System.out.println("-------- Deposit Transaction Details --------");
System.out.println("Transaction ID: \t" + getTransactionID());
System.out.println("Deposit Amount: \t" + getAmount());
System.out.println("Transaction Date: \t" + getDate().getTime());
System.out.println("----------------------------------------------");
}

/**
* apply() - Overrides BaseTransaction's apply() method.
* Adds the deposit amount to the bank account's current balance.
* This demonstrates method overriding and polymorphism (late binding):
* even if a DepositTransaction is referenced by a BaseTransaction variable,
* this overridden method will be called at runtime.
* @param ba the BankAccount to apply the deposit to
*/
@Override
public void apply(BankAccount ba){
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());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package Lecture4_interfaces_abstract_classes;

/**
* InsufficientFundsException - Custom exception class for handling insufficient funds
* during withdrawal transactions.
* Extends Exception to make it a checked exception (actual Java Exception class via inheritance).
* This demonstrates using inheritance to create a custom Java Exception class.
*/
public class InsufficientFundsException extends Exception {

private final double shortfall; // The amount that could not be withdrawn

/**
* Constructor with a custom error message.
* @param message the error message describing the exception
*/
public InsufficientFundsException(String message) {
super(message);
this.shortfall = 0;
}

/**
* Constructor with a custom error message and the shortfall amount.
* @param message the error message describing the exception
* @param shortfall the amount that could not be covered by the account balance
*/
public InsufficientFundsException(String message, double shortfall) {
super(message);
this.shortfall = shortfall;
}

/**
* Gets the shortfall amount (the amount not withdrawn due to insufficient funds).
* @return the shortfall amount as a double
*/
public double getShortfall() {
return shortfall;
}
}
158 changes: 144 additions & 14 deletions src/Lecture4_interfaces_abstract_classes/WithdrawalTransaction.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,172 @@

import java.util.Calendar;

/**
* WithdrawalTransaction - A subclass of BaseTransaction that handles withdrawal operations.
* Withdrawals CAN be reversed (unlike deposits which are irreversible).
* Overrides the apply() method to subtract the transaction amount from the bank account balance.
* Implements an overloaded apply() method with exception handling for insufficient funds.
*/
public class WithdrawalTransaction extends BaseTransaction {

private double amountNotWithdrawn; // Tracks the amount that could not be withdrawn
private BankAccount targetAccount; // Reference to the bank account the transaction was applied to
private boolean applied; // Flag to track if the transaction has been applied

/**
* WithdrawalTransaction Constructor
* @param amount the withdrawal amount as an integer
* @param date Not null, the date of the transaction
*/
public WithdrawalTransaction(int amount, @NotNull Calendar date) {
super(amount, date);
this.amountNotWithdrawn = 0;
this.targetAccount = null;
this.applied = false;
}

private boolean checkDepositAmount(int amt) {
/**
* Validates the withdrawal amount.
* @param amt the amount to check
* @return true if the amount is non-negative, false otherwise
*/
private boolean checkWithdrawalAmount(int amt) {
if (amt < 0) {
return false;
} else {
return true;
}
}

// Method to reverse the transaction
/**
* getAmountNotWithdrawn() - Returns the amount that was not withdrawn
* in cases where the balance was less than the requested withdrawal.
* @return the shortfall amount
*/
public double getAmountNotWithdrawn() {
return amountNotWithdrawn;
}

/**
* reverse() - Reverses the withdrawal transaction.
* This method restores the balance in the BankAccount to its original amount
* before the transaction was applied.
* Withdrawals can be reversed (design assumption), while deposits cannot.
* @return true if the reversal was successful, false if the transaction was never applied
*/
public boolean reverse() {
if (!applied || targetAccount == null) {
System.out.println("Reversal Failed: Transaction was never applied to an account.");
return false;
}
// Restore the withdrawn amount back to the account
double amountActuallyWithdrawn = getAmount() - amountNotWithdrawn;
double currentBalance = targetAccount.getBalance();
targetAccount.setBalance(currentBalance + amountActuallyWithdrawn);
System.out.println("Withdrawal Reversed Successfully. Balance Restored to: " + targetAccount.getBalance());
applied = false; // Mark as no longer applied
return true;
} // return true if reversal was successful
}

// Method to print a transaction receipt or details
/**
* printTransactionDetails() - Overrides BaseTransaction to print withdrawal-specific details.
* Displays the type of transaction (Withdrawal) along with the transaction information.
*/
@Override
public void printTransactionDetails() {
System.out.println("Deposit Trasaction: " + this.toString());
System.out.println("-------- Withdrawal Transaction Details --------");
System.out.println("Transaction ID: \t" + getTransactionID());
System.out.println("Withdrawal Amount: \t" + getAmount());
System.out.println("Transaction Date: \t" + getDate().getTime());
if (amountNotWithdrawn > 0) {
System.out.println("Amount Not Withdrawn: \t" + amountNotWithdrawn);
}
System.out.println("-------------------------------------------------");
}

/*
Oportunity for assignment: implementing different form of withdrawal
/**
* apply() - Overrides BaseTransaction's apply() method.
* Subtracts the withdrawal amount from the bank account's current balance.
* Uses the 'throws' keyword to declare that InsufficientFundsException may be thrown
* if the balance is less than the withdrawal amount.
* This demonstrates method overriding and polymorphism (late binding).
* @param ba the BankAccount to apply the withdrawal to
* @throws InsufficientFundsException if the account balance is less than the withdrawal amount
*/
public void apply(BankAccount ba) {
@Override
public void apply(BankAccount ba) throws InsufficientFundsException {
double curr_balance = ba.getBalance();
if (curr_balance > getAmount()) {
double new_balance = curr_balance - getAmount();
ba.setBalance(new_balance);

// Check if the balance covers the withdrawal amount
if (curr_balance < getAmount()) {
throw new InsufficientFundsException(
"Insufficient funds! Balance: " + curr_balance + ", Withdrawal Amount: " + getAmount(),
getAmount() - curr_balance
);
}

double new_balance = curr_balance - getAmount();
ba.setBalance(new_balance);
this.targetAccount = ba;
this.applied = true;
System.out.println("Withdrawal Applied Successfully. New Balance: " + ba.getBalance());
}

/*
Assignment 1 Q3: Write the Reverse method - a method unique to the WithdrawalTransaction Class
/**
* apply() - Overloaded apply method that handles insufficient funds gracefully.
* This method not only checks if the balance covers the withdrawal amount, but also
* checks if the balance is greater than 0. In the case where 0 < balance < withdrawal amount,
* it withdraws all the available balance and keeps a record of the amount not withdrawn.
*
* Implements exception handling using try{...} catch{...} finally{...} block.
*
* @param ba the BankAccount to apply the withdrawal to
* @param checkAvailable a flag to enable available-balance withdrawal mode
*/
}
public void apply(BankAccount ba, boolean checkAvailable) {
double curr_balance = ba.getBalance();

try {
// First check: is the balance sufficient for a full withdrawal?
if (curr_balance < getAmount()) {
throw new InsufficientFundsException(
"Insufficient funds for full withdrawal. Balance: " + curr_balance +
", Requested: " + getAmount(),
getAmount() - curr_balance
);
}

// Full withdrawal is possible
double new_balance = curr_balance - getAmount();
ba.setBalance(new_balance);
this.targetAccount = ba;
this.applied = true;
this.amountNotWithdrawn = 0;
System.out.println("Full Withdrawal Applied. New Balance: " + ba.getBalance());

} catch (InsufficientFundsException e) {
// Handle the case where 0 < balance < withdrawal amount
System.out.println("Exception Caught: " + e.getMessage());

if (checkAvailable && curr_balance > 0) {
// Withdraw all available balance and record the shortfall
this.amountNotWithdrawn = getAmount() - curr_balance;
ba.setBalance(0);
this.targetAccount = ba;
this.applied = true;
System.out.println("Partial Withdrawal Applied: Withdrew " + curr_balance +
" from available balance.");
System.out.println("Amount Not Withdrawn: " + amountNotWithdrawn);
} else {
// Balance is 0 or negative, cannot withdraw anything
this.amountNotWithdrawn = getAmount();
System.out.println("Withdrawal Failed: No available funds in the account.");
}

} finally {
// This block always executes regardless of whether an exception occurred
System.out.println("Transaction Complete. Final Account Balance: " + ba.getBalance());
printTransactionDetails();
}
}
}
Loading