মূল বিষয়বস্তুতে যান
Clean Code Mastery

Inline Class: যে Class কিছুই করে না, তাকে মিলিয়ে দাও

Inline Class refactoring শেখো একটা school committee-র গল্পের মাধ্যমে। যে class কিছুই করে না তাকে তার user-এর সাথে মিলিয়ে দাও আর অকারণ layer মুছে ফেলো।

19 মিনিট আপডেট: June 11, 2026beginner
refactoringsmoving-featuresinline-classlazy-classsimplicitytypescriptcsharp

একজন সদস্যের একটা committee

ধরো তোমার school-এ অনেক committee আছে। Sports committee, cultural committee, discipline committee — আর আছে বিখ্যাত Library Committee

আগে Library Committee বিশাল ব্যাপার ছিল। আটজন সদস্য, book fair, reading week, লেখক আসতেন। কিন্তু ধীরে ধীরে সব কাজ অন্যদের কাছে চলে গেল। Book fair গেল cultural committee-তে। Reading week গেল class teacher-দের কাছে। Librarian নাসরিন ম্যাম নিজেই বই কিনতে শুরু করলেন, কারণ committee meeting মানে শুধু দেরি। এখন Library Committee-তে আছে মাত্র একজন — বেচারা রুবেল, class 8 — আর একটাই কাজ: মাসে একবার notice board-এ "নতুন বই এসেছে" তালিকা লাগানো।

কিন্তু এটা officially committee, তাই পুরো ceremony চলছেই। রুবেলকে নিজের সাথে "committee meeting" করতে meeting room বুক করতে হয়। সে একা বড় table-এ বসে meeting শুরু করে, minutes লেখে — "Present: রুবেল। Apologies: none।" Principal জামাল স্যারকে monthly report পাঠায়, জামাল স্যার না পড়েই file করেন। Cultural committee-কে তালিকা চাইতে formal চিঠি লিখতে হয়, রুবেলের formal reply আসে। তিনটা কাগজ, দুদিন দেরি — একটা board-এ একটা pin মারার জন্য।

এই committee আসলে কীসে সময় দেয়:

চিত্র ১: একজন সদস্যের committee কীসে সময় দেয় — আসল কাজটা সবচেয়ে পাতলা টুকরো

Annual review-এ জামাল স্যার finally জিজ্ঞেস করলেন: "এই committee এখনো কেন আছে?" কেউ উত্তর দিতে পারল না — এমনকি রুবেলও না, বরং সে স্বস্তির নিঃশ্বাস ফেলল। তাই committee ভেঙে দেওয়া হলো। রুবেল cultural committee-তে যোগ দিল, "নতুন বই" pin করা হলো cultural committee-র duty list-এর একটা লাইন — আর সব চিঠি, minutes, monthly report উধাও। কিছু হারায়নি — শুধু ceremony সরে গেছে।

চিত্র ২: ভেঙে দেওয়ার আগে-পরে — আগে ভারী ceremony, পরে একটা সহজ duty line

Code-এও এরকম class আমরা নিয়মিত দেখি। একসময় কাজের ছিল, বা বড় আশা নিয়ে বানানো হয়েছিল — কিন্তু আজকে একটা field আর একটা call forward করে মাত্র। এই class-টাকে use করতে গেলে একটা extra hop, একটা extra file, একটা extra নাম মনে রাখতে হয়। জামাল স্যারের solution-এর একটা নাম আছে আমাদের catalog-এ: Inline Class

Inline Class মানে কী?

Inline Class হলো একটা refactoring যেখানে আমরা একটা প্রায়-খালি class-এর সব কিছু তার user class-এর ভেতরে নিয়ে যাই, তারপর খালি shell টা মুছে দিই। Martin Fowler-এর Refactoring catalog-এ এটা তার mirror twin-এর পাশেই আছে — এটা হলো Extract Class-এর exact উল্টো

এক কথায় recipe:

  1. Confirm করো class-এ সত্যিই প্রায় কোনো responsibility নেই, আর সাধারণত একটাই client আছে।
  2. প্রতিটা field Move Field দিয়ে host class-এ নিয়ে যাও।
  3. প্রতিটা method Move Method দিয়ে host class-এ নিয়ে যাও।
  4. বাকি সব reference host-এ point করো, তারপর খালি class মুছো।

সবসময়ের মতো, behaviour পরিবর্তন হয় না। একই answer আসে, শুধু একটা wrapping layer সরে যায়। নতুন বইয়ের তালিকা board-এ ঠিকই যায় — শুধু তার জন্য আর committee, meeting room, আর তিনটা চিঠি লাগে না।

ছোট একটা class সরানোর কষ্ট কেন করবে? কারণ কোনো class-ই বিনামূল্যে না। প্রতিটা class-এর একটা মূল্য আছে: একটা নাম শিখতে হয়, একটা file খুলতে হয়, code trace করতে গেলে একটা jump দিতে হয়। যখন class-টা সত্যিকারের কাজ করে — genuine concept, real behaviour, project জুড়ে reuse — তখন এই মূল্য দেওয়া worth it। কিন্তু যখন class শুধু pass through করে, মূল্য চলতেই থাকে অথচ benefit শেষ হয়ে গেছে। এই class হলো Lazy Class smell — আর তখন পড়তে গেলে যা হয়: খুললে Shipment, দেখলে সে TrackingCode.asString() call করছে, খুললে TrackingCode, দেখলে asString() সেই string-টাই ফেরত দিচ্ছে যেটা দিয়েছিলে। দুটো file, একটা hop, শূন্য information।

চিত্র ৩: Inline Class এক নজরে — কীভাবে class lazy হয়, কী check করতে হয়, আর কখন refuse করবে
💡

Laziness বোঝার এক-প্রশ্নের test: "এই class না থাকলে আর তার সব কিছু তার একমাত্র user-এর ভেতরে থাকলে, আমরা কী হারাবো?" উত্তর যদি হয় "কিছু না — একই behaviour, একটা file কম" — তাহলে inline করো। উত্তর যদি হয় "type safety হারাবো / একটা বড় হওয়ার concept আছে / তিনটা client ব্যবহার করছে" — তাহলে class টা কাজ করছে, ছেড়ে দাও।

Class কীভাবে lazy হয়? দুটো common গল্প:

  • অনেক আগে বানানো হয়েছিল। কেউ extract করেছিল "feature বড় হলে কাজে লাগবে" ভেবে। Feature কখনো বড় হয়নি। এটা Speculative Generality আর Lazy Class একসাথে।
  • ধীরে ধীরে খালি হয়ে গেছে। Class একসময় কাজের ছিল, কিন্তু refactoring-এর পর refactoring তার responsibility অন্যদিকে সরিয়ে দিয়েছে — Library Committee-র মতোই, book fair আর reading week চলে যাওয়ার পর।

দুটো গল্পের শেষ একটাই: class আর rent দিচ্ছে না, আর Inline Class হলো সেই বিনয়ী eviction।

একটু গভীরে যাই: metrics-এর ভাষায়, lazy class হলো Large Class-এর উল্টো failure। God class-এর সমস্যা cohesion-এ (অনেক unrelated member, high LCOM), আর lazy class-এর সমস্যা size আর contribution-এ — খুব কম WMC (Weighted Methods per Class, method-গুলোর summed complexity প্রায় শূন্য কারণ তারা শুধু forward করে), ছোট NOM (Number of Methods), আর fan-in মাত্র একটা। প্রতিটা extra class code-এ conceptual weight বাড়ায় — maintainer-কে আরো বেশি নাম মাথায় রাখতে হয়। Research দেখিয়েছে, indirection যদি কোনো সিদ্ধান্ত না নেয়, কোনো invariant না রাখে — তাহলে সে comprehension time বাড়ায়, bug কমায় না। Inline Class দিয়ে এই tax কমানো হয়।

কখন দরকার হয়?

এই situations-এ Inline Class ব্যবহার করো:

১. Lazy Class — প্রধান কারণ। একটা class-এ মাত্র এক-দুটো member, নিজে কোনো সিদ্ধান্ত নেয় না, শুধু forward করে। Lazy Class হলো smell, Inline Class হলো cure।

২. নিজের Extract Class অনেক বেশি করে ফেলেছিলে। Large Class ভাঙতে গিয়ে একটা piece বের করলে যেটার আসলে কোনো কাজ নেই। Seesaw-এ উল্টো দিকে ফেরা সঠিক কাজ — extract আর inline সেই seesaw-এর দুটো দিক।

৩. Class আকারে Middle Man। Class-এর বেশিরভাগ method অন্য object-এ delegate করে। কখনো Remove Middle Man ব্যবহার করো, আর যখন একটাই client আছে তখন পুরো class টাকে সেই client-এ inline করাই ভালো।

৪. বড় reshuffle-এর আগে। Fowler একটা practical trick বলেছেন: কখনো দুটো class একসাথে temporarily inline করো, তারপর ভালো seam বরাবর আবার split করো। Inline Class শুধু destination না — staging step-ও হতে পারে।

৫. Over-splitting থেকে Shotgun Surgery। একটা ছোট change চারটা micro-class-এ edit করতে বাধ্য করলে, responsibilities বেশি কেটে ফেলা হয়েছিল। মিলিয়ে দাও — দেখো Shotgun Surgery

কখন inline করবে না, এমনকি class ছোট দেখালেও?

  • Type-safety wrapper। TrackingCode নামের class যদি একটা string wrap করে শুধু compiler-কে বলতে যে customer name আর tracking code এক জিনিস না — সেটা কাজ করছে। Primitive Obsession smell আর real bug আটকাচ্ছে। ছোট মানেই lazy না — class কী আটকাচ্ছে সেটা দেখো।
  • Multiple client। তিনটা client ব্যবহার করে এমন class inline করা মানে তিনটার কাছ থেকে নিয়ে একটায় ঢোকানো — নতুন coupling তৈরি হয়। রাখো।
  • সত্যিই বড় হবে। Real, scheduled feature যদি শীঘ্রই আসছে — আজকে inline করে পরের মাসে আবার extract করা শুধু churn। কিন্তু সৎ থাকো — "একদিন হয়তো" মানে scheduled না।

এই table টা জামাল স্যার মাথায় মাথায় যে checklist চালিয়েছিলেন, সেটাই:

প্রশ্নLazy Class উত্তররাখো উত্তর
কতটা member আছে?এক-দুটো, সব trivialঅনেক, real body আছে
কোনো সিদ্ধান্ত নেয়?না — forward বা wrap করেহ্যাঁ — validation, rules, invariants
কতটা client ব্যবহার করে?একটাদুই বা বেশি
না থাকলে কী ভাঙবে?কিছু নাType safety, shared seam, বড় হওয়ার concept
Growth সত্যিই scheduled?"একদিন, হয়তো"Real ticket, এই quarter-এ

যেকোনো ছোট class এই দুটো axis-এ plot করো আর সিদ্ধান্ত পরিষ্কার হয়ে যাবে:

চিত্র ৪: Inline করবো নাকি রাখবো — একটা client আর কোনো real responsibility নেই যে class-এর, সে লুকাতে পারবে না

আগে আর পরে এক নজরে

একটা ছোট TypeScript example। LateFineRule অনেক আগে বড় plan নিয়ে extract করা হয়েছিল — আজকে তার কাছে আছে একটা number আর একটা multiplication:

// BEFORE — a class that is one number in a costume
class LateFineRule {
  private finePerDay = 2;
 
  fineFor(daysLate: number): number {
    return daysLate * this.finePerDay;
  }
}
 
class Library {
  private rule = new LateFineRule();
 
  totalFine(daysLate: number): number {
    return this.rule.fineFor(daysLate); // a hop that buys nothing
  }
}

totalFine বুঝতে হলে পাঠককে দ্বিতীয় একটা file খুলতে হবে, দ্বিতীয় একটা নাম শিখতে হবে — আর সেই jump-এর শেষে পাবে daysLate * 2। এই hop-টা দেখো — প্রতিটা call এই toll দেয়:

চিত্র ৫: অকারণ hop — caller-এর প্রশ্ন একটা one-member class পার হয়ে সরাসরি ফিরে আসে

Committee ভেঙে দাও:

// AFTER — same behaviour, one class, zero hops
class Library {
  private finePerDay = 2;
 
  totalFine(daysLate: number): number {
    return daysLate * this.finePerDay;
  }
}

একটা কম class, একটা কম file, আর Library উপর থেকে নিচ পর্যন্ত সোজা পড়া যায়। কিছু হারায়নি — শুধু ceremony গেছে।

চিত্র ৬: আগে — প্রতিটা fine calculation একটা one-member class-এর মধ্য দিয়ে অকারণ hop নেয়
চিত্র ৭: পরে — rule তার একমাত্র user-এর ভেতরে থাকে, খালি shell মুছে গেছে

নিরাপদ পদ্ধতিতে ধাপে ধাপে

ছোট merge-ও ছোট ছোট ধাপে করা উচিত। জামাল স্যার committee-র সব file পুড়িয়ে দেননি। শেষ একটা meeting হয়েছে, handover note লেখা হয়েছে, cultural committee-র duty list quietly update হয়েছে — তারপর staffroom board থেকে committee-র নাম সরানো হয়েছে। এই same সতর্কতাই procedure হিসেবে:

চিত্র ৮: ভাঙার pipeline — আগে verify, member by member নাও, সবশেষে shell মুছো

ধাপ ১ — Laziness verify করো। "Find Usages" দিয়ে class টা check করো। Confirm করো: (a) খুব কম member, (b) real সিদ্ধান্ত নেয় না, (c) একটাই client। Keep-it check-ও করো: type-safety wrapper? Growth scheduled? দুটো উত্তরই না হলে এগোও।

ধাপ ২ — Host prepare করো। Absorbing class-এ আসছে সব কিছুর জন্য জায়গা বানাও: incoming field আর method stub declare করো। এখনো কিছু delete করো না।

ধাপ ৩ — Field গুলো সরাও। প্রতিটা field-এর জন্য Move Field ব্যবহার করো। মাঝের অবস্থায় lazy class host-এ backwards delegate করে কাজ চলতে থাকে:

// INTERMEDIATE STATE — field has moved to Library; old class delegates
class Library {
  finePerDay = 2; // moved in
  private rule = new LateFineRule(this);
 
  totalFine(daysLate: number): number {
    return this.rule.fineFor(daysLate); // still hopping, for one more step
  }
}
 
class LateFineRule {
  constructor(private host: Library) {}
 
  fineFor(daysLate: number): number {
    return daysLate * this.host.finePerDay; // reads from its future home
  }
}

Compile করো, test চালাও। Behaviour একই — data সরে গেছে, method এখনো না।

ধাপ ৪ — Method গুলো সরাও। প্রতিটা method-এর জন্য Move Method ব্যবহার করো। fineFor-এর body Library.totalFine-এ ঢোকে, পুরানো method unused হয়ে যায়।

ধাপ ৫ — External reference redirect করো। যদি অন্য কোনো code এখনো LateFineRule mention করে (বানানো হচ্ছে, import হচ্ছে, type-এ আছে) — প্রতিটা site একটা একটা করে Library-তে point করো, মাঝে মাঝে compile করো।

ধাপ ৬ — Shell মুছে দাও। "Find Usages" শূন্য দেখালে class আর তার file মুছে দাও। তারপর sweep করো: rule field, leftover constructor parameter, unused import সব সরাও।

ধাপ ৭ — Host-কে আবার দেখো। Inline করার পর পরের refactoring দেখা যায়। হয়তো existing host code-এর সাথে duplication দেখবে, বা host নিজেই বড় হয়ে গেছে বুঝবে। Refactoring একটা conversation, single move না।

পুরো dissolution states হিসেবে — প্রতিটা state-ই একটা working, shippable program:

চিত্র ৯: একটা class-এর dissolution lifecycle — সন্দেহ থেকে deletion পর্যন্ত, প্রতিটা ধাপে safe stop আছে
⚠️

Test skip করো না কারণ class "obviously কিছু করে না"। Classic Inline Class accident হলো merge-এর সময় subtle behaviour change — যেমন lazy class তার value first use-এ বানাত, আর তুমি inline করে constructor-এ বানাচ্ছো, initialisation order বদলে গেছে। ধাপ ৩, ৪, ৫, ৬-এর পর আলাদা আলাদা suite চালাও। Lazy class-এ test না থাকলে আগে একটা characterisation test লেখো, trivial হলেও।

বড় real-life example

এখন পুরো committee গল্পটা TypeScript-এ। এই হলো one-member Library Committee, সব paperwork সমেত, আর cultural committee যাকে চিঠি লিখতে হয়:

// BEFORE — a committee of one, with full paperwork
class LibraryCommittee {
  private member = "Nikhil";
  private newArrivals: string[] = [];
 
  recordArrival(book: string): void {
    this.newArrivals.push(book);
  }
 
  monthlyNoticeText(): string {
    return `New arrivals (by ${this.member}): ${this.newArrivals.join(", ")}`;
  }
}
 
class CulturalCommittee {
  private boardNotices: string[] = [];
  private library = new LibraryCommittee();
 
  requestArrival(book: string): void {
    this.library.recordArrival(book); // formal letter to the committee
  }
 
  pinMonthlyNotices(): string[] {
    // formal reply received, then pinned
    this.boardNotices.push(this.library.monthlyNoticeText());
    return [...this.boardNotices];
  }
}

Checklist-এ LibraryCommittee check করো: একটাই client (CulturalCommittee), দুটো ছোট member, নিজে কোনো সিদ্ধান্ত নেয় না — নাম store করে, comma দিয়ে join করে। প্রতিটা operation forwarded call। Figure 4-এর quadrant-এ সে inline-it কোণে বসা। মিলিয়ে দাও:

// AFTER — Nikhil joins the cultural committee; the paperwork vanishes
class CulturalCommittee {
  private boardNotices: string[] = [];
  private newArrivals: string[] = [];
  private arrivalsInCharge = "Nikhil";
 
  recordArrival(book: string): void {
    this.newArrivals.push(book);
  }
 
  pinMonthlyNotices(): string[] {
    this.boardNotices.push(
      `New arrivals (by ${this.arrivalsInCharge}): ${this.newArrivals.join(", ")}`,
    );
    return [...this.boardNotices];
  }
}
 
// Callers now write:
//   committee.recordArrival("Malgudi Days");
//   committee.pinMonthlyNotices();

কী কী বাঁচল count করো। একটা class মুছে গেছে। Forwarding method requestArrival মুছে গেছে — caller-রা এখন সরাসরি recordArrival call করে। pinMonthlyNotices পড়তে গেলে আর অন্য file-এ যেতে হয় না। আর inline করার পর কী reveal হলো দেখো: duty এখন cultural committee-র list-এ একটা সৎ লাইন — ঠিক যেমনটা principal চেয়েছিলেন।

একটা নতুন programmer যখন একটা notice-pinning call trace করতে বসে তার cost:

চিত্র ১০: একটা call trace করতে পাঠকের কী লাগে — file, hop, নাম, merge-এর আগে আর পরে

একটু গভীরে: Figure 10-এ যা অর্ধেক হলো তার research নাম হলো navigation cost বা defect of locality। Program-comprehension study বলছে, understanding time বাড়ে যত বেশি delocalised fragment পাঠককে জোড়া লাগাতে হয় — প্রতিটা extra file মিলিয়ে ফেলার chance প্রায় দ্বিগুণ করে। Middle Man class coupling কমায় না — সে coupling লুকায়CulturalCommittee merge-এর আগেও arrivals data-র সাথে fully coupled ছিল — dependency শুধু একটা extra node দিয়ে যাচ্ছিল। Inline করলে সেই সত্যিকারের coupling দেখা যায় আর সস্তাও হয়।

C# আর Python-এও একই refactoring

একই merge C#-এ, courier flavor-এ। ConsignmentNumber একটা string wrap করে আর pure ceremony যোগ করে:

// BEFORE
class ConsignmentNumber
{
    public string Value { get; }
    public ConsignmentNumber(string value) => Value = value;
    public string AsText() => Value; // returns what you gave it
}
 
class Parcel
{
    private readonly ConsignmentNumber _number;
    public Parcel(string number) => _number = new ConsignmentNumber(number);
 
    public string Label() => $"Consignment: {_number.AsText()}";
}
// AFTER
class Parcel
{
    private readonly string _consignmentNumber;
    public Parcel(string number) => _consignmentNumber = number;
 
    public string Label() => $"Consignment: {_consignmentNumber}";
}

সর্বত্র copy করার আগে একটা সৎ সাবধানবাণী: ConsignmentNumber-এর মতো wrapper তখনই lazy যখন সে কিছুই করে না। যদি সে format validate করত ("CN-" prefix, বারোটা digit) বা phone number যেখানে consignment number দরকার সেখানে দেওয়া আটকাত — তাহলে সে Primitive Obsession আটকাত আর রাখাই উচিত। Costume টা inline করো, bodyguard-কে না।

Python-এ একই merge, miniature-এ:

# BEFORE — one number in a costume
class LateFineRule:
    def __init__(self):
        self.fine_per_day = 2
 
    def fine_for(self, days_late):
        return days_late * self.fine_per_day
 
class Library:
    def __init__(self):
        self._rule = LateFineRule()
 
    def total_fine(self, days_late):
        return self._rule.fine_for(days_late)  # hop
 
# AFTER — the costume comes off
class Library:
    def __init__(self):
        self._fine_per_day = 2
 
    def total_fine(self, days_late):
        return days_late * self._fine_per_day

IDE support

ToolSupportকীভাবে
IntelliJ IDEA / JetBrains familyStrongInline (Ctrl+Alt+N) methods আর বেশি কিছুর জন্য; field-এর জন্য Move-এর সাথে মেলাও
JetBrains Rider / ReSharper (C#)StrongInline Method আর Inline Variable এক keystroke-এ; class-level merge সাত ধাপেই হয়
Visual Studio (plain)ManualInline Temporary Variable আছে; পুরো class inline সেই সাত ধাপ
VS Code (TypeScript)Manual"Find All References" plus rename; field by field, method by method মেলাও

Inline Class সম্পর্কে একটা সুখের কথা: যে class সরাচ্ছো সে definition-এ ছোট, তাই fully manual version-ও সাধারণত পনের মিনিটের কাজ — যদি test দিয়ে ধাপে ধাপে করো।

সুবিধা আর ঝুঁকি

সুবিধাঝুঁকি / মূল্য
ReadabilityCaller সরাসরি পড়ে যায়, দ্বিতীয় file-এ ঘুরতে হয় নাHost আগে থেকেই বড় হলে merge Large Class-এর দিকে ঠেলতে পারে
Simplicityএকটা কম নাম, file, constructor, আর forwarding layer maintain করতে হয়বেশ কয়েকটা client ব্যবহার করা class inline করলে তারা host-এর মাধ্যমে re-couple হয়
HonestyCode-এ একটা one-liner-কে grand concept ভান করা বন্ধ হয়Type-safety wrapper inline করলে real bug ফিরে আসতে পারে
MomentumHost-এ duplication আর পরের useful refactoring reveal করেMerge-এর সময় subtle initialisation-order change ঢুকতে পারে — প্রতিটা ধাপে test করো
Design flowখারাপ boundary গলিয়ে ভালো seam-এ আবার split করার সুযোগ দেয়সত্যিই বড় হওয়ার পথে ছিল এমন class inline করলে পরের মাসে churn

Seesaw: Inline Class আর Extract Class। দুটো seat-এর plank মনে রাখো। Extract Class এক দিকে — "এই class অনেক বেশি করছে"। Inline Class অন্য দিকে — "এই class অনেক কম করছে"। Healthy codebase জীবনে দুদিকেই যায়: concept বড় হলে extract হয়, weight চলে গেলে fold back হয়। Library Committee নিজেই এই seesaw চড়েছে — book fair বড় ছিল তখন general school duty থেকে extract হয়েছিল, weight চলে যাওয়ার পর inline হয়েছে। কোনো দিকই হার না। একমাত্র ভুল হলো না নড়া — বড় class রাখো laziness থেকে, বা empty class রাখো sentiment থেকে। প্রতিটা class-কে আজকের responsibility দিয়ে judge করো, গতকালের plan দিয়ে না।

কোন smell সারায়?

SmellInline Class কীভাবে সাহায্য করে
Lazy Classসরাসরি cure — যে class rent দিচ্ছে না সে merge হয়ে যায়
Middle Manশুধু forward করে এমন class তার একমাত্র client-এ ভেঙে পড়ে
Speculative Generality"একদিন হয়তো লাগবে" class সরিয়ে দাও যতক্ষণ সত্যিকার প্রয়োজন না হয়
Shotgun SurgeryOver-split micro-class মিলে যায়, এক change এক জায়গায়
Large ClassIndirect help — inline ভালো seam-এ re-split করার staging step হতে পারে

Quick revision box

+--------------------------------------------------------------+
|                   INLINE CLASS — CHEAT CARD                  |
+--------------------------------------------------------------+
| Story    : one-member Library Committee -> merge into the    |
|            cultural committee, delete the paperwork          |
| Problem  : a class that does too little but still costs a    |
|            name, a file, and a hop on every call             |
| Smell    : Lazy Class (main), Middle Man, Speculative Gen.   |
| Fix      : verify lazy -> Move Field x N -> Move Method x N  |
|            -> redirect references -> DELETE the shell        |
| Test     : after every move; watch initialisation order      |
| Keep it  : type-safety wrappers, multi-client classes,       |
|            genuinely scheduled growth                        |
| Inverse  : Extract Class (the other end of the seesaw)       |
+--------------------------------------------------------------+

Practice exercise

ধরো এই ছোট class টা একটু সন্দেহজনক দেখাচ্ছে। তার fate decide করো, তারপর কাজ করো।

class HouseColour {
  constructor(private colour: string) {}
  get(): string {
    return this.colour;
  }
}
 
class SchoolHouse {
  private colour: HouseColour;
 
  constructor(
    public name: string,
    colour: string,
  ) {
    this.colour = new HouseColour(colour);
  }
 
  banner(): string {
    return `${this.name} House — wear ${this.colour.get()} on sports day`;
  }
}

তোমার কাজ:

  1. HouseColour-এ laziness checklist চালাও: কতটা member? কোনো সিদ্ধান্ত আছে? কতটা client? না থাকলে কী হারাবে? তারপর Figure 4-এর quadrant-এ place করো।
  2. Safe ধাপে inline করো: field SchoolHouse-এ নিয়ে যাও, get() call collapse করো, reference redirect করো, class মুছো। আগে একটা test লেখো: new SchoolHouse("Red", "red").banner() "Red House — wear red on sports day" দিতে হবে, আর প্রতিটা ধাপের পরেও pass করতে হবে।
  3. Inline করার পর SchoolHouse পড়ো। আগের চেয়ে সহজ? banner() বুঝতে একজন নতুন student-কে কতটা file খুলতে হবে — আগে আর পরে — Figure 10-এর সাথে মেলাও।
  4. Bonus question: এখন imagine করো HouseColour-এ একটা check আছে — শুধু "red", "blue", "green", "yellow" allow, নইলে error। তাহলেও কি inline করতে? কোন smell কি ফিরে আসত?
  5. Final seesaw question: এক বাক্যে লেখো (a) এমন একটা situation যেখানে এই মাসে class extract করতে, আর (b) এমন একটা situation যেখানে inline করতে — prove করো তুমি seesaw দুদিকেই চড়তে পারো।

ধাপ ৪ দেখে যদি বললে "রাখো — Primitive Obsession আটকাচ্ছে", তাহলে এই refactoring-এর সবচেয়ে বড় lesson শিখে ফেলেছ। ব্যাপারটা হলো, ছোট class মানেই lazy না — শুধু অকেজো class-ই lazy। রুবেলের committee ভেঙেছিল তার ছোট হওয়ার কারণে না — বরং কারণ তাকে ছাড়া একটুও কিছু হারাত না। এটাই test, আর এখন এটা তোমার।

সচরাচর জিজ্ঞাসা

Inline Class refactoring মানে আসলে কী?
Inline Class মানে হলো — একটা class যে প্রায় কিছুই করে না, তার সব field আর method তার user class-এর মধ্যে নিয়ে যাও, তারপর খালি class টা মুছে দাও। Program আগের মতোই কাজ করে, শুধু একটা অকারণ layer সরে যায়।
কোন class টা inline করা দরকার সেটা বুঝবো কীভাবে?
দেখো সেই class-এ মাত্র এক-দুটো member আছে কিনা, নিজে কোনো কাজ করে না শুধু forward করে, আর মাত্র একটা client আছে কিনা। নিজেকে জিজ্ঞেস করো — এই class না থাকলে কি কিছু হারাবো? উত্তর যদি 'না' হয়, তাহলে এটা Lazy Class — inline করে দাও।
Inline Class কি Extract Class-এর উল্টো?
হ্যাঁ, একদম। Extract Class ব্যবহার করো যখন একটা class অনেক বেশি কাজ করছে। আর Inline Class ব্যবহার করো যখন একটা class অনেক কম কাজ করছে। দুটো একটা seesaw-এর দুই দিক। Codebase বদলায়, দায়িত্ব এদিক-ওদিক যায় — কখনো extract করতে হয়, কখনো inline করতে হয়। দুটোই স্বাভাবিক।
কখন ছোট class টা inline করবো না?
যদি class টা type safety দেয় — যেমন TrackingCode wrapper সাধারণ string-এর সাথে মিলিয়ে ফেলা আটকায় — তাহলে রাখো। যদি একাধিক client ব্যবহার করে, রাখো। যদি সত্যিই শীঘ্রই বড় হবে, রাখো। ছোট হলেই lazy না — কী আটকাচ্ছে সেটা দেখো।
Inline Class করার ধাপগুলো কী কী?
প্রথমে confirm করো class-এ কম responsibility আছে আর সাধারণত একটাই client। তারপর Move Field দিয়ে প্রতিটা field host-এ নিয়ে যাও। Move Method দিয়ে প্রতিটা method নিয়ে যাও। বাকি সব reference host-এ point করো। তারপর খালি class টা মুছে দাও। প্রতিটা ছোট পদক্ষেপের পর compile করো আর test চালাও।

আরো দেখো

সম্পর্কিত পাঠ

Extract Class: অতিরিক্ত কাজে ডুবে যাওয়া class-কে একটু সাহায্য করো

Extract Class refactoring শেখো একটা মজার school office-এর গল্পের মাধ্যমে। একটা overloaded class-কে দুটো focused class-এ ভাগ করো — প্রতিটার একটাই কাজ।

আরও পড়ুন

Move Method: কাজটা সেই class-এ নিয়ে যাও যেখানে সে আসলে থাকে

একটা স্কুলের গল্পের মাধ্যমে Move Method রিফ্যাক্টরিং শেখো। যে class-এর data method-টা সবচেয়ে বেশি ব্যবহার করে, সেখানেই সরিয়ে নাও — যাতে behaviour আর data একসাথে থাকে।

আরও পড়ুন

Move Field: ডেটা রাখো যেখানে সে কাজে লাগে

Move Field শেখো একটা মজাদার স্কুলের গল্প দিয়ে। ডেটাকে সেই class-এ সরাও যেটা আসলে ওই ডেটা ব্যবহার করে, যাতে state আর behaviour একসাথে বাস করতে পারে।

আরও পড়ুন

Lazy Class: যে চাকরির কাজ শুধু একটা বাটন চাপা

Lazy Class code smell শিখো একটা মজার গল্পের মাধ্যমে। কোন class-গুলো টিকে থাকার যোগ্যতা রাখে না সেটা বুঝতে পারবে, আর Inline Class দিয়ে সেগুলো ঠিক করতে পারবে।

আরও পড়ুন