Each image becomes a “chapter” of OOP understanding.
In your comics, the team first makes a blueprint (class), then breaks it into parts (composition), then assembles (constructor), then uses it by pressing buttons (methods).
Learn the “why”, not just the definitions.
Same idea in 3 languages: BankAccount + composition + safe balance.
# Programmer's Picnic — OOP in Python (Champak Roy)
# Focus: composition + encapsulation + constructor
class Customer:
def __init__(self, name, age, address, phone):
self.name = name
self.age = age
self.address = address
self.phone = phone
def __repr__(self):
return f"Customer(name={self.name!r}, age={self.age}, phone={self.phone!r})"
class Money:
# A tiny helper class to show composition (BankAccount HAS-A Money)
def __init__(self, rupees=0, paise=0):
total = rupees * 100 + paise
if total < 0:
raise ValueError("Money can't be negative at creation.")
self._paise = total # internal storage in paise
@property
def paise(self):
return self._paise
def add(self, rupees=0, paise=0):
self._paise += rupees * 100 + paise
def can_subtract(self, rupees=0, paise=0):
return self._paise - (rupees * 100 + paise) >= 0
def subtract(self, rupees=0, paise=0):
amount = rupees * 100 + paise
if self._paise - amount < 0:
raise ValueError("Insufficient balance.")
self._paise -= amount
def __repr__(self):
r = self._paise // 100
p = self._paise % 100
return f"₹{r}.{p:02d}"
class BankAccount:
# Encapsulation idea: balance is internal, only changed by methods
def __init__(self, account_no, customer, opening_balance):
# Constructor = assembly: fit parts into the final object
self.account_no = account_no
self.customer = customer # composition
self._balance = opening_balance # keep it "private-ish"
def deposit(self, rupees=0, paise=0):
if rupees < 0 or paise < 0:
raise ValueError("Deposit must be non-negative.")
self._balance.add(rupees, paise)
return self._balance
def withdraw(self, rupees=0, paise=0):
if rupees < 0 or paise < 0:
raise ValueError("Withdraw must be non-negative.")
self._balance.subtract(rupees, paise)
return self._balance
def show_balance(self):
return self._balance
def __repr__(self):
return f"BankAccount({self.account_no}, {self.customer}, balance={self._balance})"
# --- Demo (objects created from classes) ---
cust = Customer("Asha", 20, "Varanasi", "90000-00000")
acct = BankAccount(119827, cust, Money(rupees=5000, paise=0))
print(acct)
acct.deposit(rupees=250)
print("After deposit:", acct.show_balance())
acct.withdraw(rupees=100)
print("After withdraw:", acct.show_balance())
// Programmer's Picnic — OOP in Java (Champak Roy)
// Focus: encapsulation + constructor + composition
class Customer {
private final String name;
private final int age;
private final String address;
private final String phone;
public Customer(String name, int age, String address, String phone) {
this.name = name;
this.age = age;
this.address = address;
this.phone = phone;
}
public String getName() { return name; }
@Override
public String toString() {
return "Customer{name='" + name + "', age=" + age + ", phone='" + phone + "'}";
}
}
class Money {
// store in paise for accuracy
private long paise;
public Money(long rupees, long paise) {
long total = rupees * 100 + paise;
if (total < 0) throw new IllegalArgumentException("Money can't be negative.");
this.paise = total;
}
public long getPaise() { return paise; }
public void add(long rupees, long paise) {
this.paise += rupees * 100 + paise;
}
public void subtract(long rupees, long paise) {
long amt = rupees * 100 + paise;
if (this.paise - amt < 0) throw new IllegalArgumentException("Insufficient balance.");
this.paise -= amt;
}
@Override
public String toString() {
long r = paise / 100;
long p = paise % 100;
return "₹" + r + "." + (p < 10 ? "0" + p : p);
}
}
class BankAccount {
private final int accountNo;
private final Customer customer; // composition
private final Money balance; // encapsulated field
public BankAccount(int accountNo, Customer customer, Money openingBalance) {
// Constructor = assembly
this.accountNo = accountNo;
this.customer = customer;
this.balance = openingBalance;
}
public void deposit(long rupees, long paise) {
if (rupees < 0 || paise < 0) throw new IllegalArgumentException("Deposit must be non-negative.");
balance.add(rupees, paise);
}
public void withdraw(long rupees, long paise) {
if (rupees < 0 || paise < 0) throw new IllegalArgumentException("Withdraw must be non-negative.");
balance.subtract(rupees, paise);
}
public Money showBalance() { return balance; }
@Override
public String toString() {
return "BankAccount{accountNo=" + accountNo + ", customer=" + customer + ", balance=" + balance + "}";
}
}
public class Main {
public static void main(String[] args) {
Customer cust = new Customer("Asha", 20, "Varanasi", "90000-00000");
BankAccount acct = new BankAccount(119827, cust, new Money(5000, 0));
System.out.println(acct);
acct.deposit(250, 0);
System.out.println("After deposit: " + acct.showBalance());
acct.withdraw(100, 0);
System.out.println("After withdraw: " + acct.showBalance());
}
}
// Programmer's Picnic — OOP in JavaScript (Champak Roy)
// Focus: encapsulation (private fields) + constructor + composition
class Customer {
constructor(name, age, address, phone) {
this.name = name;
this.age = age;
this.address = address;
this.phone = phone;
}
toString() {
return `Customer(name=${this.name}, age=${this.age}, phone=${this.phone})`;
}
}
class Money {
constructor(rupees = 0, paise = 0) {
const total = rupees * 100 + paise;
if (total < 0) throw new Error("Money can't be negative.");
this._paise = total;
}
add(rupees = 0, paise = 0) {
this._paise += rupees * 100 + paise;
}
subtract(rupees = 0, paise = 0) {
const amt = rupees * 100 + paise;
if (this._paise - amt < 0) throw new Error("Insufficient balance.");
this._paise -= amt;
}
toString() {
const r = Math.floor(this._paise / 100);
const p = this._paise % 100;
return `₹${r}.${String(p).padStart(2, "0")}`;
}
}
class BankAccount {
#balance; // private field (encapsulation)
constructor(accountNo, customer, openingBalance) {
// Constructor = assembly of parts
this.accountNo = accountNo;
this.customer = customer; // composition
this.#balance = openingBalance;
}
deposit(rupees = 0, paise = 0) {
if (rupees < 0 || paise < 0) throw new Error("Deposit must be non-negative.");
this.#balance.add(rupees, paise);
return this.#balance.toString();
}
withdraw(rupees = 0, paise = 0) {
if (rupees < 0 || paise < 0) throw new Error("Withdraw must be non-negative.");
this.#balance.subtract(rupees, paise);
return this.#balance.toString();
}
showBalance() {
return this.#balance.toString();
}
toString() {
return `BankAccount(${this.accountNo}, ${this.customer.toString()}, balance=${this.showBalance()})`;
}
}
// Demo
const cust = new Customer("Asha", 20, "Varanasi", "90000-00000");
const acct = new BankAccount(119827, cust, new Money(5000, 0));
console.log(acct.toString());
console.log("After deposit:", acct.deposit(250, 0));
console.log("After withdraw:", acct.withdraw(100, 0));
Attempt, submit, see explanations + score.
Do these in any language first, then port to the other two.