Refactoring چیست؟

Refactoring چیست؟

آموزش فارسی ریفکتورینگ (بخش دوم)

 


ریفکتورینگ کدهای مارا مرتب و باز سازی میکند بدون اینکه تغییری در خروجی کد های ما داشته باشد و معنا و قصد اصلی نویسنده کد را حفظ میکند. ما باید به نحوی کدهای خودرا پاکسازی کنیم که دیگر توسعه دهندگان از دیدن کدهای ما راضی باشند و بتوانند به راحتی کدهای مارا توسعه بدهند. ریفکتورینگ باید با دقت انجام شود تا از تغییر معنی وخروجی کدهای ما جلوگیری شود. از مزایای آن میتوان به بهبود قابلیت خواندن کد و کاهش پیچیدگی کدها نام برد و این میتواند قابلیت نگهدراری کد های مارا بهبود بخشد و کدهارا برای توسعه و گسترش مرتب تر و واضح تر نمایش بدهد. اگر ریفکتورینگ به خوبی انجام شود به توسعه دهندگان نرم افزاراین امکان را میدهد تا قسمت های آسیب پذیر پنهان یا خاموش سیستم خود را شناسایی کرده و سطوح پیچیده غیر ضروری را حذف کنند.

 

ریفکتورینگ در برنامه نویسی

آموزش فارسی ریفکتورینگ (بخش دوم)

 

ریفکتورینگ کدهای مارا مرتب و باز سازی میکند بدون اینکه تغییری در خروجی کد های ما داشته باشد و معنا و قصد اصلی نویسنده کد را حفظ میکند. ما باید به نحوی کدهای خودرا پاکسازی کنیم که دیگر توسعه دهندگان از دیدن کدهای ما راضی باشند و بتوانند به راحتی کدهای مارا توسعه بدهند. ریفکتورینگ باید با دقت انجام شود تا از تغییر معنی وخروجی کدهای ما جلوگیری شود. از مزایای آن میتوان به بهبود قابلیت خواندن کد و کاهش پیچیدگی کدها نام برد و این میتواند قابلیت نگهدراری کد های مارا بهبود بخشد و کدهارا برای توسعه و گسترش مرتب تر و واضح تر نمایش بدهد. اگر ریفکتورینگ به خوبی انجام شود به توسعه دهندگان نرم افزاراین امکان را میدهد تا قسمت های آسیب پذیر پنهان یا خاموش سیستم خود را شناسایی کرده و سطوح پیچیده غیر ضروری را حذف کنند.

 وقتی شما میخواهید یک ویژگی را به برنامه ای اضافه کنید و کدهای آن برنامه ساختار مناسبی ندارد، ابتدا باید برنامه را برای اضافه کردن این ویژگی آماده کنید و سپس آن ویژگی را به آن اضافه کنید.

در این مورد ما نیاز به تغییراتی داریم که کاربران دوست دارند از آنها استفاده کنند و آنها را انجام بدهند.
در صورتی که کاربر بخواهد تغییراتی را اعمال کند، در ابتدا یک جمله چاپ شده به وسیله  html نیاز دارند. در نظر بگیرید که این تغییر چه اثری خواهد داشت. ما با اضافه کردن اظهارات شرطی در اطراف هر بیانیه ای که رشته ای را به نتیجه اضافه می کند، مواجه هستیم. این باعث می شود تا یک تابع پیچیده تر شود. در مواجهه با آن، اکثر مردم ترجیح می دهند این متد را کپی کرده و آن را تغییر دهند تا HTML را ایجاد کنند. ساخت یک متد کپی ممکن است کار چندانی نداشته باشد، اما مشکلات زیادی را برای آینده ایجاد می کند. هر تغییری در پر کردن یا شارژ کردن منطقی و برای حاصل شدن اطمینان از آنها، مارا مجبور میکند به طور مداوم هر دو متد را بروز رسانی کنیم. اگر ما یک برنامه نوشتیم که نخواهیم آنرا دوباره تغییر دهیم، این نوع کپی و چسباندن خوب است. اما اگر یک برنامه طولانی مدت باشد، نسخه برداری و کپی کردن یک تهدید است.

بازیگران به دنبال انجام نمایشنامه های بیشتری هستند: آنها امیدوارند که نمایشنامه های تاریخی، معنوی، معنوی خنده دار، معنوی تاریخی، تاریخی ترسناک، ترسناک-تاریخی-خنده دار-معنوی، صحنه فردی و شعر های نامحدود به رپرتوار اضافه کنند. آنها دقیقا نمیدانند چه کاری و چه زمانی میخواهند انجام دهند. این تغییر هر دو نحوه پرداخت هزینه های آنها و نحوه محاسبه اعتبارات را تحت تاثیر قرار می دهد. ما بعنوان یک توسعه دهنده با تجربه، می توانیم مطمئن باشیم که هر برنامه ای که با آن روبرو می شویم، در عرض شش ماه دوباره آن را تغییر خواهیم داد. این روش بیانیه ای است که باید تغییرات را برای مقابله با تغییرات در طبقه بندی و قوانین پرکردن یا شارژ کردن انجام داد. اما اگر من بخواهم بیانیه را  داخل HTML کپی کنم باید مطمئن باشم که هرگونه تغییری سازگار باشد. این امر سخت تر خواهد بود تا دریابیم که کجا تغییرات را ایجاد کرده و سخت تر است که آنها را بدون اشتباه انجام دهیم. اجازه دهید بگویم که این تغییرات است که نیاز به انجام refactoring را بر عهده دارد.

اولین گام در Refactoring


هربار که ما بخواهیم از تکنیک refactorig استفاده کنیم، اولین گام همیشه همین است. ما باید مطمئن شویم که مجموعه ای جامع از آزمایشات در این کد ها وجود داشته باشد. حتی اگر ما از refactorings پیروی کنیم، چون انسان هستیم و هنوز هم اشتباه می کنیم به همین دلیل تست ها برای نشان دادن اشکالات و اشتباهات ما  بسیار ضروری است. هرچه برنامه بزرگتر باشد، بیشتر احتمال دارد که تغییرات ما باعث ناسازگاری شود و در عصر دیجیتال  اسم این ضعف نرم افزار است.

از آنجا که بیانیه یک رشته را باز می کند چیزی که ما انجام می دهیم چند فاکتور است وهر فاکتور چند اجرای مختلفی از نمایشنامه ها را ارائه می کند. سپس یک مقایسه بین رشته و رشته جدید و برخی رشته های مرجع انجام می دهیم که به صورت دستی کنترل می شود. تمام این تست ها را با استفاده از یک چارچوب تست نصب کردیم، بنابراین می توانیم آنها را فقط با عمل دستکاری کلید در محیط توسعه اجرا کنیم. آزمایش ها فقط چند ثانیه برای اجرا انجام می شوند و همانطور که مشاهده خواهید کرد، ما اغلب آنها را اجرا می کنیم.

بخشی از این آزمون روش گزارش نتیجه است، به این معنی که هنگام آزمایش اگر رشته ها با رشته های مرجع یکسان باشند هر دو سبز میشوند و اگر متفاوت باشند خطوط اشتباه قرمز میشوند. بنابر این برای انجام آزمایشات و چک کردن، این تست ها خود آزمایش میشوند.

 قبل از اینکه شما شروع به طراحی مجدد کنید، مطمئن شوید که مجموعه ای جامع از آزمون ها دارید. این تست ها باید خود را  نیزچک کنند.

همانطور که ما   refactoring را انجام می دهیم، روی آزمایشات هم تکیه میکنیم. باید فکر کنیم آنها چیزی هستند که از ما در برابر اشتباهات محافظت میکنند. ما باید در کد و آزمایشی که میخواهیم دوباره انجام بدهیم اشتباه کنیم، تا جستجوگر یا ردیاب را گمراه کنیم. با دوبار چک کردن، ما احتمال انجام دادن اشتباه را کاهش میدهیم. اگر چه ساختن چنین آزمایشاتی سخت است اما در نهایت به طور قابل ملاحظه ای در زمان صرفه جویی کردیم و با زمان کمتری اشکال زدایی میکنیم. این بخش مهمی از ریفکتورینگ است که ما یک فصل کامل را به آن اختصاص میدهیم.

تجزیه کردن تابع STATEMENT
هنگام بازنویسی یک عملکرد طولانی مانند این، ما باید سعی کنیم ذهنی نقاطی را شناسایی کنیم که بخش های مختلف رفتار کلی را جدا کند. اولین تکه کدی که به چشم میخورد شرح کد سوئیچی است که در آن وسط است.


 

function statement (invoice, plays) {
let totalAmount = 0;
let volumeCredits = 0;
let result = `Statement for ${invoice.customer}\n`;
const format = new Intl.NumberFormat("enUS",
{ style: "currency", currency: "USD",
minimumFractionDigits: 2 }).format;
for (let perf of invoice.performances) {
const play = plays[perf.playID];
let thisAmount = 0;
switch (play.type) {
case "tragedy":
thisAmount = 40000;
if (perf.audience > 30) {
thisAmount += 1000 * (perf.audience 30)
;
}
break;
case "comedy":
thisAmount = 30000;
if (perf.audience > 20) {
thisAmount += 10000 + 500 * (perf.audience 20)
;
}
thisAmount += 300 * perf.audience;
break;
default:
throw new Error(`unknown type: ${play.type}`);
}
// add volume credits
volumeCredits += Math.max(perf.audience 30,
0);
// add extra credit for every ten comedy attendees
if ("comedy" === play.type) volumeCredits += Math.floor(perf.audience / 5);
// print line for this order
result += ` ${play.name}: ${format(thisAmount/100)} (${perf.audience} seats)\totalAmount += thisAmount;
}
result += `Amount owed is ${format(totalAmount/100)}\n`;
result += `You earned ${volumeCredits} credits\n`;
return result;
}


همینطور که به این تکه کد نگاه میکنیم، نتیجه میگیریم که محاسبه شارژ برای یک عملکرد است. این یک شکل جسورانه ذخیره سازی است که ما به head  میفهمانیم. ما باید آنرا از پشت هد به کد خودمان حرکت دهیم. به این ترتیب باید بعدا به آن برگردیم. کد به ما میگوید مجبور نیستیم آنچه را که انجام میدهیم دوباره بفهمیم. برای اینکه این کد قابل فهم باشد باید این تکه کد را به عملکرد خود تبدیل کنیم و باید اسم آنرا چیزی شبیه مقدارFor قرار دهیم. وقتی میخواهیم یک تکه کد را به یک تابع مانند این تغییر دهیم، ما یک روش برای انجام این کار داریم که احتمال شانس گرفتن اشتباه را به حداقل می رساند. ما این روش را نوشتیم و آن را به مرجع ساده تبدیل کردیم  و آن را Extract Function نامگذاری کردیم.

اول، ما باید به قطعه ای برای هر متغیری نگاه کنیم که بعد از استخراج کد به عملکرد خودش، دیگر در محدوده آن قرار نگیرد. ما سه مورد داریم:  pref ، play و thisAmount.  دو مورد اول توسط کد استخراج استفاده می شود، اما تغییر نکرده است، بنابراین می توانیم آنها را به عنوان پارامترها منتقل کنیم. متغیرهای اصلاح شده نیاز بیشتری به مراقبت دارند. در اینجا فقط یکی وجود دارد و میتوانیم آنرا برگردانیم. ما می توانم مقدار اولیه آن را داخل کد استخراج کنیم. همه ی اینها این را بدست می آورند:


تابع statement...

 

function amountFor(perf, play) {
let thisAmount = 0;
switch (play.type) {
case "tragedy":
thisAmount = 40000;
if (perf.audience > 30) {
thisAmount += 1000 * (perf.audience 30)
;
}
break;
case "comedy":
thisAmount = 30000;
if (perf.audience > 20) {
thisAmount += 10000 + 500 * (perf.audience 20)
;
}
thisAmount += 300 * perf.audience;
break;
default:
throw new Error(`unknown type: ${play.type}`);
}
return thisAmount;
}

 

وقتی از یک هدر به عنوان "function someName ..." با خط ایتالیایی استفاده میکنیم، این بدان معنی است که کد زیر در محدوده تابع، فایل یا کلاس در هدر نامیده می شود.
کد اصلی statment که اکنون در این تابه صدا میزنیم برای جمع کردن thisAmount است:

 


function statement (invoice, plays) {
let totalAmount = 0;
let volumeCredits = 0;
let result = `Statement for ${invoice.customer}\n`;
const format = new Intl.NumberFormat("enUS",
{ style: "currency", currency: "USD",
minimumFractionDigits: 2 }).format;
for (let perf of invoice.performances) {
const play = plays[perf.playID];
let thisAmount = amountFor(perf, play);
// add volume credits
volumeCredits += Math.max(perf.audience 30,
0);
// add extra credit for every ten comedy attendees
if ("comedy" === play.type) volumeCredits += Math.floor(perf.audience / 5);
// print line for this order
result += ` ${play.name}: ${format(thisAmount/100)} (${perf.audience} seats)\totalAmount += thisAmount;
}
result += `Amount owed is ${format(totalAmount/100)}\n`;
result += `You earned ${volumeCredits} credits\n`;
return result;


هنگامی که این تغییرات را انجام دادیم، باید بلافاصله کامپایلر را تست کنیم تا ببینیم آیاچیزی را شکسته ایم یا نه و این عادت مهمی است که باید بعد از هر بار refactoring انجام بدهیم هرچند که تابع یا کد ما ساده باشد.

منبع مورد استفاده: 

refactoring.improving.the.design.of.existing.code.2nd.edition

در بخش ها بعد "refactoring چیست" به موارد بیشتری خواهیم پرداخت. با تشکر - مجموعه ABLY

نظرات یا سوالات خودرا با ما درمیان بگذارید

0912 097 5516 :شماره تماس
0713 625 1757 :شماره تماس