PDA

View Full Version : زيباتر كد بنويسيم



saed2006
یک شنبه 09 اسفند 1388, 17:11 عصر
داشتن آگاهي در مورد ساختارهاي داده‌‌ها، الگوريتم‌ها و يا عملگرهاي بيتي بسيار عالي است و يا تسلط بر نحوه‌ي كاركرد ابزارهايي مانند SharePoint و امثال آن اين روزها ضروري است. اما بايد در نظر داشت، كدي كه امروز تهيه مي‌شود شايد فردا يا ماه ديگر يا چند سال بعد نياز به تغيير داشته باشد، بنابراين دانش زيبا نوشتن يك قطعه كد كه خواندن آن‌را ساده‌تر مي‌كند و در آينده افرادي كه از آن نگهداري خواهند كرد زياد "زجر" نخواهند كشيد، نيز ضروري مي‌باشد. (اگر كامنت‌هاي سايت را خوانده باشيد يكي از دوستان پيغام گذاشته بود، اگر به من بگويند يك ميليون بگيريد و برنامه فعلي را توسعه دهيد يا رفع اشكال كنيد، حاضرم 10 هزارتومان بگيرم و آن‌را از صفر بنويسم! متاسفانه اين يك واقعيت تلخ است كه ناشي از عدم خوانا بودن كدهاي نوشته شده مي‌باشد.)
در ادامه يك سري از اصول زيبا نويسي كدها را بررسي خواهيم كرد.


1- سعي كنيد ميزان تو در تو بودن كدهاي خود را محدود كنيد.
لطفا به مثال زير دقت نمائيد:


void SetA()
{
if(a == b)
{
foreach(C c in cs)
{
if(c == d)
{
a = c;
}
}
}
}


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;
}


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
}
}


if (foo) {
if (bar) {
// do something
}
}
به صورت زير هم قابل نوشتن است (جهت كاهش ميزان nesting):



if (foo && bar) {
// do something
}


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)) {
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();
}


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 UpdateBankAccountTransactionListWithYesterdaysTran sactions()
//or?
void UpdateTransactions()


void UpdateBankAccountTransactionListWithYesterdaysTran sactions()
//or?
void UpdateTransactions()
3- تنها زماني از كامنت‌ها استفاده كنيد كه لازم هستند.
اگر مورد 2 را رعايت كرده باشيد، كمتر به نوشتن كامنت نياز خواهد بود. از توضيح موارد بديهي خودداري كنيد، زيرا آن‌ها بيشتر سبب اتلاف وقت خواهند شد تا كمك به افراد ديگر يا حتي خود شما. همچنين هيچگاه قطعه كدي را كه به آن نياز نداريد به صورت كامنت شده به مخزن كد در يك سيستم كنترل نگارش ارسال نكنيد.



//function thisReallyHandyFunction() {
// someMagic();
// someMoreMagic();
// magicNumber = evenMoreMagic();
// return magicNumber;
//}


//function thisReallyHandyFunction() {
// someMagic();
// someMoreMagic();
// magicNumber = evenMoreMagic();
// return magicNumber;
//}زمانيكه از ورژن كنترل استفاده مي‌كنيد نيازي به كامنت كردن قسمتي از كد كه شايد در آينده قرار است مجددا به آن بازگشت نمود، نيست. اين نوع سطرها بايد از كد شما حذف شوند. تمام سيستم‌هاي ورژن كنترل امكان revert و بازگشت به قبل را دارند و اساسا اين يكي از دلايلي است كه از آن‌ها استفاده مي‌شود!
به صورت خلاصه جهت نگهداري سوابق كدهاي قديمي بايد از سورس كنترل استفاده كرد و نه به صورت كامنت قرار دادن آن‌ها.

از كامنت‌هاي نوع زير پرهيز كنيد كه بيشتر سبب رژه رفتن روي اعصاب خواننده مي‌شود تا كمك به او! (خواننده را بي‌سواد فرض نكنيد)



// Get the student's id
thisId = student.getId();


// Get the student's id
thisId = student.getId();
كامنت زير بي معني است!


// TODO: This is too bad. FIX IT!


// TODO: This is too bad. FIX IT!
اگر شخص ديگري به آن مراجعه كند نمي‌داند كه منظور چيست و دقيقا مشكل كجاست. شبيه به افرادي كه به فوروم‌ها مراجعه مي‌كنند و مي‌گويند برنامه كار نمي كند و همين! طرف مقابل علم غيب ندارد كه مشكل شما را حدس بزند! به توضيحات بيشتري نياز است.


4- عدم استفاده از عبارات شرطي بي‌مورد هنگام بازگشت دادن يك مقدار bool:
مثال زير را درنظر بگيريد:



if (foo>bar) {
return true;
} else {
return false;
}


if (foo>bar) {
return true;
} else {
return false;
}آن‌را به صورت زير هم مي‌توان نوشت:



return foo>bar;


return foo>bar;
5- استفاده از متغيرهاي بي مورد:
براي مثال:



Something something = new Something(foo);
return something;


Something something = new Something(foo);
return something;كه مي‌شود آ‌ن را به صورت زير هم نوشت:



return new Something(foo);


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
}



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) { ... }
if(mode == 3){ ... }
else if(mode == 4) { ... }


بجاي اين اعداد بي مفهوم بايد از enum استفاده كرد:



if(mode == MyEnum.ShowAllUsers) { ... }
else if(mode == MyEnum.ShowOnlyActiveUsers) { ... }


if(mode == MyEnum.ShowAllUsers) { ... }
else if(mode == MyEnum.ShowOnlyActiveUsers) { ... }
10- توابع شما نبايد تعداد پارامتر زيادي داشته باشند
اگر نياز به تعداد زيادي پارامتر ورودي وجود داشت (بيش از 6 مورد) از struct و يا كلاس جهت معرفي آن‌ها استفاده كنيد.

وبلاگ تازه های برنامه نویسی

bachebahal_1363
یک شنبه 09 اسفند 1388, 17:17 عصر
جناب Saed2006
البته زیبا کد نوشتن زیباست اما زیبا انتقال دادن اطلاعات نیز زیباتر
بهتره کد هاتون رو داخل تگ کد قرار بدین تا زیبایی کارتون بهتر معلوم بشه

slashslash2009
یک شنبه 09 اسفند 1388, 18:02 عصر
البته در تگ کد که قرار گرفته
به نظر من باید الگوریتمو صحیح نوشت یعنی بهترین و کوتاهترین راه یا جواب .شاید وقتی این کدها به زبان ماشین ترجمه بشن زیاد فرقی با هم نکنند مخصوصا در صرف زمان ولی کلا کارتون خیلی جالب بود .

cimiarnm
دوشنبه 10 اسفند 1388, 00:30 صبح
البته در تگ کد که قرار گرفته
به نظر من باید الگوریتمو صحیح نوشت یعنی بهترین و کوتاهترین راه یا جواب .شاید وقتی این کدها به زبان ماشین ترجمه بشن زیاد فرقی با هم نکنند مخصوصا در صرف زمان ولی کلا کارتون خیلی جالب بود .
شما درست میگید . وقتی کدها به زمان ماشین ترجمه شن احتمالا زیاد با هم فرقی نکنند ولی موضوع تاپیک چیز دیگری است . اگر قرار باشد شما چند ماه یا چند سال بعد به کد خودتان ( دقت کنید حتی کد خودتان ) مراجعه نمائید به احتمال زیاد مجبورید برای یک تغییرکوچک در برنامه قسمتهای زیادی از برنامه رو مطالعه کنید تا حساب کار دستتان یاید که مثلا این متغیری که من تعریف کرده بودم اصلا برای چی بود و امثالهم . البته جسارت خدمتتون نشه ها ! چون خود این مشکل برای من پیش آماده بود خواستم یادآوری کنم . کد زیبا باعث میشود خود برنامه نویس وقتی کس دیگری به کد نگاه میکند( حتی اگر برنامه نویس نباشد) سرش را بالا بگیرد . ممکن است شخصی دستگاهی بسازد که بهترین کارها را انجام دهد ولی اصلا شکل ظاهری مناسبی نداشته باشد مطمئنا از اقبال خوبی برخوردار نخواهد بود

slashslash2009
دوشنبه 10 اسفند 1388, 09:03 صبح
من که نگفتم کد زیبا یا هرچی که اسمشو میزارید یا میزاریم خوب نیست بعدم گفتم که الگوریتم باید درست باشه در ضمن یک دستگاه شکل ظاهری داره ولی کد که شکل ظاهری نداره کسی نمیبینه چی میشه فقط میشه محیط برنامه یا فرم برنامه رو خوب طراحی کرد موفق باشید

saed2006
دوشنبه 10 اسفند 1388, 09:39 صبح
من این تاپیک رو ایجاد کردم تا دوستان راه کار بدن واسه نوشتن زیبا تر کد. جایی نگفتم که زیبا تر الگوریتم ننویسیم یا اصولی تر طراحی نکنیم
زیبا تر کد نوشتن هم میتونه مکملی باشه برای معماری طراحی و تحلیل اصولی و زیبا

ASKaffash
دوشنبه 10 اسفند 1388, 09:46 صبح
سلام
کدهای ذیل همه and است بهتر است اینطوری نوشته شود :


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)
&& !doFindShapePoints(flags, point, attribute)
&& !findInShape(flags, point, attribute)
&& !findFromGuide(flags,point)
&& list.count() > 0
&& flags == 1
)
doSomething();
........
}

saed2006
دوشنبه 10 اسفند 1388, 10:17 صبح
اینم خلاصه مقاله فوق به زبان اصلی