Split Temporary Variable: একটা বালতি দুই কাজ করতে পারে না
দুই বালতির গল্প দিয়ে Split Temporary Variable শেখো — TypeScript ও C# উদাহরণ আর নিরাপদ ধাপ সহ। প্রতিটা variable-কে একটাই কাজ আর একটাই সৎ নাম দাও।
এক বালতির বাড়ির গল্প
ধরো নাসরিন আন্টির বাড়িতে মাত্র একটাই বালতি। একটা নীল প্লাস্টিকের বালতি।
সকালে বালতিটা গোসলের পানি দিয়ে ভরা হয়। বাথরুমে থাকে। গোসলের পর কেউ ধুয়ে (আশা করি!) আবার ভরে — কিন্তু এবার ফিল্টার করা খাওয়ার পানি দিয়ে। একই নীল বালতি এখন রান্নাঘরে।
এক গরম দুপুরে নাসরিন আন্টির ভাগিনা জামাল ক্রিকেট খেলে ফিরে আসে, তৃষ্ণায় জান যায় অবস্থা। করিডোরে নীল বালতিটা দেখে — ঠিক বাথরুম আর রান্নাঘরের মাঝামাঝি। এক গ্লাস চুবিয়ে পান করে। প্রশ্ন হলো: সে কি খাওয়ার পানি খেল নাকি গোসলের পানি?
কেউ জানে না! সেটা নির্ভর করে কখন আর কোনটা সবার শেষে ভরা হয়েছিল তার উপর। বালতি নিজে কোনো সংকেত দেয় না। রং একই, আকার একই, শুধু অর্থ সারাদিন বদলাতে থাকে। উত্তর পেতে হলে জামালকে বালতির পুরো দিনের ইতিহাস মাথায় রাখতে হবে — শেষবার কে ভরেছে, কখন, কী দিয়ে। একটাই পাত্র, দুটো সম্পর্কহীন কাজ — এটা বিভ্রান্তির রেসিপি, আর এক্ষেত্রে হয়তো পেট খারাপেরও।
সমাধান একটাই: দ্বিতীয় বালতি কিনে নাও। নীলটা গোসলের জন্য রাখো, সবুজটা খাওয়ার পানির জন্য। আরও ভালো হয় লেবেল লিখে দাও: "গোসলের পানি" আর "খাওয়ার পানি"। এখন জামাল অনুমান না করেই পান করতে পারবে। প্রতিটা বালতির একটাই কাজ, একটাই অর্থ, একটাই সৎ লেবেল — সে যখনই আসুক।
কোডে আমরা এই এক-বালতির ভুল করি যখন একটাই variable দুটো আলাদা উদ্দেশ্যে reuse করি — প্রথমে একটা value ধরে, তারপর সম্পূর্ণ সম্পর্কহীন কিছু দিয়ে overwrite করি। পাঠক, তৃষ্ণার্ত জামালের মতো, প্রতিটা line-এ বুঝতে হয় কোন অর্থটা এখন চালু আছে। আজকের refactoring দ্বিতীয় বালতিটা কিনে দেয়। এটার নাম Split Temporary Variable (Fowler-এর Refactoring বইয়ের দ্বিতীয় সংস্করণে শুধু Split Variable নামে পরিচিত)।
Split Temporary Variable আসলে কী?
পুরো ধারণাটা এক নিঃশ্বাসে বলি:
তোমার কাছে একটা local variable আছে যেটা একাধিকবার assign হয়, আর assignment-গুলো আলাদা আলাদা জিনিস বোঝায়। প্রতিটা অর্থের জন্য আলাদা, সুন্দর নামের variable বানাও — প্রতিটা ঠিক একবারই assign করা।
একটু টেস্ট করে দেখি। আগে — এক বালতি:
let temp = 2 * (height + width); // temp মানে এখানে "perimeter"
console.log(temp);
temp = height * width; // এখন temp মানে "area"!
console.log(temp);পরে — দুটো লেবেল করা বালতি:
const perimeter = 2 * (height + width);
console.log(perimeter);
const area = height * width;
console.log(area);temp variable টা আমাদের মিথ্যা বলছিল — value সম্পর্কে না, বরং গঠন সম্পর্কে। মনে হচ্ছিল একটাই ধারণা আছে, কিন্তু আসলে দুটো। লক্ষ্য করো নামটাও: temp। লেখক একটা অর্থপূর্ণ নাম দিতে পারেননি কারণ "perimeter" আর "area" দুটোকে একসাথে cover করার কোনো সৎ নাম নেই। temp, result, value, x — এই ধরনের অস্পষ্ট নাম এই smell-এর সাধারণ বৈশিষ্ট্য।
দেখো একজন পাঠক reused variable-এর সাথে কী করতে বাধ্য হয় — এটা একটা জিজ্ঞাসাবাদ, আর উত্তর নির্ভর করে line number-এর উপর:
সোনার নিয়ম, মনে রেখো: এক variable, এক কাজ, এক সৎ নাম। যদি অর্থ মাঝপথে বদলে যাওয়ায় সত্যিকার নাম দিতে না পারো, তাহলে এটা আসলে দুটো variable এক কোট পরে আছে — ওদের আলাদা করো।
Fowler-এর কাছ থেকে সরাসরি একটা গুরুত্বপূর্ণ ব্যতিক্রম: একটা loop counter (i যায় 0, 1, 2...) আর একটা collecting variable (loop-এর ভেতরে বাড়তে থাকা sum বা accumulator) পরিবর্তন হওয়ার কথাই। গণনা করা তাদের একটাই কাজ; জমানো তাদের একটাই কাজ। এগুলো কখনো split করো না। এই refactoring শুধু সেসব variable-কে লক্ষ্য করে যাদের আলাদা assignment মানে আলাদা, সম্পর্কহীন জিনিস।
একটা অপব্যবহৃত temp-এর পুরো state journey আর তার উদ্ধার দেখতে এরকম:
কখন এটা দরকার?
যেকোনো method খুলে let variable-গুলো (বা non-final locals) পরীক্ষা করো। এগুলো হলো লক্ষণ যে split দরকার:
- দ্বিতীয় assignment অর্থ বদলায়, শুধু value নয়।
charge = baseChargeএরপরcharge = charge * taxRateএখনো একটাই অর্থ বিবর্তিত হচ্ছে। কিন্তুtemp = distance / timeএরপরtemp = target - runsহলো দুই অপরিচিত একটা বিছানায়। - নামটা অস্পষ্ট।
temp,result,val,data,x2। এই অস্পষ্টতা সাধারণত বাধ্য হয়ে করা — দুই অর্থের জন্য কোনো সুনির্দিষ্ট নাম নেই, তাই লেখক হার মেনে অর্থহীন একটা নাম বেছেছেন। - Comment পরিবর্তনের জায়গা চিহ্নিত করছে।
// এখন temp-কে discount-এর জন্য reuse করছি— এটা অপরাধের জায়গায় লেখা স্বীকারোক্তি। এভাবে এই smell Comments smell-কে পুষ্ট করে — variable নিজেকে ব্যাখ্যা করতে পারে না বলে code-এ বর্ণনার দরকার পড়ে। - অন্য refactoring করতে গিয়ে আটকে গেছ। Inline Temp আর Replace Temp with Query দুটোরই pre-condition হলো single-assignment variable। দুইবার assigned temp সেগুলো আটকে দেয়। আগে split করো; তারপর বাকি দরজাগুলো খুলে যাবে।
- একটা parameter overwrite হচ্ছে। যদি কেউ method parameter-এ মাঝপথে নতুন value assign করে, এটা একই রোগ বিশেষ জায়গায়। এর নির্দিষ্ট চিকিৎসা হলো Remove Assignments to Parameters, আজকের refactoring-এর ঘনিষ্ঠ চাচাতো ভাই। দ্বিতীয় সংস্করণের "Split Variable" এই ক্ষেত্রটাও cover করে।
এটা কতটা সাধারণ? যেকোনো পুরনো codebase-এ multi-assignment variable audit করলে এভাবে ভাগ পড়ে:
এটা কেন হয়? বেশিরভাগ ক্ষেত্রে পুরনো যুগের অভ্যাস। দশকের পর দশক আগে memory ছিল মূল্যবান, variable reuse করলে কয়েক byte বাঁচত। আজ সেই byte-এর দাম নেই, কিন্তু বিভ্রান্তির মূল্য দিতে হয় ঘণ্টার পর ঘণ্টা developer সময় আর মাঝেমাঝে সত্যিকারের bug — যেমন "গোসলের" value যেখানে "খাওয়ার" value দরকার সেখানে ব্যবহার করা। Compiler এই bug ধরতে পারে না, কারণ দুই অর্থই একই নামে লেখা। Split করলে compiler আর পাঠক, দুজনেই আলাদা করতে পারে।
কলেজ কর্নার: তোমার compiler এটা এমনিই করে — এটার নাম SSA। Compiler design-এর একটা মজার রহস্য হলো প্রায় সব গুরুত্বপূর্ণ optimising compiler — LLVM, GCC, HotSpot, .NET RyuJIT — optimize করার আগে তোমার code-কে Static Single Assignment form (SSA)-এ রূপান্তর করে। SSA-তে প্রতিটা variable ঠিক একবারই assign হয়, গঠন অনুসারে। তোমার source code যদি temp-কে তিনবার assign করে, compiler চুপচাপ সেটাকে temp1, temp2, temp3-এ ভাগ করে — ঠিক যেভাবে এই refactoring হাতে করে। যেখানে control-flow path মেলে (if-else-এর পরে) এবং দুটো version আসতে পারে, SSA একটা বিশেষ phi function যোগ করে যেটা কোনটা এসেছে সেটা বেছে নেয়। Compiler কেন এই কষ্ট করে? কারণ প্রায় সব optimization — constant propagation, dead-code elimination, value numbering — তখন অনেক সহজ হয়ে যায় যখন প্রতিটা নামের একটাই অর্থ ও একটাই defining line থাকে। একটু ভাবো: machine reused variable নিয়ে চিন্তা করতে অস্বীকার করে আর গুরুতর চিন্তা করার আগে সেগুলো rename করে নেয়। Split Temporary Variable হলো তুমি human পাঠককে সেই একই সুবিধা দিচ্ছ যেটা compiler নিজের জন্য জোর করে নেয়। Compilers course-এ SSA আনুষ্ঠানিকভাবে পড়লে এটাকে গণিতের পোশাকে আজকের পাঠ হিসেবে চিনতে পারবে।
আগে আর পরে এক নজরে
TypeScript-এ ক্রিকেট-ফ্লেভারড উদাহরণ। Function-টা একটা টিমের chase রিপোর্ট করে: প্রথমে current run rate, তারপর এখনো কত রান দরকার।
আগে:
function chaseSummary(runs: number, overs: number, target: number): string {
let temp = runs / overs; // temp = current run rate
const rateText = `Run rate: ${temp.toFixed(2)}`;
temp = target - runs; // temp = runs still needed!
const needText = `Need ${temp} more runs`;
return `${rateText}. ${needText}.`;
}পরে:
function chaseSummary(runs: number, overs: number, target: number): string {
const runRate = runs / overs;
const rateText = `Run rate: ${runRate.toFixed(2)}`;
const runsNeeded = target - runs;
const needText = `Need ${runsNeeded} more runs`;
return `${rateText}. ${needText}.`;
}প্রতিটা input-এ output একই। কিন্তু পরের version-এ দুটো অমূল্য বৈশিষ্ট্য আছে। প্রথমত, প্রতিটা নাম সৎ: runRate সবসময় একটা run rate, runsNeeded সবসময় দরকারি রান — জন্ম থেকে শেষ ব্যবহার পর্যন্ত। দ্বিতীয়ত, প্রতিটা variable const — single assignment, compiler-এর guarantee সহ। runRate একবার দেখলে বাকি function-এ তার উপর ভরসা করা যায়। কোনো মানসিক সময়-ট্র্যাকিং নেই, "এই line-এ temp মানে কী?" নেই।
কোনো নির্দিষ্ট variable split করবে কিনা? দুটো প্রশ্ন সিদ্ধান্ত নেয়: assignment-গুলো কি আলাদা অর্থ বহন করে, আর variable-এর নাম কি সর্বত্র সৎ থাকছে? এই map টা দেখো:
ধাপে ধাপে, নিরাপদ পথে
এই mechanics টা চমৎকার compiler-assisted — compiler নিজেই প্রতিটা line দেখিয়ে দেয় যেগুলো ঠিক করতে হবে। উপরের cricket function-এ করে দেখি।
ধাপ ১: প্রথম assignment খোঁজো, আর সেখানে সেই প্রথম অর্থের জন্য সৎ নামে variable rename করো। Declaration আর দ্বিতীয় assignment-এর আগ পর্যন্ত প্রতিটা read বদলাও। একই সাথে const declare করো:
function chaseSummary(runs: number, overs: number, target: number): string {
const runRate = runs / overs; // renamed + const
const rateText = `Run rate: ${runRate.toFixed(2)}`;
temp = target - runs; // পুরনো নাম এখনো আছে!
const needText = `Need ${temp} more runs`;
return `${rateText}. ${needText}.`;
}ধাপ ২: Compile করো। একটা সুন্দর জিনিস হয়: compiler এখন temp = target - runs;-এ অভিযোগ করে — "cannot find name temp"। Error list মানেই তোমার to-do list। প্রতিটা লাল squiggle দ্বিতীয় অর্থের একটা line চিহ্নিত করছে। এটা সেই বিরল সুখের মুহূর্ত যেখানে compiler error বন্ধু হয়ে পথ দেখায়, শত্রু হয়ে আটকায় না।
ধাপ ৩: দ্বিতীয় অর্থকে তার নিজস্ব সৎ নামের নতুন const দাও, আর compiler যে read-গুলো দেখিয়েছে সেগুলো ঠিক করো:
function chaseSummary(runs: number, overs: number, target: number): string {
const runRate = runs / overs;
const rateText = `Run rate: ${runRate.toFixed(2)}`;
const runsNeeded = target - runs; // দ্বিতীয় বালতি, নিজস্ব লেবেল
const needText = `Need ${runsNeeded} more runs`;
return `${rateText}. ${needText}.`;
}ধাপ ৪: Test চালাও। Behaviour একই হতে হবে — আমরা নাম আর গঠন বদলেছি, value নয়।
ধাপ ৫: আরো assignment থাকলে আবার করো। সত্যিকারের অপব্যবহৃত temp তিন বা চারটা অর্থ বহন করতে পারে। একটা একটা করে সামলাও: rename, const, compile, ঠিক করো, test। প্রতিটা চক্রে এক মিনিট।
ধাপ ৬: শেষ একবার দেখো। সব নতুন variable কি const? প্রতিটা নাম কি প্রতিটা ব্যবহারে সৎভাবে পড়া যায়? হ্যাঁ হলে refactoring সম্পূর্ণ — আর method এখন Replace Temp with Query বা Extract Method-এর মতো follow-up-এর জন্য প্রস্তুত, যেগুলো multi-job temp আটকে রেখেছিল।
প্রতিটা split-এর পরে test চালাও, পুরো cleanup-এর পরে নয়। বিপদের জায়গা হলো লম্বা method যেখানে প্রথম অর্থ গোপনে দ্বিতীয় assignment-এর পরেও read হয় — তাড়াহুড়ো করা split তখন behaviour বদলে দেয়। const + rename combination compile time-এ বেশিরভাগ এরকম ক্ষেত্র ধরে, কিন্তু শুধু সবুজ test run-ই প্রমাণ করে। আর কখনো পাঁচটা split একসাথে করে শেষে একবার test করো না: কিছু ভাঙলে বুঝতে পারবে না কোন split-টা করেছে।
একটা বড় বাস্তব উদাহরণ
ধরো নাসরিন আন্টির বাড়িকে code-এ রূপ দিই। পৌর পানির ট্যাংকার প্রতি দুই দিন পরপর আসে, আর জামাল — একই ভাগিনা, করিডোর ঘটনার জন্য এখন ক্ষমা পেয়েছে — একটা ছোট planner লেখে পরিবারের পানির চাহিদা হিসাব করতে। তার প্রথম version কাজ করে — কিন্তু amount নামের একটা বেচারা variable তিনটা আলাদা অর্থে reuse হচ্ছে:
interface Household {
members: number;
bathsPerPersonPerDay: number;
hasGarden: boolean;
}
function waterPlan(home: Household, tankerCapacity: number): string {
// amount = প্রতিদিন গোসলের পানি (লিটার)
let amount = home.members * home.bathsPerPersonPerDay * 20;
const bathingNote = `Bathing: ${amount} L/day`;
// amount reuse করা হচ্ছে প্রতিদিনের পানীয় জলের জন্য
amount = home.members * 3;
const drinkingNote = `Drinking: ${amount} L/day`;
// amount আবার REUSE করা হচ্ছে ২ দিনে কতটা ট্যাংকার ট্রিপ দরকার তার জন্য
amount = Math.ceil(
((home.members * home.bathsPerPersonPerDay * 20 + home.members * 3) * 2 +
(home.hasGarden ? 50 : 0)) /
tankerCapacity
);
return `${bathingNote}, ${drinkingNote}, Tanker trips: ${amount}`;
}একটু গণ্ডগোলটা লক্ষ্য করো। Variable amount হলো নীল বালতি: প্রথম লাইনে গোসলের পানি, মাঝখানে খাওয়ার পানি, শেষে ট্যাংকার ট্রিপ। তিনটা comment প্রতিটা পরিবর্তনে পাহারা দিচ্ছে, বর্ণনা করছে নামটা যা বলতে পারছে না। আর তৃতীয় assignment-টা দেখো — কারণ amount আগের মান ধরে নেই (overwrite হয়ে গেছে!), জামালকে trip calculation-এর ভেতরে দুটো formula আবার টাইপ করতে হয়েছে। এক-বালতির অভ্যাস সরাসরি Duplicate Code তৈরি করেছে।
এখন split করি। একটা অর্থ একসময়ে, প্রতিটা ধাপের মাঝে compile আর test। গন্তব্য:
function waterPlan(home: Household, tankerCapacity: number): string {
const bathingLitresPerDay = home.members * home.bathsPerPersonPerDay * 20;
const bathingNote = `Bathing: ${bathingLitresPerDay} L/day`;
const drinkingLitresPerDay = home.members * 3;
const drinkingNote = `Drinking: ${drinkingLitresPerDay} L/day`;
const gardenLitres = home.hasGarden ? 50 : 0;
const litresForTwoDays =
(bathingLitresPerDay + drinkingLitresPerDay) * 2 + gardenLitres;
const tankerTrips = Math.ceil(litresForTwoDays / tankerCapacity);
return `${bathingNote}, ${drinkingNote}, Tanker trips: ${tankerTrips}`;
}উন্নতিগুলো গুনে দেখো:
- প্রতিটা বালতি লেবেল করা।
bathingLitresPerDay,drinkingLitresPerDay,tankerTrips— কোনো পাঠক আর ভুল পানি পান করবে না। - তিনটা ব্যাখ্যামূলক comment অপ্রয়োজনীয় হয়ে গেছে আর মুছে ফেলা হয়েছে। নামগুলোই এখন বর্ণনা করছে।
- Duplicate formula মুছে গেছে। কারণ আগের value-গুলো overwrite হয়নি, trip calculation সরাসরি
bathingLitresPerDayআরdrinkingLitresPerDayreuse করে। Split করা শুধু code স্পষ্ট করেনি — সত্যিকারের duplication সরিয়েছে। - সবকিছু
const। Compiler এখন guarantee দেয় জন্মের পরে কোনো value বদলাবে না। পুরো function উপর থেকে নিচে এক পাসে পড়া যায়, কোনো মানসিক পিছু হটা ছাড়া।
এর প্রভাব সূক্ষ্ম না। পাঁচ জন বন্ধুকে দুটো version দাও আর সময় নাও "দুই দিনে বাগানে কত লিটার পানি যায়?" প্রশ্নের উত্তর খুঁজতে কার কত সময় লাগে — reused-variable version-এ পুরো method মাথায় replay করতে হয়; split version-এ একটা line পড়লেই হয়:
আর আগামীকাল যদি জামাল এই value-গুলো একটা WaterPlanner class-এর অন্য method-এ পাঠাতে চায়? প্রতিটা single-assignment const এখন Replace Temp with Query-এর জন্য নিখুঁত candidate — এমন একটা promotion যা amount চাকরি বদলাতে থাকার সময় অসম্ভব ছিল:
কলেজ কর্নার: live range আর reuse-এর মিথ্যা সাশ্রয়। পুরনো programmer-রা কেন variable reuse করত? বিশ্বাস ছিল এক variable মানে এক storage slot, তাই reuse করলে memory বাঁচে। আধুনিক compiler বাস্তবতা অন্য কথা বলে। Machine যা care করে তা হলো প্রতিটা value-এর live range — assignment থেকে শেষ ব্যবহার পর্যন্ত। তুমি যখন amount-কে তিনটা অর্থে reuse করো, তখন তিনটা আলাদা live range তৈরি হয় যেগুলো শুধু নাম ভাগ করে; register allocator তাদের তিনটা value হিসেবেই দেখে (SSA renaming guarantee করে)। যখন তুমি তিনটা const নামে split করো, allocator ঠিক একই তিনটা live range দেখে। Generated machine code সাধারণত একই। তাই variable reuse করলে শূন্য byte আর শূন্য nanosecond বাঁচে — শুধু পাঠকের মনোযোগ খরচ হয়। Split version "অপচয়ী কিন্তু পঠনযোগ্য" নয়; এটা পঠনযোগ্য এবং ঠিক ততটাই efficient।
C#-এ একই refactoring
C#-এ একই রোগ আর একই চিকিৎসা। ধরো একটা courier company parcel charge হিসাব করে; temp নামের একটা variable দুটো কাজ করছে:
আগে:
public class ParcelPricer
{
public string PriceSummary(double weightKg, double lengthCm,
double widthCm, double heightCm)
{
// temp = volumetric weight (courier industry formula)
double temp = (lengthCm * widthCm * heightCm) / 5000.0;
bool useVolumetric = temp > weightKg;
// reuse temp for the chargeable amount in rupees
temp = (useVolumetric ? (lengthCm * widthCm * heightCm) / 5000.0
: weightKg) * 49.0;
return $"Payable: Rs {temp:F2}";
}
}একই সংকেত: অর্থহীন নাম, পরিবর্তনের comment, আর — কারণ প্রথম value overwrite হতো — volumetric formula দুইবার টাইপ করা। এখন split:
পরে:
public class ParcelPricer
{
public string PriceSummary(double weightKg, double lengthCm,
double widthCm, double heightCm)
{
double volumetricWeightKg = (lengthCm * widthCm * heightCm) / 5000.0;
bool useVolumetric = volumetricWeightKg > weightKg;
double chargeableKg = useVolumetric ? volumetricWeightKg : weightKg;
double payableRupees = chargeableKg * 49.0;
return $"Payable: Rs {payableRupees:F2}";
}
}C#-এর একটা নোট: ভাষাটা runtime-computed local-এর জন্য const দেয় না (C#-এ const শুধু compile-time), আর local-এর জন্য readonly-ও নেই। তাই single-assignment-এর প্রতিশ্রুতি তোমার নিজের discipline আর tooling-এর উপর নির্ভর করে — আর tooling এখানে সত্যিই ভালো। Visual Studio আর Rider দুটোই সন্দেহজনকভাবে assigned আর reassigned variable-গুলো grey-out বা flag করে, আর code analysis rules (এবং ReSharper-এর "value assigned is not used" inspection) অনেক এক-বালতির অভ্যাস স্বয়ংক্রিয়ভাবে ধরে। Java-তে final দিয়ে প্রতিশ্রুতি seal করা যায়; TypeScript আর JavaScript-এ const দিয়ে; F#-এ value default-এ single-assignment — আজকের পাঠের উপর গড়া একটা পুরো ভাষা!
Python-ও একটু দেখার দাবি রাখে, কারণ এতে কোনো const নেই — discipline আর naming-ই সব:
def chase_summary(runs, overs, target):
run_rate = runs / overs # one bucket, one label
rate_text = f"Run rate: {run_rate:.2f}"
runs_needed = target - runs # second bucket, second label
need_text = f"Need {runs_needed} more runs"
return f"{rate_text}. {need_text}."এখানে single assignment enforce করার কোনো keyword নেই, তাই Python-এ linter (pylint-এর redefined-variable-type style check) আর সৎ-নামের অভ্যাসের উপর ভরসা করতে হয়।
IDE support
সরাসরি সৎ কথা: কোনো বড় IDE-তে এক-ক্লিকের "Split Variable" command নেই, কারণ IDE জানতে পারে না একটা অর্থ কোথায় শেষ হয় আর পরেরটা কোথায় শুরু — সেটার জন্য মানুষের বোঝাপড়া দরকার। তবু tools অনেক সাহায্য করে:
| Tool | কীভাবে সাহায্য করে |
|---|---|
| JetBrains Rider / IntelliJ IDEA | Rename (Shift+F6) symbol নিরাপদে rename করে; inspection reassigned local flag করে আর declaration থেকে অর্থ আলাদা করার পরামর্শ দেয় |
| Visual Studio | Rename (Ctrl+R, Ctrl+R) এবং assign কিন্তু ব্যবহার না করা value-এর জন্য code analysis warning |
| VS Code | Rename Symbol (F2); ESLint-এর prefer-const আর no-param-reassign rule স্বয়ংক্রিয়ভাবে smell flag করে |
| Linters (ESLint, ReSharper, SonarLint) | prefer-const style rule কার্যকরভাবে প্রতিটা split করার মতো variable detect করে |
Rename সম্পর্কে একটা সতর্কতা: IDE পুরো symbol rename করে — দুটো অর্থ একসাথে, সব occurrence। সেটা split নয়! সঠিক কাজ হলো: প্রথম declaration হাতে নতুন নামে আর const-এ edit করো, compiler orphaned পরবর্তী লাইনগুলো underline করুক, তারপর হাতে দ্বিতীয় variable introduce করো। Rename শুধু একটা অর্থের region-এর মধ্যে ব্যবহার করো যদি ভাষার scope তা অনুমতি দেয়। Step-by-step section-এর compiler-as-todo-list কৌশলই সবচেয়ে নিরাপদ পথ।
একটা ব্যবহারিক team tip: prefer-const (TypeScript/JavaScript) বা IDE-তে সমতুল্য inspection warning level-এ চালু করো। এটা পুরো smell-কে automatic underline-এ পরিণত করে, তাই এক-বালতির variable কখনো code review পর্যন্ত পৌঁছায় না।
সুবিধা ও ঝুঁকি
সৎ হিসাব:
| সুবিধা | ঝুঁকি / খরচ |
|---|---|
| প্রতিটা variable একটা অর্থ আর একটা সৎ নাম পায় — পড়তে সময়-ট্র্যাকিং লাগে না | একটু বেশি লাইন: shared declaration-এর বদলে প্রতিটা অর্থের জন্য আলাদা declaration |
সব split variable const/final হতে পারে — সবচেয়ে শক্তিশালী readability guarantee | ভুলে legitimate accumulator বা loop counter split করলে algorithm ভেঙে যায় |
| একটা সত্যিকারের bug class সরায়: যেখানে অর্থ B দরকার সেখানে অর্থ A ব্যবহার | ইতিমধ্যে temp-এ ভর্তি method-এ splitting আরো local যোগ করে — Replace Temp with Query বা Extract Method-এ follow up করো |
| Inline Temp, Replace Temp with Query, আর Extract Method unblock করে — এগুলো single assignment চায় | ভয়ের কিছু নেই — catalog-এ এটা সবচেয়ে নিরাপদ refactoring-গুলোর একটা |
| প্রায়ই overwritten value থেকে forced re-computation-এর duplication দূর করে | — |
এই table-এ একটা অস্বাভাবিক জিনিস লক্ষ্য করো: risk column সত্যিই পাতলা। Inline Temp (যেটা কার্যকর নাম মুছে দিতে পারে) বা Replace Temp with Query (যেটা একটু recomputation-এর বিনিময়ে নেয়)-এর মতো না, Split Temporary Variable সত্যিকারের দুই-কাজের variable-এ প্রয়োগ করলে প্রায় কোনো নেতিবাচক দিক নেই। একমাত্র সত্যিকারের ভুল হতে পারে loop counter বা collecting variable split করা — আর সেই ভুল তখনই জানা যায়, কারণ loop কাজ বন্ধ করে আর test red হয়। এজন্য অনেক শিক্ষক এটাকে beginner-এর প্রথম refactoring হিসেবে সুপারিশ করেন: বেশি সুবিধা, কম বিপদ, আর compiler সারাক্ষণ হাত ধরে থাকে।
কোন smell-গুলো সারায়?
| Smell | Split Temporary Variable কীভাবে সাহায্য করে |
|---|---|
| Long Method | Multi-purpose temp-গুলো untangle করে যেগুলো long method follow বা Extract Method-এ split করা অসম্ভব করে তোলে |
| Comments | "// এখন... এর জন্য reuse" বর্ণনা মুছে দেয়; সৎ নাম ব্যাখ্যামূলক comment-এর জায়গা নেয় |
| Duplicate Code | Overwritten value formula আবার টাইপ করতে বাধ্য করেছিল, split করলে পরের code আগের variable reuse করতে পারে |
Temp-variable refactoring-এর পরিবারে Split Temporary Variable-এর জায়গা হলো gatekeeper। Inline Temp আর Replace Temp with Query দুটোই একই pre-condition check দিয়ে শুরু করে — "এই variable কি ঠিক একবার assign হয়েছে?" — আর উত্তর না হলে, দুটোই তোমাকে আগে এখানে পাঠায়। Split করো, সব single-assignment করো, তারপর catalog-এর বাকি অংশ খুলে যায়।
পুরো পাঠ এক পাতায়, code review-এর আগের রাতের জন্য:
দ্রুত revision বক্স
+--------------------------------------------------------------------+
| SPLIT TEMPORARY VARIABLE - REVISION CARD |
+--------------------------------------------------------------------+
| WHAT : One variable assigned twice for DIFFERENT meanings -> |
| give each meaning its own well-named variable |
| STORY : One bucket for bathing AND drinking water? No! |
| Two buckets, two labels. |
| RULE : One variable, one job, one honest name |
| SIGNS : vague names (temp/result/value), changeover comments, |
| second assignment with a NEW meaning |
| EXCEPT : loop counters (i) and collecting variables (sum) -- |
| changing IS their one job; never split those |
| STEPS : 1. Rename first meaning, declare const |
| 2. Compile - errors mark the second meaning's lines |
| 3. New const for second meaning 4. Test |
| 5. Repeat per extra assignment |
| BONUS : All splits can be const/final -> easiest code to read |
| UNLOCKS : Inline Temp, Replace Temp with Query, Extract Method |
| 2ND ED. : Renamed to "Split Variable"; also covers parameters |
+--------------------------------------------------------------------+অনুশীলন
এখন তোমার পালা বালতি কেনার। নিচে একটা ট্রেন-যাত্রার helper আছে যেটা value নামের একটা variable-কে কয়েকটা কাজে অপব্যবহার করে। এটা আজ সঠিকভাবে কাজ করে — যেটা এটাকে নিখুঁত, নিরাপদ অনুশীলনের জায়গা বানায়।
interface Journey {
distanceKm: number;
farePerKm: number;
passengers: number;
isTatkal: boolean;
}
function journeyReport(j: Journey): string {
// value = পুরো গ্রুপের base fare
let value = j.distanceKm * j.farePerKm * j.passengers;
const fareNote = `Base fare: Rs ${value}`;
// value reuse করা হচ্ছে tatkal surcharge-এর জন্য
value = j.isTatkal ? j.distanceKm * j.farePerKm * j.passengers * 0.3 : 0;
const surchargeNote = `Tatkal: Rs ${value}`;
// value reuse করা হচ্ছে ভ্রমণের আনুমানিক ঘণ্টার জন্য
value = j.distanceKm / 60;
return `${fareNote}, ${surchargeNote}, Time: ${value.toFixed(1)} hrs`;
}
function totalCollected(fares: number[]): number {
let value = 0;
for (const fare of fares) {
value = value + fare; // অনেকবার assigned!
}
return value;
}তোমার কাজ:
journeyReport-এvalue-এর তিনটা অর্থ লেখো। কোনো code না বদলে আগেই প্রতিটার জন্য একটা সৎ variable নাম ঠিক করো।- Compiler-as-todo-list কৌশল ব্যবহার করে split করো: প্রথম অর্থকে
const-এ rename করো, compile করো, আর error-গুলো দ্বিতীয় অর্থের line দেখাক। তৃতীয়টার জন্য আবার করো। প্রতিটা split-এর পরে test (বা sample output যাচাই) করো। - Split করার পরে duplication খোঁজো: base-fare formula দুইবার টাইপ করা আছে। Surcharge line এখন কোন আগের variable reuse করতে পারে? সেটা rewrite করো।
totalCollected-এvaluevariable-টাও অনেকবার assign হয়। তুমি কি এটা split করবে? Loop-counter/collecting-variable exception ব্যবহার করে এক লাইনে ব্যাখ্যা করো, আর Figure 7-এর quadrant chart-এ বসিয়ে উত্তর যাচাই করো।- কলেজ শিক্ষার্থী: মূল
journeyReport-এর SSA form হাতে লিখে বের করো —value1,value2,value3, প্রতিটা assignment-এ একটা। তোমার subscript করা নামগুলো task 1-এ বেছে নেওয়া সৎ নামগুলোর সাথে তুলনা করো। রাত ২টায় debug করতে কোন version পছন্দ করবে, আর এটা তোমাকে কী বলে যে আমরা সংখ্যার বদলে অর্থপূর্ণ নাম দিয়ে split করি? - Bonus:
totalCollected-এর accumulator-কে যাই হোকtotal-এর মতো সৎ নামে rename করো, আর তোমার project-এprefer-constlint rule চালু করো। তোমার real codebase-এ এটা কতটা এক-বালতির variable খুঁজে পায়? আজই একটা ঠিক করো।
তোমার code-এর প্রতিটা variable যখন লেবেল করা বালতি হবে যেটা ঠিক এক ধরনের পানি ধরে, তখন তোমার পাঠক — ভবিষ্যতের তুমি আর তৃষ্ণার্ত জামাল — কখনো অনুমান করতে হবে না সে কী পান করছে। এতে temporary variable-এর উপর আমাদের trio সম্পূর্ণ হয়: Inline Temp অকার্যকরগুলো সরায়, Replace Temp with Query shared-গুলোকে promote করে, আর Split Temporary Variable অতিরিক্ত কাজে বোঝাই হওয়াগুলো সোজা করে দেয়।
সচরাচর জিজ্ঞাসা
- কীভাবে বুঝব একটা variable দুইটা কাজ করছে?
- ওর assignment-গুলো দেখো। যদি দ্বিতীয় assignment সম্পূর্ণ আলাদা অর্থ বহন করে — প্রথমে area ধরেছিল, পরে price — তাহলে দুইটা কাজ হচ্ছে। সহজ লক্ষণ হলো temp, result বা value-এর মতো অস্পষ্ট নাম, কারণ দুই অর্থের জন্য কোনো একটা সৎ নাম খুঁজে পাওয়া যায় না।
- Loop counter আর running total-ও কি split করতে হবে?
- না। i-এর মতো loop counter আর sum-এর মতো collecting variable ডিজাইন অনুযায়ীই পরিবর্তন হয় — গণনা করা আর জমানো এদের একটাই কাজ। Fowler স্পষ্টভাবে এগুলো বাদ দিয়েছেন। শুধু তখনই split করো যখন assignment-গুলো সম্পূর্ণ আলাদা, সম্পর্কহীন জিনিস বোঝায়।
- Split করা variable-গুলোকে const বা readonly কেন করব?
- দুটো কারণ। এটা প্রমাণ করে যে split সঠিক হয়েছে — যদি const compile হয়, সেই variable সত্যিই একটাই assignment পেয়েছে। আর এটা ভবিষ্যতের পাঠককে বলে: এই line-এর পরে এই value আর বদলাবে না, তাই বাকি method পড়তে নিশ্চিন্তে থাকো।
- যদি reuse হওয়া variable-টা method parameter হয়?
- সেটা আলাদা ক্ষেত্র, এর নিজস্ব refactoring আছে: Remove Assignments to Parameters। ধারণাটা একই — parameter-এ নতুন value assign করা মানে তার নামকে দ্বিতীয় কাজে ব্যবহার করা। একটা নতুন local variable বানাও আর parameter-টা অক্ষত রাখো।
- Split Temporary Variable আর Split Variable কি একই জিনিস?
- হ্যাঁ। Fowler-এর Refactoring বইয়ের প্রথম সংস্করণে নাম ছিল Split Temporary Variable। দ্বিতীয় সংস্করণে ছোট করে Split Variable রাখা হয়েছে, আর parameter-এর ক্ষেত্রটাও এতে ঢুকেছে। একই technique, একটু বড় পরিসর, নতুন নাম।
আরো দেখো
সম্পর্কিত পাঠ
Replace Temp with Query: তাজা জিজ্ঞেস করো, বাসি চিরকুটে ভরসা করো না
ক্যান্টিনের সিঙ্গারার গল্প দিয়ে Replace Temp with Query বোঝো — TypeScript আর C# উদাহরণ, নিরাপদ ধাপ, আর একটাই সত্যের উৎস।
Inline Temp: একবারই ব্যবহার করা রাফ নোটটা ছুঁড়ে ফেলো
Inline Temp রিফ্যাক্টরিং শেখো একটা মজার রাফ পেপারের গল্প দিয়ে — TypeScript আর C# উদাহরণ, নিরাপদ ধাপ, IDE shortcut, আর কখন variable inline করা উচিত না সেটাসহ।
Extract Variable: ছোট ছোট নামওয়ালা ধাপে বড় হিসাব সমাধান করো
Extract Variable শেখো ধাপে ধাপে। একটা বিশাল, জটিল expression কে ছোট ছোট নামওয়ালা অংশে ভাগ করো — ঠিক যেভাবে গণিতের খাতায় কাজ দেখাও।
Extract Method: একটা বিশাল ফাংশনকে ছোট ছোট নামওয়ালা helper-এ ভাগ করো
Extract Method ধাপে ধাপে শিখে নাও। একটা লম্বা ফাংশন থেকে এলোমেলো block বের করে তাকে একটা পরিষ্কার নাম দাও, আর তোমার কোডকে একটা সহজ to-do লিস্টের মতো পড়ার যোগ্য করে তোলো।