Large Class smell: অনেক কাজ করা class ভাগ করুন
যে class data রাখে, লেখা সাজায় ও বার্তা পাঠায় সে তিনটি কাজ করছে। Large Class নিরাপদে ভাগ করা শিখুন, পরীক্ষিত আগে-পরের code-সহ।

Large Class-এ অনেক field, অনেক method ও বদলানোর অনেক কারণ থাকে। প্রায়ই ছোট থেকে শুরু হয়। কেউ "আরেকটি method" যোগ করে, তারপর আরও কয়েকটি, আর class শেষে data রাখে, report সাজায় ও বার্তা পাঠায়। নিচের code ও output প্রকাশের আগে Python 3-এ চালানো।
কীভাবে চিনবেন
- নাম অস্পষ্ট (
Manager,Helper,Utils,Data) বা class বাড়তেই থাকে। - কিছু method-গোষ্ঠী আলাদা field ব্যবহার করে। অর্ধেক field অর্ধেক method ব্যবহার করে।
- email-এর লেখা বদলাতে গেলে ব্যালেন্সের নিয়ম থাকা ফাইলই বদলাতে হয়।
- এর test-এ অনেক অসম্পর্কিত প্রস্তুতি লাগে।
- বর্ণনায় নিজেকে "এবং" বলতে দেখেন: "ব্যালেন্স রাখে এবং statement ছাপে এবং alert পাঠায়"।
এটিই single responsibility principle-এর ধারণা: একটি class-এর বদলানোর কারণ একটিই।
সমাধান: Extract Class
যে field ও method-এর গোষ্ঠী একসঙ্গে থাকার, সেটি নতুন class-এ সরান এবং মূলটি সেটি ব্যবহার করুক (বা caller দুটিই)। উদাহরণে account শুধু টাকার নিয়ম রাখে। সাজানো ও বার্তা সরে গেছে।
# ---- BEFORE: one class that holds money, formats text and sends messages ----
class AccountBefore:
def __init__(self, owner):
self.owner, self.balance, self.history = owner, 0, []
def deposit(self, amount):
self.balance += amount; self.history.append(("deposit", amount))
def withdraw(self, amount):
if amount > self.balance:
raise ValueError("insufficient funds")
self.balance -= amount; self.history.append(("withdraw", amount))
def statement(self):
lines = [f"Statement for {self.owner}"] + [f"{kind:<9}{amt:>8}" for kind, amt in self.history]
return "\n".join(lines + [f"balance {self.balance:>8}"])
def to_csv(self):
return "\n".join(f"{kind},{amt}" for kind, amt in self.history)
def alert(self, outbox):
outbox.append(f"To {self.owner}: balance is {self.balance}")
# ---- AFTER: the account keeps money; formatting and messaging live elsewhere ----
class Account:
def __init__(self, owner):
self.owner, self.balance, self.history = owner, 0, []
def deposit(self, amount):
self.balance += amount; self.history.append(("deposit", amount))
def withdraw(self, amount):
if amount > self.balance:
raise ValueError("insufficient funds")
self.balance -= amount; self.history.append(("withdraw", amount))
class StatementFormatter:
def text(self, acct):
lines = [f"Statement for {acct.owner}"] + [f"{kind:<9}{amt:>8}" for kind, amt in acct.history]
return "\n".join(lines + [f"balance {acct.balance:>8}"])
def csv(self, acct):
return "\n".join(f"{kind},{amt}" for kind, amt in acct.history)
class Notifier:
def __init__(self, outbox): self.outbox = outbox
def balance_alert(self, acct):
self.outbox.append(f"To {acct.owner}: balance is {acct.balance}")
Account এখন ছোট ও ভাবা সহজ। StatementFormatter টাকার নিয়ম না ছুঁয়ে নতুন ফরম্যাট (HTML, PDF) পেতে পারে, আর Notifier test-এ fake দিয়ে বদলানো যায়।
একই আচরণ প্রমাণ করুন
old, new = AccountBefore("Amina"), Account("Amina")
for a in (old, new):
a.deposit(500); a.withdraw(120)
box_old, box_new = [], []
f = StatementFormatter()
print(f.text(new))
old.alert(box_old); Notifier(box_new).balance_alert(new)
assert old.statement() == f.text(new) and old.to_csv() == f.csv(new) and box_old == box_new
print(box_new[0])
print("same results, three small classes instead of one big one")
Statement for Amina
deposit 500
withdraw 120
balance 380
To Amina: balance is 380
same results, three small classes instead of one big one
পুরনো text statement, CSV ও alert নতুনগুলোর সঙ্গে হুবহু এক। আচরণ বদলায়নি, কিন্তু প্রতিটি কাজ এখন নিজস্ব class-এ।
নিরাপদে refactor করবেন যেভাবে
- আগে বর্তমান public আচরণের চারপাশে test লিখুন।
- যে field-গোষ্ঠী সবসময় কিছু method-এর সঙ্গে থাকে সেগুলো খুঁজুন।
- নতুন class বানিয়ে একবারে একটি method সরান, প্রতিটি সরানোর পর test চালান।
- অনেক caller নির্ভর করলে কিছুদিন পুরনো method পাতলা মোড়ক হিসেবে রাখুন, তারপর সরান।
- class-কে কাজ দিয়ে নাম দিন (
StatementFormatter), অস্পষ্ট শব্দে নয় (AccountHelper)।
কখন ছেড়ে দেবেন
- একই data ব্যবহার করা অনেক সহজ method-এর class-ও সংহত হতে পারে।
- ভাগ করলে একটি কাজ বুঝতে ছয়টি ফাইলে লাফাতে হলে ভাগ বেশি সূক্ষ্ম।
সাধারণ ভুল
- দায়িত্ব নয়, শুধু প্রযুক্তিগত স্তর ধরে ভাগ ("সব getter এখানে")।
- নতুন
Utilsclass বানানো যা পরের large class হয়ে ওঠে। - method সরিয়ে তার ব্যবহৃত field না সরানো, ফলে নতুন class পুরনোটির দিকেই হাত বাড়ায়।
- code সরানোর সময় আচরণ বদলে ফেলা। একবারে একটি কাজ করুন।
মূল কথা: class-এ আলাদা field ব্যবহার করা method-গোষ্ঠী থাকলে প্রতিটি গোষ্ঠী নিজস্ব class হতে চায়। একটি একটি method করে extract করুন, test সবুজ রাখুন, আর প্রতিটি class-কে তার একক কাজ দিয়ে নাম দিন।
Code smell ও refactoring সিরিজ, Bloaters: 2 নম্বর (মোট 5)। আগের লেখা: Long Method smell: চিনুন ও Extract Method দিয়ে সারান.


