diff --git a/assignment b/assignment new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/assignment @@ -0,0 +1 @@ + diff --git a/src/Lecture4_interfaces_abstract_classes/BankAccount.java b/src/Lecture4_interfaces_abstract_classes/BankAccount.java index 28d0d07..1f188d7 100644 --- a/src/Lecture4_interfaces_abstract_classes/BankAccount.java +++ b/src/Lecture4_interfaces_abstract_classes/BankAccount.java @@ -1,16 +1,20 @@ -package Lecture4_interfaces_abstract_classes; - public class BankAccount { + private String accountNumber; private double balance; - public BankAccount(double balance) { - this.balance = balance; + + public BankAccount(String accountNumber, double initialBalance) { + this.accountNumber = accountNumber; + this.balance = initialBalance; } - public double getBalance() { - return balance; + public String getAccountNumber() { return accountNumber; } + public double getBalance() { return balance; } + + public void deposit(double amount) { + this.balance += amount; } - public void setBalance(double balance) { - this.balance = balance; + public void withdraw(double amount) { + this.balance -= amount; } } diff --git a/src/Lecture4_interfaces_abstract_classes/BaseTransaction.java b/src/Lecture4_interfaces_abstract_classes/BaseTransaction.java index ed81eb8..e3e04c4 100644 --- a/src/Lecture4_interfaces_abstract_classes/BaseTransaction.java +++ b/src/Lecture4_interfaces_abstract_classes/BaseTransaction.java @@ -1,51 +1,38 @@ -package Lecture4_interfaces_abstract_classes; - -import org.jetbrains.annotations.NotNull; - import java.util.Calendar; +import java.util.UUID; -public abstract class BaseTransaction implements TransactionInterface { - private final int amount; - private final Calendar date; - private final String transactionID; +public class BaseTransaction implements TransactionInterface { + protected double amount; + protected Calendar date; + protected 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 - */ - public BaseTransaction(int amount, @NotNull Calendar date) { + public BaseTransaction(double amount) { this.amount = amount; - this.date = (Calendar) date.clone(); - int uniq = (int) Math.random()*10000; - transactionID = date.toString()+uniq; + this.date = Calendar.getInstance(); + this.transactionID = UUID.randomUUID().toString().substring(0, 8); // Unique short ID } - /** - * getAmount() - * @return integer - */ - public double getAmount() { - return amount; // Because we are dealing with Value types we need not worry about what we return - } + @Override + public double getAmount() { return this.amount; } + + @Override + public Calendar getDate() { return this.date; } + + @Override + public String getTransactionID() { return this.transactionID; } - /** - * getDate() - * @return Calendar 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 + @Override + public void printTransactionDetails() { + System.out.println("--- Transaction Details ---"); + System.out.println("ID: " + transactionID); + System.out.println("Date: " + date.getTime()); + System.out.println("Base Amount: KSh " + amount); } - // Method to get a unique identifier for the transaction - public String getTransactionID(){ - return transactionID; + // Question 1: Base implementation differs substantially from subclasses + @Override + public void apply(BankAccount ba) throws InsufficientFundsException { + System.out.println("[BaseTransaction] Checking system connectivity for account: " + ba.getAccountNumber()); + System.out.println("[BaseTransaction] No balance modifications done by the base layer abstraction."); } - // Method to print a transaction receipt or details - public abstract void printTransactionDetails(); - public abstract void apply(BankAccount ba); } diff --git a/src/Lecture4_interfaces_abstract_classes/DepositTrasaction.java b/src/Lecture4_interfaces_abstract_classes/DepositTrasaction.java index 81afab5..7fa695b 100644 --- a/src/Lecture4_interfaces_abstract_classes/DepositTrasaction.java +++ b/src/Lecture4_interfaces_abstract_classes/DepositTrasaction.java @@ -1,30 +1,20 @@ -package Lecture4_interfaces_abstract_classes; +public class DepositTransaction extends BaseTransaction { -import org.jetbrains.annotations.NotNull; - -import java.util.Calendar; - -public class DepositTrasaction extends BaseTransaction { - public DepositTrasaction(int amount, @NotNull Calendar date){ - super(amount, date); - } - private boolean checkDepositAmount(int amt){ - if (amt < 0){ - return false; - } - else{ - return true; - } + public DepositTransaction(double amount) { + super(amount); } - // Method to print a transaction receipt or details - public void printTransactionDetails(){ - System.out.println("Deposit Trasaction: "+this.toString()); + // Question 1: Method Overriding + @Override + public void apply(BankAccount ba) { + ba.deposit(this.amount); + System.out.println("[Deposit] KSh " + amount + " successfully credited to Account " + ba.getAccountNumber()); } - public void apply(BankAccount ba){ - double curr_balance = ba.getBalance(); - double new_balance = curr_balance + getAmount(); - ba.setBalance(new_balance); + @Override + public void printTransactionDetails() { + super.printTransactionDetails(); + System.out.println("Type: DEPOSIT (Irreversible)"); + System.out.println("---------------------------"); } } diff --git a/src/Lecture4_interfaces_abstract_classes/InsufficientFundsException.java b/src/Lecture4_interfaces_abstract_classes/InsufficientFundsException.java new file mode 100644 index 0000000..c8506b7 --- /dev/null +++ b/src/Lecture4_interfaces_abstract_classes/InsufficientFundsException.java @@ -0,0 +1,6 @@ +// Question 3: Custom Exception extending Exception class +public class InsufficientFundsException extends Exception { + public InsufficientFundsException(String message) { + super(message); + } +} diff --git a/src/Lecture4_interfaces_abstract_classes/TransactionInterface.java b/src/Lecture4_interfaces_abstract_classes/TransactionInterface.java index 5902713..5edfee9 100644 --- a/src/Lecture4_interfaces_abstract_classes/TransactionInterface.java +++ b/src/Lecture4_interfaces_abstract_classes/TransactionInterface.java @@ -1,21 +1,9 @@ -package Lecture4_interfaces_abstract_classes; import java.util.Calendar; -/** - * Interface for Transactions - * Any class that defines a transaction is expected to implement this Interface - */ public interface TransactionInterface { - - // Method to get the transaction amount double getAmount(); - - // Method to get the transaction date Calendar getDate(); - - // Method to get a unique identifier for the transaction String getTransactionID(); - + void printTransactionDetails(); + void apply(BankAccount ba) throws InsufficientFundsException; } - - diff --git a/src/Lecture4_interfaces_abstract_classes/WithdrawalTransaction.java b/src/Lecture4_interfaces_abstract_classes/WithdrawalTransaction.java index face5b6..c7be41c 100644 --- a/src/Lecture4_interfaces_abstract_classes/WithdrawalTransaction.java +++ b/src/Lecture4_interfaces_abstract_classes/WithdrawalTransaction.java @@ -1,45 +1,78 @@ -package Lecture4_interfaces_abstract_classes; - -import org.jetbrains.annotations.NotNull; - -import java.util.Calendar; - public class WithdrawalTransaction extends BaseTransaction { - public WithdrawalTransaction(int amount, @NotNull Calendar date) { - super(amount, date); + private boolean isReversed = false; + private BankAccount associatedAccount; + private double shortFallAmount = 0.0; // Tracks partial failure record + + public WithdrawalTransaction(double amount) { + super(amount); } - private boolean checkDepositAmount(int amt) { - if (amt < 0) { - return false; - } else { - return true; + // Question 1 & 3: Overriding and using the 'throws' keyword + @Override + public void apply(BankAccount ba) throws InsufficientFundsException { + this.associatedAccount = ba; // Cache reference for potential reversal + if (ba.getBalance() < this.amount) { + throw new InsufficientFundsException("Error: Insufficient funds to complete transaction of KSh " + this.amount); } + ba.withdraw(this.amount); + System.out.println("[Withdrawal] KSh " + amount + " successfully deducted from Account " + ba.getAccountNumber()); } - // Method to reverse the transaction - public boolean reverse() { - return true; - } // return true if reversal was successful + // Question 3: Overloaded apply() handling partial withdrawal using try-catch-finally + public void apply(BankAccount ba, boolean allowPartial) { + this.associatedAccount = ba; + if (!allowPartial) { + try { + apply(ba); + } catch (InsufficientFundsException e) { + System.out.println("[Handled Exception] " + e.getMessage()); + } + return; + } - // Method to print a transaction receipt or details - public void printTransactionDetails() { - System.out.println("Deposit Trasaction: " + this.toString()); + // Partial withdrawal logic: 0 < balance < withdrawal amount + try { + if (ba.getBalance() <= 0) { + throw new InsufficientFundsException("Account empty. Cannot perform partial withdrawal."); + } else if (ba.getBalance() < this.amount) { + double availableBalance = ba.getBalance(); + this.shortFallAmount = this.amount - availableBalance; + + ba.withdraw(availableBalance); // Clear the account balance + System.out.println("[Partial Withdrawal] Only KSh " + availableBalance + " could be withdrawn."); + } else { + apply(ba); // Enough money exists normally + } + } catch (InsufficientFundsException e) { + System.out.println("[Try-Catch Block Alert] " + e.getMessage()); + } finally { + System.out.println("[Finally Block Executed] Record updated. Shortfall unfulfilled amount: KSh " + shortFallAmount); + } } - /* - Oportunity for assignment: implementing different form of withdrawal - */ - public void apply(BankAccount ba) { - double curr_balance = ba.getBalance(); - if (curr_balance > getAmount()) { - double new_balance = curr_balance - getAmount(); - ba.setBalance(new_balance); + // Question 2: Reversal logic for withdrawals + public boolean reverse() { + if (isReversed) { + System.out.println("Transaction already reversed."); + return false; + } + if (associatedAccount != null) { + double amountToRestore = this.amount - this.shortFallAmount; + associatedAccount.deposit(amountToRestore); + this.isReversed = true; + System.out.println("[REVERSAL SUCCESS] Restored KSh " + amountToRestore + " to Account " + associatedAccount.getAccountNumber()); + return true; } + System.out.println("[REVERSAL FAILED] No associated bank account found."); + return false; } - /* - Assignment 1 Q3: Write the Reverse method - a method unique to the WithdrawalTransaction Class - */ + @Override + public void printTransactionDetails() { + super.printTransactionDetails(); + System.out.println("Type: WITHDRAWAL"); + System.out.println("Status: " + (isReversed ? "REVERSED" : "ACTIVE")); + System.out.println("Shortfall Record: KSh " + shortFallAmount); + System.out.println("---------------------------"); + } } - diff --git a/src/Main.java b/src/Main.java index 584a048..69e4945 100644 --- a/src/Main.java +++ b/src/Main.java @@ -1,156 +1,77 @@ -import Lecture1_adt.*; // Import all classes from Lecture1_adt package to be used in this client code +import java.util.Scanner; -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); - - // 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); - } - 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()); - } + public static void main(String[] args) { + Scanner scanner = new Scanner(System.in); + + // --- Simple School Registration System Segment -- + System.out.println(" JKUAT ADVANCED PROGRAMMING ASSIGNMENT "); + System.out.println("================================================="); + System.out.print("Enter Student Full Name: "); + String studentName = scanner.nextLine(); + System.out.print("Enter Registration Number: "); + String regNo = scanner.nextLine(); + + System.out.println("\n--- Student Verification Successful ---"); + System.out.println("Student: " + studentName.toUpperCase()); + System.out.println("Reg No: " + regNo.toUpperCase()); + System.out.println("=================================================\n"); + + // --- Question 4: Client Code and Polymorphism Testing --- + BankAccount account = new BankAccount("CK-90210", 5000.0); + System.out.println("Initial Account Balance: KSh " + account.getBalance() + "\n"); + + // 1. Testing Subclass: Deposit + DepositTransaction dep = new DepositTransaction(3000.0); + dep.apply(account); + dep.printTransactionDetails(); + System.out.println("New Balance: KSh " + account.getBalance() + "\n"); + + // 2. Testing Subclass: Normal Withdrawal + WithdrawalTransaction wit1 = new WithdrawalTransaction(2000.0); + try { + wit1.apply(account); + } 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); + wit1.printTransactionDetails(); + System.out.println("New Balance: KSh " + account.getBalance() + "\n"); + + // 3. Testing Question 2: Reversal of Withdrawal + System.out.println("Executing withdrawal reversal process..."); + wit1.reverse(); + System.out.println("Balance after reversal: KSh " + account.getBalance() + "\n"); + + // 4. Testing Question 3: Throws Exception (Insufficient Funds) + System.out.println("Testing Insufficient Funds standard exception handling..."); + WithdrawalTransaction massiveWithdrawal = new WithdrawalTransaction(25000.0); + try { + massiveWithdrawal.apply(account); + } catch (InsufficientFundsException e) { + System.err.println("[Caught 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()); + System.out.println(); + + // 5. Testing Question 3: Overloaded Try-Catch-Finally Partial Withdrawal + System.out.println("Testing Overloaded Partial Withdrawal behavior..."); + WithdrawalTransaction partialWit = new WithdrawalTransaction(10000.0); + // Balance is currently 8000. It should clean out the 8000 and keep a 2000 shortfall tracking record. + partialWit.apply(account, true); + partialWit.printTransactionDetails(); + System.out.println("Final Account Balance: KSh " + account.getBalance() + "\n"); + + // 6. Polymorphic Mapping & Type Casting Demonstration + System.out.println("Testing Polymorphism and Type Casting to Base Object..."); + BaseTransaction basePolymorphicRef = (BaseTransaction) dep; // Explicit upward cast + + try { + // Early vs Late Binding: Even though the reference variable is type BaseTransaction, + // JVM executes the Deposit class's custom apply logic due to late/dynamic binding. + basePolymorphicRef.apply(account); + } 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() + + scanner.close(); } -} \ No newline at end of file +}