
অবজেক্ট ওরিয়েন্টেড প্রোগ্রামিং শেখার সময় বেশিরভাগ শিক্ষার্থী সংজ্ঞা মুখস্থ করে ফেলেন। ক্লাস মানে নকশা, অবজেক্ট মানে সেই নকশা থেকে বানানো জিনিস, এই কথাগুলো সবাই বলতে পারেন। কিন্তু প্রশ্ন করলে বেশিরভাগই বলতে পারেন না, ফিল্ডটি private রাখলে বাস্তবে কী লাভ হলো, বা Inheritance না থাকলে কোন সমস্যায় পড়তাম।
এই টিউনে আমরা সংজ্ঞা দিয়ে শুরু করব না। বরং একটি ছোট ব্যাংক অ্যাকাউন্ট প্রোগ্রাম ধাপে ধাপে বানাব, আর প্রতিবার যখন কোনো সমস্যায় পড়ব তখন OOP এর একটি করে বৈশিষ্ট্য এনে সেই সমস্যার সমাধান করব। শেষে আপনার হাতে একটি চলমান প্রোগ্রাম থাকবে এবং কেন OOP দরকার সেই উত্তরটিও থাকবে।
আমাদের প্রোগ্রামে থাকবে দুই ধরনের ব্যাংক অ্যাকাউন্ট। সাধারণ অ্যাকাউন্টে টাকা জমা ও উত্তোলন করা যাবে, আর সেভিংস অ্যাকাউন্টে বাড়তি নিয়ম থাকবে, সেখানে সুদ যোগ হবে এবং সবসময় একটি ন্যূনতম টাকা রাখতেই হবে।
নিচের চিত্রে এক নজরে দেখানো হয়েছে Object Oriented Programming এর চারটি মূল স্তম্ভ এই ব্যাংক অ্যাকাউন্ট প্রজেক্টের ঠিক কোন জায়গায় কাজে লাগছে। পুরো টিউনটি পড়ার আগে এই চিত্রটি একবার দেখে নিলে প্রতিটি ধাপ কেন দরকার তা বুঝতে অনেক সহজ হবে।

প্রোগ্রাম বড় হতে থাকলে শুধু ভেরিয়েবল আর মেথড দিয়ে সব সামলানো কঠিন হয়ে যায়। তখন দরকার হয় এমন একটি উপায়, যেখানে ডেটা আর সেই ডেটার উপর কাজ করা মেথডগুলো একসাথে এক জায়গায় থাকবে। এই ভাবনাটাই Object Oriented Programming এর মূল কথা। একটি ক্লাস হলো নকশা, আর সেই নকশা থেকে তৈরি হওয়া প্রতিটি অবজেক্ট হলো আলাদা আলাদা বাস্তব জিনিস।
ব্যাংক অ্যাকাউন্টের উদাহরণটি এই কারণেই বেছে নেওয়া হয়েছে। একটি অ্যাকাউন্টের নিজের কিছু তথ্য থাকে, যেমন নম্বর, নাম আর ব্যালেন্স। আবার সেই অ্যাকাউন্টের উপর কিছু কাজ করা যায়, যেমন টাকা জমা দেওয়া বা তোলা। এই দুইটি জিনিস একসাথে এক ক্লাসে রাখলেই কোড অনেক পরিষ্কার হয়ে যায়। নিচে চারটি স্তম্ভ এই প্রজেক্টে কীভাবে এসেছে তা সংক্ষেপে দেখানো হলো।
ক্লাস হলো নকশা আর অবজেক্ট হলো সেই নকশা মেনে বানানো আসল জিনিস। একটি ক্লাস একবার লিখে আপনি হাজারটি অ্যাকাউন্ট বানাতে পারবেন, প্রতিটির তথ্য আলাদা থাকবে।
class BankAccount {
private final String accountNumber;
private final String holderName;
private double balance;
}
Constructor হলো সেই বিশেষ মেথড যা অবজেক্ট তৈরির মুহূর্তে চলে। এর কাজ হলো নিশ্চিত করা যে অবজেক্টটি জন্মের সময়ই বৈধ অবস্থায় থাকে, অর্ধেক তথ্য নিয়ে যেন তৈরি না হয়।
public BankAccount(String accountNumber, String holderName, double openingBalance) {
this.accountNumber = accountNumber;
this.holderName = holderName;
this.balance = openingBalance;
}
এই ধাপের মূল কথাটি নিচের চিত্রে দেখানো হয়েছে। বাইরের কোড কখনোই সরাসরি ব্যালেন্স স্পর্শ করতে পারবে না, প্রতিটি পরিবর্তন অবশ্যই মেথডের ভিতর দিয়ে যেতে হবে।

ব্যালেন্স ভেরিয়েবলটি যদি public করে রাখা হয়, তাহলে প্রোগ্রামের যেকোনো জায়গা থেকে যে কেউ তার মান বদলে দিতে পারবে। কেউ ভুল করে ঋণাত্মক মান বসিয়ে দিলে অ্যাকাউন্টের হিসাব একদম নষ্ট হয়ে যাবে, আর সমস্যাটি কোথা থেকে এসেছে তা খুঁজে বের করাও কঠিন হবে। এই ঝুঁকি এড়াতেই ভেরিয়েবলটিকে private রাখা হয়।
private করার পর ব্যালেন্স পড়তে বা বদলাতে হলে ক্লাসের নিজের মেথড ব্যবহার করতে হয়। জমা দেওয়ার মেথডটি আগে দেখে নেয় টাকার পরিমাণ শূন্যের বেশি কি না, আর তোলার মেথডটি দেখে নেয় অ্যাকাউন্টে যথেষ্ট টাকা আছে কি না। শর্ত না মিললে মেথডটি কাজ না করে একটি বার্তা দেখায়। এভাবে যাচাইয়ের নিয়মগুলো এক জায়গায় জমা থাকে, তাই ভুল হওয়ার সম্ভাবনা অনেক কমে যায়।
এখন সবচেয়ে গুরুত্বপূর্ণ অংশ। ব্যালেন্স যদি public হতো, তাহলে প্রোগ্রামের যেকোনো জায়গা থেকে কেউ লিখে দিতে পারত ব্যালেন্স সমান দশ লাখ। Encapsulation মানে হলো ডেটা লুকিয়ে রেখে শুধু নিয়ন্ত্রিত পথে তাতে হাত দিতে দেওয়া।
public void deposit(double amount) {
if (amount <= 0) {
System.out.println("জমার পরিমাণ শূন্যের বেশি হতে হবে");
return;
}
balance = balance + amount;
System.out.println(amount + " টাকা জমা হয়েছে");
}
public void withdraw(double amount) {
if (amount > balance) {
System.out.println("ব্যালেন্স যথেষ্ট নয়");
return;
}
balance = balance - amount;
System.out.println(amount + " টাকা উত্তোলন হয়েছে");
}
public double getBalance() {
return balance;
}
public String getHolderName() {
return holderName;
}
ব্যালেন্স বদলানোর একমাত্র রাস্তা এখন deposit ও withdraw মেথড, আর সেই দুই রাস্তাতেই পাহারা বসানো আছে। ভবিষ্যতে যদি নিয়ম বদলায়, যেমন প্রতিবার উত্তোলনে দশ টাকা চার্জ কাটতে হবে, তাহলে শুধু একটি মেথডে পরিবর্তন করলেই পুরো প্রোগ্রামে সেটি কার্যকর হবে।
অবজেক্ট সরাসরি ছাপালে জাভা ক্লাসের নাম আর একটি হেক্সাডেসিমেল কোড দেখায়, যা পড়ে কিছু বোঝা যায় না। toString মেথডটি নিজের মতো করে লিখে নিলে সমস্যাটি মিটে যায়।
@Override
public String toString() {
return holderName + " (" + accountNumber + ") ব্যালেন্স " + balance;
}
নিচের চিত্রে দেখানো হয়েছে মূল ক্লাস থেকে কীভাবে আলাদা ধরনের অ্যাকাউন্ট তৈরি হয় এবং কোন মেথডটি নতুন করে লেখা হয়।

সেভিংস অ্যাকাউন্ট আসলে সাধারণ অ্যাকাউন্টেরই একটি বিশেষ রূপ। এতেও নম্বর, নাম আর ব্যালেন্স থাকে, টাকা জমা দেওয়া ও তোলার কাজও একই নিয়মে হয়। শুধু বাড়তি হিসেবে এতে সুদ যোগ করার একটি ব্যবস্থা থাকে। তাই আগের সব কোড আবার লেখার কোনো দরকার নেই, শুধু মূল ক্লাসটিকে বাড়িয়ে নিলেই হয়। এই কাজটিই করা হয় extends কীওয়ার্ড দিয়ে।
নতুন ক্লাসের ভিতরে চাইলে মূল ক্লাসের কোনো মেথডকে নিজের মতো করে আবার লেখা যায়, এটিকেই বলা হয় Override করা। যেমন তথ্য দেখানোর মেথডটি সেভিংস অ্যাকাউন্টের জন্য একটু আলাদা লেখা হয়, যাতে সুদের হারও দেখা যায়। এর ফলেই সবচেয়ে মজার সুবিধাটি পাওয়া যায়, একই তালিকায় দুই ধরনের অ্যাকাউন্ট রেখে যখন একই মেথড ডাকা হয়, তখন প্রতিটি অবজেক্ট নিজের নিয়ম অনুযায়ী আলাদা ফল দেয়।
সেভিংস অ্যাকাউন্টেও জমা, উত্তোলন আর ব্যালেন্স লাগবে। পুরো কোড আবার লেখার বদলে আমরা BankAccount ক্লাসটিকে extends করব, ফলে সব সুবিধা এমনিতেই পাওয়া যাবে, শুধু নতুন নিয়মটুকু যোগ করলেই হবে।
class SavingsAccount extends BankAccount {
private final double interestRate;
public SavingsAccount(String accountNumber, String holderName, double openingBalance, double interestRate) {
super(accountNumber, holderName, openingBalance);
this.interestRate = interestRate;
}
public void addInterest() {
double interest = getBalance() * interestRate / 100;
deposit(interest);
}
@Override
public void withdraw(double amount) {
if (getBalance() - amount < 500) {
System.out.println("সেভিংস অ্যাকাউন্টে অন্তত ৫০০ টাকা রাখতে হবে");
return;
}
super.withdraw(amount);
}
}
দুটি ক্লাস আলাদা হলেও দুটোই BankAccount ধরনের। তাই একই অ্যারেতে দুটোকেই রাখা যায়, আর লুপ চালালে জাভা নিজেই বুঝে নেয় কোন অবজেক্টের কোন মেথডটি চালাতে হবে।
public class BankApp {
public static void main(String[] args) {
BankAccount current = new BankAccount("CA-1001", "নাছিরুল", 2000);
SavingsAccount savings = new SavingsAccount("SA-2001", "রুমানা", 5000, 5);
current.deposit(1500);
current.withdraw(1000);
savings.withdraw(4800);
savings.addInterest();
BankAccount[] accounts = {current, savings};
for (BankAccount account : accounts) {
System.out.println(account);
}
}
}
প্রোগ্রামটি রান করলে কনসোলে নিচের লেখাগুলো আসবে। সেভিংস অ্যাকাউন্ট থেকে ৪৮০০ টাকা তোলার চেষ্টা আটকে গেছে, কারণ তাতে ব্যালেন্স ৫০০ এর নিচে নেমে যেত।
1500.0 টাকা জমা হয়েছে 1000.0 টাকা উত্তোলন হয়েছে সেভিংস অ্যাকাউন্টে অন্তত ৫০০ টাকা রাখতে হবে 250.0 টাকা জমা হয়েছে নাছিরুল (CA-1001) ব্যালেন্স 2500.0 রুমানা (SA-2001) ব্যালেন্স 5250.0
নতুন অবস্থায় অনেকেই মনে করেন Inheritance যত বেশি ব্যবহার করা যায় ততই ভালো। বাস্তবে ব্যাপারটি উল্টো। Inheritance তখনই মানানসই, যখন নতুন ক্লাসটি সত্যিই মূল ক্লাসের একটি ধরন হয়। সেভিংস অ্যাকাউন্ট সত্যিই একধরনের ব্যাংক অ্যাকাউন্ট, তাই এখানে extends ব্যবহার করা যুক্তিসঙ্গত। কিন্তু সব সম্পর্ককে এভাবে দেখা যায় না।
ধরুন অ্যাকাউন্টের সাথে লেনদেনের একটি তালিকা রাখতে চান। লেনদেনের তালিকা কোনোভাবেই ব্যাংক অ্যাকাউন্টের একটি ধরন নয়, বরং সেটি অ্যাকাউন্টের ভিতরে থাকা একটি অংশ। এমন ক্ষেত্রে নতুন ক্লাস বানিয়ে সেটিকে অ্যাকাউন্ট ক্লাসের ভিতরে একটি ভেরিয়েবল হিসেবে রাখাই সঠিক উপায়। এই পদ্ধতিকে বলা হয় Composition। সহজ নিয়ম হলো, সম্পর্কটি যদি একধরনের হয় তবে Inheritance, আর সম্পর্কটি যদি ভিতরে আছে ধরনের হয় তবে Composition।
Composition এর আরেকটি বড় সুবিধা আছে। মূল ক্লাসে কোনো পরিবর্তন আনলে তার প্রভাব সব সাবক্লাসে গিয়ে পড়ে, ফলে বড় প্রজেক্টে হঠাৎ অপ্রত্যাশিত সমস্যা তৈরি হতে পারে। কিন্তু ভিতরে রাখা আলাদা ক্লাসটি স্বাধীন থাকে, তাই একটি বদলালে অন্যটি ক্ষতিগ্রস্ত হয় না। এই কারণেই অভিজ্ঞ ডেভেলপাররা প্রয়োজন ছাড়া ইনহেরিটেন্সের স্তর বাড়াতে চান না।
পড়ে বোঝা আর নিজে লিখে বোঝার মধ্যে অনেক তফাত। উপরের কোডটি একবার চালানোর পর নিচের কাজগুলো নিজে চেষ্টা করে দেখুন। প্রতিটি কাজ ছোট, কিন্তু এগুলো করলে ক্লাস, অবজেক্ট আর ইনহেরিটেন্সের ধারণা অনেক পরিষ্কার হয়ে যাবে।
প্যারেন্ট ক্লাসে যদি প্যারামিটারসহ Constructor থাকে, তাহলে চাইল্ড ক্লাসে super দিয়ে সেটি ডাকা বাধ্যতামূলক। না ডাকলে কম্পাইলার বলবে প্যারেন্ট ক্লাসে খালি Constructor নেই। super এর লাইনটি অবশ্যই Constructor এর একদম প্রথম লাইনে বসাতে হবে।
account.balance লিখলে কম্পাইলার বলবে balance has private access। এটি ভুল নয়, বরং এটাই Encapsulation কাজ করছে তার প্রমাণ। মান দরকার হলে getBalance ব্যবহার করুন, বদলাতে চাইলে deposit বা withdraw ব্যবহার করুন।
শেখার জন্য double ঠিক আছে, কিন্তু বাস্তব আর্থিক প্রজেক্টে দশমিকের হিসাবে সামান্য ভুল জমতে থাকে। সেখানে BigDecimal ব্যবহার করাই নিয়ম, অথবা টাকার হিসাব পয়সায় রূপ দিয়ে long টাইপে রাখা হয়।
ক্লাস, অবজেক্ট, ইনহেরিটেন্স আর পলিমরফিজম বুঝে গেলে জাভার আসল শক্তির জায়গায় ঢোকার পথ খুলে যায়। এরপর যে বিষয়গুলো শিখলে সবচেয়ে বেশি কাজে লাগবে সেগুলো ধাপে ধাপে সাজিয়ে নেওয়া ভালো। একবারে সব ধরতে গেলে গুলিয়ে যাওয়ার আশঙ্কা থাকে, তাই একটি একটি করে অভ্যাস করাই বুদ্ধিমানের কাজ।
প্রথমে শেখা উচিত অ্যাবস্ট্রাক্ট ক্লাস আর ইন্টারফেস। এই দুইটি জিনিস দিয়ে ঠিক করে দেওয়া যায় কোন কোন কাজ সব ধরনের অ্যাকাউন্টে অবশ্যই থাকতে হবে, অথচ কাজটি কীভাবে হবে তা প্রতিটি ক্লাস নিজের মতো ঠিক করে নিতে পারে। বড় প্রজেক্টে এই ব্যবস্থাটি খুব দরকারি, কারণ এতে নিয়ম আর বাস্তবায়ন আলাদা থাকে।
এরপর হাত দেওয়া যায় কালেকশন আর জেনেরিকসে, যেখানে অনেকগুলো অবজেক্ট একসাথে গুছিয়ে রাখা শেখা যায়। তার সাথে ফাইলে তথ্য জমা রাখা শিখলে আপনার ব্যাংক অ্যাকাউন্ট প্রোগ্রামটিকে সত্যিকারের ছোট সফটওয়্যারে রূপ দেওয়া সম্ভব হবে, কারণ তখন প্রোগ্রাম বন্ধ করলেও ব্যালেন্স হারিয়ে যাবে না।
খেয়াল করে দেখুন, আমরা একবারও OOP এর সংজ্ঞা মুখস্থ করিনি। তবু ক্লাস, অবজেক্ট, Constructor, Encapsulation, Inheritance আর Polymorphism, ছয়টি ধারণাই ব্যবহার হয়ে গেছে এবং প্রতিটির পেছনে একটি বাস্তব কারণ ছিল। এভাবে শিখলে পরীক্ষার খাতায় নয়, আসল প্রজেক্টেও কাজে লাগবে।
এবার আপনি নিজে চেষ্টা করুন, একটি LoanAccount ক্লাস বানান যেখানে ব্যালেন্স ঋণাত্মক হতে পারে। কোথায় আটকে যাচ্ছেন টিউমেন্টে জানান। এই প্রোগ্রামটি রান করে নিজেই নতুন নিয়ম যোগ করুন, তাহলেই OOP এর সুবিধাটি হাতে কলমে ধরা পড়বে।
আমি নাছিরুল হক। বিশ্বের সর্ববৃহৎ বিজ্ঞান ও প্রযুক্তির সৌশল নেটওয়ার্ক - টেকটিউনস এ আমি 1 সপ্তাহ 5 দিন যাবৎ যুক্ত আছি। টেকটিউনস আমি এ পর্যন্ত 24 টি টিউন ও 3 টি টিউমেন্ট করেছি। টেকটিউনসে আমার 0 ফলোয়ার আছে এবং আমি টেকটিউনসে 2 টিউনারকে ফলো করি।
আমি নাছিরুল হক। প্রোগ্রামিং আর প্রযুক্তি নিয়ে ঘাঁটাঘাঁটি করতে ভালোবাসি। টেকটিউনসে মূলত Java প্রোগ্রামিং নিয়ে ধাপে ধাপে হাতে কলমে টিউটোরিয়াল লিখি — JDK ইনস্টল ও এনভায়রনমেন্ট সেটআপ থেকে শুরু করে IntelliJ IDEA, Exception Handling, OOP, Java Swing ও ফাইল হ্যান্ডলিং পর্যন্ত। এছাড়া স্মার্টফোন, ব্যাটারি ও নেটওয়ার্ক নিয়ে সহজ ভাষায় ব্যাখ্যামূলক...