هنگام یادگیری برنامه نویسی، زمان زیادی برای نوشتن کد صرف می شود. اکثر توسعه دهندگان مبتدی بر این باورند که این فعالیت آینده آنهاست. این تا حدی درست است، اما وظایف یک برنامه نویس همچنین شامل نگهداری و بازسازی کد است. امروز در مورد refactoring صحبت خواهیم کرد.
Refactoring در دوره JavaRush
دوره JavaRush دو بار مبحث refactoring را پوشش می دهد:- چالش بزرگ در سطح 5 از تلاش Multithreading .
- سخنرانی در مورد refactoring در Intellij IDEA در سطح 9 جستجوی مجموعه های جاوا .
Refactoring چیست؟
این تغییر در ساختار کد بدون تغییر عملکرد آن است. به عنوان مثال، روشی وجود دارد که 2 عدد را با هم مقایسه می کند و اگر عدد اول بزرگتر باشد true و در غیر این صورت false برمی گرداند :public boolean max(int a, int b) {
if(a > b) {
return true;
} else if(a == b) {
return false;
} else {
return false;
}
}
نتیجه کد بسیار دست و پا گیر بود. حتی افراد مبتدی به ندرت چنین چیزی را می نویسند، اما چنین خطری وجود دارد. if-else
به نظر می رسد، اگر می توانید یک روش را 6 خط کوتاهتر بنویسید، چرا یک بلوک در اینجا وجود دارد :
public boolean max(int a, int b) {
return a>b;
}
اکنون این روش ساده و ظریف به نظر می رسد، اگرچه همان کار مثال بالا را انجام می دهد. refactoring اینگونه عمل می کند: ساختار کد را تغییر می دهد بدون اینکه بر ماهیت آن تأثیر بگذارد. روش ها و تکنیک های بازسازی بسیاری وجود دارد که با جزئیات بیشتر به بررسی آنها خواهیم پرداخت.
چرا بازسازی مجدد مورد نیاز است؟
دلایل متعددی وجود دارد. به عنوان مثال، پیگیری سادگی و مختصر بودن کد. طرفداران این نظریه معتقدند که رمز باید تا حد امکان مختصر باشد، حتی اگر برای درک آن نیاز به ده ها خط تفسیر باشد. سایر توسعه دهندگان بر این باورند که کد باید به گونه ای بازسازی شود که با حداقل تعداد نظرات قابل درک باشد. هر تیمی موقعیت خود را انتخاب می کند، اما باید به خاطر داشته باشیم که اصلاح مجدد یک کاهش نیست . هدف اصلی آن بهبود ساختار کد است. چندین هدف را می توان در این هدف جهانی گنجاند:- Refactoring درک کد نوشته شده توسط توسعه دهنده دیگر را بهبود می بخشد.
- به یافتن و رفع خطاها کمک می کند.
- به شما امکان می دهد سرعت توسعه نرم افزار را افزایش دهید.
- به طور کلی ترکیب نرم افزار را بهبود می بخشد.
«بوی کد»
وقتی کد نیاز به refactoring دارد، می گویند "بو می دهد". البته نه به معنای واقعی کلمه، اما چنین کدی واقعاً خیلی خوب به نظر نمی رسد. در زیر تکنیک های اصلی بازسازی را برای مرحله اولیه در نظر خواهیم گرفت.عناصر بزرگ غیر ضروری
کلاس ها و روش های دست و پا گیر زیادی وجود دارد که دقیقاً به دلیل اندازه بزرگ آنها نمی توان به طور مؤثر با آنها کار کرد.کلاس بزرگ
چنین کلاسی دارای تعداد زیادی خط کد و روش های مختلف است. معمولاً برای یک توسعهدهنده آسانتر است که به جای ایجاد یک کلاس جدید، یک ویژگی را به کلاس موجود اضافه کند، به همین دلیل است که رشد میکند. به عنوان یک قاعده، عملکرد این کلاس بیش از حد بارگذاری می شود. در این مورد، جداسازی بخشی از عملکرد به یک کلاس جداگانه کمک می کند. در بخش تکنیک های بازسازی در این مورد با جزئیات بیشتری صحبت خواهیم کرد.روش بزرگ
این «بو» زمانی رخ میدهد که یک توسعهدهنده عملکرد جدیدی را به یک روش اضافه میکند. "چرا باید بررسی پارامتر را در یک متد جداگانه قرار دهم اگر بتوانم آن را اینجا بنویسم؟"، "چرا لازم است متد را برای یافتن حداکثر عنصر در آرایه جدا کنیم، اجازه دهید آن را اینجا بگذاریم. به این ترتیب کد واضحتر میشود» و تصورات غلط دیگر. دو قانون برای بازسازی یک روش بزرگ وجود دارد:- اگر هنگام نوشتن یک متد، می خواهید یک نظر به کد اضافه کنید، باید این قابلیت را به یک متد جداگانه جدا کنید.
- اگر یک متد بیش از 10-15 خط کد می گیرد، باید وظایف و کارهای فرعی را که انجام می دهد شناسایی کنید و سعی کنید وظایف فرعی را به یک متد جداگانه جدا کنید.
- بخشی از عملکرد یک روش را به یک روش جداگانه جدا کنید.
- اگر متغیرهای محلی به شما اجازه استخراج بخشی از عملکرد را نمی دهند، می توانید کل شی را به روش دیگری منتقل کنید.
استفاده از بسیاری از انواع داده های اولیه
به طور معمول، این مشکل زمانی رخ می دهد که تعداد فیلدهای ذخیره داده در یک کلاس در طول زمان افزایش یابد. به عنوان مثال، اگر از انواع اولیه به جای اشیاء کوچک برای ذخیره داده ها (واحد پول، تاریخ، شماره تلفن و غیره) یا ثابت برای رمزگذاری هر اطلاعاتی استفاده کنید. یک تمرین خوب در این مورد، گروه بندی منطقی فیلدها و قرار دادن آنها در یک کلاس جداگانه (انتخاب یک کلاس) است. همچنین می توانید متدهایی را برای پردازش این داده ها در کلاس قرار دهید.لیست طولانی گزینه ها
یک اشتباه نسبتا رایج، به خصوص در ترکیب با یک روش بزرگ. معمولاً اگر عملکرد روش بیش از حد بارگذاری شود یا این روش چندین الگوریتم را ترکیب کند، اتفاق می افتد. درک لیست های طولانی پارامترها بسیار دشوار است و استفاده از چنین روش هایی ناخوشایند است. بنابراین، بهتر است کل شی را منتقل کنید. اگر شی داده کافی ندارد، ارزش آن را دارد که از یک شی کلی تر استفاده کنید یا عملکرد روش را تقسیم کنید تا داده های منطقی مرتبط را پردازش کند.گروه های داده
گروه های منطقی مرتبط از داده ها اغلب در کد ظاهر می شوند. به عنوان مثال، پارامترهای اتصال به پایگاه داده (URL، نام کاربری، رمز عبور، نام طرح و غیره). اگر نمی توان یک فیلد واحد را از لیست عناصر حذف کرد، پس لیست گروهی از داده ها است که باید در یک کلاس جداگانه (انتخاب کلاس) قرار گیرند.راه حل هایی که مفهوم OOP را خراب می کند
این نوع "بو" زمانی رخ می دهد که توسعه دهنده طرح OOP را نقض کند. این در صورتی اتفاق می افتد که او توانایی های این پارادایم را به طور کامل درک نکند، از آنها به طور ناقص یا نادرست استفاده کند.امتناع از ارث
اگر یک زیر کلاس از حداقل بخشی از توابع کلاس والد استفاده کند، بوی یک سلسله مراتب نادرست می دهد. به طور معمول، در این مورد، روشهای غیر ضروری به سادگی نادیده گرفته نمیشوند یا استثنائات پرتاب میشوند. اگر یک کلاس از دیگری به ارث برده شود، این به معنای استفاده تقریباً کامل از عملکرد آن است. مثالی از سلسله مراتب صحیح: مثالی از سلسله مراتب نادرست:عبارت switch
مشکل یک اپراتور چیستswitch
؟ وقتی طراحی آن بسیار پیچیده باشد بد است. این شامل بسیاری از بلوک های تو در تو نیز می شود if
.
کلاس های جایگزین با رابط های مختلف
چندین کلاس در واقع یک کار را انجام می دهند، اما نام روش های آنها متفاوت است.میدان موقت
اگر کلاس حاوی یک فیلد موقت باشد که شی فقط گاهی اوقات به آن نیاز دارد، زمانی که با مقادیر پر می شود، و بقیه زمان ها خالی یا خدای ناکردهnull
. تصمیم گیری
بوهایی که اصلاح را دشوار می کند
این "بوها" جدی تر هستند. بقیه عمدتاً درک کد را مختل می کنند، در حالی که اینها امکان تغییر آن را فراهم نمی کند. هنگام معرفی هر ویژگی، نیمی از توسعه دهندگان کار را ترک می کنند و نیمی دیگر دیوانه می شوند.سلسله مراتب موازی وراثت
هنگامی که یک زیر کلاس از یک کلاس ایجاد می کنید، باید زیر کلاس دیگری برای کلاس دیگر ایجاد کنید.توزیع وابستگی یکنواخت
هنگام انجام هر گونه تغییری، باید به دنبال تمام وابستگی ها (کاربردهای) این کلاس باشید و تغییرات کوچک زیادی ایجاد کنید. یک تغییر - ویرایش در بسیاری از کلاس ها.درخت اصلاح پیچیده
این بو برعکس بوی قبلی است: تغییرات بر تعداد زیادی از روش های همان کلاس تأثیر می گذارد. به عنوان یک قاعده، وابستگی در چنین کدی آبشاری است: با تغییر یک روش، باید چیزی را در روش دیگر و سپس در روش سوم و غیره اصلاح کنید. یک کلاس - تغییرات زیادی."بوی زباله"
دسته نسبتاً ناخوشایندی از بوها که باعث سردرد می شود. کد بی فایده، غیر ضروری، قدیمی. خوشبختانه، IDE ها و لنترهای مدرن یاد گرفته اند که در مورد چنین بوهایی هشدار دهند.تعداد زیادی نظرات در روش
این روش تقریباً در هر خط نظرات توضیحی زیادی دارد. این معمولاً با یک الگوریتم پیچیده همراه است، بنابراین بهتر است کد را به چندین روش کوچکتر تقسیم کنید و نام های معنی دار برای آنها بگذارید.تکرار کد
کلاس ها یا روش های مختلف از بلوک های کد مشابهی استفاده می کنند.کلاس تنبل
این کلاس عملکرد بسیار کمی دارد، اگرچه بسیاری از آن برنامه ریزی شده بود.کد استفاده نشده
یک کلاس، متد یا متغیر در کد استفاده نمی شود و "وزن مرده" است.جفت شدن بیش از حد
این دسته از بوها با تعداد زیادی اتصالات غیر ضروری در کد مشخص می شود.روش های شخص ثالث
یک روش از داده های یک شی دیگر بسیار بیشتر از داده های خود استفاده می کند.صمیمیت نامناسب
یک کلاس از فیلدهای سرویس و متدهای کلاس دیگر استفاده می کند.تماس های طولانی کلاس
یک کلاس کلاس دیگری را فراخوانی می کند که از کلاس سوم، از کلاس چهارم و غیره داده درخواست می کند. چنین زنجیره طولانی تماس ها به معنای سطح بالایی از وابستگی به ساختار کلاس فعلی است.کلاس-کار-فروشنده
یک کلاس فقط برای انتقال یک کار به کلاس دیگر مورد نیاز است. شاید باید حذف شود؟تکنیک های بازسازی
در زیر در مورد تکنیک های بازسازی اولیه صحبت خواهیم کرد که به از بین بردن بوی کد شرح داده شده کمک می کند.انتخاب کلاس
کلاس عملکردهای زیادی را انجام می دهد، برخی از آنها باید به کلاس دیگری منتقل شوند. به عنوان مثال، یک کلاس وجود داردHuman
که شامل آدرس مسکونی و متدی است که آدرس کامل را ارائه می دهد:
class Human {
private String name;
private String age;
private String country;
private String city;
private String street;
private String house;
private String quarter;
public String getFullAddress() {
StringBuilder result = new StringBuilder();
return result
.append(country)
.append(", ")
.append(city)
.append(", ")
.append(street)
.append(", ")
.append(house)
.append(" ")
.append(quarter).toString();
}
}
ایده خوبی است که اطلاعات آدرس و روش (رفتار پردازش داده) را در یک کلاس جداگانه قرار دهید:
class Human {
private String name;
private String age;
private Address address;
private String getFullAddress() {
return address.getFullAddress();
}
}
class Address {
private String country;
private String city;
private String street;
private String house;
private String quarter;
public String getFullAddress() {
StringBuilder result = new StringBuilder();
return result
.append(country)
.append(", ")
.append(city)
.append(", ")
.append(street)
.append(", ")
.append(house)
.append(" ")
.append(quarter).toString();
}
}
انتخاب روش
اگر هر یک از عملکردهای یک متد را بتوان گروه بندی کرد، باید آن را در یک متد جداگانه قرار داد. به عنوان مثال، روشی که ریشه های یک معادله درجه دوم را محاسبه می کند:public void calcQuadraticEq(double a, double b, double c) {
double D = b * b - 4 * a * c;
if (D > 0) {
double x1, x2;
x1 = (-b - Math.sqrt(D)) / (2 * a);
x2 = (-b + Math.sqrt(D)) / (2 * a);
System.out.println("x1 = " + x1 + ", x2 = " + x2);
}
else if (D == 0) {
double x;
x = -b / (2 * a);
System.out.println("x = " + x);
}
else {
System.out.println("Equation has no roots");
}
}
بیایید محاسبه هر سه گزینه ممکن را به روش های جداگانه منتقل کنیم:
public void calcQuadraticEq(double a, double b, double c) {
double D = b * b - 4 * a * c;
if (D > 0) {
dGreaterThanZero(a, b, D);
}
else if (D == 0) {
dEqualsZero(a, b);
}
else {
dLessThanZero();
}
}
public void dGreaterThanZero(double a, double b, double D) {
double x1, x2;
x1 = (-b - Math.sqrt(D)) / (2 * a);
x2 = (-b + Math.sqrt(D)) / (2 * a);
System.out.println("x1 = " + x1 + ", x2 = " + x2);
}
public void dEqualsZero(double a, double b) {
double x;
x = -b / (2 * a);
System.out.println("x = " + x);
}
public void dLessThanZero() {
System.out.println("Equation has no roots");
}
کد هر روش بسیار کوتاه تر و واضح تر شده است.
انتقال کل شی
هنگام فراخوانی یک متد با پارامترها، گاهی اوقات می توانید کدهایی مانند زیر را مشاهده کنید:public void employeeMethod(Employee employee) {
// Некоторые действия
double yearlySalary = employee.getYearlySalary();
double awards = employee.getAwards();
double monthlySalary = getMonthlySalary(yearlySalary, awards);
// Продолжение обработки
}
public double getMonthlySalary(double yearlySalary, double awards) {
return (yearlySalary + awards)/12;
}
در روش، employeeMethod
2 خط برای به دست آوردن مقادیر و ذخیره آنها در متغیرهای اولیه اختصاص داده می شود. گاهی اوقات چنین طرح هایی تا 10 خط طول می کشد. انتقال خود شی به روش بسیار ساده تر است ، از آنجا می توانید داده های لازم را استخراج کنید:
public void employeeMethod(Employee employee) {
// Некоторые действия
double monthlySalary = getMonthlySalary(employee);
// Продолжение обработки
}
public double getMonthlySalary(Employee employee) {
return (employee.getYearlySalary() + employee.getAwards())/12;
}
ساده، کوتاه و مختصر.
گروه بندی منطقی فیلدها و قرار دادن آنها در یک کلاس جداگانه
علیرغم این واقعیت که مثالهای بالا بسیار ساده هستند و هنگام نگاه کردن به آنها، بسیاری ممکن است این سوال را بپرسند که «در واقع چه کسی این کار را انجام میدهد؟»، بسیاری از توسعهدهندگان به دلیل بیتوجهی، عدم تمایل به اصلاح مجدد کد، یا به سادگی «این کار را انجام خواهد داد»، این کار را انجام میدهند. خطاهای ساختاری مشابهچرا refactoring موثر است
نتیجه یک refactoring خوب برنامه ای است که کد آن به راحتی خوانده می شود، تغییرات در منطق برنامه به تهدید تبدیل نمی شود و معرفی ویژگی های جدید به جهنم تجزیه کد تبدیل نمی شود، بلکه به یک فعالیت خوشایند برای چند روز تبدیل می شود. . اگر بازنویسی برنامه از ابتدا آسانتر باشد، نباید از Refactoring استفاده شود. به عنوان مثال، تیم هزینه های نیروی کار برای تجزیه، تجزیه و تحلیل و بازسازی کد را بالاتر از اجرای همان عملکرد از ابتدا تخمین می زند. یا کدهایی که نیاز به اصلاح مجدد دارند دارای خطاهای زیادی هستند که اشکال زدایی آنها دشوار است. دانستن چگونگی بهبود ساختار کد در کار یک برنامه نویس الزامی است. خوب، بهتر است برنامه نویسی جاوا را در JavaRush یاد بگیرید - یک دوره آنلاین با تاکید بر تمرین. بیش از 1200 کار با تأیید فوری، حدود 20 پروژه کوچک، وظایف بازی - همه اینها به شما کمک می کند تا در کدنویسی احساس اطمینان کنید. بهترین زمان برای شروع همین الان است :)منابعی برای غواصی بیشتر در بازسازی
مشهورترین کتاب در مورد بازسازی مجدد، «بازسازی مجدد است. بهبود طراحی کدهای موجود» نوشته مارتین فاولر. همچنین یک انتشار جالب در مورد بازسازی مجدد وجود دارد که بر اساس کتاب قبلی - "بازسازی با الگوها" توسط جاشوا کیریفسکی نوشته شده است. صحبت از قالب ها شد. هنگام بازسازی، دانستن الگوهای اصلی طراحی اپلیکیشن همیشه بسیار مفید است. این کتاب های عالی به این امر کمک خواهند کرد:- "الگوهای طراحی" - توسط اریک فریمن، الیزابت فریمن، کتی سیرا، برت بیتس از سری Head First.
- "کد خواندنی، یا برنامه نویسی به عنوان یک هنر" - داستین باسبول، ترور فاچر.
- "کد کامل" نوشته استیو مک کانل، که اصول کد زیبا و ظریف را تشریح می کند.
GO TO FULL VERSION