فصل 1. بدهی فنی / Technical debt
technical debt وظیفهایست که در آینده باید انجام شود زیرا امروز به دلایلی انجام نشده.
مثلا از نوشتن تستها به دلیل کمبود وقت صرف نظر میکنیم تا به زمان تحویل پروژه برسیم.
به طور کلی technical debt زمانی اتفاق میافتد که به هر دلیلی تحویل کار از کیفیت آن مهمتر باشد.
میتوان technical debt را به دو نوع کلی تقسیم کرد.
- برنامهریزی شده
این نوع از بدهی با برنامهریزی تیم جهت رسیدن به اهداف مهمتر و همچنین در نظر گرفتن زمان مناسب برای پرداختن به آنها صورت میگیرد.
مثالی که در ابتدا زدم از همین نوع بود. تیم از نوشتن تستها در مدتی مشخص صرف نظر میکند تا به deadline پروژه برسد و پس از آن زمانی را برای نوشتن تستها در نظر میگیرد.
- ناخواسته
هیچ برنامه ریزی برای آن وجود نداشته و به عنوان مثال تجربه کم علت بروز چنین اتفاقی شده.
technical debt هم خوب است و هم بد. فرض کنید تصمیم به خرید چیزی گرفتهاید در حالی که به اندازه کافی پول برایش ندارید. فروشنده پیشنهاد خرید قسطی را میهد و قبول میکنید. خوب است زیرا شما صاحب آن شدهاید و بد است چون تا چند سال آینده مشغول پرداخت قسط خواهید بود که شامل بازپرداخت اصل پول به همراه سود آن است. این اقساط قدرت خرید شما را در آینده محدود و پیشرفتتان را کند خواهد کرد.
فصل 2. بوی بد کد / Code Smell
code smell ها نشانههایی هستند که با مشکلات نرمافزار مرتبطاند و ممکن است به شما در یافتن مشکلات کمک کنند. محلی که قرار است از تکنیکهای refactoring استفاده شود.
این اصطلاح اولین بار توسط Martin Fowler مطرح شد. اما دلیل این نام گذاری چیست؟ اول اینکه بوها قابل تشخیص هستند و بیشتر اوقات شما را به سمت مشکل خواهند برد و دوم، ممکن است گاهی نشانهی مشکلی نباشند!
همچنین code smell ها شاید در حال حاضر از دید فنی بدون ایراد به نظر برسند اما ممکن است در آینده باعث کند شدن توسعه و بروز مشکلات شوند.
برای درک بهتر smell ها و داشتن یک تصویر کلی، آنها به 5 گروه تقسیم شدهاند. اینجا به توضیح مختصرشان میپردازیم و در ادامه با تکنیکهای refactoring بازسازی/بازنویسی و اصلاحشان را انجام خواهیم داد.
فصل 3. Extract Method
تبدیل متدهای بزرگ و طولانی به چند متد کوچکتر. این کار باعث افزایش خوانایی، استفاده مجدد، حذف کد تکراری و انسجام بهتر کلاس و متدها خواهد شد. متدهای بزرگی که داخل بدنه خود چند وظیفه دارند را به چند متد تک وظیفهای تبدیل کنید.
public function delete(int id)
{
order = this.find(id)
order.delete()
}
وظایف را از هم تفکیک کنید و سپس برای هر وظیفه یک متد جدید بنویسید.
public function delete(Order order)
{
order.delete()
}
public function findByID(int id)
{
order = this.find(id)
return order
}
فصل 4. Introduce Foreign Method
مناسبترین کلاس برای تعریف یک متد جدید، کلاسی است که در آن فیلدهای مورد نیاز متد وجود داشته باشد. این کار باعث کاهش وابستگی خواهد شد زیرا دیگر نیاز به استفاده از فیلد و متد سایر کلاسها ندارید.
اما همیشه این کار قابل انجام نیست، به عنوان مثال اضافه کردن متد به کتابخانهها و کلاسهای کمکی وجود ندارد یا اینکه نباید چنین کاری را انجام داد.
class Report
{
public function sendReport()
{
nextDay = new Date(previousEnd.getYear(), previousEnd.getMonth(), previousEnd.getDate() + 1)
// ...
}
}
در مثال بالا هر چند با استفاده از پارامترهای کتابخانه Date به روز بعد رسیدیم اما این روش درست نیست زیرا ممکن است در بخشهای مختلف برنامه به آن نیاز داشته باشیم و تکرار شود.
حال آنکه نمیتوان متد جدید را داخل کتابخانه Date افزود، در کلاس کلاینت خود یک متد با پارامتری از جنس کلاس Date تعریف و استفاده کنید.
class Report
{
public function sendReport()
{
Date newStart = this._nextDay(previousEnd)
// ...
}
private function _nextDay(Date arg)
{
return new Date(arg.getYear(), arg.getMonth(), arg.getDate() + 1)
}
}
فصل 5. Replace Array with Object
آرایههایی شامل مقادیر ناهمگن(غیر مشابه) خوانایی کافی را ندارد.
String[] row = new String[3]
row[0] = "refactoring"
row[1] = "[email protected]"
row[2] = "157298"
همچنین ممکن است مانند آخرین عنصر مجبور شوید عدد را به عنوان رشته تعریف کنید در صورتی که تایپ آن مناسب نیست.
در چنین حالتی آرایه را با یک شئ جایگزین و برای هر عنصر یک فیلد در نظر بگیرید.
Performance row = new Performance()
row.setName("refactoring")
row.setEmail("[email protected]")
row.setID(157298)
فصل 6. Replace Nested Conditional with Guard Clauses
شرطهای تو در تو را جهت درک بهتر جریان کد، ساده کنید.
if (number >= 0)
{
if (number == 0)
{
return "the number is zero"
}
else
{
return "positive number"
}
}
else
{
return "negative number"
}
حالت ایدهآل عبارات شرطی، تعدادی عبارت هم سطح است.
if (number < 0)
{
return "negative number"
}
if (number == 0)
{
return "the number is zero"
}
return "positive number"
فصل 7. Parameterize Method
متدهایی که عمل مشابهی انجام میدهند اما در مقادیر بدنه خود با هم متفاوتاند را ادغام کنید.
date = new Date
date.addOneWeek()
date.addTwoWeek()
date.addThreeWeek()
date.addFourWeek()
متدهای تکراری یعنی کد تکراری.
date = new Date
date.addWeeks(1)
date.addWeeks(2)
date.addWeeks(3)
date.addWeeks(4)
فصل 8. Pull Up Constructor Body
subclass constructor هایی که مشابه constructor کلاس پدر عمل میکنند را ویرایش و به جای آن constructor پدر را صدا بزنید.
class Employee
{
public function Employee(data)
{
parentConstructor(data) // call parent construct
}
}