آموزش اصولی کد نویسی تمیز - بخش اول

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

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

در بعضی موارد چندین برنامه نویس با سطوح مختلف طی سالیان متمادی اقدام به توسعه نرم افزار نموده بودند که دست خطها و طرز فکر های مختلف به وضوح در نرم افزار مشاهده میشد.

مدتها به این مساله فکر میکردم که چه چیزی در آموزشها برنامه نویسی اشکال دارد که با چنین مشکلی مواجه هستیم تا اینکه چند سال پیش به کتابی برخوردم که پاسخ این پرسش من را به خوبی داده بود. آن کتاب چیزی نبود جز "کد نویسی تمیز (Clean Code) اثر رابرت سی مارتین"

کتاب Clean Code
کتاب Clean Code


این کتاب یکی از بهترین کتب در این موضوع است . قصد دارم به صورت مختصر و تحت چند مقاله خلاصه ای از آن را منتشر کنم.

بنده بعد از سالها آموزش دیدن و تدریس در موضوع برنامه نویسی به شدت جای خالی چنین موضوعی را در آموزش آکادمیک برنامه نویسی خالی میدانم و دوست دارم بتوانم سهمی جهت پر کردن این خلأ داشته باشم .



نام گذاری معنا دار و صریح

ما از نام گذاری در بخشهای مختلف یک برنامه استفاده میکنیم . از متغیرهای محلی ، نام کلاسها ، متدها و حتی فایلهای جانبی و منابع مورد استفاده در بخشهای مختلف را نام گذاری میکنیم.

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

برای نام گذاری صحیح در یک تکه برنامه ابتدا باید چند سوال از خودمان بپرسیم :

1- ورودی ها و خروجی تکه برنامه ما چیست و قرار است چه کاری انجام دهد؟

2- چه متغیرهای و از چه نوعی مورد نیازمان است و کاربردشان چیست ؟

3- چه مقادیر ثابتی مورد نیاز ما است و کاربردشان چیست ؟

4- چه زیر برنامه ها (توابعی) مورد استفاده قرار میگیرد یا قرار است نوشته شود؟

با پاسخ به این پرسشها میتوانیم برای نام گذاری هایمان دلیل و مدرک داشته باشیم. این نکته را هم بگوییم وقتی نامی را برای یک جزء انتخاب کردید و پس از نوشتن کد نام دیگری را مناسب دیدید آن را تغییر دهید تا قبل از انتشار نسخه نهایی و خود را محدود به انتخاب اول ننمایید.

البته در نسخه های Release هم بعضا تغییر نامهایی رخ میدهد که این بسته به تصمیم برنامه نویس در این خصوص دارد.

در ادامه با مثالی انتخاب نامهای مناسب را توضیح میدهیم :

public List getThem()

{

List list1 = new ArrayList();

for (int[] x : theList)

if (x[0] == 4) list1.add(x);
return list1;

}

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

این برنامه عملرد ساده ای دارد اما نام گذاری ها در آن کاملا بی معناست و برنامه نویس دیگری از کاربرد آن در برنامه نمیتواند سر در بیاورد. حال به همین تکه برنامه با نام گذاری صحیح توجه کنید :


public List getFlaggedCells()

{

List flaggedCells = new ArrayList();

for (int[] cell : gameBoard)

if (cell[STATUS_VALUE] == FLAGGED) flaggedCells.add(cell);

return flaggedCells;

}

ملاحظه میکنید همان تکه برنامه با نام گذاری صحیح چقدر خوانا تر است. در اینجا به جای مقادیر 0 و 4 که در برنامه قبلی به صورت ثابت استفاده شده بود از دو ثابت به نامهای STATUS_VALUE و FLAGGED استفاده شده.

حال اگر بخواهیم برنامه ما خوانا تر باشد و کد زیباتری داشته باشد میتوانیم بخشهایی را که امکان دارد به صورت method تعریف کنیم را از بدنه اصلی جدا کنیم مانند نمونه زیر :

public List getFlaggedCells()
{
List flaggedCells = new ArrayList();
for (Cell cell : gameBoard)
if (cell.isFlagged())
flaggedCells.add(cell);
return flaggedCells;
}

در مثال فوق به جای نوشتن عبارت شرطی مستقیم آنرا در متد ()isFlagged قراردادیم تا باز هم کد خوانا تر شود. البته تشخیص اینکه در کجا از متد استفاده کنیم با برنامه نویس است. معمولا زمانی که کدها تکرار میشود بهترین زمان استفاده از متدهاست و یا زمانی که باعث فهم ساده تر و منطق روان تر برنامه میگردد.