For practicing designing systems I created a simple bank ATM system.
I didn't want to make it to complicated so I left out things like cash bin per ATM and opening cash trays, etc. I also wanted to neglect APIs to validate pins, etc. (it's just a function that will always return True). I wanted to focus on classes and methods.
So in my example I have
- an
Account
class which has an account number, a balance and withdraw and deposit functions. - a
Card
class which has accounts and a select account function which will search for the account number on the card and then return it. - an
ATM
class which has the following variables:card_inserted
,card_validated
,current_account
,current_card
and the main function areperform_request
(which will either give out the balance, deposit or withdraw money),validate_pin
(which will make the call to the API, mocked in my code),select_account
(which will select an account from the card)
I did write a test for it and it worked. I was wondering if I could get some feedback on that (not the test, I know I can do lot's of things to make the tests better but that was just a quick and dirty version of it)?
atm.py:
def validate_api(card_nr, pin):
# validate account nr and pin
# return true or fale
return True
class Card():
def __init__(self, accounts = []):
self.accounts = accounts
def select_account(self, account_nr):
# check if there is a built in filter func in python
for account in self.accounts:
if account.account_nr == account_nr:
return account
raise Exception("Account number invalid")
class Account():
def __init__(self, account_nr = None):
self.account_nr = account_nr
self.balance = 0
def deposit(self, amount):
self.balance += amount
def withdraw(self, amount):
self.balance -= amount
class ATM():
def __init__(self):
self.card_inserted = False
self.card_validated = False
self.current_account = None
self.current_card = None
def validate_card_and_pin(self):
if not self.card_inserted:
raise Exception('Card must be inserted first')
if self.card_validated is False:
raise Exception
# raise Exception("Card not validated")
def insert_card(self, bank_card):
# code to accept card
self.card_inserted = True
self.current_card = bank_card
def eject_card(self):
self.end_session()
# code to push card out
def validate_pin(self, pin):
if not self.card_inserted:
raise Exception('Card must be inserted first')
# card is inserted, pin has to be validated
# post pin and card number to api
# response will be saved in validated variable
validated = validate_api(card_nr=0,pin=0)
if validated == False:
self.card_validated = False
return self.card_validated
self.card_validated = True
return self.card_validated
def select_account(self, account_nr):
self.validate_card_and_pin()
if self.current_card is None:
raise Exception("no card in ATM")
if self.card_validated is False:
raise Exception("card not validated")
current_account = self.current_card.select_account(account_nr)
self.current_account = current_account
'''
1 = check balance
2 = deposit money
3 = withdraw money
'''
def perform_request(self, request, amount = 0):
self.validate_card_and_pin()
if request == 1:
return self.check_balance()
elif request == 2:
return self.accept_cash(amount)
elif request == 3:
return self.give_out_cash(amount)
else:
raise Exception("invalid request")
def accept_cash(self, amount):
# open cash tray
# count cash
self.current_account.deposit(amount)
return amount
def give_out_cash(self, amount):
# count cash
# open tray
self.current_account.withdraw(amount)
return amount
def check_balance(self):
return self.current_account.balance
def end_session(self):
self.card_inserted = False
self.card_validated = False
self.current_account = None
self.current_card = None
return True
And here the test:
def test_depositing_50_and_withdrawing_20():
checking = Account(1)
saving = Account(2)
bank_card = Card([checking, saving])
atm = ATM()
atm.insert_card(bank_card)
atm.validate_pin("123")
atm.select_account(1)
atm.perform_request(2, 50)
if atm.perform_request(1) is not 50:
raise Exception("depositing function doesn't work")
atm.end_session()
atm.insert_card(bank_card)
atm.validate_pin("123")
atm.select_account(1)
atm.perform_request(3, 20)
if atm.perform_request(1) is not 30:
raise Exception("withdrawing function doesn't work")
atm.select_account(2)
atm.validate_pin("123")
atm.perform_request(2, 10)
if atm.perform_request(1) is not 10:
raise Exception("depositing function doesn't work")
print("Test successful")