داشتن آگاهی در مورد ساختارهای دادهها، الگوریتمها و یا عملگرهای بیتی بسیار عالی است و یا تسلط بر نحوهی کارکرد ابزارهایی مانند SharePoint و امثال آن این روزها ضروری است. اما باید در نظر داشت، کدی که امروز تهیه میشود شاید فردا یا ماه دیگر یا چند سال بعد نیاز به تغییر داشته باشد، بنابراین دانش زیبا نوشتن یک قطعه کد که خواندن آنرا سادهتر میکند و در آینده افرادی که از آن نگهداری خواهند کرد زیاد "زجر" نخواهند کشید، نیز ضروری میباشد. (اگر کامنتهای سایت را خوانده باشید یکی از دوستان پیغام گذاشته بود، اگر به من بگویند یک میلیون بگیرید و برنامه فعلی را توسعه دهید یا رفع اشکال کنید، حاضرم 10 هزارتومان بگیرم و آنرا از صفر بنویسم! متاسفانه این یک واقعیت تلخ است که ناشی از عدم خوانا بودن کدهای نوشته شده میباشد.)
در ادامه یک سری از اصول زیبا نویسی کدها را بررسی خواهیم کرد.
1- سعی کنید میزان تو در تو بودن کدهای خود را محدود کنید.
لطفا به مثال زیر دقت نمائید:
void SetA()
{
if(a == b)
{
foreach(C c in cs)
{
if(c == d)
{
a = c;
}
}
}
}
توصیه شده است فقط یک سطح تو در تو بودن را در یک تابع لحاظ کنید. تابع فوق 4 سطح تو رفتگی را نمایش میدهد (برای رسیدن به a=c باید چهار بار از tab استفاده کنید). برای کاهش این تعداد سطح میتوان به صورت زیر عمل کرد:
void SetA()
{
if(a != b)
return;
foreach(C c in cs)
a = GetValueOfA(c);
}
TypeOfA GetValueOfA(C c)
{
if(c == d)
return c;
return a;
}
خواناتر نشد؟!
افزونههای CodeRush و refactor pro مجموعهی DevExpress از لحاظ مباحث refactoring در ویژوال استودیو حرف اول را میزنند. فقط کافی است برای مثال قطعه کد if داخلی را انتخاب کنید، بلافاصله سه نقطه زیر آن ظاهر شده و با کلیک بر روی آن امکان استخراج یک تابع از آنرا برای شما به سرعت فراهم خواهد کرد.
مثالی دیگر:
if (foo) {
if (bar) {
// do something
}
}
به صورت زیر هم قابل نوشتن است (جهت کاهش میزان nesting):
if (foo && bar) {
// do something
}
افزونهی Resharper امکان merge خودکار این نوع if ها را به همراه دارد.
و یا یک مثال دیگر:
میزان تو در تو بودن این تابع جاوا اسکریپتی را ملاحظه نمائید:
function findShape(flags, point, attribute, list) {
if(!findShapePoints(flags, point, attribute)) {
if(!doFindShapePoints(flags, point, attribute)) {
if(!findInShape(flags, point, attribute)) {
if(!findFromGuide(flags,point) {
if(list.count() > 0 && flags == 1) {
doSomething();
}
}
}
}
}
}
آنرا به صورت زیر هم میتوان نوشت با همان کارآیی اما بسیار خواناتر:
function findShape(flags, point, attribute, list) {
if(findShapePoints(flags, point, attribute)) {
return;
}
if(doFindShapePoints(flags, point, attribute)) {
return;
}
if(findInShape(flags, point, attribute)) {
return;
}
if(findFromGuide(flags,point) {
return;
}
if (!(list.count() > 0 && flags == 1)) {
return;
}
doSomething();
}
2- نامهای با معنایی را برای متغیرها وتوابع خود انتخاب کنید.
با وجود پیشرفتهای زیادی که در طراحی و پیاده سازی IDE ها صورت گرفته و با بودن ابزارهای تکمیل سازی خودکار متن تایپ شده در آنها، این روزها استفاده از نامهای بلند برای توابع یا متغیرها مشکل ساز نیست و وقت زیادی را تلف نخواهد کرد. برای مثال به نظر شما اگر پس از یک سال به کدهای زیر نگاه کنید کدامیک خود توضیح دهندهتر خواهند بود (بدون مراجعه به مستندات موجود)؟
void UpdateBankAccountTransactionListWithYesterdaysTransactions()
//or?
void UpdateTransactions()
3- تنها زمانی از کامنتها استفاده کنید که لازم هستند.
اگر مورد 2 را رعایت کرده باشید، کمتر به نوشتن کامنت نیاز خواهد بود. از توضیح موارد بدیهی خودداری کنید، زیرا آنها بیشتر سبب اتلاف وقت خواهند شد تا کمک به افراد دیگر یا حتی خود شما. همچنین هیچگاه قطعه کدی را که به آن نیاز ندارید به صورت کامنت شده به مخزن کد در یک سیستم کنترل نگارش ارسال نکنید.
//function thisReallyHandyFunction() {
// someMagic();
// someMoreMagic();
// magicNumber = evenMoreMagic();
// return magicNumber;
//}
زمانیکه از ورژن کنترل استفاده میکنید نیازی به کامنت کردن قسمتی از کد که شاید در آینده قرار است مجددا به آن بازگشت نمود، نیست. این نوع سطرها باید از کد شما حذف شوند. تمام سیستمهای ورژن کنترل امکان revert و بازگشت به قبل را دارند و اساسا این یکی از دلایلی است که از آنها استفاده میشود!
به صورت خلاصه جهت نگهداری سوابق کدهای قدیمی باید از سورس کنترل استفاده کرد و نه به صورت کامنت قرار دادن آنها.
از کامنتهای نوع زیر پرهیز کنید که بیشتر سبب رژه رفتن روی اعصاب خواننده میشود تا کمک به او! (خواننده را بیسواد فرض نکنید)
// Get the student's id
thisId = student.getId();
کامنت زیر بی معنی است!
// TODO: This is too bad. FIX IT!
اگر شخص دیگری به آن مراجعه کند نمیداند که منظور چیست و دقیقا مشکل کجاست. شبیه به افرادی که به فورومها مراجعه میکنند و میگویند برنامه کار نمی کند و همین! طرف مقابل علم غیب ندارد که مشکل شما را حدس بزند! به توضیحات بیشتری نیاز است.
4- عدم استفاده از عبارات شرطی بیمورد هنگام بازگشت دادن یک مقدار bool:
مثال زیر را درنظر بگیرید:
if (foo>bar) {
return true;
} else {
return false;
}
آنرا به صورت زیر هم میتوان نوشت:
5- استفاده از متغیرهای بی مورد:
برای مثال:
Something something = new Something(foo);
return something;
که میشود آن را به صورت زیر هم نوشت:
return new Something(foo);
البته یکی از خاصیتهای استفاده از افزونهی Resharper ویژوال استودیو، گوشزد کردن و اصلاح خودکار موارد 4 و 5 است.
6- در نگارشهای جدید دات نت فریم ورک استفاده از ArrayList منسوخ شده است. بجای آن بهتر است از لیستهای جنریک استفاده شود. کدی که در آن از ArrayList استفاده میشود طعم دات نت فریم ورک 1 را میدهد!
7- لطفا بین خطوط فاصله ایجاد کنید. ایجاد فواصل مجانی است!
دو تابع جاوا اسکریپتی زیر را (که در حقیقت یک تابع هستند) در نظر بگیرید:
function getSomeAngle() {
// Some code here then
radAngle1 = Math.atan(slope(center, point1));
radAngle2 = Math.atan(slope(center, point2));
firstAngle = getStartAngle(radAngle1, point1, center);
secondAngle = getStartAngle(radAngle2, point2, center);
radAngle1 = degreesToRadians(firstAngle);
radAngle2 = degreesToRadians(secondAngle);
baseRadius = distance(point, center);
radius = baseRadius + (lines * y);
p1["x"] = roundValue(radius * Math.cos(radAngle1) + center["x"]);
p1["y"] = roundValue(radius * Math.sin(radAngle1) + center["y"]);
pt2["x"] = roundValue(radius * Math.cos(radAngle2) + center["y"]);
pt2["y"] = roundValue(radius * Math.sin(radAngle2) + center["y");
// Now some more code
}
function getSomeAngle() {
// Some code here then
radAngle1 = Math.atan(slope(center, point1));
radAngle2 = Math.atan(slope(center, point2));
firstAngle = getStartAngle(radAngle1, point1, center);
secondAngle = getStartAngle(radAngle2, point2, center);
radAngle1 = degreesToRadians(firstAngle);
radAngle2 = degreesToRadians(secondAngle);
baseRadius = distance(point, center);
radius = baseRadius + (lines * y);
p1["x"] = roundValue(radius * Math.cos(radAngle1) + center["x"]);
p1["y"] = roundValue(radius * Math.sin(radAngle1) + center["y"]);
pt2["x"] = roundValue(radius * Math.cos(radAngle2) + center["y"]);
pt2["y"] = roundValue(radius * Math.sin(radAngle2) + center["y");
// Now some more code
}
کدامیک خواناتر است؟
استفاده از فاصله بین خطوط در تابع دوم باعث بالا رفتن خوانایی آن شده است و این طور به نظر میرسد که سطرهایی با عملکرد مشابه در یک گروه کنار هم قرار گرفتهاند.
8- توابع خود را کوتاه کنید.
یک تابع نباید بیشتر از 50 سطر باشد (البته در این مورد بین علما اختلاف هست!). اگر بیشتر شد بدون شک نیاز به refactoring داشته و باید به چند قسمت تقسیم شود تا خوانایی کد افزایش یابد.
به صورت خلاصه یک تابع فقط باید یک کار را انجام دهد و باید بتوان عملکرد آنرا در طی یک جمله توضیح داد.
9- از اعداد جادویی در کدهای خود استفاده نکنید!
کد زیر هیچ معنایی ندارد!
if(mode == 3){ ... }
else if(mode == 4) { ... }
بجای این اعداد بی مفهوم باید از enum استفاده کرد:
if(mode == MyEnum.ShowAllUsers) { ... }
else if(mode == MyEnum.ShowOnlyActiveUsers) { ... }
10- توابع شما نباید تعداد پارامتر زیادی داشته باشند
اگر نیاز به تعداد زیادی پارامتر ورودی وجود داشت (بیش از 6 مورد) از struct و یا کلاس جهت معرفی آنها استفاده کنید.