diff --git a/src/Bank account b/src/Bank account
new file mode 100644
index 0000000..c1b9692
--- /dev/null
+++ b/src/Bank account
@@ -0,0 +1,36 @@
+/**
+ * Represents a simple bank account with a balance.
+ */
+public class BankAccount {
+
+ private double balance;
+ private String accountID;
+
+ public BankAccount(String accountID, double initialBalance) {
+ this.accountID = accountID;
+ this.balance = initialBalance;
+ }
+
+ public double getBalance() {
+ return balance;
+ }
+
+ public String getAccountID() {
+ return accountID;
+ }
+
+
+ public void credit(double amount) {
+ this.balance += amount;
+ }
+
+
+ public void debit(double amount) {
+ this.balance -= amount;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("BankAccount[ID=%s, Balance=%.2f]", accountID, balance);
+ }
+}
diff --git a/src/Base Transaction b/src/Base Transaction
new file mode 100644
index 0000000..7e37022
--- /dev/null
+++ b/src/Base Transaction
@@ -0,0 +1,86 @@
+import java.util.Calendar;
+import java.util.UUID;
+
+/**
+ * BaseTransaction is a concrete class implementing TransactionInterface.
+ */
+public class BaseTransaction implements TransactionInterface {
+
+ // ----- Fields -----
+ private final double amount;
+ private final Calendar date;
+ private final String transactionID;
+
+ // ----- Constructor -----
+
+
+ public BaseTransaction(double amount, Calendar date) {
+ this.amount = amount;
+ this.date = date;
+ this.transactionID = UUID.randomUUID().toString();
+ }
+
+ // ----- Getters (Q1 required methods) -----
+
+ /**
+ * Returns the transaction amount.
+ * Early binding: resolved at compile time for BaseTransaction references.
+ */
+ @Override
+ public double getAmount() {
+ return amount;
+ }
+
+ /**
+ * Returns the transaction date.
+ */
+ @Override
+ public Calendar getDate() {
+ return date;
+ }
+
+ /**
+ * Returns the unique transaction ID.
+ */
+ @Override
+ public String getTransactionID() {
+ return transactionID;
+ }
+
+ // ----- Common transaction methods -----
+
+ /**
+ * Prints the details of this transaction to standard output.
+ */
+ @Override
+ public void printTransactionDetails() {
+ System.out.println("=== Transaction Details ===");
+ System.out.println("Transaction ID : " + transactionID);
+ System.out.printf ("Amount : %.2f%n", amount);
+ System.out.printf ("Date : %04d-%02d-%02d%n",
+ date.get(Calendar.YEAR),
+ date.get(Calendar.MONTH) + 1, // Calendar months are 0-indexed
+ date.get(Calendar.DAY_OF_MONTH));
+ System.out.println("Type : Base Transaction");
+ System.out.println("===========================");
+ }
+
+ /**
+ * Base implementation of apply().
+ *
+ * This implementation DIFFERS SUBSTANTIALLY from DepositTransaction and
+ * WithdrawalTransaction. Here we only log the transaction without modifying
+ * the bank account — simulating a "pending" or "record-only" transaction.
+ *
+ * Demonstrates EARLY BINDING: when called on a BaseTransaction reference,
+ * this method is resolved at compile time.
+ *
+ * @param ba the BankAccount on which to (not yet) apply the transaction
+ */
+ @Override
+ public void apply(BankAccount ba) throws InsufficientFundsException {
+ System.out.println("[BaseTransaction] Recording transaction " + transactionID
+ + " for account " + ba.getAccountID()
+ + " — no balance change (base behaviour).");
+ }
+}
diff --git a/src/Deposit Transaction b/src/Deposit Transaction
new file mode 100644
index 0000000..10ff099
--- /dev/null
+++ b/src/Deposit Transaction
@@ -0,0 +1,70 @@
+import java.util.Calendar;
+
+/**
+ * DepositTransaction represents an irreversible credit to a BankAccount.
+ *
+ * Design assumption (Q2): deposits are IRREVERSIBLE — no reverse() method.
+ *
+ * Demonstrates LATE BINDING / POLYMORPHISM:
+ * When apply() is called on a TransactionInterface or BaseTransaction reference
+ * that actually holds a DepositTransaction object, the JVM resolves the call
+ * to THIS override at runtime — not the base-class version.
+ */
+public class DepositTransaction extends BaseTransaction {
+
+ // ----- Constructor -----
+
+ /**
+ * Creates a DepositTransaction.
+ *
+ * @param amount the amount to deposit (should be > 0)
+ * @param date the date of the deposit
+ */
+ public DepositTransaction(double amount, Calendar date) {
+ super(amount, date);
+ }
+
+ // ----- Overridden methods -----
+
+ /**
+ * Prints deposit-specific details, then delegates to the superclass
+ * for common fields.
+ */
+ @Override
+ public void printTransactionDetails() {
+ System.out.println("=== Deposit Transaction Details ===");
+ System.out.println("Transaction ID : " + getTransactionID());
+ System.out.printf ("Deposit Amount : %.2f%n", getAmount());
+ System.out.printf ("Date : %04d-%02d-%02d%n",
+ getDate().get(Calendar.YEAR),
+ getDate().get(Calendar.MONTH) + 1,
+ getDate().get(Calendar.DAY_OF_MONTH));
+ System.out.println("Reversible : No");
+ System.out.println("===================================");
+ }
+
+ /**
+ * Applies the deposit by crediting the given BankAccount.
+ *
+ * LATE BINDING: this override is selected at runtime when the actual
+ * object is a DepositTransaction, regardless of the reference type used.
+ *
+ * Differs from BaseTransaction.apply() (which makes no balance change)
+ * and WithdrawalTransaction.apply() (which debits and may throw exceptions).
+ *
+ * @param ba the BankAccount to credit
+ */
+ @Override
+ public void apply(BankAccount ba) throws InsufficientFundsException {
+ System.out.printf("[DepositTransaction] Crediting %.2f to account %s.%n",
+ getAmount(), ba.getAccountID());
+
+ double balanceBefore = ba.getBalance();
+ ba.credit(getAmount());
+
+ System.out.printf(" Balance before : %.2f%n", balanceBefore);
+ System.out.printf(" Balance after : %.2f%n", ba.getBalance());
+
+ printTransactionDetails();
+ }
+}
diff --git a/src/InsufficientFundsException b/src/InsufficientFundsException
new file mode 100644
index 0000000..d41dc54
--- /dev/null
+++ b/src/InsufficientFundsException
@@ -0,0 +1,32 @@
+/**
+ * Custom exception thrown when a withdrawal exceeds the available balance.
+ * Extends Exception to make it a checked exception, requiring explicit handling.
+ */
+public class InsufficientFundsException extends Exception {
+
+ private double amountRequested;
+ private double amountAvailable;
+
+
+ public InsufficientFundsException(double amountRequested, double amountAvailable) {
+ super(String.format(
+ "Insufficient funds: attempted to withdraw %.2f but only %.2f is available.",
+ amountRequested, amountAvailable
+ ));
+ this.amountRequested = amountRequested;
+ this.amountAvailable = amountAvailable;
+ }
+
+ public double getAmountRequested() {
+ return amountRequested;
+ }
+
+ public double getAmountAvailable() {
+ return amountAvailable;
+ }
+
+
+ public double getShortfall() {
+ return amountRequested - amountAvailable;
+ }
+}
diff --git a/src/Lecture4_interfaces_abstract_classes/BankAccount.java b/src/Lecture4_interfaces_abstract_classes/BankAccount.java
index 28d0d07..f865220 100644
--- a/src/Lecture4_interfaces_abstract_classes/BankAccount.java
+++ b/src/Lecture4_interfaces_abstract_classes/BankAccount.java
@@ -1,16 +1,40 @@
-package Lecture4_interfaces_abstract_classes;
-
+/**
+ * Represents a simple bank account with a balance.
+ */
public class BankAccount {
+
private double balance;
- public BankAccount(double balance) {
- this.balance = balance;
+ private String accountID;
+
+ public BankAccount(String accountID, double initialBalance) {
+ this.accountID = accountID;
+ this.balance = initialBalance;
}
public double getBalance() {
return balance;
}
- public void setBalance(double balance) {
- this.balance = balance;
+ public String getAccountID() {
+ return accountID;
+ }
+
+ /**
+ * Credits (adds) an amount to the account balance.
+ */
+ public void credit(double amount) {
+ this.balance += amount;
+ }
+
+ /**
+ * Debits (subtracts) an amount from the account balance.
+ */
+ public void debit(double amount) {
+ this.balance -= amount;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("BankAccount[ID=%s, Balance=%.2f]", accountID, balance);
}
}
diff --git a/src/Main.java b/src/Main.java
index 584a048..c03cbaf 100644
--- a/src/Main.java
+++ b/src/Main.java
@@ -1,156 +1,218 @@
-import Lecture1_adt.*; // Import all classes from Lecture1_adt package to be used in this client code
-
import java.util.Calendar;
-import java.util.GregorianCalendar;
-import java.util.ArrayList;
-import java.util.List;
-
-//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);
+public class Main {
- // 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
+ // ------------------------------------------------------------------ //
+ // Helper: separator line //
+ // ------------------------------------------------------------------ //
+ private static void section(String title) {
+ System.out.println("\n" + "=".repeat(55));
+ System.out.println(" " + title);
+ System.out.println("=".repeat(55));
}
+
+ // Test 1 — BaseTransaction (base / early-binding apply) //
+
+ static void testBaseTransaction() {
+ section("TEST 1 — BaseTransaction.apply() [early binding]");
+
+ BankAccount account = new BankAccount("ACC-001", 1000.00);
+ Calendar today = Calendar.getInstance();
- /** @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
- * */
+ // BaseTransaction is a concrete class — instantiate directly
+ BaseTransaction base = new BaseTransaction(250.00, today);
+ base.printTransactionDetails();
- public static Transaction2 makeNextPayment(Transaction2 t) {
- Calendar d = t.getDate();
- d.add(Calendar.MONTH, 1);
- return new Transaction2(t.getAmount(), d);
+ System.out.println("\nAccount before apply: " + account);
+ try {
+ base.apply(account); // calls BaseTransaction.apply() — no balance change
+ } catch (InsufficientFundsException e) {
+ System.out.println("Error: " + e.getMessage());
+ }
+ System.out.println("Account after apply : " + account);
}
- /*
- Testing Transaction2 class
- */
- public static void testTransaction2() {
+ // ------------------------------------------------------------------ //
+ // Test 2 — DepositTransaction //
+ // ------------------------------------------------------------------ //
+ static void testDepositTransaction() {
+ section("TEST 2 — DepositTransaction.apply()");
- Calendar d1 = new GregorianCalendar();
+ BankAccount account = new BankAccount("ACC-002", 500.00);
+ Calendar today = Calendar.getInstance();
- Lecture1_adt.Transaction2 t = new Lecture1_adt.Transaction2(1000, d1);
+ DepositTransaction deposit = new DepositTransaction(300.00, today);
- Lecture1_adt.Transaction2 modified_t = makeNextPayment(t);
+ System.out.println("Account before deposit: " + account);
+ try {
+ deposit.apply(account);
+ } catch (InsufficientFundsException e) {
+ System.out.println("Error: " + e.getMessage());
+ }
+ System.out.println("Account after deposit : " + account);
+ }
- 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());
+ // ------------------------------------------------------------------ //
+ // Test 3 — WithdrawalTransaction: sufficient funds //
+ // ------------------------------------------------------------------ //
+ static void testWithdrawalSufficientFunds() {
+ section("TEST 3 — WithdrawalTransaction.apply() [sufficient funds]");
- 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());
+ BankAccount account = new BankAccount("ACC-003", 800.00);
+ Calendar today = Calendar.getInstance();
- /* 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
- */
+ WithdrawalTransaction withdrawal = new WithdrawalTransaction(200.00, today);
+ System.out.println("Account before withdrawal: " + account);
+ try {
+ withdrawal.apply(account);
+ } catch (InsufficientFundsException e) {
+ System.out.println("UNEXPECTED ERROR: " + e.getMessage());
+ }
+ System.out.println("Account after withdrawal : " + account);
}
+ // ------------------------------------------------------------------ //
+ // Test 4 — WithdrawalTransaction: insufficient funds (throws) //
+ // ------------------------------------------------------------------ //
+ static void testWithdrawalInsufficientFunds() {
+ section("TEST 4 — WithdrawalTransaction.apply() [insufficient funds]");
- /** @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 {
+ BankAccount account = new BankAccount("ACC-004", 50.00);
+ Calendar today = Calendar.getInstance();
- List listOfTransaction3s = new ArrayList();
- Calendar date = new GregorianCalendar(2024, Calendar.JANUARY, 3);
+ WithdrawalTransaction withdrawal = new WithdrawalTransaction(200.00, today);
+ System.out.println("Account before withdrawal: " + account);
+ try {
+ withdrawal.apply(account);
+ } catch (InsufficientFundsException e) {
+ System.out.println("Caught InsufficientFundsException: " + e.getMessage());
+ System.out.printf(" Shortfall: %.2f%n", e.getShortfall());
+ }
+ System.out.println("Account after (unchanged): " + account);
+ }
- for (int i = 0; i < 12; i++) {
- listOfTransaction3s.add(new Transaction3(amount, date));
- date.add(Calendar.MONTH, 1);
+ // ------------------------------------------------------------------ //
+ // Test 5 — Type casting: subtype → base type (late-binding demo) //
+ // ------------------------------------------------------------------ //
+ static void testPolymorphismTypeCast() {
+ section("TEST 5 — Polymorphism / late binding via type cast");
+
+ BankAccount account = new BankAccount("ACC-005", 1000.00);
+ Calendar today = Calendar.getInstance();
+
+ // Create subtype objects
+ DepositTransaction depositSub = new DepositTransaction(400.00, today);
+ WithdrawalTransaction withdrawSub = new WithdrawalTransaction(150.00, today);
+
+ // Cast subtypes to base type — the reference is BaseTransaction,
+ // but the actual object is still Deposit / Withdrawal.
+ BaseTransaction baseRef1 = (BaseTransaction) depositSub;
+ BaseTransaction baseRef2 = (BaseTransaction) withdrawSub;
+
+ System.out.println("Calling apply() via BaseTransaction reference on DepositTransaction:");
+ System.out.println("Account before: " + account);
+ // LATE BINDING: JVM resolves to DepositTransaction.apply() at runtime
+ try {
+ baseRef1.apply(account);
+ } catch (InsufficientFundsException e) {
+ System.out.println("Error: " + e.getMessage());
+ }
+ System.out.println("Account after deposit (via base ref): " + account);
+
+ System.out.println("\nCalling apply() via BaseTransaction reference on WithdrawalTransaction:");
+ try {
+ // LATE BINDING: JVM resolves to WithdrawalTransaction.apply() at runtime
+ baseRef2.apply(account);
+ } catch (InsufficientFundsException e) {
+ System.out.println("Caught: " + e.getMessage());
}
- return listOfTransaction3s;
+ System.out.println("Account after withdrawal (via base ref): " + account);
}
- /*
- Testing Transaction3 class
- */
- public static void testTransaction3() {
+ // ------------------------------------------------------------------ //
+ // Test 6 — Partial withdrawal (overloaded apply + try-catch-finally) //
+ // ------------------------------------------------------------------ //
+ static void testPartialWithdrawal() {
+ section("TEST 6 — Partial withdrawal [overloaded apply(ba, true)]");
- List allPaymentsIn2024 = makeYearOfPayments(1000);
+ BankAccount account = new BankAccount("ACC-006", 80.00);
+ Calendar today = Calendar.getInstance();
- for (Transaction3 t3 : allPaymentsIn2024) {
+ // Try to withdraw 200 when only 80 is available
+ WithdrawalTransaction withdrawal = new WithdrawalTransaction(200.00, today);
- // 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());
- }
+ System.out.println("Account before: " + account);
+ try {
+ withdrawal.apply(account, true); // allowPartial = true
+ } catch (InsufficientFundsException e) {
+ System.out.println("Caught: " + 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
- */
+ System.out.printf("Amount not withdrawn: %.2f%n", withdrawal.getAmountNotWithdrawn());
+ System.out.println("Account after: " + account);
}
+ // ------------------------------------------------------------------ //
+ // Test 7 — reverse() a withdrawal //
+ // ------------------------------------------------------------------ //
+ static void testReverseWithdrawal() {
+ section("TEST 7 — Reversing a WithdrawalTransaction");
- /** @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
- * */
+ BankAccount account = new BankAccount("ACC-007", 500.00);
+ Calendar today = Calendar.getInstance();
- public static List makeYearOfPaymentsFinal (int amount) throws NullPointerException {
+ WithdrawalTransaction withdrawal = new WithdrawalTransaction(150.00, today);
- List listOfTransaction4s = new ArrayList();
- Calendar date = new GregorianCalendar(2024, Calendar.JANUARY, 3);
+ System.out.println("Account before withdrawal : " + account);
+ try {
+ withdrawal.apply(account);
+ } catch (InsufficientFundsException e) {
+ System.out.println("Error: " + e.getMessage());
+ }
+ System.out.println("Account after withdrawal : " + account);
+ // Now reverse it
+ boolean reversed = withdrawal.reverse();
+ System.out.println("Reversal successful: " + reversed);
+ System.out.println("Account after reversal : " + account);
- for (int i = 0; i < 12; i++) {
- listOfTransaction4s.add(new Transaction4(amount, date));
- date.add(Calendar.MONTH, 1);
- }
- return listOfTransaction4s;
+ // Attempt a second reversal (should fail)
+ System.out.println("\nAttempting second reversal...");
+ withdrawal.reverse();
}
- /*
- Testing Transaction3 class
- */
- public static void testTransaction4() {
+ // ------------------------------------------------------------------ //
+ // Test 8 — Zero-balance account (partial withdrawal edge case) //
+ // ------------------------------------------------------------------ //
+ static void testZeroBalance() {
+ section("TEST 8 — Zero balance account [should throw]");
- /*
- * Call the function to make all the Twelve transaction in a year of our business
- */
+ BankAccount account = new BankAccount("ACC-008", 0.00);
+ Calendar today = Calendar.getInstance();
- List transactionsIn2024 = makeYearOfPaymentsFinal(1200);
+ WithdrawalTransaction withdrawal = new WithdrawalTransaction(100.00, today);
- // 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());
+ try {
+ withdrawal.apply(account, true); // allowPartial, but balance is 0
+ } catch (InsufficientFundsException e) {
+ System.out.println("Correctly caught: " + 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()
+ testBaseTransaction();
+ testDepositTransaction();
+ testWithdrawalSufficientFunds();
+ testWithdrawalInsufficientFunds();
+ testPolymorphismTypeCast();
+ testPartialWithdrawal();
+ testReverseWithdrawal();
+ testZeroBalance();
+
+ System.out.println("\n" + "=".repeat(55));
+ System.out.println(" All tests complete.");
+ System.out.println("=".repeat(55));
}
-}
\ No newline at end of file
+}
diff --git a/src/Transaction Interface b/src/Transaction Interface
new file mode 100644
index 0000000..290530b
--- /dev/null
+++ b/src/Transaction Interface
@@ -0,0 +1,16 @@
+import java.util.Calendar;
+public interface TransactionInterface {
+
+
+ double getAmount();
+
+ Calendar getDate();
+
+ String getTransactionID();
+
+
+ void printTransactionDetails();
+
+
+ void apply(BankAccount ba) throws InsufficientFundsException;
+}