• Black Friday Sale: Use coupon code THANKSGIVING to get 50% OFF on all our courses.
    If you already have a subscription and still interested in taking advantage of this offer, you can buy now and apply in your future subscriptions. This offer is also valid on GIFTS. Click here to get this offer.

  • Our one of a kind Low Level Design course is now released for General Availability. Go check it out.






Problem Statement:

Design an ATM using object-oriented principles.


Solution:


Object Oriented Analysis:


We will design ATM in a very methodical and systematic way by using the below framework:
  • Step #1: Figure out all the Actors:
    Think about all the actors involved. Figure out all the different types of actors involved and all the different types of users who will be using the system.
  • Step #2. Figure out all the User Activities and Use Cases involved:
    For each type of actors, you need to think through the different features that they would be interested in using, and different activities that they would be doing.
  • Step #3. Figure out all Entities involved:
    Determine different core components that will be working together in sync with each other to make the system, as a whole, functional. Entities are often non-living things.
The Actors and Entities become Classes that we need to design. The user activities and use cases become the methods or properties.

So in short, what the above frameworks asks us to do is: start with getting an overall high-level sense of the system you are going to design. Using the information you have so far, start figuring out all user types, and then for each user type figure out all the relevant user activities. Analysing all the user activities will give you all the entities and components that are needed for the system to function.
Remember to ask your interviewer clarifying questions at all steps to accomplish your goal. An integral part of design interviews is to showcase how good you are at requirements gathering and requirements analysis.


Just like how in real world software engineering project the first stage of the software development lifecycle (SDLC) is to gather software requirements from the business analyse the requirements, in a design interview your first task is to have a conversation with your interviewer to gather the requirements he/she has in mind. The interviewer would intentionally keep the problem statement vague to see if you are someone who jumps on implementing something, making a lot of assumptions on your own and not clarifying the requirements. To key to success in object oriented design or low level design interview, as well as in your software engineering career, is to ask a lot of clarifying questions, have critical thinking ability and analytical skills.

Remember that, even though your interviewer might have something very specific for you to design in his/her mind, the onus is on you to come up with different relevant features for the system once you have gotten a hint on what your interviewer wants you to design. You need to ask questions, come up with various features of the system, and then validate them with your interviewer to know if your interviewer is okay with the system having those features or not.

The framework discussed above will help us with coming up with various features effectively and seamlessly in a very systematic way. We have shown below how we can effectively use the framework to nail desiging an ATM. Our goal in this course is to help you master the logical and systematic thinking that would help you to nail any design interview even if the system you are asked to design is alien to you. Now let's see the framework in action:

Actors:

Below are the actor(s) who would be using the system in some way or the other:

  • Customers: anyone with a valid debit or credit card can use an ATM as long as the card type happens to be one of the card types that the ATM can process.


User Activities and Different Use cases:




A systematic way to figure out all the user activities and use cases is to putting yourself in the shoe of each type of users and thinking through if you use the system what the different activities are that you would want to perform on the system. A good way to think through it is to think from start to finish sequentially. For each step try to be as thorough and detailed as possible. For an ATM user the first thing that the user would do is swipe his/her card in the ATM machine and would go through the authentication process where ATM makes sure that the user is an authorized user of the card. The authentication process involves the user putting a PIN using the PIN pad or Keypad that the ATM contains. After that he/she might be interested in one or more of the below activities:

  • Balance Inquiry: a customer can view his/her account balance. Customer can view his/her balance either on the screen or in the paper receipt or may choose both.
  • Cash Withdrwawal: a customer can withdraw a certain amount of cash from his/her account. Customer will be using cash dispenser component of the ATM to get cash.
  • Deposit: a customer can deposit cash or checks. Customer will be using the deposit slot component of the ATM to perform deposit operation.
  • at the end of a transaction, the customer might want a printed receipt for the transaction. ATM needs to have a printer for this purpose.



Use Case Diagram


Entities:

From the discussion above, we can say that we would need the below components for an Automated Teller Machine to function:
  • Card Reader
  • Screen or Display
  • Keypad
  • Cash Dispenser
  • Deposit Slot
  • Printer to print receipt.
  • Bank Account which is linked to the customer's ATM card.
  • Bank where the customer has his/her account.
  • Transaction (Withdraw, Deposit, Transfer, Balance Enquiry)
  • Check associated with check deposit
  • last but not the least, ATM card (debit or credit card)

Object Oriented Design and Implementation:

Now we will be designing all the required classes, leveraging the discussion we had so far.

Customer class:

Let's start with the Customer class. A customer has a name, email, phone number, billing address, ATM Card, and an associated bank account.


package OOD.ATM;

/**
 * Created by Abhishek on 10/16/21.
 */
public class Customer {
    private String name;
    private String email;
    private String phone;
    private Address billingAdddress;
    private ATMCard cardAssociatedWithThisAccoungt;
    private Account account;

    public Customer(String name, String email, Account account, Address address, String phone) {
        this.name = name;
        this.email = email;
        this.account = account;
        this.phone = phone;
        this.billingAdddress = address;
    }

    public void assignCard(ATMCard card) {
        this.cardAssociatedWithThisAccoungt = card;
    }

    public boolean makeTransaction(Transaction transaction) {
        return transaction.executeTransaction();
    }
    public Address getBillingAddress() {
        return billingAdddress;
    }


    public String getName() {
        return name;
    }

    public String getEmail() {
        return email;
    }

    public String getPhone() {
        return phone;
    }

    public Address getBillingAdddress() {
        return billingAdddress;
    }

    public ATMCard getCardAssociatedWithThisAccount() {
        return cardAssociatedWithThisAccoungt;
    }

    public Account getAccount() {
        return account;
    }

}


Address class:


Address generally is consist of street address, city, state, country and zip code.


package OOD.ATM;

/**
 * Created by Abhishek on 10/16/21.
 */
public class Address {
    private String streetAddress;
    private String city;
    private String state;
    private String zipCode;
    private String country;

    public Address(String streetAddress, String city, String state, String zipcode, String country) {
        this.streetAddress = streetAddress;
        this.city = city;
        this.state = state;
        this.zipCode = zipcode;
        this.country = country;
    }


    public String getStreetAddress() {
        return streetAddress;
    }

    public String getCity() {
        return city;
    }

    public String getState() {
        return state;
    }

    public String getZipCode() {
        return zipCode;
    }

    public String getCountry() {
        return country;
    }

}


ATM Card:


An ATM card has an owner (customer), card number, expiration date, CVV and a PIN. A new card can be activated. An activated card can be blocked. Remember that a card can also be compromised if it is stolen or due to some other reason.

The card owner should also be able to change the PIN of his/her card.


package OOD.ATM;

import java.util.Date;

/**
 * Created by Abhishek on 10/16/21.
 */
public class ATMCard {

    public enum CardStatus {
        ACTIVE, BLOCKED, COMPROMISED, ACCOUNT_CLOSED, NOT_YET_ACTIVATED
    }

    private String cardNumber;
    private Customer customer;
    private Date expirationDate;
    private int pin; // this should be an encrypted value
    private int cvv; // this should be an encrypted value
    private CardStatus status;

    // atm cards are created with card number, exporation date and cvv and later assigned to a customer (using setCustomer)
    public ATMCard(String cardNumber, Date expirationDate, int cvv) {
        this.cardNumber = cardNumber;
        this.expirationDate = expirationDate;
        this.cvv = cvv;
        status = CardStatus.NOT_YET_ACTIVATED; //when a new card is produced it is
                // not active unless the card is assigned to a customer and the customer activates the card
    }

    public void activateCard() {
        status = CardStatus.ACTIVE;
    }

    public void blockCard() {
        status = CardStatus.BLOCKED;
    }

    public void closeAccount() {
        status = CardStatus.ACCOUNT_CLOSED;
    }

    public void void markAsCompromised() {
        status = CardStatus.COMPROMISED;
    }

    public String getCardNumber() {
        return cardNumber;
    }

    public Customer getCustomer() {
        return customer;
    }

    public Date getExpirationDate() {
        return expirationDate;
    }

    public int getPin() {
        return pin;
    }

    public int getCvv() {
        return cvv;
    }

    public CardStatus getStatus() {
        return status;
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
    }

    public void changePin(int newPin) {
        this.pin = newPin;
    }

    public void setStatus(CardStatus status) {
        this.status = status;
    }
}



Bank Account of the customer:


A Bank Account has an issuing bank, an account number. A customer woul want to know the following about his/her account:
  • total balance
  • available balance
A customer should be able to do the following operations:
  • add fund
  • withdraw fund

package OOD.ATM;

/**
 * Created by Abhishek on 10/16/21.
 */
public class Account {
    private long accountNumber;
    private double totalBalance;
    private double availableBalance;
    private Bank bank;

    public Account(int accountNumber, Bank bank) {
        this.accountNumber = accountNumber;
        this.bank = bank;
    }

    public void addFund(double amount) {
        totalBalance += amount;
    }

    public void withdrawFund(double amount) {
        totalBalance -= amount;
        availableBalance -= amount;
    }

    public long getAccountNumber() {
        return accountNumber;
    }

    public double getTotalBalance() {
        return totalBalance;
    }

    public double getAvailableBalance() {
        return availableBalance;
    }
}



Bank:



package OOD.ATM;

/**
 * Created by Abhishek on 10/16/21.
 */
public class Bank {
    private String name;
    private String bankCode;
    private Address branchLocation;

    public Bank(String name, String bankCode, Address branchLocation) {
        this.name = name;
        this.bankCode = bankCode;
        this.branchLocation = branchLocation;
    }

    public String getName() {
        return name;
    }

    public Address getBranchLocation() {
        return branchLocation;
    }

}



Transaction:


Balance Inquiry, Deposit (Cash or Check), Cash Withdrawal are all transactions and has below properties in common:
  • all transactions can either be in success, failure or pending state
  • they have a transaction id
  • they have an associated bank account from which the transaction has been originated
  • all transactions should be executed (that's the whole purpose of initiating a transaction in the first place)

package OOD.ATM;

import java.util.Date;

/**
 * Created by Abhishek on 10/16/21.
 */
public abstract class Transaction {

    public enum TransactionStatus {
        SUCCESS, FAILURE, PENDING
    }

    protected Account accountThatInitiatedTransaction;
    protected String transactionId;
    protected long creationTime;
    protected TransactionStatus status;


    public Transaction(String transactionId, Account accountThatInitiatedTransaction) {
        this.transactionId = transactionId;
        this.accountThatInitiatedTransaction = accountThatInitiatedTransaction;
        this.creationTime = System.currentTimeMillis();
    }

    public abstract boolean executeTransaction();

    public TransactionStatus getStatus() {
        return status;
    }
}


Balance Inquiry:



package OOD.ATM;

/**
 * Created by Abhishek on 10/16/21.
 */
public class BalanceInquiry extends Transaction {

    public BalanceInquiry(String transactionId, Account customerAccountNumber) {
        super(transactionId, customerAccountNumber);
    }

    public String getBalance() {
        return "Available Balance: " + accountThatInitiatedTransaction.getAvailableBalance()
                + "\n Total balance: " + accountThatInitiatedTransaction.getTotalBalance();
    }
}



Cash Withdrawal:


package OOD.ATM;

/**
 * Created by Abhishek on 10/16/21.
 */
public class Withdrawal extends Transaction {
    private double amount;
    private CashDispenser cashDispenser; // dependency
    public Withdrawal(String transactionId, Account customerAccountNumber, double amount, CashDispenser cashDispenser) {
        super(transactionId, customerAccountNumber);

        this.amount = amount;
        this.cashDispenser = cashDispenser;
    }
    public double getAmount() {
        return amount;
    }

    public boolean executeTransaction() {
        accountThatInitiatedTransaction.withdrawFund(amount);
        cashDispenser.dispenseCash(amount);
    }
}



Deposit:

Deposit on its own makes no sense unless you specify if it is a cash deposit or a check deposit. Both Cash Deposit and Check Deposit have following things in common:
  • Deposit needs to be made through the deposit slot in the ATM.
  • It is important to know the amount we are depositing.
  • Deposit is a transaction as already mentioned earlier, and has all th properties of a transcation.

package OOD.ATM;

/**
 * Created by Abhishek on 10/16/21.
 */
public abstract class Deposit extends Transaction {

    protected double amount;
    protected DepositSlot depositSlot; // dependency

    public Deposit(String transactionId, Account customerAccountNumber) {
        super(transactionId, customerAccountNumber);
    }

    public abstract double getAmount();

    public boolean executeTransaction() {
        accountThatInitiatedTransaction.addFund(amount);
        // return true is transaction goes thru, false otherwise
    }
}

Cash Deposit:



package OOD.ATM;

/**
 * Created by Abhishek on 10/16/21.
 */
public class CashDeposit extends Deposit {

    private double amountBeingDeposited;

    public CashDeposit(String transactionId, Account customerAccountNumber, double amount) {
        super(transactionId, customerAccountNumber);
        amountBeingDeposited = amount;
    }

    public double getAmount() {
        return amountBeingDeposited;
    }
}

    


Check Deposit:



package OOD.ATM;

/**
 * Created by Abhishek on 10/16/21.
 */
public class CheckDeposit extends Deposit {
    private Check check;
    private double amount;

    public CheckDeposit(String transactionId, Account customerAccountNumber, Check check, double amount) {
        super(transactionId, customerAccountNumber);
        this.check = check;
        this.amount = amount;
    }

    public Check getCheckDetails() {
        return check;
    }
    
    public double getAmount() {
        
    }

}

    


Deposit Slot:

We have two kinds of deposit slot:
  • cash deposit slot
  • check deosit slot

Bot should have the capability to accept the deposit, perform initial validation and execute the deposit transaction. But depending on whether the deposit is cash deposit or check deposit the validation process would be widely different.

In cash deposit validation process would entail scanning the deposited bills and check if they are not forged and identifying the amount they bear.

Whereas, in the cash deposit the validation process entails scanning the check for forgery and if the check is valid then determining the amount it bears.

Since both cash deposit slot and check deposit slot would need to have processDeposit(), we would have a Deposit interface and both Cash Deposit Slot and Check Deposit SLot would implement the Deposit interface.

Deposit Slot Interface:



package OOD.ATM;

/**
 * Created by Abhishek on 10/16/21.
 */
public interface DepositSlot { 

    public Deposit processDeposit();
}



Cash Deposit Slot:



package OOD.ATM;

/**
 * Created by Abhishek on 10/18/21.
 */
public class CashDepositSlot extends DepositSlot{

    public CashDepositSlot() {
        init();
    }

    @Override
    public Deposit processDeposit() {
        // accept the check and validate the check and create a deposit.
        // use the methods validateDeposit(), scanCheckAndReturnAmount() and deposit.executeTransaction()
    }
    private  boolean validateDeposit() {
        // scan the bills and do an initial check that the bills are not forged
        //
        // there is potential use of embedded system here.
    }

    private double scanCashDeposited() {
        // scan check and return the amount that is written on the check

        // there is potential use of embedded system here.
    }

    private void init() {
        // initialize Cash Deposit Slot (application of Embedded Systems)

        // initialization happens when ATM boots up (when powered on, and starts or restarts)
    }
}


Check Deposit Slot:



package OOD.ATM;

/**
 * Created by Abhishek on 10/18/21.
 */
public class CheckDepositSlot extends DepositSlot{

    public CheckDepositSlot() {
        init();
    }

    @Override
    public Deposit processDeposit() {
        // accept the check and validate the check and create a deposit.
        // use the methods validateDeposit(), scanCheckAndReturnAmount() and deposit.executeTransaction()
    }
    private  boolean validateDeposit() {
        // scan the check and do an initial check that the check is not forged
        // if the check looks valid then scan the amount that the check bears
        // amount = scanCheckAndReturnAmount();

        // there is potential use of embedded system here.
    }

    private double scanCheck() {
        // scan check and return the amount that is written on the check

        // there is potential use of embedded system here.
    }

    private void init() {
        // initialize check deposit slot (application of embedded systems)

        // initialization happens when ATM boots up (when powered on, and starts or restarts)
    }
}



Check:


Here is a sample Check:


The important properties of a Check are:
  • Bank Name
  • Routing Number
  • Account Number
  • Customer Name and Address

package OOD.ATM;

/**
 * Created by Abhishek on 10/17/21.
 */
public class Check {
    private String bankName;
    private Customer customer; // get Customer Name And Customer Address
    private String routingNumber;
    private String accountNumber;

    // checks are often generated without the name and address of the customer
    // and then later they get customized using setCustomer()
    public Check(String bankName, String routingNumber, String accountNumber) {
        this.accountNumber = accountNumber;
        this.bankName = bankName;
        this.routingNumber = routingNumber;
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
    }

    public String getBankName() {
        return bankName;
    }

    public Customer getCustomer() {
        return customer;
    }

    public String getRoutingNumber() {
        return routingNumber;
    }

    public String getAccountNumber() {
        return accountNumber;
    }

}



Printer:



package OOD.ATM;

/**
 * Created by Abhishek on 10/16/21.
 */
public class ATMPrinter {

    public ATMPrinter() {
        init();
    }

    public void printReceipt(Transaction transaction) {
        // print receipt
    }

    private void init() {
        // initialize printer (application of embedded systems)

        // initialization happens when ATM boots up (when powered on, and starts or restarts)
    }

}



Screen:



package OOD.ATM;

/**
 * Created by Abhishek on 10/16/21.
 */
public class ScreenDisplay {

    public ScreenDisplay() {
        init();
    }

    private void init() {
        // initialize screen (application of Embedded Systems)

        // show all the default texts


        // initialization happens when ATM boots up (when powered on, and starts or restarts)
    }

    public void showMessage(String message) {
        // show message on screen
    }
}


Keypad:



package OOD.ATM;

/**
 * Created by Abhishek on 10/16/21.
 */
public class Keypad {
    public Keypad() {
        init();
    }

    public void getInput() {
        // get input from user
    }

    private void init() {
        // initialize key pad (application of Embedded Systems)


        // initialization happens when ATM boots up (when powered on, and starts or restarts)
    }
}


Cash Dispenser:



package OOD.ATM;

/**
 * Created by Abhishek on 10/16/21.
 */
public class CashDispenser {

    public CashDispenser() {
        init();
    }

    public boolean dispenseCash(double amount) {
        // dispense cash

        // there is potential use of embedded system here.
    }

    private void init() {
        // initialize cash dispenser (application of Embedded Systems)


        // initialization happens when ATM boots up (when powered on, and starts or restarts)
    }
}


ATM:

In an ATM, there should be only one instance of the ATM class. So we need to implement ATM class as a Singleton.
Singleton is a creational design pattern, which ensures that only one object of its kind exists and provides a single point of access to it for any other code.


package OOD.ATM;

/**
 * Created by Abhishek on 10/16/21.
 */
public class ATM {
    private int atmID;
    private Address location;

    private CashDispenser cashDispenser;
    private Keypad keypad;
    private ScreenDisplay screen;
    private ATMPrinter printer;
    private CheckDepositSlot checkDepositSlot;
    private CashDepositSlot cashDepositSlot;

    private Customer currentCustomer; // customer who is using the ATM.
                // currentCustomer is set once a customer authenticates
    private Transaction mostRecentTransaction;

    private static ATM instance;

    private ATM(int atmID, Address location) {
        this.atmID = atmID;
        this.location = location;

        // initialize all hardwares
        cashDispenser = new CashDispenser();
        keypad = new Keypad();
        screen = new ScreenDisplay();
        printer = new ATMPrinter();
        checkDepositSlot = new CheckDepositSlot();
        cashDepositSlot = new CashDepositSlot();
    }

    public static ATM getInstance(int atmID, Address location) {
        if (instance == null) {
            instance = new ATM(atmID, location);
        }
        return instance;
    }

    public boolean authenticateUser(ATMCard card) {
        // contact customer's bank's authentication server
        // and authenticate user using atm card PIN.
        // this needs to be done in an end-to-end encrypted manner
        // since we have sensitive data like atm card pin and card number.

        // if (authentication successful) {
        // currentCustomer = user;
        // }
    }

    public void logoutCustomer() {
        // log out current customer
        currentCustomer = null; // reset
    }
    /*public boolean makeTransaction(Transaction transaction) {
        mostRecentTransaction = transaction;
        return transaction.executeTransation();
    }*/

    public void depositCash() {
        mostRecentTransaction = cashDepositSlot.acceptDeposit();
        // in case you are wondering how and when this method is called:
        // in the touch screen when you select deposit cash, the frontend UI calls this method.
    }

    // this method is called when you select "Deposit Check" in the ATM frontend UI
    public void depositCheck() {
        mostRecentTransaction = checkDepositSlot.acceptDeposit();
    }

    // this method is called when you select "Balance Inquiry" from ATM frontend UI
    public void balanceInquiry() {
        String uniqueTransactionId = java.util.UUID.randomUUID().toString();
        mostRecentTransaction = new BalanceInquiry(uniqueTransactionId, currentCustomer.getAccount();
        screen.showMessage(((BalanceInquiry) mostRecentTransaction).getBalance());
    }

    // this method is called when you select "Balance Inquiry" from ATM frontend UI
    public void withdrawCash(double amountToBeWithdrawn) {
        String uniqueTransactionId = java.util.UUID.randomUUID().toString();
        mostRecentTransaction = new Withdrawal(uniqueTransactionId, currentCustomer.getAccount(), amountToBeWithdrawn, cashDispenser);
        mostRecentTransaction.executeTransation();
    }

    public void changePin(int newPin) {
        currentCustomer.getCardAssociatedWithThisAccount().changePin(newPin);
    }

    // this method is called when the customer selects "print receipt"
    public void printReceipt() {
        if (mostRecentTransaction != null) {
            printer.printReceipt(mostRecentTransaction);
        }
    }
}




Still hungry to learn more? You might find the below chapters interesting:




The above content is written by:

Abhishek Dey

Abhishek Dey

A Visionary Software Engineer With A Mission To Empower Every Person & Every Organization On The Planet To Achieve More

Microsoft | University of Florida

View LinkedIn profile


If you have any feedback, please use this form: https://thealgorists.com/Feedback.




Subscribe to Our Youtube Channel

Follow Us On LinkedIn
wave