• مقالات
  • /
  • Clean Code
  • /
  • اصول نامگذاری متغییر ها و توابع

اصول نامگذاری متغییر ها و توابع


وقتی داریم کد مینویسیم، یکی از چیزایی که برامون سوال میشه اینه که چجوری نام متغییرها و توابع و کلاس ها و ... رو اتخاب کنیم که هم استاندارد باشه و هم به خوانایی کدمون کمک کنه. همونطور که تو مقالات قبلی بهتون معرفی کرده بودم کتاب Clean Code نوشته Uncle Bob یکی از کتابای مهم در زمینه معرفی اصول نوشتن کد تمیزه. که به مباحث مختلفی از جمله قوانین نامگذاری پرداخته. در ادامه سعی میکنم خلاصه این قوانین رو براتون بیان کنم.

  • انتخاب اسامی با معنی:

اسامی باید جوری انتخاب بشن که مقصود از تعریف اونها رو برسونه، یعنی باید با معنا باشن و عملا با خوندن اسم اونها بشه فهمید که شامل چه مقادیری هستن. اینجوری با یک نگاه به اسم متغییر ها میشه به محتواشون پی برد و یا با نگاه کردن به اسم توابع میشه فهمید که اون تابع داره چی کار میکنه و عملا فهم کد رو راحتتر میکنه . اسم متغییرها و تابع ها و کلاس ها و ... باید جوری انتخاب بشن که به همه سوالای بزرگ ما جواب بدن! مثلا کار کلی کلاس مورد نظر چیه و یا حتی نحوه استفاده از اون چجوریه!

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

int d; // elapsed time in days

این اسم گویا نیست و عملا هیچ اطلاع خاصی در مورد اینکه چه مقداری رو داره نگه میداره و ... به ما نمیده! خوب چرا ما به جای کد بالا از یکی از نام گذاری های زیر استفاده نکنیم؟؟

int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;

این اسما خیلی گویاترن و با یک نگاه ساده میشه فهمید که حاوی چه مقادیری هستن

یا مثلا به قطعه کد زیر نگاه کنین!!

public List<int[]> getThem() {
    List<int[]> list1 = new ArrayList<int[]>();
    for (int[] x : theList)
        if (x[0] == 4)
            list1.add(x);
    return list1;
}

با اینکه این کد شامل هیچ چیز پیچیده ای نیست ولی عملا از خوندن این کد چیز زیادی دستگیرتون نشد. چون اسم های انتخاب شده خیلی نامناسب بودن و مفهوم خاصی رو به خواننده نمیرسوندن. خوب آخه از کجا باید بفهمیم که مثلا تابع بالا که اسمش getThem هست داره چیکار میکنه؟؟ عبارت getThem یعنی "آنها را بگیر"!!  منظور برنامه نویس چی بوده؟ این تابع چی رو داره میگیره؟؟ یا مثلا متغییرهایی که داخل تابع استفاده شده، مثل list1 چه چیزو رو دارن نگه میدارن؟؟؟ هیچ اطلاعات خاصی از این اسم ها نمیشه گرفت!

حالا به سعی کردیم همون کد بالا را تا حدی بهتر و تمیزتر بویسیم:

public List<int[]> getFlaggedCells() {
    List<int[]> flaggedCells = new ArrayList<int[]>();
    for (int[] cell : gameBoard)
        if (cell[STATUS_VALUE] == FLAGGED)
            flaggedCells.add(cell);
    return flaggedCells;
}

جالبه!  کد بالا رو دوباره گاه کنید، فقط اسامی عوض شدن! ولی با خوندنش حداقل میشه فهمید که مربوط به یه بازی هستش! میپرسین چرا؟ چو یه متغییر توشه به نام gameBoard و همین اسم ساده از یه تیکه خیلی کوچیک از یک برنامه کامل میتونه به شما این ایده رو بده که این قطعه کد مربوط به یه gameBoard هست! یه بازی تخته ای!! مثل شطرنج، تخته و یا هر چیزی که بشه بهش gameBoard گفت. این قدرت اسم گذاری فوق العادس و یه اسم کوچیک میتونه به شما بفهمونه که کل پروژه راجع به چه چیزیه!!

در کل کد بالا مربوط به بازی minesweeper هستش که قبلا جزو بازیهای ویندوز XP بود. یادتون اومد ای بازی رو ؟؟؟ اگه بازی یادتو باشه باید جاهایی که بمب بود رو با علامت پرچم مشخص میکردیم! پس حالا واضح تر شد که این قطعه کد داره چی کار میکنه! همونطور که اسم تابع داره بهمون میگه، این تابه میخواد getFlaggedCells کنه یا ساده تر بگم، میخواد اون خونه هایی از صفحه بازی رو که بازیگر با علامت پرچم مشخص کرده بهمون برگردونه.

 حتی با کمی ذوق و سلیقه کد بالا رو میشه بهترشم کرد. به کد زیر دقت کنید:

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

اینجوری خیلی قشنگتر و ساده تر شد! توی شرط if به جای اینکه از عبارت "cell[STATUS_VALUE] == FLAGGED" استفاده کنه از "()cell.isFlagged" استفاده کرده و فهم شرط رو ساده کرده!

 

  • تمایزهای معنی داری ایجاد کنید

مسلما متغییرهای مختلف با هم متمایز هستند، پس باید دارای اسامی متمایز و معنی دار باشد. این تمایز در نام باید در حدی باشد که با یک گاه بتوان متغییرهای مختلف را از هم تشخیص داد. به عوان مثال هرگز از سری های عددی برای تغییر نام و ایجاد تمایز استفاده نکنید. برای مثال از متغییرهای a1,a2,a3 به هیچ وجه استفاده نکنید، زیرا نه تنها اسامی دارای معنای خاصی نیستند و بلکه به دلیل تمایز آنها در اعداد انتهایی،  وجه تمایز روشن و واضحی ندارند و به همین دلیل ممکن است به راحتی و به اشتباه از یکی به جای دیگری استفاده نمایید. همچنین توصیه میشود از Noise word ها در اسامی استفاده نکید. برای مثال هرگز نباید در نامگذاری های جداول دیتابیس از کلمه table استفاده کنید و یا در نامگذاری متغییرها از کلمه variable استفاده ننمایید.

تصور کنید در برنامه ای با دو کلاس با نامهای  Customer و CustomerObject مواجه شوید. از دید شما این دو کلاس با هم چه تفاوتی میکنند؟ آیا وجه تمایز این دو از روی اسامی آنها مشخص است؟  و یا مثلا چه تفاوتی بین توابع زیر وجود دارد؟؟

getActiveAccount();
getActiveAccounts();
getActiveAccountInfo();

در چه موقع باید از کدام یک از توابع فوق استفاده کرد؟ آیا خروجی انها با هم تفاوت دارد؟

 

  •  از اسامی قابل تلفظ استفاده نمایید

در اتخاب اسامی باید دقت کرد. این اسامی باید به گونه ای انتخاب شوند که نتوان آنها را تلفظ کرد. چون ما هر روز با آنها سر و کار داریم و در مورد آنها با همکاران خود صحبت میکنیم. مثلا در شرکت از متغییری به نام genymdhms استفاده میشد که مخفف generation date, year, month, day, hour, minute بود. و همکاران هر روز موقع صحبت کرد با هم و هنگامی که میخواستنددز مورد این متغییر صحبت کنند، با مشکل برای تلفظ ان رو به رو بودند! خوب چرا نباید از اسامی استفاده کرد که موقع تلفظ آنها راحت زبامان بچرخد و بتوانیم آنها را تلفظ کنیم؟؟ قاعدتا به جای ای اسم بی معنی و سخت میتوان از کلماتی مانند زیر استفاده نمود:

private Date generationTimestamp;
private Date modificationTimestamp;;

 

  • از اسامی قابل جستجو استفاده کنید

اگه از اسمی تک کاراکتری و ثابت های عددی برای نامگذاری استفاده کنید خیلی سخته که توی یک کد طولای بتونین پیداشون کنید و یا حتی بتونین با جستجو پیداشون کنید. مثلا خیلی راحته که عبارت MAX_CLASSES_PER_STUDENT رو تو کد پیدا کنین ولی عملا غیر ممکنه که بتونین عدد 7 رو جستجو کنید و پیداش کنید. پس بهتره به جای اینکه از اسامی مثل a یا b یا اسامی تک کاراکتری دیگه استفاده کنیم که عملا با سرچ توی کد نمیتونیم اونا رو پیدا کنیم ، از اسامی استفاده کنیم که به راحتی قابل جستجو و پیدا کردن باشن و یادتون باشه که هیچ وقت از اعداد ثابت به صورت مستقیم توی کد استفاده نکنین و همیشه برای هر عدد یک ثابت با نامی با معنی و قابل جستجو انتخاب کنید.

تجربه شخصی من نشون میده که متغییرهایی با اسم های تک کاراکتری تنها وقتی متغییر های محلی با اسکوپ کاربردی کوچیک هستن قابل استفادن. 

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

برای واضح شدن موضوع به قطعه کد های زیر دقت کنید:

for (int j=0; j<34; j++) {
    s += (t[j]*4)/5;
}

 

توی کد بالا اصن معلوم نیست عدد 3 و 4 معرف چه چیزی هست!! و حتی نمیشه فهمید که اعداد ثابت 4 و 5 که تو محاسبات استفاده شدن چی هستن و بدتر از همه اگه روزی بخواهیم با جستجو توی کد این ثابت های عددی رو پیدا کنیم و تغییر بدیم، عملا امکان پذیر نیست!! ما یکم کد بالا رو دستکاری کردیم و سعی کردیم خواناترش کنیم. حالا به کد زیر دقت کنید، ببینین چقدر تمیز و خوانا شده و مهمتر از همه اینکه به راحتی میشه با جستجوی اسم های انتخاب شده، اونها رو پیدا کرد و در صورت یاز مقادیرشون رو تغییر داد.

 

int realDaysPerIdealDay = 4;
const int WORK_DAYS_PER_WEEK = 5;
int sum = 0;
for (int j=0; j < NUMBER_OF_TASKS; j++) {
    int realTaskDays = taskEstimate[j] * realDaysPerIdealDay;
    int realTaskWeeks = (realdays / WORK_DAYS_PER_WEEK);
    sum += realTaskWeeks;
}

 

  • از چسبندن نوع متغییر ها به اسم انتخاب پرهیز کنید!

مثلا لازم نیست اگر متغییری به نام firstName داریم که مسلما از نوع رشته است از عبارت firstNameString برای نام گذاری آن استفاده کنیم و یا اگه آرایه ای از قیمت ها داریم لازم نیست از priceArray برای آن استفاده کنیم.

برای نامگذاری متغییرها نیازی نیست از قوانین خاصی برای encoding استفاده کنید

 

  • از Mental Mapping دوری کنید

کسانی که کد شما رو میخونن نباید به صورت ناخودآگاه اسامی شما رو ترجمه کنند یا استنباط دیگری از آنها بکنند. مثلا استفاده از متغییرهای I , j برای استفاده در بلاک for بسیار رایج است پس چرا باید از کاراکتر دیگری به جای این دو استفاده کرد؟؟ مسلما اگر شما کدی را مرور کنید و ببینیند که در حلقه for از q  به جای I,j استفاده شده است کمی گیج میشوید و به دنبال دلیل قانع کننده ای برای استفاده از این متغییر خواهید گشت یا حداقل به این فکر میافتید که آیا w معنی خاصی دارد و به مقصوذی خاصی در این حلقه استفاده شده است؟؟!!

پس بهتر است که در نامگذاری ها از این دست از ابتکار عمل ها استفاده نکنید! قاعدتا برنامه نویس ها آدم های باهوشی هستند و خیلی از آنها حافظه قوی دارند و میتوانند به خاطر بسپارند که مثلا در متغییر r آدرس url را ذخیره کرده اند!! اما چرا باید اینگونه نامگذاری کنند که نفر بعدی که کد را میخواند با مشکل رو به رو شود.

درر واقع فرق یک Smart Programmer با یک Professional Programmer در این است که برنامه نویس حرفه ای متوجه این نکته هست که شفافیت در کد مهمترین اصل است یا به عبارتی Clarity is KING !!

برنامه نویسی حرفه ایست که از تمام توان خود برای نوشتن کدی شفاف  که دیگران به راحتی متوجه آن شوند استفاده کند

 

 

  • نامگذاری کلاس ها

برای نامگذاری کلاس ها و آبجکت ها باید از کلماتی استفاه کنیم که از لحاظ دستور زبانی اسم باشند و نه فعل. مثلا Customer, Account و ....

 

  • نام گذاری متدها

نامی که برای متدها انتخاب میکنید از لحاظ دستور زبانی باید فعل باشد مانند save، delete، pay و .... چون عملا این متدها هستند که فعلی را انجام میدهند. دقت کنید که برای نامگذاری Accessors, mutators, predicates  ها باید از ترکیب عبارت get set و is  به همراه یک اسم استفاده نمود . برای مثال :

string name = employee.getName();
customer.setName("mike");
if (paycheck.isPosted())...

 

  • از عبارت بامزه استفاده نکنید!!

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

Say what you mean. Mean what you say.

 

  • برای هر مفهوم یک کلمه انتخاب کنید:

سعی کنید برای هر مفهوم یک کلمه ثابت انتخاب کنید و همیشه و در تمام پروژه این کلمه را عوض نکنید مثلا اگر برای خواندن اطلاعات از متد retrieve استفاده کرده اید، در تمام کلاس های دیگر هم از همین عبارت برای خواندن مدل استفاده کنید نه اینکه در یک کلاس از عبارت read و در کلاس دیگر از retrieve و در دیگری  از عبارت دیگری استفاده کنید.

 

  • از عبارات تخصصی برنامه نویسی استفاده نمایید:

فراموش نکنیند که خوانندگان کد شما هم برنامه نویس هستند و انتظار می رود با اصطلاحات و عبارات این حوزه آشنا باشند. پس به راحتی از عبارات تخصصی و نام الگوریتم ها و نام دیزاین پترن ها و ... استفاده نمایید  مثلا عبارت AccountVisitor برای فردی که با دیزاین پترن ویزیتور آشنایی دارد مفهوم خاصی دارد و یا JobQueue برای همه برنامه نویسان از مفهوم خاص خود برخوردار است.

 

  • از عبارات حوزه فعالیت تجاری خود استفاده نمایید:

در هنگام برنامه نویسی خود اگه نیاز داشتید، از عبارت فنی و تخصصیی حوزه بیزنسی خود استفاده کنید. زیرا در صورتی که برنامه نویس جدید به شما بپیوند و کد را مطالعه کند حتما از اصطلاحات بیزنسی که دارد در آن کار میکند مطلع است. مثلا اگر در حوزه فروش بلیط هواپیما کار میکنید اشکالی ندارد که از اصطلاحات Voucher و Arrival و Departure استفاده کنید.

 

در نهایت باید گفت که وظیفه شما به عنوان برنامه نویس این است که کلاس ها، آبجکت ها، متد ها و متغییر هایی با اسامی قابل فهمی را در چهارچوب درستی طراحی نمایید. برای مثال فرض کنید که متغییرهایی با نام های firstName, lastName, street, houseNumber,   ،city, state و zipcode دارید. در نگاه اولیهبه آنها در کنار هم کاملا مشخص است که این متغییرها مربوط مفهومی به نام آدرس هستند.. اما اگر به آنها به صورت تکی نگاه کنید چی؟ مثلا اگر به state به صورت تکی نگاه کنید آیا متوجه میشوید که مربوط به یک آدرس است؟

برای این مثال یکی از راه حل ها این است که از پیشوندaddr برای متغییرها استفاده کنیم و آنها را به این شکل نامگذاری کنیم :

addrFirstName, addrLastName, addrState

که با دیدن پیشوند addr متوجه شویم که این مفاهیم مربوط به آدرس هستند.

راه حل بهتر این است که کل مجموعه را به صورت بزرگتر نگاه کنیم و کلاسی تعریف کنیم به نام Address که این متغییرها در آن تعریف شوند.

 

جمع بندی:

به عنوان جمع بندی باید بگیم که نام گذاری صحیح متغییرها و کلاس ها و ... به تمرین زیاد و رشد توان تفسیری شما بستگی داره.  عملا این قابلیت یک بحث قابل یادگیری است و به توانایی فنی و تکنولوژیک شما ربطی ندارد. معمولا برنامه نویسان شجاعت تغییر نام متغییر ها و کلاس ها را ندارند در صورتی که باید پذیرای آن باشند (البته در صورتی که آن تغییر نام به شفاف تر شدن کد کمک کند)

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

 

ارتباط با ما

کد تمیز مشتاق شنیدن نظرات و پیشنهادات سازنده شما عزیزان میباشد.

تماس با ما

با توجه گسترش روزافزون تکنولوژی ها و لزوم آشنایی برنامه نویسان با این تکنولوژی ها، قصد دارم تا تجربیات شخصی خود را با تمامی دوستان و مخاطبان عزیز به اشتراک بگذارم و امیدوارم تا مطالب ارائه شده در این سایت بتواند کاربردی و مفید باشد.

در حوزه گیت

; ;