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

Extract Method: একটা বিশাল ফাংশনকে ছোট ছোট নামওয়ালা helper-এ ভাগ করো

Extract Method ধাপে ধাপে শিখে নাও। একটা লম্বা ফাংশন থেকে এলোমেলো block বের করে তাকে একটা পরিষ্কার নাম দাও, আর তোমার কোডকে একটা সহজ to-do লিস্টের মতো পড়ার যোগ্য করে তোলো।

23 মিনিট আপডেট: June 11, 2026beginner
refactoringcomposing methodsextract methodextract functionclean codelong method

বিশাল বিয়ের কাজের লিস্ট 📝

ধরো তোমার বড় ভাই তারিকের বিয়ে আর দুই মাস পরে। বাড়িতে উৎসবের আমেজ। নানি একটা বড় খাতা নিয়ে একটানা লিখতে শুরু করলেন। কমিউনিটি সেন্টার বুক করো। ক্যাটারার ঠিক করো। ২০০ কেজি চাল অর্ডার করো। গাঁদাফুল কিনো। ব্যান্ড ঠিক করো। দাওয়াতনামা ছাপাও। বরযাত্রীর জন্য মাইক্রোবাস ঠিক করো। গেস্টদের জন্য ফিরতি উপহার কিনো। লিস্ট বাড়তেই থাকে। শুক্রবার রাতের মধ্যে একশো বিশটা কাজ, সব এলোমেলোভাবে, নানি যখন যেটা মনে পড়েছে সেভাবে লেখা।

এখন শুরু হলো ঝামেলা। রুবেল মামা খাবারের দায়িত্বে। খাবারের কাজগুলো দেখতে চান। কিন্তু দশটা লাইন খুঁজতে পুরো একশো বিশটা লাইন পড়তে হচ্ছে। সুমাইয়া আপু সাজসজ্জার দায়িত্বে। ফুলের কাজগুলো মাইক্রোবাস বুকিং আর কার্ড ছাপার মাঝখানে ছড়িয়ে আছে। জামাল চাচা বারবার জিজ্ঞেস করছেন, "ব্যান্ড বুক হয়েছে কি?" আর পুরো খাতা না পড়ে কেউ উত্তর দিতে পারছে না। সবাই সব পড়ছে, সবাই বিভ্রান্ত। দুটো কাজ দুইবার হয়ে গেছে। তিনটা কাজ কেউ করেইনি। বিয়ে ছয় সপ্তাহ দূরে আর পরিবারের WhatsApp গ্রুপ আগুনে।

তখন নানা চুপচাপ একটা বুদ্ধিমানের কাজ করলেন। রোববার সকালে তিনি খাতা, একটা কাঁচি আর পাঁচটা নতুন পাতা নিয়ে বসলেন। বিশাল লিস্টটাকে ছোট ছোট, নামওয়ালা লিস্টে ভাগ করলেন। একটা পাতায় লিখলেন "খাবারের লিস্ট — রুবেল"। একটায় "সাজসজ্জার লিস্ট — সুমাইয়া"। একটায় "যানবাহনের লিস্ট — জামাল"। একটায় "দাওয়াতনামা — নানি"। একটায় "উপহার — তারিকের আব্বু"। তারপর একটা নতুন মাস্টার পাতা বানালেন মাত্র পাঁচটা লাইন দিয়ে: খাবার, সাজসজ্জা, যানবাহন, দাওয়াতনামা, উপহার — প্রতিটা লাইনে দায়িত্বশীলের নাম।

পরিবর্তনটা জাদুর মতো। যে কেউ মাস্টার পাতাটা দশ সেকেন্ডে পড়ে পুরো পরিকল্পনা বুঝতে পারছে। রুবেল মামা শুধু তাঁর খাবারের পাতা খুলে শুধু নিজের কাজ দেখছেন। জামাল চাচা ব্যান্ডের কথা জিজ্ঞেস করলে নানা এক লাইন দেখান: "সংগীত সাজসজ্জার নিচে, সুমাইয়াকে জিজ্ঞেস করো।" ব্যস।

একটা গুরুত্বপূর্ণ ব্যাপার খেয়াল করো। মোট কাজের পরিমাণ বদলায়নি। একই ১২০টা কাজ এখনো আছে। চালের অর্ডার কেউ বাতিল করেনি। কিন্তু সংগঠন বদলে গেছে, আর হঠাৎ সব পরিষ্কার, সব খুঁজে পাওয়া যাচ্ছে, প্রতিটা মানুষ তার কাজ জানছে।

চিত্র ১: পরিবারের যাত্রা — একটা বিশাল লিস্ট থেকে ছোট ছোট নামওয়ালা লিস্টে

Extract Method রিফ্যাক্টরিং code-এর সাথে ঠিক এটাই করে। আর আমাদের সোনার কথাটা মনে রেখো: রিফ্যাক্টরিং মানে code-এর বাইরের আচরণ না বদলে ভেতরটা উন্নত করা। প্রোগ্রাম আগের মতোই কাজ করে। শুধু সাজানোটা ভালো হয়। নানার কাঁচিটা মাথায় রাখো। বাকি পোস্টে আমরা কোডে সেটা ব্যবহার করব।

Extract Method কী?

Extract Method হলো দুনিয়ার সবচেয়ে বিখ্যাত রিফ্যাক্টরিংগুলোর একটা। সহজ ভাষায় বলতে গেলে:

একটা লম্বা ফাংশনের ভেতরে তুমি এমন একটা code block খুঁজে পাও যেটা একটা পরিষ্কার কাজ করে। সেই block-টা একটা নতুন ফাংশনে সরিয়ে নাও। নতুন ফাংশনটাকে এমন একটা নাম দাও যেটা বলে সে কী করে। তারপর পুরোনো জায়গায় শুধু নতুন ফাংশনটাকে call করো।

লম্বা ফাংশনটা ছোট হয়ে যায়। এখন সেটা নানার মাস্টার পাতার মতো দেখায়: পরিষ্কার ধাপের একটা ছোট লিস্ট। প্রতিটা ধাপের একটা নাম আছে। কোনো পাঠক বিস্তারিত জানতে চাইলে সেই একটা helper ফাংশন খোলে — ঠিক যেমন সুমাইয়া আপু শুধু সাজসজ্জার পাতা খোলেন। বিস্তারিত না চাইলে নামটাই গল্পটা বলে দেয়।

💡

এক লাইনে মনে রাখার উপায়: Extract Method = একটা টুকরো কেটে নাও, তাকে নাম দাও, পুরোনো জায়গায় call করো। আচরণ একই থাকে; শুধু পড়তে সহজ হয়।

নামটা নিয়ে একটু কথা। Martin Fowler ক্লাসিক বই Refactoring লিখেছেন। প্রথম সংস্করণে (১৯৯৯) এই রিফ্যাক্টরিংকে বলা হতো Extract Method। দ্বিতীয় সংস্করণে (২০১৮) তিনি নাম বদলে রাখেন Extract Function, কারণ এই কৌশল শুধু class-এর ভেতরের method-এর জন্য না, যেকোনো function-এর জন্যও কাজ করে। তাই refactoring.com-এ "Extract Function" আর Refactoring.Guru বা IDE মেনুতে "Extract Method" দেখলে বিভ্রান্ত হবে না। এগুলো একই রিফ্যাক্টরিং, দুটো আলাদা নামে। এই পোস্টে আমরা Extract Method বলব, কারণ বেশিরভাগ IDE মেনুতে এখনো এটাই লেখা।

Fowler এই রিফ্যাক্টরিং নিয়ে সুন্দর একটা কথা বলেন: এটা তিনি সবচেয়ে বেশি ব্যবহার করেন। কোনো code বুঝতে কষ্ট হলে তিনি সেটা বের করে তার উদ্দেশ্য অনুযায়ী নাম দেন। নামটা চিরকালের জন্য ব্যাখ্যা করে দেয়। তাঁর নিয়মটা সাহসী: extract করার সিদ্ধান্ত দৈর্ঘ্যের ব্যাপার না, এটা intention আর implementation-এর ফাঁকের ব্যাপার। যেই মুহূর্তে তোমাকে ভাবতে হয় "এই অংশটা আসলে কী করছে?", সেই অংশটার নিজস্ব একটা নাম পাওয়া উচিত।

এখানে একটা ছোট্ট মানচিত্রে পুরো ধারণাটা। এখন একবার দেখো, পোস্ট শেষ হলে আবার দেখো — দ্বিতীয়বার দেখলে পুরনো বন্ধুর মতো লাগবে।

চিত্র ২: Extract Method একটা mind map-এ — কখন করবে, কীভাবে করবে, কী সারায়

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

কীভাবে বুঝবে একটা ফাংশন Extract Method চাইছে? এই চিহ্নগুলো দেখো। প্রতিটা চিহ্ন নানির নোটবুকের একটা পাতার মতো হাত নাড়ছে।

  1. ফাংশনটা লম্বা। পড়তে scroll করতে হলে, ফাংশনটা সম্ভবত একসাথে অনেক কাজ করছে। এটা ক্লাসিক Long Method smell, আর Extract Method হলো তার প্রধান ওষুধ। নানির খাতা লম্বা ছিল কারণ খাবার, সাজসজ্জা, যানবাহন সব এক জায়গায় গাদাগাদি ছিল।
  2. Comment গুলো section heading-এর মতো কাজ করছে। একটা ফাংশনের ভেতরে // মোট হিসাব করো, // রসিদ প্রিন্ট করো, // ইনপুট যাচাই করো দেখলে বুঝবে প্রতিটা comment আসলে ঘোষণা করছে, "এখানে একটা method জন্ম নিতে চাইছে!" এটা Comments smell। নানা আক্ষরিক অর্থেই প্রতিটা section heading-কে একটা পাতার শিরোনাম বানিয়েছিলেন।
  3. একই block অনেক জায়গায় আছে। তিনটা ফাংশনে একটা হিসাব copy-paste করলে সেটা Duplicate Code। block-টা একবার extract করো, নাম দাও, আর তিনটা জায়গা থেকে call করো। ভাবো চালের অর্ডার যদি তিনটা আলাদা পাতায় লেখা থাকত — একটায় পরিমাণ বদলালে বাকি দুটো চুপচাপ ভুল থেকে যেত।
  4. বড় আর ছোট ধারণা মিশে গেছে। একটা লাইনে "invoice পাঠাও" (বড় ধারণা) আর পরের দশটা লাইনে string formatting-এর খুঁটিনাটি (ছোট বিষয়)। পাঠক দুই স্তর পার হতে হতে ক্লান্ত হয়ে পড়ে। মাস্টার পাতায় কখনো গাঁদাফুলের সংখ্যা উল্লেখ নেই; শুধু আছে "সাজসজ্জা — সুমাইয়া"। বিস্তারিত extract করলে একটা মসৃণ পড়ার মাত্রা ফিরে আসে।
  5. একটা টুকরো আলাদাভাবে test করা যাচ্ছে না। একটা ফাংশনের মাঝখানে চাপা পড়া হিসাব আলাদাভাবে unit test করা যায় না। একবার extract হলে সেটা test করা যায়, পুনরায় ব্যবহার করা যায়, এমনকি subclass-এ override করা যায়। রুবেল মামা পুরো পরিবার একসাথে না বসেও নিজের catering list যাচাই করতে পারেন।

একটা সাধারণ লম্বা ফাংশনের ভেতরে আসলে কী থাকে? খুলে দেখলে সাধারণত কিছু আলাদা কাজ একসাথে আটকানো পাওয়া যায়:

চিত্র ৩: একটা সাধারণ long method হলো একই body-তে আটকানো কয়েকটা আলাদা কাজ

সেই pie-এর প্রতিটা টুকরো তার নিজস্ব নামওয়ালা method পাওয়ার দাবিদার। Validation হয় validateOrder। Calculation হয় calculateTotal। Printing হয় printReceipt। Saving হয় saveToDatabase। চারটা টুকরো, চারটা পাতা, একটা ছোট master ফাংশন।

এই রিফ্যাক্টরিংয়ের সবচেয়ে কঠিন আর সবচেয়ে মূল্যবান অংশ হলো নাম বেছে নেওয়া। তোমাকে জবাব দিতে হবে, "এই block-টা আসলে কী করছে?" নাম খুঁজে না পেলে সেটাও দরকারি তথ্য — হয়তো block-টা একটা কাজ না, অনেক কিছু মিলিয়ে আছে, ভিন্ন সীমানা বেছে নেওয়া উচিত। নানা কখনো "বিবিধ জিনিস — যে পারে সে" নামে কোনো পাতা বানাননি। প্রতিটা পাতার একটা সৎ মালিক আর একটা সৎ শিরোনাম ছিল।

কলেজের কথা: method size নিয়ে গবেষণা সবসময় একটাই দিক দেখায়। বড় codebase-এর গবেষণায় (বিখ্যাত open-source Java আর C# system-এর বিশ্লেষণ সহ) দেখা গেছে অনেক লম্বা method-এ বেশি bug থাকে আর বুঝতে বেশি সময় লাগে। ছোট ভালো-নামওয়ালা ফাংশনে ভরা codebase-এ দলগুলো দ্রুত bug খুঁজে পায়। Cognitive psychology ব্যাখ্যা করে কেন: মানুষের working memory একসাথে মাত্র কয়েকটা "chunk" ধরতে পারে (George Miller-এর বিখ্যাত 7±2 তত্ত্ব)। ৬০ লাইনের method-এ একসাথে ডজনখানেক তথ্য মনে রাখতে হয়। ৫ লাইনের method যেটা named helper call করে সেখানে প্রতিটা helper একটাই chunk। Extract Method আক্ষরিক অর্থেই source code-এ chunking প্রয়োগ। Robert C. Martin-এর Clean Code একই ধারণাকে চরম সীমায় নিয়ে যায় — "ফাংশন একটাই কাজ করবে" — আর Fowler-এর নিজের নিয়মটা একটু নরম কিন্তু মর্মে একই: যখনই intention আর implementation আলাদা হয়ে পড়ে, extract করো।

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

এখানে একটা ছোট্ট উদাহরণ। এই ফাংশনটা বিয়ের বিল প্রিন্ট করে। "before" পড়ো আর দেখো মাঝখানে তোমার চোখ কতটা ধীর হয়ে যায়।

// BEFORE: one function, three jobs glued together
function printWeddingBill(booking: Booking): void {
  console.log("=== SHAADI BILL ===");
  console.log(`Family: ${booking.familyName}`);
 
  // calculate total amount
  let total = 0;
  for (const item of booking.items) {
    total += item.price * item.quantity;
  }
  total -= booking.advancePaid;
 
  console.log(`Amount due: Rs. ${total.toFixed(2)}`);
  console.log(`Pay by: ${booking.dueDate.toDateString()}`);
}
// AFTER: the function reads like Nana's master page
function printWeddingBill(booking: Booking): void {
  printHeader(booking);
  const total = calculateAmountDue(booking);
  printFooter(booking, total);
}
 
function printHeader(booking: Booking): void {
  console.log("=== SHAADI BILL ===");
  console.log(`Family: ${booking.familyName}`);
}
 
function calculateAmountDue(booking: Booking): number {
  let total = 0;
  for (const item of booking.items) {
    total += item.price * item.quantity;
  }
  return total - booking.advancePaid;
}
 
function printFooter(booking: Booking, total: number): void {
  console.log(`Amount due: Rs. ${total.toFixed(2)}`);
  console.log(`Pay by: ${booking.dueDate.toDateString()}`);
}

// calculate total amount comment-টা গায়েব হয়ে গেছে। কারণ calculateAmountDue method-এর নামটাই এখন একই কথা বলছে। আর নাম কখনো comment-এর মতো পুরনো হয়ে ভুল হয়ে যায় না।

রিফ্যাক্টরিংয়ের পুরো কাজের ছন্দটা একটা লাইনের তীরে ধরা যায়:

চিত্র ৪: Extract Method-এর ছন্দ — খোঁজো, নাম দাও, extract করো, যাচাই করো

তীরগুলো আবার পড়ো। Tests still green সাজসজ্জা না। এটা প্রতিটা extraction-এর শেষ রেখা। Test সবুজ না হলে তুমি রিফ্যাক্টর করোনি — কিছু ভেঙেছ।

ক্লাসিক উদাহরণ: printOwing 🧾

প্রতিটা রিফ্যাক্টরিং বইয়ের ছাত্র একই বিখ্যাত উদাহরণের সাথে পরিচিত হয়। তোমারও হওয়া উচিত, কারণ interviewer আর senior-রা এটার কথা তুলবেন। Fowler-এর বইয়ে একটা Invoice class-এ printOwing নামের একটা method আছে যেটা banner প্রিন্ট করে, বকেয়া পরিমাণ হিসাব করে, আর বিস্তারিত প্রিন্ট করে — একটা body-তে তিনটা কাজ, আমাদের বিয়ের বিলের মতোই।

// BEFORE: Fowler's classic shape
class Invoice {
  constructor(public orders: Order[], public customer: string) {}
 
  printOwing(): void {
    console.log("***********************");
    console.log("**** Customer Owes ****");
    console.log("***********************");
 
    let outstanding = 0;
    for (const order of this.orders) {
      outstanding += order.amount;
    }
 
    console.log(`name: ${this.customer}`);
    console.log(`amount: ${outstanding}`);
  }
}

তিনটা extraction-এর পর — printBanner, calculateOutstanding, আর printDetails — class-টা এরকম দেখায়:

// AFTER: three named helpers, one readable master method
class Invoice {
  constructor(public orders: Order[], public customer: string) {}
 
  printOwing(): void {
    this.printBanner();
    const outstanding = this.calculateOutstanding();
    this.printDetails(outstanding);
  }
 
  private printBanner(): void {
    console.log("***********************");
    console.log("**** Customer Owes ****");
    console.log("***********************");
  }
 
  private calculateOutstanding(): number {
    let outstanding = 0;
    for (const order of this.orders) {
      outstanding += order.amount;
    }
    return outstanding;
  }
 
  private printDetails(outstanding: number): void {
    console.log(`name: ${this.customer}`);
    console.log(`amount: ${outstanding}`);
  }
}

রিফ্যাক্টরিংয়ের পরে class-এর গঠনটা আঁকার যোগ্য। একটা public method গল্পটা বলে; তিনটা private helper বিস্তারিত ধরে রাখে:

চিত্র ৫: Extraction-এর পরে Invoice class — একজন গল্পকার, তিনটা সাহায্যকারী

আর runtime-এ call-টা একটা ভদ্র ছোট্ট কথোপকথন। printOwing প্রতিটা helper-কে তার একটাই কাজ করতে বলে আর উত্তরের জন্য অপেক্ষা করে — ঠিক যেমন নানা রুবেল মামাকে catering-এর খবর জিজ্ঞেস করলে এক লাইনের উত্তর পান:

চিত্র ৬: printOwing-এর এক call — master জিজ্ঞেস করে, helper উত্তর দেয়

কথোপকথনের আকারটা দেখো। Master কখনো helper-এর কাজের মাঝখানে হাত ঢোকায় না। Input পাঠায়, output নেয়, এগিয়ে যায়। এই পরিষ্কার handshake-ই প্রতিটা helper-কে আলাদাভাবে testable করে।

ধাপে ধাপে, নিরাপদ পথে 🪜

রিফ্যাক্টরিং এক লাফে করার জিনিস না। আমরা ছোট ছোট পদক্ষেপে এগোই, আর প্রতিটা ধাপের পরে test চালাই। চলো বিয়ের বিল থেকে calculateAmountDue আস্তে আস্তে extract করি।

ধাপ ১ — fragment বেছে নাও আর নাম খোঁজো। মোট হিসাব করার loop-টা বেছে নিই। জিজ্ঞেস করি: এটা কী করে? পরিবারকে এখনো কত টাকা দিতে হবে সেটা হিসাব করে। নাম: calculateAmountDueকী করছে সেই অনুযায়ী নাম দাও, কখনো কীভাবে করছে সেই অনুযায়ী না। (loopOverItems একটা খারাপ নাম হবে — এটা যন্ত্রপাতি বর্ণনা করে, উদ্দেশ্য না।)

ধাপ ২ — নতুন খালি method তৈরি করো আর fragment copy করো। এখনো original থেকে কিছু মুছবে না। শুধু copy করো।

function calculateAmountDue(): number {
  let total = 0;
  for (const item of booking.items) {   // problem: booking is unknown here!
    total += item.price * item.quantity;
  }
  total -= booking.advancePaid;
  return total;
}

Copy-টা compile-ই হয় না। এটা স্বাভাবিক। পরের ধাপগুলো সেটা ঠিক করবে।

ধাপ ৩ — fragment যেসব variable বাইরে থেকে READ করে সেগুলো খোঁজো। সেগুলোকে parameter বানাও। আমাদের fragment booking পড়ছে, যেটা fragment-এর বাইরে declare করা। তাই booking parameter হয়ে যায়।

function calculateAmountDue(booking: Booking): number {
  let total = 0;
  for (const item of booking.items) {
    total += item.price * item.quantity;
  }
  total -= booking.advancePaid;
  return total;
}

ধাপ ৪ — fragment যেসব variable বদলায় যেগুলো পরে ব্যবহার হয় সেগুলো খোঁজো। সেগুলো return করো। Fragment total বদলায়, আর fragment-এর পরের code (footer printing) total ব্যবহার করে। এরকম ঠিক একটাই variable আছে, তাই সেটা return করি। যদি দুটো বা বেশি বদলানো variable পরে ব্যবহার হতো, সরাসরি extraction আটকে যেত — কারণ একটা ফাংশন মাত্র একটা value return করতে পারে। সেক্ষেত্রে আগে Split Temporary Variable বা Replace Temp with Query লাগত, অথবা বড় tool Replace Method with Method Object ব্যবহার করতে হতো।

ধাপ ৫ — fragment-এর ভেতরে জন্ম নেওয়া আর শুধু ভেতরে ব্যবহৃত variable local থেকে যায়। এখানে item শুধু loop-এর ভেতরে আছে, তাই সেটা নতুন method-এ local থেকে যায়। কিছু করার নেই।

ধাপ ৬ — original block-টা একটা call দিয়ে replace করো। এখন original ফাংশন ছোট হয়ে যায়।

function printWeddingBill(booking: Booking): void {
  console.log("=== SHAADI BILL ===");
  console.log(`Family: ${booking.familyName}`);
 
  const total = calculateAmountDue(booking);
 
  console.log(`Amount due: Rs. ${total.toFixed(2)}`);
  console.log(`Pay by: ${booking.dueDate.toDateString()}`);
}

ধাপ ৭ — Compile করো আর সব test চালাও। আগে যে বিল প্রিন্ট হতো এখনো সেটাই হতে হবে, পয়সায় পয়সায় মিলিয়ে। Test fail করলে শেষ ছোট ধাপ undo করো আর আবার চেষ্টা করো। ধাপটা ছোট ছিল, তাই ভুল খুঁজে পাওয়া সহজ।

ধাপ ৮ — আবার দেখো। বারবার করো। হিসাব চলে যাওয়ার পরে দুটো console.log cluster স্পষ্ট দুটো আলাদা কাজ হিসেবে দেখা যাচ্ছে। একই পদ্ধতিতে printHeader আর printFooter extract করো, একটা একটা করে, প্রতিটার পরে test করে।

⚠️

শেষে না, প্রতিটা একক ধাপের পরে test চালাও। পাঁচটা ধাপ করে তারপর test করলে, failure পাঁচটার যেকোনোটায় লুকিয়ে থাকতে পারে। প্রতিটা ধাপের পরে test করলে, failure সরাসরি সেই ধাপটাকেই দেখায়। ছোট পদক্ষেপ আর ঘন ঘন test হলো নিরাপদ রিফ্যাক্টরিংয়ের পুরো রহস্য।

একটা fragment-এর যাত্রা, এলোমেলো থেকে যাচাইকৃত helper পর্যন্ত, পরিষ্কার পর্যায়গুলোর মধ্য দিয়ে যায়। এই ছোট্ট state machine-টা মনে রাখো — এটা পুরো কৌশলের হৃদস্পন্দন:

চিত্র ৭: একটা extraction-এর জীবন — প্রতিটা fragment এই অবস্থাগুলো পার করে

Verified অবস্থায় test fail করলে আতঙ্কিত হবে না, এক ঘণ্টা debug করবে না। একটা ছোট ধাপ undo করো আর আবার চেষ্টা করো। এটাই ছোট পদক্ষেপের সুবিধা।

বাস্তব জীবনের বড় উদাহরণ: কোডে তারিকের বিয়ের পরিকল্পনা

এখন বিয়ের গল্পটা আসল code হিসেবে লিখি। এখানে একটা wedding planning ফাংশন যেভাবে প্রায়ই প্রথমে লেখা হয় — সব একজায়গায়, ঠিক সেই বিশৃঙ্খল শুক্রবার রাতে নানির বিশাল খাতার মতো।

interface Wedding {
  familyName: string;
  guests: { name: string; isVip: boolean }[];
  platePriceRs: number;
  flowerMetres: number;
  ratePerMetreRs: number;
  lightingPackageRs: number;
  budgetRs: number;
}
 
// BEFORE: one giant function, four jobs tangled together
function planWedding(wedding: Wedding): void {
  // print guest summary
  console.log(`Wedding of the ${wedding.familyName} family`);
  let vipCount = 0;
  for (const guest of wedding.guests) {
    if (guest.isVip) vipCount++;
  }
  console.log(`Guests: ${wedding.guests.length}, VIPs: ${vipCount}`);
 
  // calculate catering cost
  let cateringCost = wedding.guests.length * wedding.platePriceRs;
  cateringCost += vipCount * 200; // special thali for VIPs
 
  // calculate decoration cost
  let decorationCost = wedding.flowerMetres * wedding.ratePerMetreRs;
  decorationCost += wedding.lightingPackageRs;
 
  // print budget report
  const totalCost = cateringCost + decorationCost;
  console.log(`Catering: Rs. ${cateringCost}`);
  console.log(`Decoration: Rs. ${decorationCost}`);
  console.log(`Total: Rs. ${totalCost}`);
  if (totalCost > wedding.budgetRs) {
    console.log("Warning: over budget! Talk to the family.");
  }
}

Comment label গুলো গণনা করো: guest summary, catering cost, decoration cost, budget report। চারটা comment মানে চারটা লুকানো method। নানা যেমন প্রতিটা helper-এর জন্য একটা নামওয়ালা পাতা বানিয়েছিলেন, সেভাবে একটা একটা করে মুক্ত করি।

প্রথমে guest গোনার অংশ extract করি। এটা wedding.guests পড়ে আর vipCount তৈরি করে, যেটা পরে ব্যবহার হয়। তাই vipCount return value হয়।

function countVips(wedding: Wedding): number {
  let vipCount = 0;
  for (const guest of wedding.guests) {
    if (guest.isVip) vipCount++;
  }
  return vipCount;
}

Test চালাও। সবুজ। এরপর catering। এটা wedding আর VIP count পড়ে, আর একটা সংখ্যা তৈরি করে।

function calculateCateringCost(wedding: Wedding, vipCount: number): number {
  const baseCost = wedding.guests.length * wedding.platePriceRs;
  const vipExtra = vipCount * 200;
  return baseCost + vipExtra;
}

আবার test। সবুজ। তারপর decoration, তারপর report। চারটা extraction-এর পরে master function এরকম দেখায়।

// AFTER: planWedding reads like the master page of the to-do list
function planWedding(wedding: Wedding): void {
  const vipCount = countVips(wedding);
  printGuestSummary(wedding, vipCount);
  const cateringCost = calculateCateringCost(wedding, vipCount);
  const decorationCost = calculateDecorationCost(wedding);
  printBudgetReport(wedding, cateringCost, decorationCost);
}
 
function printGuestSummary(wedding: Wedding, vipCount: number): void {
  console.log(`Wedding of the ${wedding.familyName} family`);
  console.log(`Guests: ${wedding.guests.length}, VIPs: ${vipCount}`);
}
 
function calculateDecorationCost(wedding: Wedding): number {
  const flowerCost = wedding.flowerMetres * wedding.ratePerMetreRs;
  return flowerCost + wedding.lightingPackageRs;
}
 
function printBudgetReport(
  wedding: Wedding,
  cateringCost: number,
  decorationCost: number
): void {
  const totalCost = cateringCost + decorationCost;
  console.log(`Catering: Rs. ${cateringCost}`);
  console.log(`Decoration: Rs. ${decorationCost}`);
  console.log(`Total: Rs. ${totalCost}`);
  if (totalCost > wedding.budgetRs) {
    console.log("Warning: over budget! Talk to the family.");
  }
}

এখন planWedding দেখো। মাত্র পাঁচটা লাইন। নতুন একজন team member দশ সেকেন্ডে পড়ে পুরো পরিকল্পনা বুঝতে পারবে — VIP গণনা করো, অতিথি দেখাও, খাবারের খরচ করো, সাজসজ্জার খরচ করো, বাজেট রিপোর্ট করো। Catering-এর বিস্তারিত জানতে চাও? শুধু calculateCateringCost খোলো, যেমন রুবেল মামা শুধু তাঁর catering পাতা খোলেন। Decoration-এর অংক unit test করতে চাও? এখন পারবে, কারণ এটা পরিষ্কার input আর একটা output সহ আলাদা ফাংশন। আর প্রতিটা comment label গায়েব হয়ে গেছে — পুরোনো হতে পারে না এমন নামে পরিণত হয়েছে।

একটা দ্বিতীয়, শান্ত সুবিধাও আছে। যখন bug report আসে — ধরো কেউ ফোন করে বলে "decoration cost ভুল দেখাচ্ছে" — তখন আর ২৫ লাইনের জট খুঁজতে হয় না। calculateDecorationCost খোলো, ছয় লাইন, bug-এর লুকানোর জায়গা নেই। Debugging-এর গতিতে পার্থক্যটা কম না:

চিত্র ৮: ছোট নামওয়ালা method bug খোঁজার সময় নাটকীয়ভাবে কমায়

উপরের সংখ্যাগুলো উদাহরণস্বরূপ, কিন্তু প্রতিটা কর্মরত programmer সেই chart-এর আকার চেনেন। বড় method-এ যেকোনো কিছু খুঁজতে সব পড়তে হয় — যেমন রুবেল মামা দশটা খাবারের কাজ খুঁজতে ১২০ লাইন scan করেন। ছোট method-এ সঠিক method-এর নাম সরাসরি সঠিক ছয়টা লাইনে নিয়ে যায়।

C# আর Python-এ একই রিফ্যাক্টরিং

ব্যাপারটা হলো এই ধারণা সব ভাষায় একই। এখানে billing অংশের একটা ছোট C# version।

// BEFORE
public void PrintBill(Booking booking)
{
    Console.WriteLine($"Family: {booking.FamilyName}");
 
    // calculate amount due
    decimal total = 0;
    foreach (var item in booking.Items)
    {
        total += item.Price * item.Quantity;
    }
    total -= booking.AdvancePaid;
 
    Console.WriteLine($"Amount due: Rs. {total:F2}");
}
 
// AFTER
public void PrintBill(Booking booking)
{
    Console.WriteLine($"Family: {booking.FamilyName}");
    decimal total = CalculateAmountDue(booking);
    Console.WriteLine($"Amount due: Rs. {total:F2}");
}
 
private decimal CalculateAmountDue(Booking booking)
{
    decimal total = 0;
    foreach (var item in booking.Items)
    {
        total += item.Price * item.Quantity;
    }
    return total - booking.AdvancePaid;
}

আর Python-এ একই পদক্ষেপ, যেখানে standalone function-গুলো "Extract Function" নামটাকে খুব স্বাভাবিক মনে করায়:

# BEFORE
def print_bill(booking):
    print(f"Family: {booking.family_name}")
 
    # calculate amount due
    total = 0
    for item in booking.items:
        total += item.price * item.quantity
    total -= booking.advance_paid
 
    print(f"Amount due: Rs. {total:.2f}")
 
# AFTER
def print_bill(booking):
    print(f"Family: {booking.family_name}")
    total = calculate_amount_due(booking)
    print(f"Amount due: Rs. {total:.2f}")
 
def calculate_amount_due(booking):
    total = 0
    for item in booking.items:
        total += item.price * item.quantity
    return total - booking.advance_paid

সব জায়গায় একই recipe: বাইরে থেকে পড়া জিনিস parameter হয়, একটা বদলানো-আর-ব্যবহৃত value return হয়, comment method-এর নাম হয়। শুধু পোশাক বদলায়; নাচ একই।

IDE-এর সাহায্য ⚙️

সুখবর: variable bookkeeping হাতে করতে হয় না। আধুনিক editor স্বয়ংক্রিয়ভাবে Extract Method করে দেয়। লাইনগুলো select করো, refactoring trigger করো, নাম টাইপ করো, আর tool নিজেই parameter আর return value বের করে দেয়।

IDEকীভাবে extract করবেShortcut
Visual StudioCode select করো → Edit → Refactor → Extract Method, অথবা Quick ActionsCtrl+R, Ctrl+M (অথবা Ctrl+. তারপর "Extract method")
VS CodeCode select করো → Refactor menu → "Extract to function/method"Ctrl+Shift+R (Refactor) অথবা Ctrl+. (Quick Fix)
IntelliJ IDEA / Rider / অন্য JetBrains IDECode select করো → Refactor → Extract MethodCtrl+Alt+M (Mac-এ Cmd+Option+M)
ReSharper in Visual StudioCode select করো → Refactor This → Extract MethodCtrl+Shift+R তারপর Extract Method বেছে নাও

দুটো ছোট্ট পরামর্শ। প্রথমত, selection cleanly extract না হলে tool সতর্ক করবে — যেমন দুটো বদলানো variable পরে ব্যবহার হলে। সেই সতর্কতা ধাপ ৪-এ শেখা একই নিয়ম শেখাচ্ছে। দ্বিতীয়ত, tool দিয়ে করলেও test চালাও। Tool চমৎকার, কিন্তু তোমার test হলো চূড়ান্ত বিচারক।

Extract করব নাকি ছেড়ে দেব? সিদ্ধান্ত নেওয়া 🤔

প্রতিটা block extract করার যোগ্য না। নানার কাঁচি তোলার আগে দুটো প্রশ্ন করো: এই block পড়তে কতটা কঠিন? আর কত জায়গায় এটা দরকার? উত্তরগুলো block-টাকে একটা সহজ সিদ্ধান্তের মানচিত্রে রাখে:

চিত্র ৯: তোমার code block কোথায় পড়ে সেটাই extract করব কি না সেটা নির্ধারণ করে

মানচিত্রটা এভাবে পড়ো। একটা block যেটা পড়তে কঠিন আর অনেক জায়গায় দরকার সেটা অবশ্যই extract করতে হবে — এক ঘায়ে clarity আর duplication দুটোই ঠিক হয়। পড়তে কঠিন কিন্তু একবারই ব্যবহার হয়? তাও সাধারণত extract করা উচিত, শুধু নামের ব্যাখ্যার শক্তির জন্য। পরিষ্কার one-liner যেটা একবারই ব্যবহার? ছেড়ে দাও। পরিষ্কার block অনেক জায়গায় ব্যবহার হয়? শুধু copy সরাতে extract করো।

এখানে বিয়ের স্বাদে একই বিচারের জন্য একটা দ্রুত রেফারেন্স টেবিল:

পরিস্থিতিবিয়ের সংস্করণসিদ্ধান্ত
কঠিন block, অনেক ফাংশনে ব্যবহারবিল, রিপোর্ট, আর SMS-এ দরকার thali cost নিয়মএখনই extract করো — clarity আর reuse দুটোই
কঠিন block, একবার ব্যবহারএকটা ফাংশনের ভেতরে VIP গোনার loopExtract করো — নামটা চিরকাল ব্যাখ্যা করবে
সহজ লাইন, অনেক জায়গায় ব্যবহারপ্রতিটা পরিমাণে "টাকা" formattingExtract করো — পরে এক জায়গায় বদলাতে পারবে
সহজ লাইন, একবার ব্যবহারএকটা greeting console.logছেড়ে দাও — নাম কিছু যোগ করবে না

কলেজের কথা: Performance কী হবে? তুমি হয়তো ভাবছো বেশি function call মানে ধীর প্রোগ্রাম। Computing-এর পুরনো দিনে মাঝে মাঝে সত্য ছিল। আজকাল optimising compiler আর JIT runtime স্বয়ংক্রিয়ভাবে inlining করে: JVM-এর HotSpot compiler ছোট hot method inline করে, .NET RyuJIT ছোট IL body-র জন্য একই করে, আর C/C++ compiler -O2-এ আক্রমণাত্মকভাবে inline করে। মানে হলো তুমি মানুষের জন্য ছোট পাঠযোগ্য method লেখো, আর machine চুপচাপ নিজের জন্য বড় দ্রুত blob তৈরি করে নেয়। ক্লাসিক engineering পরামর্শ: যে speed তুমি মাপোনি তার জন্য পাঠযোগ্যতা বিসর্জন দিও না। আগে profile করো; bottleneck প্রায় কখনোই function call না।

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

পয়েন্ট
Calling function একটা ছোট, নিজেকে-ব্যাখ্যাকারী summary হয়ে যায় — code to-do list-এর মতো পড়ায়।
ভালো নাম comment-কে replace করে, আর নাম কখনো পুরনো হয়ে ভুল হয় না।
Extracted logic copy-paste-এর বদলে সব জায়গায় পুনরায় ব্যবহার করা যায়।
ছোট ফাংশন unit test করা, debug করা, আর override করা সহজ।
Move Method আর Replace Temp with Query-এর মতো অন্য রিফ্যাক্টরিংয়ের দরজা খুলে দেয়।
⚠️বেশি extraction করলে দুর্বল নামওয়ালা one-line method-এর ঝাঁক তৈরি হয়; পাঠককে একটা সহজ গল্প বুঝতে দশটা জায়গায় লাফ দিতে হয়।
⚠️পরে ব্যবহৃত কয়েকটা variable বদলানো fragment সরাসরি extract করা যায় না — আগে temp ঠিক করো অথবা Replace Method with Method Object ব্যবহার করো।
⚠️অত্যন্ত গরম loop-এ অতিরিক্ত function call সামান্য speed হারাতে পারে — কিন্তু আধুনিক compiler call inline করে, তাই চিন্তা করার আগে measure করো।

কখন ব্যবহার করবে না: fragment-কে সৎ নাম দিতে না পারলে, বা extraction শুধু doStuff1 আর doStuff2 তৈরি করবে, থামো। খারাপ নাম extraction না করার চেয়ে ক্ষতিকর। নানা কখনো "বিভিন্ন জিনিস — যে ফাঁকা আছে" শিরোনামের পাতা বানাতেন না।

Inline Method-এর সাথে দাঁড়িপাল্লা। Extract Method আর Inline Method হলো একদম বিপরীত, দাঁড়িপাল্লার দুই প্রান্তের মতো। Extract Method একটা নামওয়ালা স্তর যোগ করে; Inline Method একটা স্তর সরিয়ে দেয় যেটা আর কাজে আসছে না। কোনো দিকই চিরকালের "সঠিক" না। যদি বেশি eagerly extract করো আর কোনো method-এর body তার নামের চেয়ে পরিষ্কার হয়, inline করে ফিরিয়ে দাও। ফাংশন আবার বেশি লম্বা হলে আবার extract করো। সুস্থ code এই দাঁড়িপাল্লায় উঠানামা করতে থাকে যতক্ষণ না clarity-তে ভারসাম্য আসে।

কোন smell সারায়?

SmellExtract Method কীভাবে সাহায্য করে
Long Methodপ্রধান ওষুধ। বিশালটাকে ছোট ছোট নামওয়ালা ধাপে ভাঙো, বিয়ের লিস্টের মতো।
Commentsএকটা block-কে label করা comment সেই extracted method-এর নাম হয়ে যায়, আর comment মুছে যায়।
Duplicate Codeবারবার আসা block একবার extract করো; প্রতিটা পুরোনো copy একটা এক-লাইনের call হয়ে যায়।
জটিল conditionalcondition আর প্রতিটা branch extract করা হলো Decompose Conditional-এর মূল কথা।

দ্রুত পুনরাবৃত্তির বাক্স

+--------------------------------------------------------------+
|                EXTRACT METHOD — QUICK REVISION               |
+--------------------------------------------------------------+
| WHAT   : Move a code block into a new, well-named function   |
|          and call it from the old place.                     |
| 2ND ED : Fowler now calls it "Extract Function".             |
| WHEN   : Long Method, comment-labelled blocks,               |
|          duplicate blocks, untestable pieces.                |
| STEPS  : 1. Pick fragment, find a what-it-does name          |
|          2. Copy body into new method                        |
|          3. Outside reads  -> parameters                     |
|          4. One changed-and-used value -> return             |
|          5. Inside-only vars stay local                      |
|          6. Replace fragment with a call                     |
|          7. TEST after every step                            |
| INVERSE: Inline Method (the other end of the seesaw)         |
| RULE   : No good name? Then do not extract yet.              |
+--------------------------------------------------------------+

অনুশীলনের কাজ 🏏

তোমার পালা! ধরো এই ফাংশনটা একটা school-এর report card প্রিন্ট করে। ভেতরে তিনটা comment-labelled কাজ লুকিয়ে আছে। সেগুলোকে ভালো-নামওয়ালা method-এ extract করো। মনে রেখো: একটা extraction একবারে, আর প্রতিটা ধাপের পরে test চালানোর ভান করো।

function printReportCard(student: Student): void {
  // print student details
  console.log(`Name: ${student.name}`);
  console.log(`Class: ${student.className}, Roll: ${student.rollNo}`);
 
  // calculate percentage
  let totalMarks = 0;
  for (const subject of student.subjects) {
    totalMarks += subject.marks;
  }
  const percentage = totalMarks / student.subjects.length;
 
  // print result
  console.log(`Percentage: ${percentage.toFixed(1)}%`);
  if (percentage >= 40) {
    console.log("Result: PASS");
  } else {
    console.log("Result: FAIL — meet the class teacher.");
  }
}

একটু ভাবো: মাঝের block student পড়ে আর percentage তৈরি করে — যেটা return value হবে। শেষে printReportCard ঠিক তিনটা লাইনের হওয়ার লক্ষ্য রাখো। শেষ হলে তিনটা জিনিস দেখো, নানার পদ্ধতিতে: master ফাংশন কি পাঁচ সেকেন্ডের summary-র মতো পড়ায়? প্রতিটা comment কি method-এর নামে পরিণত হয়ে গেছে? প্রতিটা helper কি আলাদাভাবে test করা যায়? তিনটাতেই হ্যাঁ হলে — শাবাশ! তুমি দুনিয়ার সবচেয়ে দরকারি রিফ্যাক্টরিং করে ফেলেছ। তারিকের বিয়ের পরিকল্পনাকারী গর্বিত যে তুমি পরিবার কমিটিতে আছো।

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

সহজ ভাষায় Extract Method রিফ্যাক্টরিং কী?
Extract Method মানে হলো একটা লম্বা ফাংশন থেকে একটা code block বের করে নতুন একটা ফাংশনে রাখা, সেই ফাংশনকে একটা পরিষ্কার নাম দেওয়া, আর পুরোনো জায়গা থেকে সেটাকে call করা। code আগের মতোই কাজ করে, কিন্তু পড়তে অনেক সহজ লাগে।
Martin Fowler কেন Extract Method-এর নাম বদলে Extract Function রাখলেন?
তাঁর Refactoring বইয়ের ২য় সংস্করণে Fowler নাম বদলে Extract Function রাখেন, কারণ এই কৌশলটা শুধু class-এর ভেতরের method-এর জন্য না, যেকোনো সাধারণ function-এর জন্যও কাজ করে। দুটো নামই একই রিফ্যাক্টরিং বোঝায়।
কোন code block টা extract করব বুঝব কীভাবে?
এমন একটা block খোঁজো যেটা একটা পরিষ্কার কাজ করে, বিশেষ করে যেটার উপরে কী করছে সেটা বোঝাতে একটা comment লেখা আছে। যদি block-টাকে একটা ভালো নাম দিতে পারো, তাহলে বুঝবে সেটা নিজস্ব একটা method হওয়ার যোগ্য।
Extract Method কি আমার প্রোগ্রামের কাজ বদলে দেয়?
না। রিফ্যাক্টরিং কখনো প্রোগ্রামের বাইরের আচরণ বদলায় না। Extract Method শুধু code-এর ভেতরের সংগঠন বদলায়। তাই প্রতিটা ছোট ধাপের পরে test চালাতে হবে — প্রমাণ করতে হবে কিছু ভাঙেনি।
বেশি বেশি method extract করা কি ক্ষতিকর?
হ্যাঁ। দুর্বল নামওয়ালা ডজন ডজন এক-লাইনের method বানালে পাঠককে সহজ code বুঝতে সব জায়গায় লাফ দিয়ে বেড়াতে হয়। যখন কোনো method-এর body তার নামের চেয়ে বেশি পরিষ্কার, তখন বিপরীত রিফ্যাক্টরিং Inline Method ব্যবহার করে সেটা আবার ভেতরে ঢুকিয়ে দাও।

আরো দেখো

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

Inline Method: যে Shortcut আসলে Shortcut ছিলই না

Inline Method শেখো ধাপে ধাপে। যখন একটা ছোট method-এর body তার নামের চেয়ে বেশি পরিষ্কার, তখন সেই body-টা সরাসরি caller-এ বসিয়ে দাও আর একটা বাড়তি hop সরিয়ে ফেলো।

আরও পড়ুন

Extract Variable: ছোট ছোট নামওয়ালা ধাপে বড় হিসাব সমাধান করো

Extract Variable শেখো ধাপে ধাপে। একটা বিশাল, জটিল expression কে ছোট ছোট নামওয়ালা অংশে ভাগ করো — ঠিক যেভাবে গণিতের খাতায় কাজ দেখাও।

আরও পড়ুন

Replace Temp with Query: তাজা জিজ্ঞেস করো, বাসি চিরকুটে ভরসা করো না

ক্যান্টিনের সিঙ্গারার গল্প দিয়ে Replace Temp with Query বোঝো — TypeScript আর C# উদাহরণ, নিরাপদ ধাপ, আর একটাই সত্যের উৎস।

আরও পড়ুন

Decompose Conditional: জটিল if-কে সহজ নামে ভেঙে ফেলো

Decompose Conditional refactoring শেখো স্কুলের নোটিশের গল্প দিয়ে — সহজ TypeScript ও C# উদাহরণ, নিরাপদ ধাপ, আর IDE shortcut সহ।

আরও পড়ুন