Practical Projects and Exercises Combining Concepts
Lesson Overview
This lesson ties everything together with a practical project: a Banking System. You will build a complete system from scratch using classes, objects, encapsulation, inheritance, and abstraction. This project simulates a real-world library where you can add books, register members, lend books, and handle returns, demonstrating how OOP principles work together in a real application.
Lesson Content
OOP Banking System: From Basics to Professional Application
Project Overview
In this comprehensive project, you'll build a professional Banking System using all the Object-Oriented Programming concepts you've learned. This isn't just theory—this is how real banking applications are structured in production systems.
By the end of this lesson, you'll have created a fully functional banking system that demonstrates:
- Abstract Base Classes and Interfaces
- Inheritance and Method Overriding
- Encapsulation with Private Variables
- Getter and Setter Methods with Validation
- Instance, Class, and Static Methods
- Polymorphism in Action
Why This Project Matters
Banks handle millions of transactions daily. Their systems must be secure, consistent, and maintainable. OOP provides the architectural foundation that makes this possible
Real-World Connection:
- All Banks use OOP principles in their core banking systems
- Payment integrators rely on abstract interfaces to integrate multiple payment methods
- ATM systems use encapsulation to protect your account data
The Challenge: Managing Multiple Account Types
Imagine you're the CTO of a new bank. You need to support three different account types:
Savings Account: Has minimum balance requirements and earns interest
Current Account: Designed for businesses, allows overdrafts, no interest
Each account type has different rules but shares common features like deposits, withdrawals, and balance updates. How do you organize this code so that:
50 developers can work on different account types without breaking each other's code
New account types (like Credit Cards or Loans) can be added easily
Core banking operations remain consistent across all accounts
Sensitive data like balance and PIN are protected from unauthorized access
This is where OOP principles become your architectural foundation.
Step 1: The Solution: Abstract Base Classes
Solution: Create an Abstract Base Class that says: "Every account type MUST have these exact methods, or Python won't let you create objects."
The Architecture at a Glance
BankAccount (Abstract Base Class)
├── Private Variables: __balance, __pin, __account_number # Private variables that adds security, need to use setter and getter methods to access or update data
├── Public Variables: account_holder_name, account_creation_date # Public Variables
├── Abstract Methods: calculate_interest(), withdraw() # Methods that are must needeed when implementing a Child Classes, these methods cannot be ingnored
├── Concrete Methods: deposit(), get_balance(), set_pin() # Instance Methods that add functionlity/featrues
├── Class Methods: get_total_accounts(), get_bank_name() # Methods that are based on Class, but not based on Instance
└── Static Methods: calculate_tds() # Standalone method that used to do some calcualtions.
Code Implementation:
from abc import ABC, abstractmethod
from datetime import datetime
import random
# Abstract Base Class - The Contract
class BankAccount(ABC):
"""
Blueprint for all bank accounts.
Every account type must implement these methods.
"""
# Class variable - shared across all accounts
total_accounts = 0
bank_name = "ByLearning Bank of India"
def __init__(self, account_holder_name, initial_deposit):
# Private variables (encapsulation)
self.__account_number = self._generate_account_number() # Calls a private method to generate account number
self.__balance = 0
self.__pin = None
# Public variables
self.account_holder_name = account_holder_name
self.account_creation_date = datetime.now().strftime("%Y-%m-%d")
# Increment class variable
BankAccount.total_accounts += 1
# Initial deposit
self.deposit(initial_deposit) # calls a method to deposit money
# Private method (helper method)
def _generate_account_number(self):
"""Generates unique 10-digit account number"""
return random.randint(1000000000, 9999999999)
# Abstract methods - MUST be implemented by child classes
@abstractmethod
def calculate_interest(self):
"""Each account type calculates interest differently"""
pass
@abstractmethod
def withdraw(self, amount):
"""Each account type has different withdrawal rules"""
pass
# Concrete methods - inherited by all child classes
def deposit(self, amount):
"""Common deposit logic for all accounts"""
if amount <= 0:
print("Deposit amount must be positive.")
return False
self.__balance += amount
print(f"₹{amount} deposited successfully.")
return True
# Getter methods
def get_balance(self):
"""Safe way to view balance"""
return self.__balance
def get_account_number(self):
"""Safe way to view account number"""
return self.__account_number
# Setter method with validation
def set_pin(self, new_pin):
"""Set a 4-digit PIN"""
if len(str(new_pin)) != 4 or not str(new_pin).isdigit():
print("PIN must be exactly 4 digits.")
return False
self.__pin = new_pin
print("PIN set successfully.")
return True
def verify_pin(self, pin):
"""Verify PIN without exposing it"""
return self.__pin == pin
# Class method - works on the class, not instances
@classmethod
def get_total_accounts(cls):
"""Returns total accounts created across all types"""
return cls.total_accounts
@classmethod
def get_bank_name(cls):
"""Returns the bank's name"""
return cls.bank_name
# Static methods - utility functions
@staticmethod
def calculate_tds(interest_amount):
"""Calculates 10% TDS on interest earned"""
return interest_amount * 0.10
Step 2: Savings Account - Inheritance & Method Overriding
Real-World Rules:
- Minimum balance: ₹1,000
- Interest rate: 4% per annum
- Daily withdrawal limit: ₹50,000
Code Implementation:
class SavingsAccount(BankAccount):
"""
Savings account with minimum balance requirement
"""
# Class variables specific to savings accounts
minimum_balance = 1000
interest_rate = 0.04 # 4%
daily_withdrawal_limit = 50000
def __init__(self, account_holder_name, initial_deposit):
if initial_deposit < self.minimum_balance:
raise ValueError(f"Initial deposit must be at least ₹{self.minimum_balance}")
super().__init__(account_holder_name, initial_deposit) # Initating the exiting variables, methods on Abstract class
self.account_type = "Bylearning Savings Account"
self.__daily_withdrawn_today = 0
# Overriding abstract method
def calculate_interest(self):
"""Calculate 4% interest on current balance"""
balance = self.get_balance()
interest = balance * self.interest_rate
# Deduct TDS
tds = BankAccount.calculate_tds(interest)
net_interest = interest - tds
print(f"Interest Earned: ₹{interest:.2f}")
print(f"TDS Deducted (10%): ₹{tds:.2f}")
print(f"Net Interest Credited: ₹{net_interest:.2f}")
self.deposit(net_interest)
return net_interest
# Overriding abstract method
def withdraw(self, amount, pin):
"""Withdraw with minimum balance and daily limit checks"""
# PIN verification
if not self.verify_pin(pin):
print("Incorrect PIN.")
return False
# Validation checks
if amount <= 0:
print("Withdrawal amount must be positive.")
return False
balance = self.get_balance()
# Check minimum balance
if balance - amount < self.minimum_balance:
print(f"Cannot withdraw. Minimum balance of ₹{self.minimum_balance} must be maintained.")
return False
# Check daily limit
if self.__daily_withdrawn_today + amount > self.daily_withdrawal_limit:
print(f"Daily withdrawal limit of ₹{self.daily_withdrawal_limit} exceeded.")
return False
# Process withdrawal
self._BankAccount__balance -= amount
self.__daily_withdrawn_today += amount
print(f"₹{amount} withdrawn successfully.")
print(f"Daily withdrawal used: ₹{self.__daily_withdrawn_today}/{self.daily_withdrawal_limit}")
return True
💬 Comments (0)
Login to join the discussion
No comments yet. Be the first to share your thoughts!