Design (LLD) a ATM machine - Machine Coding

Design (LLD) a ATM machine - Machine Coding

My project (2).png

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:

  1. Card reader: to read the users’ ATM cards.

  2. Keypad: to enter information into the ATM e.g. PIN. cards.

  3. Screen: to display messages to the users.

  4. Cash dispenser: for dispensing cash.

  5. Deposit slot: For users to deposit cash or checks.

  6. Printer: for printing receipts.

  7. 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);
  }
}