ATM Machine
An automated teller machine (ATM) is an electronic telecommunications instrument that provides the clients of a financial institution with access to financial transactions in a public space without the need for a cashier or bank teller. ATMs are necessary as not all the bank branches are open every day of the week, and some customers may not be in a position to visit a bank each time they want to withdraw or deposit money.
**System Requirements ** The main components of the ATM that will affect interactions between the ATM and its users are:
Card reader: to read the users’ ATM cards.
Keypad: to enter information into the ATM e.g. PIN. cards.
Screen: to display messages to the users.
Cash dispenser: for dispensing cash.
Deposit slot: For users to deposit cash or checks.
Printer: for printing receipts.
Communication/Network Infrastructure: it is assumed that the ATM has a communication infrastructure to communicate with the bank upon any transaction or activity.
Code
// ENUMS and common classes
public enum TransactionType {
BALANCE_INQUIRY, DEPOSIT_CASH, DEPOSIT_CHECK, WITHDRAW, TRANSFER
}
public enum TransactionStatus {
SUCCESS, FAILURE, BLOCKED, FULL, PARTIAL, NONE
}
public enum CustomerStatus {
ACTIVE, BLOCKED, BANNED, COMPROMISED, ARCHIVED, CLOSED, UNKNOWN
}
// Address class remains the same
public class Address {
private String streetAddress;
private String city;
private String state;
private String zipCode;
private String country;
}
// Abstract Transaction Class with Template Method pattern
public abstract class Transaction {
protected int transactionId;
protected Date creationTime;
protected TransactionStatus status;
// Template method defining the steps for a transaction
public final void executeTransaction() {
verifyAccount();
process();
updateBalance();
generateReceipt();
}
protected abstract void verifyAccount();
protected abstract void process();
protected abstract void updateBalance();
protected abstract void generateReceipt();
}
// Strategy Pattern for Accounts
interface AccountStrategy {
double getAvailableBalance();
void deposit(double amount);
void withdraw(double amount);
}
public class CheckingAccount implements AccountStrategy {
private int accountNumber;
private double totalBalance;
private double availableBalance;
private String debitCardNumber;
@Override
public double getAvailableBalance() {
return availableBalance;
}
@Override
public void deposit(double amount) {
availableBalance += amount;
}
@Override
public void withdraw(double amount) {
availableBalance -= amount;
}
}
public class SavingAccount implements AccountStrategy {
private int accountNumber;
private double totalBalance;
private double availableBalance;
private double withdrawLimit;
@Override
public double getAvailableBalance() {
return availableBalance;
}
@Override
public void deposit(double amount) {
availableBalance += amount;
}
@Override
public void withdraw(double amount) {
if (amount <= withdrawLimit) {
availableBalance -= amount;
}
}
}
// Concrete Transaction classes
public class BalanceInquiry extends Transaction {
private int accountId;
private AccountStrategy account;
public BalanceInquiry(AccountStrategy account) {
this.account = account;
}
@Override
protected void verifyAccount() {
System.out.println("Verifying account " + accountId);
}
@Override
protected void process() {
System.out.println("Balance: " + account.getAvailableBalance());
}
@Override
protected void updateBalance() {
// No balance update needed for balance inquiry
}
@Override
protected void generateReceipt() {
System.out.println("Receipt generated for Balance Inquiry");
}
}
public class Withdraw extends Transaction {
private double amount;
private AccountStrategy account;
private CashDispenser cashDispenser;
public Withdraw(AccountStrategy account, double amount, CashDispenser cashDispenser) {
this.account = account;
this.amount = amount;
this.cashDispenser = cashDispenser;
}
@Override
protected void verifyAccount() {
System.out.println("Verifying account for withdrawal.");
}
@Override
protected void process() {
if (cashDispenser.canDispenseCash() && account.getAvailableBalance() >= amount) {
account.withdraw(amount);
cashDispenser.dispenseCash(amount);
} else {
System.out.println("Insufficient funds or ATM can't dispense cash.");
}
}
@Override
protected void updateBalance() {
System.out.println("Balance updated after withdrawal.");
}
@Override
protected void generateReceipt() {
System.out.println("Receipt generated for withdrawal.");
}
}
public class Deposit extends Transaction {
protected double amount;
protected AccountStrategy account;
public Deposit(AccountStrategy account, double amount) {
this.account = account;
this.amount = amount;
}
@Override
protected void verifyAccount() {
System.out.println("Verifying account for deposit.");
}
@Override
protected void process() {
account.deposit(amount);
}
@Override
protected void updateBalance() {
System.out.println("Balance updated after deposit.");
}
@Override
protected void generateReceipt() {
System.out.println("Receipt generated for deposit.");
}
}
public class Transfer extends Transaction {
private AccountStrategy sourceAccount;
private AccountStrategy destinationAccount;
private double amount;
public Transfer(AccountStrategy sourceAccount, AccountStrategy destinationAccount, double amount) {
this.sourceAccount = sourceAccount;
this.destinationAccount = destinationAccount;
this.amount = amount;
}
@Override
protected void verifyAccount() {
System.out.println("Verifying accounts for transfer.");
}
@Override
protected void process() {
if (sourceAccount.getAvailableBalance() >= amount) {
sourceAccount.withdraw(amount);
destinationAccount.deposit(amount);
} else {
System.out.println("Insufficient funds for transfer.");
}
}
@Override
protected void updateBalance() {
System.out.println("Balance updated after transfer.");
}
@Override
protected void generateReceipt() {
System.out.println("Receipt generated for transfer.");
}
}
// Factory Method Pattern for Transaction creation
class TransactionFactory {
public static Transaction createTransaction(TransactionType type, AccountStrategy account, double amount, CashDispenser cashDispenser) {
switch (type) {
case BALANCE_INQUIRY:
return new BalanceInquiry(account);
case DEPOSIT_CASH:
return new Deposit(account, amount);
case WITHDRAW:
return new Withdraw(account, amount, cashDispenser);
case TRANSFER:
// Assuming we have a destination account
AccountStrategy destinationAccount = new CheckingAccount(); // for example
return new Transfer(account, destinationAccount, amount);
default:
throw new IllegalArgumentException("Invalid transaction type");
}
}
}
// Observer Pattern for Notification
interface Observer {
void update(String message);
}
class Customer implements Observer {
private String name;
public Customer(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println("Notification for " + name + ": " + message);
}
}
// ATM class that uses Command pattern
class ATM {
private CashDispenser cashDispenser;
private Screen screen;
public ATM(CashDispenser cashDispenser, Screen screen) {
this.cashDispenser = cashDispenser;
this.screen = screen;
}
public void executeTransaction(Transaction transaction) {
transaction.executeTransaction();
}
}
// Main class for testing
public class Main {
public static void main(String[] args) {
AccountStrategy checkingAccount = new CheckingAccount();
CashDispenser cashDispenser = new CashDispenser();
ATM atm = new ATM(cashDispenser, new Screen());
// Create transaction using Factory Method
Transaction transaction = TransactionFactory.createTransaction(TransactionType.WITHDRAW, checkingAccount, 100.0, cashDispenser);
// Execute the transaction
atm.executeTransaction(transaction);
}
}