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
19 changes: 19 additions & 0 deletions src/BankAccount.java
Original file line number Diff line number Diff line change
@@ -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;
}
}
137 changes: 137 additions & 0 deletions src/BaseTransaction.java
Original file line number Diff line number Diff line change
@@ -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("---------------------------");
}
}
6 changes: 6 additions & 0 deletions src/InsufficientFundsException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// QUESTION 3 - Custom Exception Class
public class InsufficientFundsException extends Exception {
public InsufficientFundsException(String message) {
super(message);
}
}
208 changes: 63 additions & 145 deletions src/Main.java
Original file line number Diff line number Diff line change
@@ -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 <b>Run</b> code, press <shortcut actionId="Run"/> or
// click the <icon src="AllIcons.Actions.Execute"/> 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<Transaction3> makeYearOfPayments (int amount) throws NullPointerException {

List<Transaction3> listOfTransaction3s = new ArrayList<Transaction3>();
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<Transaction3> 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<Transaction4> makeYearOfPaymentsFinal (int amount) throws NullPointerException {

List<Transaction4> listOfTransaction4s = new ArrayList<Transaction4>();
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<Transaction4> 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()
}
}
}
9 changes: 9 additions & 0 deletions src/TransactionInterface.java
Original file line number Diff line number Diff line change
@@ -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;
}