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
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}")
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")
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")
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
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
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)