DEV Community

Cover image for Modeling a Banking system in OOP
Youdiowei Eteimorde
Youdiowei Eteimorde

Posted on

Modeling a Banking system in OOP

The beauty of Object-oriented programming is that any real-world system can easily be modeled with it. Using abstraction a complex system can be made simple and represented in code.

In this post, I will be modeling a Banking system and along the way, I will be introducing key concepts from OOP.

Abstraction

First of all, let's create an abstraction of the whole system. Banks have accounts that they manage for their clients. A client can create an account. These accounts have balances from which they can make withdrawals and deposits.

Following the Single Responsibility Principle let's create two classes one called Account that manages the balances. Then another class called Bank that manages and creates Accounts.

Implementation

UML diagram for BankAccounts

Here's the UML diagram of the system. First, we have to implement an abstract base class from which all account class inherit from.



from abc import ABC, abstractmethod
from datetime import datetime


class AccountBase(ABC):
    """
    Abstract base class for account.
    """

    def __init__(self, account_name: str, bank_name: str, balance: float = 0 ) -> None:
        """
        Initialization method for AccountBase subclasses.
        """

        self._account_name = account_name
        self._bank_name = bank_name
        self._created = datetime.now()
        self._updated = None
        self._balance = balance

    def withdraw(self, amount: float) -> None:
        """
        method to make withdrawals.
        """

        if self._balance >= amount:
            self._balance -= amount
            self._created = datetime.now()
            print(f"Detail: ₦{amount} succesfully withdrawn.")
        else:
            print("Withdrawal failed")

    @abstractmethod
    def deposit(self, amount: float):
        """
        Abstract method to make deposit. To be implemented by subclasses.
        """

        pass

    def balance(self):
        """
        Method to return blance of account
        """

        return self._balance

    def info(self):
        print(f"Bank name: {self._bank_name}")
        print(f"Account name: {self._account_name}")
        print(f"Account balance: ₦{self._balance}")
        print(f"Account created at: {self._created}")
        print(f"Account updated at: {self._updated}")


Enter fullscreen mode Exit fullscreen mode

The AccountBase class is abstract which means no object can be created from it. It contains five instance variables and five methods. Four of which have been implemented. The deposit method is an abstract method that should be implemented by the AccountBase subclasses.

Let's create the first subclass of the AccountBase:



class AdultAccount(AccountBase):
    """
    Adult account class
    """

    def deposit(self, amount: float):
        """
        AdultAccount implementation of deposit.
        """

        self._balance += amount
        self._created = datetime.now()
        print("Deposit succesful")



Enter fullscreen mode Exit fullscreen mode

The AdultAccount class inherits from the AccountBase. It is a concrete class meaning objects can be created from it. It implemented the deposit method and allows an unlimited amount of deposits.

Let's take a look at another subclass:



class StudentAccount(AccountBase):
    """
    Student account class
    """

    ACCOUNT_LIMIT = 4E5

    def deposit(self, amount: float):
        """
        StudentAccount implementation of deposit.
        """

        if (self._balance + amount) > self.ACCOUNT_LIMIT:
            print("Account limit exceeded.")
        else:
            self._balance += amount
            self._updated = datetime.now()
            print("Deposit succesful")


Enter fullscreen mode Exit fullscreen mode

This subclass has a limit to the amount that can be deposited. The ACCOUNT_LIMIT is a class variable

The fact that the two subclasses have different implementations is an example of polymorphism(many forms).

Now let's create the Bank class:



class Bank:
    """
    Factory class for Account class.
    A Bank can create Accounts.
    """

    def __init__(self, name: str):
        """
        Initialization method for bank.
        """

        self._name = name
        self._accounts: list[AccountBase] = []

    def create_adult_account(self, account_name: str, initial_deposit: float) -> AccountBase:
        """
        Creation method to create AdultAccount
        """

        acct = AdultAccount(account_name, self._name, initial_deposit)
        self._accounts.append(acct)
        return acct

    def create_student_account(self, account_name: str, initial_deposit: float) -> AccountBase:
        """
        Creation method to create StudentAccount
        """

        acct = StudentAccount(account_name, self._name, initial_deposit)
        self._accounts.append(acct)
        return acct

    def list_accounts(self) -> list[AccountBase]:
        """
        Return list of accounts in Bank.
        """

        return self._accounts

    def total_amount(self) -> float:
        """
        Return total amount in bank.
        """

        total = sum([acct.balance() for acct in self._accounts])
        return total


Enter fullscreen mode Exit fullscreen mode

The bank class creates accounts. It has two creation methods that can create AdultAccount and StudentAccount respectively. This class is a factory that produces accounts. It contains two instance variables self._name and self._accounts. The self._accounts stores every account created by the object.

Client code

Here's the client code that will use the Bank system:



b = Bank(name="devbank")

acct1 = b.create_adult_account(account_name="EteimZ", initial_deposit=40000) # create an adult account with initial deposits of 40000
acct2 = b.create_student_account(account_name="John", initial_deposit=4000) # create a student account with initial deposits of 4000

for acct in b.list_accounts():
    acct.info() # displays all account's info from bank

b.total_amount() # 44000 
acct1.deposit(8000) # make deposit in acct1
b.total_amount() # 52000


Enter fullscreen mode Exit fullscreen mode

First, we created an instance of the Bank class called b. Using that instance we created an Adult and Student Account. These results are instances of the AccountBase subclasses. Thanks to the creation methods we don't have to worry about creating the objects ourselves.

From each account, we can make deposits and withdrawals. The _accounts instance variable maintains a list of all accounts created from that instance. It can be accessed from the list_accounts() method of the Bank class. The total amount in the bank can also be retrieved from the total_amount() method.

Top comments (0)