পাঠ ৫০ · ৫৮-এর মধ্যে · মডিউল ১১
Home / Courses / Software Engineering Principles & Git / রিফ্যাক্টরিং টেকনিক

রিফ্যাক্টরিং টেকনিক

Refactoring techniques
৮ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • রিফ্যাক্টরিংয়ের সংজ্ঞা এবং কেন "আচরণ অপরিবর্তিত" এর একমাত্র অলঙ্ঘনীয় শর্ত
  • কেন regression testing ছাড়া রিফ্যাক্টরিং ঝুঁকিপূর্ণ
  • চারটি স্ট্যান্ডার্ড রিফ্যাক্টরিং টেকনিক ও প্রতিটি কোন স্মেল ঠিক করে
  • একটি real Extract Method রিফ্যাক্টরিং, original ও refactored ফাংশনের identical আউটপুট কোড দিয়ে প্রমাণ করা

১ · রিফ্যাক্টরিং কী — এবং সবচেয়ে গুরুত্বপূর্ণ নিয়ম

রিফ্যাক্টরিংRefactoringবিদ্যমান কোডের অভ্যন্তরীণ গঠন পুনর্বিন্যাস করা, বাহ্যিক আচরণ অপরিবর্তিত রেখে। মানে বিদ্যমান কোডের অভ্যন্তরীণ গঠন পুনর্বিন্যাস করা, তার বাহ্যিক আচরণ পরিবর্তন না করে — M10/L45-এর TDD-এর "Refactor" ধাপের সরাসরি, প্রেসাইজ ফরমালাইজেশন, এবং M11/L49-এ শনাক্ত করা কোড স্মেলের সরাসরি ব্যবহারিক জবাব। সবচেয়ে গুরুত্বপূর্ণ, একমাত্র অলঙ্ঘনীয় শর্তটি আবার বলা দরকার: আচরণ বদলানো যাবে না — একই ইনপুটে ঠিক একই আউটপুট, রিফ্যাক্টরিংয়ের আগে ও পরে দুটোতেই।

২ · কেন regression testing ছাড়া রিফ্যাক্টরিং বিপজ্জনক

এই "আচরণ অপরিবর্তিত" শর্তটি পালন হচ্ছে কিনা — শুধু কোড পড়ে সেটা নিশ্চিতভাবে বলা কঠিন, বিশেষত বড় কোডবেসে। এখানেই M10/L48-এর regression suite অপরিহার্য হয়ে ওঠে: একটি regression suite হলো একমাত্র বাস্তবসম্মত উপায় প্রমাণ করার যে রিফ্যাক্টর করা কোড এখনো ঠিক আগের মতোই আচরণ করছে — রিফ্যাক্টরিং করার আগে suite চালান (সব PASS), রিফ্যাক্টরিং করুন, আবার suite চালান — যদি সবকিছু এখনো PASS করে, রিফ্যাক্টরিং নিরাপদ। regression suite ছাড়া রিফ্যাক্টরিং করা মানে অন্ধভাবে বিশ্বাস করা যে কিছু ভাঙেনি।

৩ · চারটি স্ট্যান্ডার্ড রিফ্যাক্টরিং টেকনিক

Extract Method
একটি লম্বা মেথডের একটি অংশ (M11/L49-এর Long Method স্মেল) আলাদা করে একটি ছোট, স্পষ্ট-নামের নতুন মেথডে নিয়ে যাওয়া।
Extract Class
একটি গড-ক্লাসের (M11/L49-এর Large Class স্মেল) সম্পর্কিত দায়িত্বগুলো একটি নতুন, ফোকাসড ক্লাসে সরানো — M4/L16-এর Employee স্প্লিট উদাহরণের পুনঃব্যবহার।
Rename Variable/Method
একটি খারাপ-নামের আইডেন্টিফায়ারের স্পষ্টতা উন্নত করা — অন্য ডেভেলপারদের জন্য কোড পড়া সহজ করা (M1/L03-এর maintainability-এর সরাসরি প্রয়োগ)।
Replace Conditional with Polymorphism
একটি if/elif চেইনকে পলিমরফিজম দিয়ে বদলানো — M4/L16-এর OCP শেপ-এরিয়া উদাহরণের সরাসরি পুনঃব্যবহার।
লম্বা মেথড (Long Method স্মেল) Extract Method প্রয়োগ ৩টি ছোট হেল্পার ফাংশন verify (L48)
Extract Method করার পর আউটপুট original-এর সাথে হুবহু মিলছে কিনা, তা regression suite দিয়ে verify করাই রিফ্যাক্টরিংকে "নিরাপদ" করে তোলে।

৪ · Extract Method বাস্তবে — আগে ও পরে, ফলাফল যাচাই

নিচের কোড সেলে M11/L49-এর স্মেলি process_order ফাংশনটিকে সত্যিকারের চলমান কোডে বাস্তবায়ন করে Extract Method প্রয়োগ করা হয়েছে — ভ্যালিডেশন, ক্যালকুলেশন, ফরম্যাটিং তিনটি আলাদা, স্পষ্ট-নামের হেল্পার ফাংশনে ভাগ করা হয়েছে, একটি সংক্ষিপ্ত orchestrating ফাংশন দিয়ে সব একসাথে কল করা হয়েছে। তারপর একই টেস্ট ইনপুটের সেট original ও refactored — দুটো ভার্সনেই চালিয়ে, প্রতিটির আউটপুট identical কিনা তা assert দিয়ে যাচাই করা হয়েছে।

Python
# --- মূল (রিফ্যাক্টরের আগে) -- ভ্যালিডেশন, ক্যালকুলেশন, ফরম্যাটিং সব একসাথে একটি লম্বা ফাংশনে ---
def process_order_original(order):
    if order["customer"] is None:
        raise ValueError("গ্রাহক নেই")
    if not order["items"]:
        raise ValueError("আইটেম নেই")

    total = 0
    for item in order["items"]:
        total += item["price"] * item["quantity"]
    if order["customer"]["is_vip"]:
        total = total * 0.9

    receipt = f"গ্রাহক: {order['customer']['name']} | মোট: {total:.2f}"
    return receipt


# --- Extract Method প্রয়োগের পরে -- একই কাজ, ৩টি ছোট, স্পষ্ট-নামের হেল্পার ফাংশনে ভাগ করা ---
def validate_order(order):
    if order["customer"] is None:
        raise ValueError("গ্রাহক নেই")
    if not order["items"]:
        raise ValueError("আইটেম নেই")

def calculate_total(order):
    total = sum(item["price"] * item["quantity"] for item in order["items"])
    if order["customer"]["is_vip"]:
        total = total * 0.9
    return total

def format_receipt(order, total):
    return f"গ্রাহক: {order['customer']['name']} | মোট: {total:.2f}"

def process_order_refactored(order):
    validate_order(order)
    total = calculate_total(order)
    return format_receipt(order, total)


# --- টেস্ট ইনপুট -- একাধিক বাস্তবসম্মত অর্ডার ---
test_orders = [
    {"customer": {"name": "রাহুল", "is_vip": False}, "items": [{"price": 100, "quantity": 2}]},
    {"customer": {"name": "সুমি", "is_vip": True}, "items": [{"price": 200, "quantity": 3}]},
    {"customer": {"name": "করিম", "is_vip": True}, "items": [{"price": 50, "quantity": 1}, {"price": 30, "quantity": 4}]},
]

print("=== Extract Method রিফ্যাক্টরিং যাচাই: original বনাম refactored ===")
for i, order in enumerate(test_orders, start=1):
    original_result = process_order_original(order)
    refactored_result = process_order_refactored(order)
    match = original_result == refactored_result
    print(f"টেস্ট ইনপুট {i}: original  = {original_result!r}")
    print(f"              refactored = {refactored_result!r}")
    print(f"              identical: {match}")
    assert match, f"রিফ্যাক্টরিং আচরণ পাল্টে দিয়েছে! টেস্ট ইনপুট {i}"

# --- ভ্যালিডেশন এরর কেসও একই আচরণ করে কিনা যাচাই ---
bad_order = {"customer": None, "items": []}
try:
    process_order_original(bad_order)
    original_raised = None
except ValueError as e:
    original_raised = str(e)

try:
    process_order_refactored(bad_order)
    refactored_raised = None
except ValueError as e:
    refactored_raised = str(e)

print(f"\nভ্যালিডেশন এরর কেস -- original raises: {original_raised!r}, refactored raises: {refactored_raised!r}")
assert original_raised == refactored_raised

print("\nসবগুলো টেস্ট ইনপুটে original ও refactored ফাংশন হুবহু IDENTICAL আউটপুট দিয়েছে -- রিফ্যাক্টরিং আচরণ অক্ষুণ্ণ রেখেছে")

    
লক্ষ্য করুন — calculate_total-এর টেস্ট ইনপুট ২ (সুমি, VIP): total = 200×3 = 600, VIP ছাড় 600×0.9 = 540.0। টেস্ট ইনপুট ৩ (করিম, VIP): total = 50×1 + 30×4 = 170, VIP ছাড় 170×0.9 = 153.0। উভয় ফাংশন হুবহু একই সূত্র ব্যবহার করছে বলেই আউটপুট identical হওয়াটা প্রত্যাশিত — কিন্তু এই "প্রত্যাশা"-কে কোড দিয়ে assert করাই একে অনুমান থেকে প্রমাণে পরিণত করে।
মূল কথা · Key takeaway

রিফ্যাক্টরিং ও রিগ্রেশন টেস্টিং একসাথে একটি জোড়া — রিফ্যাক্টরিং টেকনিক আপনাকে বলে কীভাবে কোড উন্নত করবেন, regression suite আপনাকে বলে নিশ্চিতভাবে কিছু ভাঙেনি। এই দুটো ছাড়া, কোড উন্নত করা সবসময় একটি ঝুঁকি — এই দুটো একসাথে থাকলে, এটি একটি নিয়মিত, নিরাপদ অভ্যাস হয়ে ওঠে।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ যদি Extract Method প্রয়োগ করার সময় ভুলবশত calculate_total-এ VIP ছাড়ের লাইনটি বাদ পড়ে যেত, উপরের কোড কীভাবে তা ধরত?

টেস্ট ইনপুট ২ ও ৩ (দুটোই VIP গ্রাহক) -এ refactored_result-এর total ছাড় ছাড়া হিসাব হতো (যেমন 600.00 বনাম প্রত্যাশিত 540.00), ফলে original_result != refactored_result হতো এবং assert match লাইনটি একটি AssertionError ছুড়ত — ঠিক সাথে সাথে জানিয়ে দিত কোন টেস্ট ইনপুটে রিফ্যাক্টরিং আচরণ বদলে দিয়েছে। এটাই দেখায় কেন behavior-preservation চেক শুধু "ভালো অভ্যাস" নয়, বাস্তবে বাগ ধরার একটি সরাসরি মেকানিজম।

প্র ০২ validate_order, calculate_total, format_receipt — এই তিনটি নতুন ফাংশনের প্রতিটির নিজস্ব একটি, স্পষ্ট দায়িত্ব আছে। এটি M4/L16-এর কোন নীতির সাথে সরাসরি সম্পর্কিত?

Single Responsibility Principle (SRP) — প্রতিটি ফাংশনের ঠিক একটি "পরিবর্তনের কারণ" আছে (ভ্যালিডেশন নিয়ম বদলালে শুধু validate_order বদলাবে, ছাড়ের হিসাব বদলালে শুধু calculate_total)। এটাই Extract Method-এর গভীর মূল্য — শুধু ফাংশন ছোট করা নয়, বরং প্রতিটি নতুন ফাংশনকে একটি একক, স্পষ্ট দায়িত্ব দেওয়া, যা M11/L49-এর Long Method স্মেলের মূল কারণ (SRP লঙ্ঘন) সরাসরি ঠিক করে।

প্র ০৩ উপরের কোডে bad_order কেসটি কেন আলাদাভাবে টেস্ট করা হলো, শুধু সফল অর্ডারগুলোই যথেষ্ট ছিল না কেন?

কারণ রিফ্যাক্টরিংয়ের "আচরণ অপরিবর্তিত" শর্ত শুধু সফল, স্বাভাবিক পথে (happy path) নয় — এরর/এক্সসেপশন আচরণেও প্রযোজ্য। যদি Extract Method-এর সময় ভুলবশত validate_order-এর একটি চেক বাদ পড়ে যেত, শুধু সফল কেসগুলো টেস্ট করলে সেই বাগ ধরা পড়ত না — কারণ সফল ইনপুটগুলো কখনো সেই ভ্যালিডেশন-এরর পথে যায়ই না। M10/L44-এর alternative flow-এর ধারণারই সরাসরি প্রয়োগ।

অনুশীলন

  1. পরীক্ষা করুন: উপরের test_orders-এ একটি চতুর্থ অর্ডার যোগ করুন (আপনার পছন্দমতো নাম, দাম, পরিমাণ, VIP স্ট্যাটাস দিয়ে) এবং কোড রান করে নিশ্চিত করুন original ও refactored এখনো identical ফলাফল দেয়।

    যেহেতু process_order_original ও process_order_refactored (এর হেল্পার ফাংশনগুলোর মাধ্যমে) হুবহু একই সূত্র ব্যবহার করে, যেকোনো নতুন বৈধ অর্ডারেও দুটো ফলাফল identical হওয়া উচিত — লুপ প্রতিটি নতুন এন্ট্রির জন্য একই assert match চেক স্বয়ংক্রিয়ভাবে চালাবে, তাই কোনো অতিরিক্ত কোড লেখার প্রয়োজন নেই, শুধু test_orders লিস্টে একটি নতুন dict যোগ করলেই যথেষ্ট।

  2. চিন্তা করুন: Rename Variable/Method রিফ্যাক্টরিং কি Extract Method-এর মতো একই ধরনের regression-suite যাচাই দাবি করে?

    হ্যাঁ, নীতিগতভাবে — যেকোনো রিফ্যাক্টরিং, এমনকি শুধু একটি ভেরিয়েবলের নাম বদলানোও, আচরণ অপরিবর্তিত আছে কিনা তা যাচাই করা উচিত। বাস্তবে rename সাধারণত কম ঝুঁকিপূর্ণ (যদি সব রেফারেন্স সঠিকভাবে আপডেট হয়, লজিক একেবারেই বদলায় না), কিন্তু একটি ভুলভাবে করা rename (যেমন দুটো ভিন্ন ভেরিয়েবলকে একই নামে মিশিয়ে ফেলা) সহজেই একটি real বাগ তৈরি করতে পারে — তাই একই নিয়ম প্রযোজ্য, শুধু ঝুঁকির মাত্রা কম।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

পূর্ববর্তী পাঠ
কোড স্মেল