অবজেক্ট-ওরিয়েন্টেড প্রোগ্রামিংয়ে (OOP) Encapsulation (এনক্যাপসুলেশন) হলো এমন একটি প্রক্রিয়া, যার মাধ্যমে একটি ক্লাসের ডেটা (Variables/Fields) এবং সেই ডেটা ম্যানিপুলেট বা অ্যাক্সেস করার মেথডগুলোকে (Functions/Methods) একটি একক ইউনিটের ভেতরে আবদ্ধ করে রাখা হয়।
এর পাশাপাশি এনক্যাপসুলেশন এমন একটি প্রতিরক্ষামূলক দেয়াল তৈরি করে, যা বাইরের কোনো কোডকে সরাসরি অবজেক্টের অভ্যন্তরীণ ইন্টারনাল স্টেট পরিবর্তন করতে বাধা দেয়। এটিকে প্রায়শই Data Hiding বা তথ্য গোপনকরণের ভিত্তি বলা হয়। বাইরের ক্লাসের সাথে যোগাযোগ হয় শুধুমাত্র অনুমোদিত পাবলিক মেথড বা ইন্টারফেসের মাধ্যমে।
Encapsulation কেন গুরুত্বপূর্ণ ও এর সুবিধা
এনক্যাপসুলেশন কোনো সফটওয়্যার সিস্টেমকে সুরক্ষিত, মেইনটেইনেবল এবং নিয়ন্ত্রিত রাখতে সরাসরি সাহায্য করে:
ডেটা নিয়ন্ত্রণ ও ভ্যালিডেশন (Control and Validation): ফিল্ডগুলোকে প্রাইভেট রেখে গেটার এবং সেটার (Getters/Setters) মেথডের মাধ্যমে ডেটা সেট করার সময় ভ্যালিডেশন বা নিরাপত্তা যাচাই যুক্ত করা যায়। উদাহরণস্বরূপ, ব্যালেন্স কখনো নেগেটিভ হতে না দেওয়া।
ডেটা হাইডিং ও সিকিউরিটি (Data Hiding): ক্লাসের ইন্টারনাল আর্কিটেকচার বা সংবেদনশীল ডেটা বাইরে সরাসরি এক্সপোজ হয় না, ফলে অনাকাঙ্ক্ষিত পরিবর্তন (Accidental Mutation) বা বাগ তৈরি হওয়ার ঝুঁকি থাকে না।
সহজ পরিবর্তনযোগ্যতা (Flexibility and Maintainability): ইন্টারনাল ইমপ্লিমেন্টেশন পরিবর্তন করলেও বাইরের কোডে কোনো প্রভাব পড়ে না, যদি মেথড সিগনেচার একই থাকে।
রিড-অনলি বা রাইট-অনলি সুবিধা: শুধু গেটার মেথড দিলে ফিল্ডটি রিড-অনলি (Read-only) হয় এবং শুধু সেটার মেথড দিলে তা রাইট-অনলি (Write-only) করা সম্ভব।
Encapsulation বনাম Abstraction
এই দুটি ধারণার মধ্যে সূক্ষ্ম পার্থক্য রয়েছে:
Encapsulation (বাস্তবায়নের নিরাপত্তা): এটি সম্পর্কিত ডেটা ও ফাংশনালিটিকে একত্রে গুটিয়ে রাখে এবং ডেটা হাইডিংয়ের মাধ্যমে স্টেটকে ভুল ব্যবহার থেকে রক্ষা করে ("How to protect data")।
Abstraction (জটিলতা কমানো): এটি মূল ইন্টারফেস উন্মুক্ত রেখে ভেতরের জটিল ব্যাকগ্রাউন্ড লজিক ও বিবরণ আড়াল করে ("What features are visible")।
বাস্তব জীবনের উদাহরণ: Bank Account System
একটি ব্যাংকিং অ্যাকাউন্টের ক্ষেত্রে যদি কোনো ইউজার বা প্রোগ্রাম সরাসরি account.balance = -5000; লিখে দিতে পারে, তবে তা সিস্টেমের অখণ্ডতা ধ্বংস করবে।
এনক্যাপসুলেশনের মাধ্যমে balance ফিল্ডটিকে প্রাইভেট রাখা হয় এবং ডিপোজিট বা উইথড্র করার জন্য সুনির্দিষ্ট নিয়মাবলি সম্বলিত মেথড প্রদান করা হয়, যেখানে নেতিবাচক মান বা অতিরিক্ত টাকা উত্তোলনের চেষ্টা রুখে দেওয়া যায়।
Java Implementation
জাভাতে অ্যাক্সেস মডিফায়ার (যেমন: private, protected, public) ব্যবহার করে কঠোরভাবে এনক্যাপসুলেশন নিশ্চিত করা হয়। ফিল্ডগুলোকে সাধারণত private ঘোষণা করে পাবলিক গেটার ও সেটার মেথড দিয়ে অ্যাক্সেস নিয়ন্ত্রণ করা হয়।
public class BankAccount {
// Hidden internal state
private final String accountNumber;
private double balance;
// Constructor to initialize state safely
public BankAccount(String accountNumber, double initialBalance) {
if (initialBalance < 0.0) {
throw new IllegalArgumentException("Initial balance cannot be negative.");
}
this.accountNumber = accountNumber;
this.balance = initialBalance;
}
// Read-only getter
public String getAccountNumber() {
return accountNumber;
}
// Controlled getter
public double getBalance() {
return balance;
}
// Controlled mutator with validation
public void deposit(double amount) {
if (amount <= 0.0) {
throw new IllegalArgumentException("Deposit amount must be greater than zero.");
}
this.balance += amount;
System.out.println("Deposited: $" + amount + " | Current Balance: $" + this.balance);
}
// Controlled mutator enforcing business logic
public void withdraw(double amount) {
if (amount <= 0.0) {
throw new IllegalArgumentException("Withdrawal amount must be greater than zero.");
}
if (amount > this.balance) {
System.out.println("Declined: Insufficient funds. Requested: $" + amount);
return;
}
this.balance -= amount;
System.out.println("Withdrawn: $" + amount + " | Current Balance: $" + this.balance);
}
public static void main(String[] args) {
BankAccount account = new BankAccount("ACC-987654", 500.0);
// Accessing state via controlled public methods
account.deposit(200.0);
account.withdraw(100.0);
account.withdraw(700.0);
// account.balance = -100; // Compile-time error: balance has private access
}
}Python Implementation
পাইথনে কোনো কম্পাইলার-এনফোর্সড private কিওয়ার্ড নেই। পরিবর্তে, প্রাইভেট ফিল্ড বোঝাতে ডাবল আন্ডারস্কোর (__) ব্যবহার করা হয় যা নেইম ম্যাংলিং (Name Mangling) ট্রিগার করে। এছাড়াও পাইথনিক এনক্যাপসুলেশন বাস্তবায়নে @property ডেকোরেটর ব্যবহার করা হয়।
class BankAccount:
def __init__(self, account_number: str, initial_balance: float) -> None:
if initial_balance < 0.0:
raise ValueError("Initial balance cannot be negative.")
self._account_number = account_number
self.__balance = initial_balance # Name-mangled private variable
# Property acts as a clean getter
@property
def account_number(self) -> str:
return self._account_number
@property
def balance(self) -> float:
return self.__balance
def deposit(self, amount: float) -> None:
if amount <= 0.0:
raise ValueError("Deposit amount must be greater than zero.")
self.__balance += amount
print(f"Deposited: ${amount} | Current Balance: ${self.__balance}")
def withdraw(self, amount: float) -> None:
if amount <= 0.0:
raise ValueError("Withdrawal amount must be greater than zero.")
if amount > self.__balance:
print(f"Declined: Insufficient funds. Requested: ${amount}")
return
self.__balance -= amount
print(f"Withdrawn: ${amount} | Current Balance: ${self.__balance}")
if __name__ == "__main__":
account = BankAccount("ACC-987654", 500.0)
# Accessing via public methods/properties
account.deposit(200.0)
account.withdraw(100.0)
account.withdraw(700.0)
print(f"Final Balance: ${account.balance}")
# Direct access like account.__balance will raise an AttributeError
# because of Python name mangling (_BankAccount__balance)এনক্যাপসুলেশন বজায় রাখার সেরা চর্চা (Best Practices)
ডিফল্টভাবে ফিল্ড প্রাইভেট রাখা: ক্লাসের সব ইনস্ট্যান্স ভ্যারিয়েবল শুরুতেই
privateরাখুন। যতক্ষণ না বাইরে থেকে অ্যাক্সেস প্রয়োজন হচ্ছে, কোনো ভ্যারিয়েবল এক্সপোজ করবেন না।ব্লাইন্ড সেটার তৈরি থেকে বিরত থাকা: প্রতিটি ফিল্ডের জন্য অন্ধভাবে সেটার মেথড লিখবেন না। যেসব ফিল্ড পরিবর্তন করার প্রয়োজন নেই, সেগুলোর সেটার না রাখাই শ্রেয় (ইমিউটেবিলিটি বজায় থাকে)।
রেফারেন্স টাইপ ও ডিফেন্সিভ কপি (Defensive Copying): যদি কোনো ক্লাসের ভেতর মিউটেবল অবজেক্ট (যেমন
List,Dateবা অন্য কোনো কাস্টম অবজেক্ট) থাকে, তবে গেটার মেথডের মাধ্যমে সেই মূল রেফারেন্সটি রিটার্ন না করে একটি নতুন কপি রিটার্ন করা উচিত; নতুবা বাইরের কোড অবজেক্টের অভ্যন্তরীণ স্টেট পরিবর্তন করে ফেলতে পারে।