ATM System
Introduction
Section titled “Introduction”In this comprehensive case study, we’ll design an ATM (Automated Teller Machine) System from scratch using the systematic 8-step approach. This is a popular LLD interview problem that demonstrates state management, authentication, concurrent transaction handling, and multiple design patterns.
Problem Statement
Section titled “Problem Statement”Design an ATM System that can:
- Allow users to perform basic banking transactions (check balance, withdraw cash, deposit cash)
- Authenticate users via card insertion and PIN entry
- Communicate with the bank’s main system to verify accounts and process transactions
- Dispense cash using multiple note denominations ($100, $50, $20)
- Handle concurrent transactions safely while maintaining data consistency
- Manage ATM states (idle, card inserted, authenticated) with clear transitions
Step 1: Clarify Requirements
Section titled “Step 1: Clarify Requirements”Before designing, let’s ask the right questions!
Clarifying Questions
Section titled “Clarifying Questions”Functional Requirements:
- Transaction Types: What operations should the ATM support? → Check balance, withdraw cash, deposit cash
- Authentication: How should users authenticate? → Insert ATM card and enter PIN
- Bank Integration: Should ATM operate independently? → No, must communicate with bank’s main system
- Cash Dispensing: What denominations? → Multiple denominations ($100, $50, $20) using chain-based approach
- Concurrency: How to handle multiple users? → Thread-safe operations at account level, even if one user per ATM physically
Non-Functional Requirements:
- Concurrency: Multiple ATMs accessing same account → Thread-safe balance updates
- State Management: Clear state transitions → Idle → Card Inserted → Authenticated → Idle
- Extensibility: Easy to add transaction types → Modular design with interfaces
- Security: Authentication before transactions → Card and PIN validation
Edge Cases to Consider:
- What if invalid card is inserted?
- What if incorrect PIN is entered?
- What if ATM doesn’t have enough cash?
- What if account has insufficient balance?
- What if multiple ATMs access same account simultaneously?
- What if cash dispenser cannot provide exact amount?
Requirements Summary
Section titled “Requirements Summary”Step 2: Identify Actors
Section titled “Step 2: Identify Actors”Actors are external entities that interact with the system.
Who Uses This System?
Section titled “Who Uses This System?”Primary Actors:
- Customer/User - Inserts card, enters PIN, performs transactions
- Bank Administrator - (Future) Manages accounts, monitors transactions
- ATM Maintenance Staff - (Future) Refills cash, performs maintenance
Secondary Actors:
- Bank’s Main System - External system that manages accounts (represented by
BankService) - Payment Network - (Future) External payment processing
Actor Interactions
Section titled “Actor Interactions”Step 3: Identify Entities
Section titled “Step 3: Identify Entities”Entities are core objects in your system that have data and behavior.
The Noun Hunt
Section titled “The Noun Hunt”Looking at the requirements, we identify these core entities:
- ATMSystem - Main orchestrator, manages state and transactions
- Card - Represents ATM card with card number and PIN
- Account - Bank account with balance and linked cards
- BankService - Bank’s backend system managing accounts and cards
- CashDispenser - Handles physical cash dispensing
- ATMState - Interface for ATM states (Idle, HasCard, Authenticated)
- DispenseChain - Interface for cash dispensing chain
- NoteDispenser - Handles specific denomination dispensing
Entity Relationships
Section titled “Entity Relationships”Each class should have a single, well-defined responsibility (SRP).
Responsibility Assignment
Section titled “Responsibility Assignment”ATMSystem:
- Manage ATM state transitions
- Delegate operations to current state
- Coordinate transactions with bank service
- Track transaction counter
BankService:
- Manage cards and accounts
- Authenticate users (card + PIN)
- Process transactions (withdraw, deposit, balance check)
- Link cards to accounts
Account:
- Manage account balance
- Provide thread-safe withdraw/deposit operations
- Track linked cards
Card:
- Store card information (card number, PIN)
- Provide access to card data
CashDispenser:
- Check if cash can be dispensed
- Dispense cash using chain of responsibility
- Manage note dispenser chain
ATMState (IdleState, HasCardState, AuthenticatedState):
- Handle operations based on current state
- Validate state transitions
- Provide appropriate responses for invalid operations
NoteDispenser:
- Handle specific denomination dispensing
- Check if denomination can fulfill request
- Pass remaining amount to next chain link
Responsibility Visualization
Section titled “Responsibility Visualization”Step 5: Design Class Diagrams
Section titled “Step 5: Design Class Diagrams”Class diagrams show structure, relationships, and design patterns.
classDiagram
class ATMSystem {
-ATMSystem INSTANCE
-AtomicLong transactionCounter
-ATMState currentState
-Card currentCard
-CashDispenser cashDispenser
-BankService bankService
+getInstance() ATMSystem
+insertCard(String) void
+enterPin(String) void
+selectOperation(OperationType, int) void
+ejectCard() void
+checkBalance() void
+withdrawCash(int) void
+depositCash(int) void
+changeState(ATMState) void
}
class BankService {
-Map~String,Card~ cards
-Map~Card,Account~ cardAccountMap
-Map~String,Account~ accounts
+createCard(String, String) Card
+createAccount(String, double) Account
+linkCardToAccount(Card, Account) void
+getCard(String) Card
+authenticate(Card, String) boolean
+getBalance(Card) double
+withdrawMoney(Card, double) boolean
+depositMoney(Card, double) void
}
class Account {
-String accountNumber
-double balance
-Map~String,Card~ cards
+withdraw(double) boolean
+deposit(double) void
+getBalance() double
+addCard(Card) void
}
class Card {
-String cardNumber
-String pin
+getCardNumber() String
+getPin() String
}
class CashDispenser {
-DispenseChain chain
+canDispenseCash(int) boolean
+dispenseCash(int) void
}
class ATMState {
<<interface>>
+insertCard(ATMSystem, String) void
+enterPin(ATMSystem, String) void
+selectOperation(ATMSystem, OperationType, int) void
+ejectCard(ATMSystem) void
}
class IdleState {
+insertCard(ATMSystem, String) void
+enterPin(ATMSystem, String) void
+selectOperation(ATMSystem, OperationType, int) void
+ejectCard(ATMSystem) void
}
class HasCardState {
+insertCard(ATMSystem, String) void
+enterPin(ATMSystem, String) void
+selectOperation(ATMSystem, OperationType, int) void
+ejectCard(ATMSystem) void
}
class AuthenticatedState {
+insertCard(ATMSystem, String) void
+enterPin(ATMSystem, String) void
+selectOperation(ATMSystem, OperationType, int) void
+ejectCard(ATMSystem) void
}
class DispenseChain {
<<interface>>
+dispense(int) void
+setNextChain(DispenseChain) void
+canDispense(int) boolean
}
class NoteDispenser {
<<abstract>>
-int noteValue
-DispenseChain nextChain
-int numNotes
+dispense(int) void
+canDispense(int) boolean
+setNextChain(DispenseChain) void
}
class NoteDispenser100 {
+NoteDispenser100(int)
}
class NoteDispenser50 {
+NoteDispenser50(int)
}
class NoteDispenser20 {
+NoteDispenser20(int)
}
class OperationType {
<<enumeration>>
WITHDRAW_CASH
DEPOSIT_CASH
CHECK_BALANCE
}
ATMSystem "1" *-- "1" ATMState : has
ATMSystem "1" *-- "1" CashDispenser : has
ATMSystem "1" o-- "1" BankService : uses
ATMSystem --> OperationType : uses
ATMSystem --> Card : references
BankService "1" *-- "many" Account : manages
BankService "1" *-- "many" Card : manages
Account "1" *-- "many" Card : linked to
CashDispenser --> DispenseChain : uses
DispenseChain <|.. NoteDispenser
NoteDispenser <|-- NoteDispenser100
NoteDispenser <|-- NoteDispenser50
NoteDispenser <|-- NoteDispenser20
NoteDispenser --> DispenseChain : nextChain
ATMState <|.. IdleState
ATMState <|.. HasCardState
ATMState <|.. AuthenticatedState
1. State Pattern - ATMState and implementations
- Encapsulates ATM behavior based on current state
- Eliminates complex if-else chains
- Easy to add new states
2. Chain of Responsibility Pattern - DispenseChain and NoteDispenser
- Handles cash dispensing with multiple denominations
- Flexible fallback mechanism
- Easy to extend with new denominations
3. Singleton Pattern - ATMSystem
- Ensures single ATM instance per physical machine
- Prevents multiple instances causing inconsistencies
Step 6: Define Contracts & APIs
Section titled “Step 6: Define Contracts & APIs”Contracts define how classes interact - method signatures, interfaces, and behavior.
ATMState Interface:
1class ATMState(ABC):2 """3 Interface for ATM states.4 Each state defines behavior for ATM operations.5 """6 @abstractmethod7 def insert_card(self, atm, card_number: str):8 """9 Handle card insertion.10
11 Args:12 atm: ATMSystem instance13 card_number: Card number to insert14 """15 pass16
17 @abstractmethod18 def enter_pin(self, atm, pin: str):19 """20 Handle PIN entry.21
22 Args:23 atm: ATMSystem instance24 pin: PIN entered by user25 """26 pass27
28 @abstractmethod29 def select_operation(self, atm, op: OperationType, amount: int):30 """31 Handle operation selection.32
33 Args:34 atm: ATMSystem instance35 op: Operation type (WITHDRAW_CASH, DEPOSIT_CASH, CHECK_BALANCE)36 amount: Amount for transaction (0 for balance check)37 """38 pass39
40 @abstractmethod41 def eject_card(self, atm):42 """43 Handle card ejection.44
45 Args:46 atm: ATMSystem instance47 """48 passDispenseChain Interface:
1class DispenseChain(ABC):2 """3 Interface for cash dispensing chain.4 Each handler processes request or passes to next.5 """6 @abstractmethod7 def dispense(self, amount: int):8 """9 Dispense cash amount.10
11 Args:12 amount: Amount to dispense13 """14 pass15
16 @abstractmethod17 def set_next_chain(self, next_chain: DispenseChain):18 """19 Set next chain handler.20
21 Args:22 next_chain: Next handler in chain23 """24 pass25
26 @abstractmethod27 def can_dispense(self, amount: int) -> bool:28 """29 Check if amount can be dispensed.30
31 Args:32 amount: Amount to check33
34 Returns:35 bool: True if can dispense, False otherwise36 """37 passATMState Interface:
1interface ATMState {2 /**3 * Handle card insertion.4 * @param atm ATMSystem instance5 * @param cardNumber Card number to insert6 */7 void insertCard(ATMSystem atm, String cardNumber);8
9 /**10 * Handle PIN entry.11 * @param atm ATMSystem instance12 * @param pin PIN entered by user13 */14 void enterPin(ATMSystem atm, String pin);15
16 /**17 * Handle operation selection.18 * @param atm ATMSystem instance19 * @param op Operation type (WITHDRAW_CASH, DEPOSIT_CASH, CHECK_BALANCE)20 * @param amount Amount for transaction (0 for balance check)21 */22 void selectOperation(ATMSystem atm, OperationType op, int amount);23
24 /**25 * Handle card ejection.26 * @param atm ATMSystem instance27 */28 void ejectCard(ATMSystem atm);29}DispenseChain Interface:
1interface DispenseChain {2 /**3 * Dispense cash amount.4 * @param amount Amount to dispense5 */6 void dispense(int amount);7
8 /**9 * Set next chain handler.10 * @param next Next handler in chain11 */12 void setNextChain(DispenseChain next);13
14 /**15 * Check if amount can be dispensed.16 * @param amount Amount to check17 * @return True if can dispense, False otherwise18 */19 boolean canDispense(int amount);20}Core Class Contracts
Section titled “Core Class Contracts”ATMSystem:
1class ATMSystem:2 @classmethod3 def get_instance(cls) -> 'ATMSystem':4 """5 Get singleton instance of ATM system.6
7 Returns:8 ATMSystem: The single instance9 """10 pass11
12 def insert_card(self, card_number: str) -> None:13 """14 Insert ATM card.15
16 Args:17 card_number: Card number to insert18
19 Raises:20 InvalidCardException: If card is invalid21 """22 pass23
24 def enter_pin(self, pin: str) -> None:25 """26 Enter PIN for authentication.27
28 Args:29 pin: PIN entered by user30 """31 pass32
33 def select_operation(self, op: OperationType, amount: int) -> None:34 """35 Select transaction operation.36
37 Args:38 op: Operation type39 amount: Amount for transaction (0 for balance check)40 """41 pass42
43 def withdraw_cash(self, amount: int) -> None:44 """45 Withdraw cash from account.46
47 Args:48 amount: Amount to withdraw49
50 Raises:51 InsufficientBalanceException: If account has insufficient balance52 InsufficientCashException: If ATM doesn't have enough cash53 """54 pass55
56 def deposit_cash(self, amount: int) -> None:57 """58 Deposit cash to account.59
60 Args:61 amount: Amount to deposit62 """63 pass64
65 def check_balance(self) -> None:66 """67 Check account balance.68 """69 pass70
71 def eject_card(self) -> None:72 """73 Eject ATM card.74 """75 passAccount:
1class Account:2 def withdraw(self, amount: float) -> bool:3 """4 Withdraw amount from account (thread-safe).5
6 Args:7 amount: Amount to withdraw8
9 Returns:10 bool: True if successful, False if insufficient balance11 """12 pass13
14 def deposit(self, amount: float) -> None:15 """16 Deposit amount to account (thread-safe).17
18 Args:19 amount: Amount to deposit20 """21 pass22
23 def get_balance(self) -> float:24 """25 Get current account balance (thread-safe).26
27 Returns:28 float: Current balance29 """30 passATMSystem:
1class ATMSystem {2 /**3 * Get singleton instance of ATM system.4 * @return The single instance5 */6 public static ATMSystem getInstance();7
8 /**9 * Insert ATM card.10 * @param cardNumber Card number to insert11 * @throws InvalidCardException If card is invalid12 */13 public void insertCard(String cardNumber);14
15 /**16 * Enter PIN for authentication.17 * @param pin PIN entered by user18 */19 public void enterPin(String pin);20
21 /**22 * Select transaction operation.23 * @param op Operation type24 * @param amount Amount for transaction (0 for balance check)25 */26 public void selectOperation(OperationType op, int amount);27
28 /**29 * Withdraw cash from account.30 * @param amount Amount to withdraw31 * @throws InsufficientBalanceException If account has insufficient balance32 * @throws InsufficientCashException If ATM doesn't have enough cash33 */34 public void withdrawCash(int amount);35
36 /**37 * Deposit cash to account.38 * @param amount Amount to deposit39 */40 public void depositCash(int amount);41
42 /**43 * Check account balance.44 */45 public void checkBalance();46
47 /**48 * Eject ATM card.49 */50 public void ejectCard();51}Account:
1class Account {2 /**3 * Withdraw amount from account (thread-safe).4 * @param amount Amount to withdraw5 * @return True if successful, False if insufficient balance6 */7 public synchronized boolean withdraw(double amount);8
9 /**10 * Deposit amount to account (thread-safe).11 * @param amount Amount to deposit12 */13 public synchronized void deposit(double amount);14
15 /**16 * Get current account balance (thread-safe).17 * @return Current balance18 */19 public synchronized double getBalance();20}Contract Visualization
Section titled “Contract Visualization”Step 7: Handle Edge Cases
Section titled “Step 7: Handle Edge Cases”Edge cases are scenarios that might not be obvious but are important to handle.
Critical Edge Cases
Section titled “Critical Edge Cases”1. Invalid Card
- Validate card exists in bank system
- Return appropriate error message
- Stay in IdleState
2. Incorrect PIN
- Validate PIN matches card’s PIN
- Allow retry (stay in HasCardState)
- Don’t block card (in basic version)
3. Insufficient Account Balance
- Check balance before withdrawal
- Return error message
- Don’t dispense cash if insufficient
4. Insufficient ATM Cash
- Check if ATM can dispense requested amount
- Validate before account withdrawal
- Return error if cannot fulfill
5. Concurrent Access
- Multiple ATMs accessing same account
- Race condition on balance updates
- Solution: Synchronized methods in Account
6. Cash Dispensing Failure
- What if exact amount cannot be dispensed?
- Check availability before dispensing
- Return error if cannot fulfill
7. Invalid State Transitions
- Operations in wrong state (e.g., withdraw in IdleState)
- Each state validates operations
- Return appropriate error messages
Edge Case Handling Flow
Section titled “Edge Case Handling Flow”Thread Safety Considerations
Section titled “Thread Safety Considerations”1# Thread-safe account operations2class Account:3 def __init__(self, account_number: str, initial_balance: float):4 self.account_number = account_number5 self.balance = initial_balance6 self.lock = Lock() # Thread-safe lock7
8 def withdraw(self, amount: float) -> bool:9 with self.lock: # Critical section10 if self.balance >= amount:11 self.balance -= amount12 return True13 return False14
15 def deposit(self, amount: float):16 with self.lock: # Critical section17 self.balance += amount18
19 def get_balance(self) -> float:20 with self.lock: # Critical section21 return self.balance1// Thread-safe account operations2class Account {3 private double balance;4 private final Object lock = new Object();5
6 public boolean withdraw(double amount) {7 synchronized (lock) { // Critical section8 if (balance >= amount) {9 balance -= amount;10 return true;11 }12 return false;13 }14 }15
16 public void deposit(double amount) {17 synchronized (lock) { // Critical section18 balance += amount;19 }20 }21
22 public double getBalance() {23 synchronized (lock) { // Critical section24 return balance;25 }26 }27}Step 8: Code Implementation
Section titled “Step 8: Code Implementation”Finally, implement your design following SOLID principles and design patterns.
Complete Implementation
Section titled “Complete Implementation”1from enum import Enum2from abc import ABC, abstractmethod3from threading import Lock4from typing import Optional, Dict5import uuid6
7# =====================8# ENUMERATION9# =====================10
11class OperationType(Enum):12 WITHDRAW_CASH = "WITHDRAW_CASH"13 DEPOSIT_CASH = "DEPOSIT_CASH"14 CHECK_BALANCE = "CHECK_BALANCE"15
16# =====================17# MODELS18# =====================19
20class Card:21 def __init__(self, card_number: str, pin: str):22 self.card_number = card_number23 self.pin = pin24
25 def get_card_number(self) -> str:26 return self.card_number27
28 def get_pin(self) -> str:29 return self.pin30
31class Account:32 def __init__(self, account_number: str, initial_balance: float):33 self.account_number = account_number34 self.balance = initial_balance35 self.cards: Dict[str, Card] = {}36 self.lock = Lock()37
38 def withdraw(self, amount: float) -> bool:39 with self.lock:40 if self.balance >= amount:41 self.balance -= amount42 return True43 return False44
45 def deposit(self, amount: float):46 with self.lock:47 self.balance += amount48
49 def get_balance(self) -> float:50 with self.lock:51 return self.balance52
53 def get_account_number(self) -> str:54 return self.account_number55
56 def add_card(self, card: Card):57 self.cards[card.get_card_number()] = card58
59# =====================60# SERVICES61# =====================62
63class BankService:64 def __init__(self):65 self.cards: Dict[str, Card] = {}66 self.card_account_map: Dict[Card, Account] = {}67 self.accounts: Dict[str, Account] = {}68
69 def create_card(self, card_number: str, pin: str) -> Card:70 card = Card(card_number, pin)71 self.cards[card_number] = card72 return card73
74 def create_account(self, account_number: str, initial_balance: float) -> Account:75 account = Account(account_number, initial_balance)76 self.accounts[account_number] = account77 return account78
79 def link_card_to_account(self, card: Card, account: Account):80 self.card_account_map[card] = account81 account.add_card(card)82
83 def get_card(self, card_number: str) -> Optional[Card]:84 return self.cards.get(card_number)85
86 def authenticate(self, card: Card, pin: str) -> bool:87 return card is not None and card.get_pin() == pin88
89 def get_balance(self, card: Card) -> float:90 account = self.card_account_map.get(card)91 return account.get_balance() if account else 0.092
93 def withdraw_money(self, card: Card, amount: float) -> bool:94 account = self.card_account_map.get(card)95 return account.withdraw(amount) if account else False96
97 def deposit_money(self, card: Card, amount: float):98 account = self.card_account_map.get(card)99 if account:100 account.deposit(amount)101
102# =====================103# STATE PATTERN104# =====================105
106class ATMState(ABC):107 @abstractmethod108 def insert_card(self, atm, card_number: str):109 pass110
111 @abstractmethod112 def enter_pin(self, atm, pin: str):113 pass114
115 @abstractmethod116 def select_operation(self, atm, op: OperationType, amount: int):117 pass118
119 @abstractmethod120 def eject_card(self, atm):121 pass122
123class IdleState(ATMState):124 def insert_card(self, atm, card_number: str):125 card = atm.get_bank_service().get_card(card_number)126 if card:127 atm.set_current_card(card)128 atm.change_state(HasCardState())129 print("Card inserted. Please enter PIN.")130 else:131 print("Invalid card.")132
133 def enter_pin(self, atm, pin: str):134 print("Please insert card first.")135
136 def select_operation(self, atm, op: OperationType, amount: int):137 print("Please insert card and authenticate first.")138
139 def eject_card(self, atm):140 print("No card to eject.")141
142class HasCardState(ATMState):143 def insert_card(self, atm, card_number: str):144 print("Card already inserted. Please enter PIN or eject card.")145
146 def enter_pin(self, atm, pin: str):147 if atm.authenticate(pin):148 atm.change_state(AuthenticatedState())149 print("Authentication successful. Please select operation.")150 else:151 print("Invalid PIN. Please try again.")152
153 def select_operation(self, atm, op: OperationType, amount: int):154 print("Please enter PIN first.")155
156 def eject_card(self, atm):157 atm.set_current_card(None)158 atm.change_state(IdleState())159 print("Card ejected.")160
161class AuthenticatedState(ATMState):162 def insert_card(self, atm, card_number: str):163 print("Card already inserted and authenticated.")164
165 def enter_pin(self, atm, pin: str):166 print("Already authenticated. Please select operation.")167
168 def select_operation(self, atm, op: OperationType, amount: int):169 if op == OperationType.CHECK_BALANCE:170 atm.check_balance()171 elif op == OperationType.WITHDRAW_CASH:172 atm.withdraw_cash(amount)173 elif op == OperationType.DEPOSIT_CASH:174 atm.deposit_cash(amount)175
176 def eject_card(self, atm):177 atm.set_current_card(None)178 atm.change_state(IdleState())179 print("Card ejected. Thank you!")180
181# =====================182# CHAIN OF RESPONSIBILITY183# =====================184
185class DispenseChain(ABC):186 @abstractmethod187 def dispense(self, amount: int):188 pass189
190 @abstractmethod191 def set_next_chain(self, next_chain):192 pass193
194 @abstractmethod195 def can_dispense(self, amount: int) -> bool:196 pass197
198class NoteDispenser(DispenseChain):199 def __init__(self, note_value: int, num_notes: int):200 self.note_value = note_value201 self.num_notes = num_notes202 self.next_chain: Optional[DispenseChain] = None203
204 def set_next_chain(self, next_chain: DispenseChain):205 self.next_chain = next_chain206
207 def dispense(self, amount: int):208 if amount >= self.note_value and self.num_notes > 0:209 notes_needed = min(amount // self.note_value, self.num_notes)210 dispensed_amount = notes_needed * self.note_value211 self.num_notes -= notes_needed212 print(f"Dispensing {notes_needed} notes of ${self.note_value}")213
214 remaining = amount - dispensed_amount215 if remaining > 0 and self.next_chain:216 self.next_chain.dispense(remaining)217 elif self.next_chain:218 self.next_chain.dispense(amount)219 elif amount > 0:220 print(f"Cannot dispense remaining amount: ${amount}")221
222 def can_dispense(self, amount: int) -> bool:223 if amount >= self.note_value and self.num_notes > 0:224 notes_needed = min(amount // self.note_value, self.num_notes)225 remaining = amount - (notes_needed * self.note_value)226 if remaining == 0:227 return True228 if self.next_chain:229 return self.next_chain.can_dispense(remaining)230 return False231 if self.next_chain:232 return self.next_chain.can_dispense(amount)233 return False234
235class NoteDispenser100(NoteDispenser):236 def __init__(self, num_notes: int):237 super().__init__(100, num_notes)238
239class NoteDispenser50(NoteDispenser):240 def __init__(self, num_notes: int):241 super().__init__(50, num_notes)242
243class NoteDispenser20(NoteDispenser):244 def __init__(self, num_notes: int):245 super().__init__(20, num_notes)246
247class CashDispenser:248 def __init__(self):249 dispenser100 = NoteDispenser100(10)250 dispenser50 = NoteDispenser50(20)251 dispenser20 = NoteDispenser20(30)252
253 dispenser100.set_next_chain(dispenser50)254 dispenser50.set_next_chain(dispenser20)255 self.chain = dispenser100256
257 def can_dispense_cash(self, amount: int) -> bool:258 return self.chain.can_dispense(amount)259
260 def dispense_cash(self, amount: int):261 if self.can_dispense_cash(amount):262 self.chain.dispense(amount)263 else:264 print(f"Cannot dispense ${amount}. Insufficient cash in ATM.")265
266# =====================267# SINGLETON - MAIN SYSTEM268# =====================269
270class ATMSystem:271 _instance = None272 _lock = Lock()273
274 def __new__(cls):275 if cls._instance is None:276 with cls._lock:277 if cls._instance is None:278 cls._instance = super().__new__(cls)279 return cls._instance280
281 def __init__(self):282 if hasattr(self, '_initialized'):283 return284 self.transaction_counter = 0285 self.transaction_lock = Lock()286 self.current_state: ATMState = IdleState()287 self.current_card: Optional[Card] = None288 self.cash_dispenser = CashDispenser()289 self.bank_service = BankService()290 self._initialized = True291
292 @classmethod293 def get_instance(cls):294 return cls()295
296 def insert_card(self, card_number: str):297 self.current_state.insert_card(self, card_number)298
299 def enter_pin(self, pin: str):300 self.current_state.enter_pin(self, pin)301
302 def select_operation(self, op: OperationType, amount: int):303 self.current_state.select_operation(self, op, amount)304
305 def eject_card(self):306 self.current_state.eject_card(self)307
308 def change_state(self, state: ATMState):309 self.current_state = state310
311 def set_current_card(self, card: Optional[Card]):312 self.current_card = card313
314 def get_current_card(self) -> Optional[Card]:315 return self.current_card316
317 def authenticate(self, pin: str) -> bool:318 if self.current_card:319 return self.bank_service.authenticate(self.current_card, pin)320 return False321
322 def check_balance(self):323 if self.current_card:324 balance = self.bank_service.get_balance(self.current_card)325 print(f"Your account balance is: ${balance}")326 with self.transaction_lock:327 self.transaction_counter += 1328
329 def withdraw_cash(self, amount: int):330 if not self.current_card:331 print("Please authenticate first.")332 return333
334 if not self.cash_dispenser.can_dispense_cash(amount):335 print(f"ATM does not have enough cash to dispense ${amount}")336 return337
338 if self.bank_service.withdraw_money(self.current_card, amount):339 self.cash_dispenser.dispense_cash(amount)340 print("Transaction successful. Please take your cash.")341 with self.transaction_lock:342 self.transaction_counter += 1343 else:344 print("Insufficient balance in your account.")345
346 def deposit_cash(self, amount: int):347 if not self.current_card:348 print("Please authenticate first.")349 return350
351 self.bank_service.deposit_money(self.current_card, amount)352 print(f"Deposit successful. ${amount} deposited to your account.")353 with self.transaction_lock:354 self.transaction_counter += 1355
356 def get_bank_service(self) -> BankService:357 return self.bank_service358
359 def get_transaction_count(self) -> int:360 with self.transaction_lock:361 return self.transaction_counter362
363# =====================364# DEMO365# =====================366
367if __name__ == "__main__":368 atm = ATMSystem.get_instance()369 bank_service = atm.get_bank_service()370
371 account = bank_service.create_account("ACC001", 1000.0)372 card = bank_service.create_card("CARD001", "1234")373 bank_service.link_card_to_account(card, account)374
375 print("=== ATM Transaction Flow ===")376 atm.insert_card("CARD001")377 atm.enter_pin("1234")378 atm.select_operation(OperationType.CHECK_BALANCE, 0)379 atm.select_operation(OperationType.WITHDRAW_CASH, 150)380 atm.select_operation(OperationType.DEPOSIT_CASH, 50)381 atm.select_operation(OperationType.CHECK_BALANCE, 0)382 atm.eject_card()383
384 print(f"\nTotal transactions processed: {atm.get_transaction_count()}")1import java.util.*;2import java.util.concurrent.*;3import java.util.concurrent.atomic.AtomicLong;4
5// =====================6// ENUMERATION7// =====================8
9enum OperationType {10 WITHDRAW_CASH,11 DEPOSIT_CASH,12 CHECK_BALANCE13}14
15// =====================16// MODELS17// =====================18
19class Card {20 private String cardNumber;21 private String pin;22
23 public Card(String cardNumber, String pin) {24 this.cardNumber = cardNumber;25 this.pin = pin;26 }27
28 public String getCardNumber() { return cardNumber; }29 public String getPin() { return pin; }30}31
32class Account {33 private String accountNumber;34 private double balance;35 private Map<String, Card> cards;36
37 public Account(String accountNumber, double initialBalance) {38 this.accountNumber = accountNumber;39 this.balance = initialBalance;40 this.cards = new HashMap<>();41 }42
43 public synchronized boolean withdraw(double amount) {44 if (balance >= amount) {45 balance -= amount;46 return true;47 }48 return false;49 }50
51 public synchronized void deposit(double amount) {52 balance += amount;53 }54
55 public synchronized double getBalance() { return balance; }56 public String getAccountNumber() { return accountNumber; }57 public void addCard(Card card) { cards.put(card.getCardNumber(), card); }58}59
60// =====================61// SERVICES62// =====================63
64class BankService {65 private Map<String, Card> cards;66 private Map<Card, Account> cardAccountMap;67 private Map<String, Account> accounts;68
69 public BankService() {70 this.cards = new ConcurrentHashMap<>();71 this.cardAccountMap = new ConcurrentHashMap<>();72 this.accounts = new ConcurrentHashMap<>();73 }74
75 public Card createCard(String cardNumber, String pin) {76 Card card = new Card(cardNumber, pin);77 cards.put(cardNumber, card);78 return card;79 }80
81 public Account createAccount(String accountNumber, double initialBalance) {82 Account account = new Account(accountNumber, initialBalance);83 accounts.put(accountNumber, account);84 return account;85 }86
87 public void linkCardToAccount(Card card, Account account) {88 cardAccountMap.put(card, account);89 account.addCard(card);90 }91
92 public Card getCard(String cardNumber) {93 return cards.get(cardNumber);94 }95
96 public boolean authenticate(Card card, String pin) {97 return card != null && card.getPin().equals(pin);98 }99
100 public double getBalance(Card card) {101 Account account = cardAccountMap.get(card);102 return account != null ? account.getBalance() : 0.0;103 }104
105 public boolean withdrawMoney(Card card, double amount) {106 Account account = cardAccountMap.get(card);107 if (account != null) {108 return account.withdraw(amount);109 }110 return false;111 }112
113 public void depositMoney(Card card, double amount) {114 Account account = cardAccountMap.get(card);115 if (account != null) {116 account.deposit(amount);117 }118 }119}120
121// =====================122// STATE PATTERN123// =====================124
125interface ATMState {126 void insertCard(ATMSystem atm, String cardNumber);127 void enterPin(ATMSystem atm, String pin);128 void selectOperation(ATMSystem atm, OperationType op, int amount);129 void ejectCard(ATMSystem atm);130}131
132class IdleState implements ATMState {133 @Override134 public void insertCard(ATMSystem atm, String cardNumber) {135 Card card = atm.getBankService().getCard(cardNumber);136 if (card != null) {137 atm.setCurrentCard(card);138 atm.changeState(new HasCardState());139 System.out.println("Card inserted. Please enter PIN.");140 } else {141 System.out.println("Invalid card.");142 }143 }144
145 @Override146 public void enterPin(ATMSystem atm, String pin) {147 System.out.println("Please insert card first.");148 }149
150 @Override151 public void selectOperation(ATMSystem atm, OperationType op, int amount) {152 System.out.println("Please insert card and authenticate first.");153 }154
155 @Override156 public void ejectCard(ATMSystem atm) {157 System.out.println("No card to eject.");158 }159}160
161class HasCardState implements ATMState {162 @Override163 public void insertCard(ATMSystem atm, String cardNumber) {164 System.out.println("Card already inserted. Please enter PIN or eject card.");165 }166
167 @Override168 public void enterPin(ATMSystem atm, String pin) {169 if (atm.authenticate(pin)) {170 atm.changeState(new AuthenticatedState());171 System.out.println("Authentication successful. Please select operation.");172 } else {173 System.out.println("Invalid PIN. Please try again.");174 }175 }176
177 @Override178 public void selectOperation(ATMSystem atm, OperationType op, int amount) {179 System.out.println("Please enter PIN first.");180 }181
182 @Override183 public void ejectCard(ATMSystem atm) {184 atm.setCurrentCard(null);185 atm.changeState(new IdleState());186 System.out.println("Card ejected.");187 }188}189
190class AuthenticatedState implements ATMState {191 @Override192 public void insertCard(ATMSystem atm, String cardNumber) {193 System.out.println("Card already inserted and authenticated.");194 }195
196 @Override197 public void enterPin(ATMSystem atm, String pin) {198 System.out.println("Already authenticated. Please select operation.");199 }200
201 @Override202 public void selectOperation(ATMSystem atm, OperationType op, int amount) {203 switch (op) {204 case CHECK_BALANCE:205 atm.checkBalance();206 break;207 case WITHDRAW_CASH:208 atm.withdrawCash(amount);209 break;210 case DEPOSIT_CASH:211 atm.depositCash(amount);212 break;213 }214 }215
216 @Override217 public void ejectCard(ATMSystem atm) {218 atm.setCurrentCard(null);219 atm.changeState(new IdleState());220 System.out.println("Card ejected. Thank you!");221 }222}223
224// =====================225// CHAIN OF RESPONSIBILITY226// =====================227
228interface DispenseChain {229 void dispense(int amount);230 void setNextChain(DispenseChain next);231 boolean canDispense(int amount);232}233
234abstract class NoteDispenser implements DispenseChain {235 protected int noteValue;236 protected DispenseChain nextChain;237 protected int numNotes;238
239 public NoteDispenser(int noteValue, int numNotes) {240 this.noteValue = noteValue;241 this.numNotes = numNotes;242 }243
244 @Override245 public void setNextChain(DispenseChain next) {246 this.nextChain = next;247 }248
249 @Override250 public void dispense(int amount) {251 if (amount >= noteValue && numNotes > 0) {252 int notesNeeded = Math.min(amount / noteValue, numNotes);253 int dispensedAmount = notesNeeded * noteValue;254 numNotes -= notesNeeded;255 System.out.println("Dispensing " + notesNeeded + " notes of $" + noteValue);256
257 int remaining = amount - dispensedAmount;258 if (remaining > 0 && nextChain != null) {259 nextChain.dispense(remaining);260 }261 } else if (nextChain != null) {262 nextChain.dispense(amount);263 } else if (amount > 0) {264 System.out.println("Cannot dispense remaining amount: $" + amount);265 }266 }267
268 @Override269 public boolean canDispense(int amount) {270 if (amount >= noteValue && numNotes > 0) {271 int notesNeeded = Math.min(amount / noteValue, numNotes);272 int remaining = amount - (notesNeeded * noteValue);273 if (remaining == 0) return true;274 if (nextChain != null) return nextChain.canDispense(remaining);275 return false;276 }277 if (nextChain != null) return nextChain.canDispense(amount);278 return false;279 }280}281
282class NoteDispenser100 extends NoteDispenser {283 public NoteDispenser100(int numNotes) {284 super(100, numNotes);285 }286}287
288class NoteDispenser50 extends NoteDispenser {289 public NoteDispenser50(int numNotes) {290 super(50, numNotes);291 }292}293
294class NoteDispenser20 extends NoteDispenser {295 public NoteDispenser20(int numNotes) {296 super(20, numNotes);297 }298}299
300class CashDispenser {301 private DispenseChain chain;302
303 public CashDispenser() {304 NoteDispenser100 dispenser100 = new NoteDispenser100(10);305 NoteDispenser50 dispenser50 = new NoteDispenser50(20);306 NoteDispenser20 dispenser20 = new NoteDispenser20(30);307
308 dispenser100.setNextChain(dispenser50);309 dispenser50.setNextChain(dispenser20);310 this.chain = dispenser100;311 }312
313 public boolean canDispenseCash(int amount) {314 return chain.canDispense(amount);315 }316
317 public void dispenseCash(int amount) {318 if (canDispenseCash(amount)) {319 chain.dispense(amount);320 } else {321 System.out.println("Cannot dispense $" + amount + ". Insufficient cash in ATM.");322 }323 }324}325
326// =====================327// SINGLETON - MAIN SYSTEM328// =====================329
330class ATMSystem {331 private static ATMSystem INSTANCE;332 private AtomicLong transactionCounter;333 private ATMState currentState;334 private Card currentCard;335 private CashDispenser cashDispenser;336 private BankService bankService;337
338 private ATMSystem() {339 this.transactionCounter = new AtomicLong(0);340 this.currentState = new IdleState();341 this.cashDispenser = new CashDispenser();342 this.bankService = new BankService();343 }344
345 public static synchronized ATMSystem getInstance() {346 if (INSTANCE == null) {347 INSTANCE = new ATMSystem();348 }349 return INSTANCE;350 }351
352 public void insertCard(String cardNumber) {353 currentState.insertCard(this, cardNumber);354 }355
356 public void enterPin(String pin) {357 currentState.enterPin(this, pin);358 }359
360 public void selectOperation(OperationType op, int amount) {361 currentState.selectOperation(this, op, amount);362 }363
364 public void ejectCard() {365 currentState.ejectCard(this);366 }367
368 public void changeState(ATMState state) {369 this.currentState = state;370 }371
372 public void setCurrentCard(Card card) {373 this.currentCard = card;374 }375
376 public Card getCurrentCard() {377 return currentCard;378 }379
380 public boolean authenticate(String pin) {381 if (currentCard != null) {382 return bankService.authenticate(currentCard, pin);383 }384 return false;385 }386
387 public void checkBalance() {388 if (currentCard != null) {389 double balance = bankService.getBalance(currentCard);390 System.out.println("Your account balance is: $" + balance);391 transactionCounter.incrementAndGet();392 }393 }394
395 public void withdrawCash(int amount) {396 if (currentCard == null) {397 System.out.println("Please authenticate first.");398 return;399 }400
401 if (!cashDispenser.canDispenseCash(amount)) {402 System.out.println("ATM does not have enough cash to dispense $" + amount);403 return;404 }405
406 if (bankService.withdrawMoney(currentCard, amount)) {407 cashDispenser.dispenseCash(amount);408 System.out.println("Transaction successful. Please take your cash.");409 transactionCounter.incrementAndGet();410 } else {411 System.out.println("Insufficient balance in your account.");412 }413 }414
415 public void depositCash(int amount) {416 if (currentCard == null) {417 System.out.println("Please authenticate first.");418 return;419 }420
421 bankService.depositMoney(currentCard, amount);422 System.out.println("Deposit successful. $" + amount + " deposited to your account.");423 transactionCounter.incrementAndGet();424 }425
426 public BankService getBankService() {427 return bankService;428 }429
430 public long getTransactionCount() {431 return transactionCounter.get();432 }433}434
435// =====================436// DEMO437// =====================438
439class ATMDemo {440 public static void main(String[] args) {441 ATMSystem atm = ATMSystem.getInstance();442 BankService bankService = atm.getBankService();443
444 Account account = bankService.createAccount("ACC001", 1000.0);445 Card card = bankService.createCard("CARD001", "1234");446 bankService.linkCardToAccount(card, account);447
448 System.out.println("=== ATM Transaction Flow ===");449 atm.insertCard("CARD001");450 atm.enterPin("1234");451 atm.selectOperation(OperationType.CHECK_BALANCE, 0);452 atm.selectOperation(OperationType.WITHDRAW_CASH, 150);453 atm.selectOperation(OperationType.DEPOSIT_CASH, 50);454 atm.selectOperation(OperationType.CHECK_BALANCE, 0);455 atm.ejectCard();456
457 System.out.println("\nTotal transactions processed: " + atm.getTransactionCount());458 }459}Key Implementation Highlights
Section titled “Key Implementation Highlights”1. State Pattern:
IdleState,HasCardState,AuthenticatedStateencapsulate behavior- Clear state transitions
- No complex if-else chains
2. Chain of Responsibility Pattern:
NoteDispenser100,NoteDispenser50,NoteDispenser20handle denominations- Flexible fallback mechanism
- Easy to extend with new denominations
3. Thread Safety:
synchronizedmethods inAccount(Java)LockinAccount(Python)ConcurrentHashMapinBankServiceAtomicLongfor transaction counter
- Single ATM instance per physical machine
- Thread-safe initialization
Problem: Without State Pattern, ATMSystem methods would look like:
1void insertCard(String cardNumber) {2 if (state == IDLE) {3 // insert card logic4 } else if (state == HAS_CARD) {5 System.out.println("Card already inserted");6 } else if (state == AUTHENTICATED) {7 System.out.println("Already authenticated");8 }9}Why State Pattern?
- Eliminates if-else chains: Each state encapsulates its own behavior
- Open/Closed Principle: Add new states without modifying
ATMSystem - Single Responsibility: Each state class handles one behavior
- Easier to test: Test each state independently
- Clear state transitions: States explicitly define valid transitions
stateDiagram-v2
[*] --> IdleState
IdleState --> HasCardState: insertCard()
HasCardState --> AuthenticatedState: enterPin() valid
HasCardState --> IdleState: ejectCard()
AuthenticatedState --> IdleState: ejectCard()
AuthenticatedState --> AuthenticatedState: selectOperation()
note right of IdleState
No card inserted
Can insert card
end note
note right of HasCardState
Card inserted
Can enter PIN
Can eject card
end note
note right of AuthenticatedState
PIN verified
Can perform transactions
Can eject card
end note
Problem: How should ATM dispense cash using different note denominations?
- If user requests $150, should it use: 1×$100 + 1×$50, or 3×$50, or 7×$20 + 1×$10?
- What if $100 notes are out? Fall back to $50 notes.
- What if ATM doesn’t have exact combination?
Why Chain of Responsibility Pattern?
- Flexible Dispensing: Each dispenser handles one denomination
- Fallback Mechanism: If one denomination can’t fulfill, try next
- Easy to Extend: Add new denominations without changing existing code
- Separation of Concerns: Each dispenser only knows about its denomination
sequenceDiagram
participant ATM
participant Chain as CashDispenser Chain
participant D100 as $100 Dispenser
participant D50 as $50 Dispenser
participant D20 as $20 Dispenser
ATM->>Chain: dispenseCash(150)
Chain->>D100: Can dispense $100?
D100->>D100: Dispense 1×$100
D100->>D50: Remaining: $50
D50->>D50: Dispense 1×$50
D50-->>Chain: Success! Remaining: $0
Chain-->>ATM: Cash dispensed
How it works:
- Chain starts with highest denomination ($100)
- Each
NoteDispensertries to fulfill as much as possible with its denomination - Remaining amount is passed to next chain link
- If chain completes and amount is 0, dispensing succeeds
- If amount remains and no more links, dispensing fails
Problem: Multiple gates need to see the same ATM state.
Without Singleton:
1// Gate A2ATMSystem atmA = new ATMSystem(); // Different instance3atmA.insertCard("CARD001");4
5// Gate B (different instance!)6ATMSystem atmB = new ATMSystem(); // Different instance7atmA.insertCard("CARD001"); // State inconsistency!With Singleton:
1// Gate A2ATMSystem atmA = ATMSystem.getInstance(); // Same instance3atmA.insertCard("CARD001");4
5// Gate B6ATMSystem atmB = ATMSystem.getInstance(); // Same instance!7// Both see same stateSystem Flow Diagrams
Section titled “System Flow Diagrams”Authentication Flow
Section titled “Authentication Flow”sequenceDiagram
participant User
participant ATM as ATMSystem
participant Bank as BankService
User->>ATM: Insert card
ATM->>Bank: Validate card exists
Bank-->>ATM: Card valid
ATM->>ATM: Transition to HasCardState
User->>ATM: Enter PIN
ATM->>Bank: authenticate(card, PIN)
alt PIN correct
Bank-->>ATM: Authentication success
ATM->>ATM: Transition to AuthenticatedState
else PIN incorrect
Bank-->>ATM: Authentication failed
ATM->>ATM: Stay in HasCardState
end
Withdrawal Flow
Section titled “Withdrawal Flow”flowchart TD
A[User: Withdraw $100] --> B[Check Authenticated]
B -->|Yes| C[Check Cash Available]
B -->|No| D[Error: Not authenticated]
C -->|Yes| E[Check Account Balance]
C -->|No| F[Error: Insufficient cash]
E -->|Sufficient| G[Dispense Cash]
E -->|Insufficient| H[Error: Insufficient balance]
G --> I[Deduct from Account]
I --> J[Transaction Complete]
style D fill:#ef4444
style F fill:#ef4444
style H fill:#ef4444
style J fill:#10b981
Cash Dispensing Flow
Section titled “Cash Dispensing Flow”Extensibility & Future Enhancements
Section titled “Extensibility & Future Enhancements”Easy to Extend
Section titled “Easy to Extend”1. Add New State:
1class MaintenanceState(ATMState):2 """ATM is under maintenance."""3
4 def insert_card(self, atm, card_number: str):5 print("ATM is under maintenance. Please try later.")6
7 def enter_pin(self, atm, pin: str):8 print("ATM is under maintenance.")9
10 def select_operation(self, atm, op: OperationType, amount: int):11 print("ATM is under maintenance.")12
13 def eject_card(self, atm):14 print("ATM is under maintenance.")1class MaintenanceState implements ATMState {2 /** ATM is under maintenance. */3
4 @Override5 public void insertCard(ATMSystem atm, String cardNumber) {6 System.out.println("ATM is under maintenance. Please try later.");7 }8
9 @Override10 public void enterPin(ATMSystem atm, String pin) {11 System.out.println("ATM is under maintenance.");12 }13
14 @Override15 public void selectOperation(ATMSystem atm, OperationType op, int amount) {16 System.out.println("ATM is under maintenance.");17 }18
19 @Override20 public void ejectCard(ATMSystem atm) {21 System.out.println("ATM is under maintenance.");22 }23}2. Add New Denomination:
1class NoteDispenser10(NoteDispenser):2 def __init__(self, num_notes: int):3 super().__init__(10, num_notes)4
5# Update CashDispenser6class CashDispenser:7 def __init__(self):8 dispenser100 = NoteDispenser100(10)9 dispenser50 = NoteDispenser50(20)10 dispenser20 = NoteDispenser20(30)11 dispenser10 = NoteDispenser10(40) # New!12
13 dispenser100.set_next_chain(dispenser50)14 dispenser50.set_next_chain(dispenser20)15 dispenser20.set_next_chain(dispenser10) # Add to chain16 self.chain = dispenser1001class NoteDispenser10 extends NoteDispenser {2 public NoteDispenser10(int numNotes) {3 super(10, numNotes);4 }5}6
7// Update CashDispenser8class CashDispenser {9 public CashDispenser() {10 NoteDispenser100 dispenser100 = new NoteDispenser100(10);11 NoteDispenser50 dispenser50 = new NoteDispenser50(20);12 NoteDispenser20 dispenser20 = new NoteDispenser20(30);13 NoteDispenser10 dispenser10 = new NoteDispenser10(40); // New!14
15 dispenser100.setNextChain(dispenser50);16 dispenser50.setNextChain(dispenser20);17 dispenser20.setNextChain(dispenser10); // Add to chain18 this.chain = dispenser100;19 }20}3. Add Transaction Classes (Strategy Pattern):
1class Transaction(ABC):2 """Base transaction class."""3
4 @abstractmethod5 def execute(self, atm: ATMSystem, card: Card, amount: int) -> bool:6 pass7
8class WithdrawalTransaction(Transaction):9 def execute(self, atm: ATMSystem, card: Card, amount: int) -> bool:10 if not atm.cash_dispenser.can_dispense_cash(amount):11 return False12 if atm.bank_service.withdraw_money(card, amount):13 atm.cash_dispenser.dispense_cash(amount)14 return True15 return False16
17class DepositTransaction(Transaction):18 def execute(self, atm: ATMSystem, card: Card, amount: int) -> bool:19 atm.bank_service.deposit_money(card, amount)20 return True1abstract class Transaction {2 abstract boolean execute(ATMSystem atm, Card card, int amount);3}4
5class WithdrawalTransaction extends Transaction {6 @Override7 public boolean execute(ATMSystem atm, Card card, int amount) {8 if (!atm.getCashDispenser().canDispenseCash(amount)) {9 return false;10 }11 if (atm.getBankService().withdrawMoney(card, amount)) {12 atm.getCashDispenser().dispenseCash(amount);13 return true;14 }15 return false;16 }17}18
19class DepositTransaction extends Transaction {20 @Override21 public boolean execute(ATMSystem atm, Card card, int amount) {22 atm.getBankService().depositMoney(card, amount);23 return true;24 }25}Future Enhancements
Section titled “Future Enhancements”1. PIN Attempt Limits:
- Track failed PIN attempts
- Lock card after 3 failed attempts
- Add
CardBlockedState
2. Session Timeout:
- Auto-eject card after inactivity
- Add timeout mechanism
3. Transaction History:
- Store transaction log
- Add
Transactionclass with timestamps
4. Multi-Account Support:
- Link multiple accounts to one card
- Allow account selection
5. Different Currency Support:
- Handle multiple currencies
- Currency conversion
6. Biometric Authentication:
- Fingerprint/face recognition
- Add
BiometricAuthState
7. Network Failure Handling:
- Queue transactions when bank is unreachable
- Retry mechanism
Summary
Section titled “Summary”Key Takeaways
Section titled “Key Takeaways”- Follow Systematic Approach - Don’t jump to code
- Identify Actors First - Who uses the system?
- Identify Entities - What are the core objects?
- Assign Responsibilities - Single Responsibility Principle
- Design Class Diagrams - Visualize structure and relationships
- Define Contracts - Clear interfaces and APIs
- Handle Edge Cases - Think about errors and concurrency
- Use Design Patterns - State Pattern, Chain of Responsibility, Singleton
- Make it Extensible - Easy to add new features
- State Pattern - ATM state management
- Chain of Responsibility Pattern - Cash dispensing with multiple denominations
- Singleton Pattern - Single ATM instance
Best Practices Demonstrated
Section titled “Best Practices Demonstrated”- SOLID principles (SRP, OCP)
- Thread safety considerations
- Clear separation of concerns
- Extensible design
- Error handling
- Clean code structure
Next Steps
Section titled “Next Steps”Now that you’ve mastered the ATM System:
Practice Similar Problems:
- Parking Lot System
- Elevator System
- Library Management System
- Restaurant Management System
Explore More Patterns:
- Strategy Pattern (for transaction processing)
- Observer Pattern (for notifications)
- Command Pattern (for operations)
Deepen Your Understanding:
- Read about concurrency patterns
- Study database design for persistence
- Learn about distributed systems
- Explore security best practices