Problem Statement
Design an ATM system that allows users to withdraw cash, check balance, and deposit money using a debit card. The system should validate the card, authenticate the user using a PIN, and interact with the bank account securely. It should also handle cash dispensing and ensure account balances and ATM cash are updated correctly after every transaction.
Functional Requirements
- Support card-based authentication
- Require PIN verification before transactions
- Allow users to check account balance
- Allow cash withdrawal
- Allow cash deposit
- Dispense cash only if ATM and account both have sufficient balance
- Maintain ATM cash inventory
- Record all transactions for audit purposes
Objects Required
- ATM
- Card
- Customer (logical user)
- BankAccount
- Transaction
- TransactionType
- CashDispenser
TransactionType Enum
public enum TransactionType {
WITHDRAW,
DEPOSIT,
BALANCE_CHECK
}
The TransactionType enum defines the supported ATM operations. It avoids using raw strings and keeps transaction classification consistent across the system.
Card Class
public class Card {
private String cardNumber;
private int pin;
private BankAccount account;
public Card(String cardNumber, int pin, BankAccount account) {
this.cardNumber = cardNumber;
this.pin = pin;
this.account = account;
}
public boolean validatePin(int enteredPin) {
return this.pin == enteredPin;
}
public BankAccount getAccount() {
return account;
}
}
The constructor initializes the card and links it to a specific bank account.
The validatePin() method verifies whether the entered PIN matches the stored PIN. This acts as the first security checkpoint before any ATM operation is allowed.
The getAccount() method returns the linked bank account so that ATM operations can be performed without exposing internal account details directly.
BankAccount Class
public class BankAccount {
private String accountNumber;
private double balance;
public BankAccount(String accountNumber, double balance) {
this.accountNumber = accountNumber;
this.balance = balance;
}
public double getBalance() {
return balance;
}
public void withdraw(double amount) {
if (amount <= balance) {
balance -= amount;
}
}
public void deposit(double amount) {
balance += amount;
}
}
The constructor sets up the account with an account number and initial balance.
The getBalance() method returns the current available balance for display or validation.
The withdraw() method reduces the balance only if sufficient funds exist, preventing overdraft situations.
The deposit() method increases the account balance when money is added through the ATM.
Transaction Class
import java.time.LocalDateTime;
public class Transaction {
private String transactionId;
private TransactionType type;
private double amount;
private LocalDateTime timestamp;
private BankAccount account;
public Transaction(String transactionId,
TransactionType type,
double amount,
BankAccount account) {
this.transactionId = transactionId;
this.type = type;
this.amount = amount;
this.account = account;
this.timestamp = LocalDateTime.now();
}
}
The constructor creates a transaction record whenever an ATM operation occurs.
The Transaction class stores the type, amount, timestamp, and linked account to maintain a complete audit trail of ATM activity.
CashDispenser Class
public class CashDispenser {
private double cashAvailable;
public CashDispenser(double cashAvailable) {
this.cashAvailable = cashAvailable;
}
public boolean canDispense(double amount) {
return cashAvailable >= amount;
}
public void dispense(double amount) {
if (canDispense(amount)) {
cashAvailable -= amount;
}
}
public void refill(double amount) {
cashAvailable += amount;
}
}
The constructor initializes the ATM with available cash.
The canDispense() method checks whether the ATM has enough cash before approving a withdrawal.
The dispense() method reduces ATM cash after a successful withdrawal.
The refill() method is used by bank staff to reload cash into the ATM machine.
ATM Class
import java.util.UUID;
public class ATM {
private CashDispenser dispenser;
public ATM(CashDispenser dispenser) {
this.dispenser = dispenser;
}
public void checkBalance(Card card) {
System.out.println("Balance: " + card.getAccount().getBalance());
}
public void withdraw(Card card, int pin, double amount) {
if (!card.validatePin(pin)) {
System.out.println("Invalid PIN");
return;
}
if (!dispenser.canDispense(amount)) {
System.out.println("ATM has insufficient cash");
return;
}
BankAccount account = card.getAccount();
if (account.getBalance() < amount) {
System.out.println("Insufficient account balance");
return;
}
account.withdraw(amount);
dispenser.dispense(amount);
System.out.println("Please collect your cash");
}
public void deposit(Card card, int pin, double amount) {
if (!card.validatePin(pin)) {
System.out.println("Invalid PIN");
return;
}
BankAccount account = card.getAccount();
account.deposit(amount);
System.out.println("Amount deposited successfully");
}
}
The ATM class acts as the central controller for all operations.
The checkBalance() method retrieves the account balance linked to the card and displays it to the user.
The withdraw() method first validates the PIN, then checks ATM cash availability, and finally ensures sufficient account balance before completing the transaction. Only after all validations does it update both the account and ATM cash state.
The deposit() method validates the PIN and then directly updates the bank account balance.
Main Class
public class Main {
public static void main(String[] args) {
BankAccount account =
new BankAccount("ACC123", 5000);
Card card =
new Card("CARD123", 1234, account);
CashDispenser dispenser =
new CashDispenser(10000);
ATM atm =
new ATM(dispenser);
atm.checkBalance(card);
atm.withdraw(card, 1234, 1000);
atm.deposit(card, 1234, 2000);
atm.checkBalance(card);
}
}
The main method simulates a complete ATM session starting from balance check, followed by withdrawal and deposit operations.
It demonstrates how authentication, account updates, and cash dispensing work together in a real ATM flow.
Comments
Post a Comment