چگونه زیبا کدنویسی کنیم؟

چگونه زیبا کدنویسی کنیم؟

یکی از مشکلات کلیدی برنامه‌نویسان و دولوپرها، به‌خصوص در کارهای گروهی، ناتوانی در نوشتن کدهای زیبا و اصولی است اما سؤالی که در اینجا به ذهن می‌رسد این است که اساساً به چه نوع کدی زیبا گفته می‌شود؟ در پاسخ به این سؤال می‌توان گفت سورس‌کدی زیبا است که تمیز، خوانا، مرتب و اصولی نوشته ‌شده باشد به ‌گونه‌‌ای که وقتی سایر دولوپرها به آن نگاه می‌کنند، بتواند آن را بدون نیاز به‌ دقت بیش از حد و یا سردرگمی درک کنند. در همین راستا، در ادامه راه‌کارهایی به منظور نحوهٔ نوشتن کدهای زیبا معرفی خواهیم کرد.

خروج سریع از شرط‌‌ها
برای درک بهتر این مورد، بلوک کد زیر که با زبان برنامه‌نویسی جاوااسکریپت نوشته شده است را مد نظر قرار دهید:

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

همان‌طور که مشاهده می‌کنید، چنانچه از به وجود آمدن شرط‌‌های تو در تو جلوگیری شود، می‌توانیم صاحب کدی زیباتر و قابل‌‌درک‌‌تر باشیم.

در توابع بولینی خودِ شرط را بازگردانید 
برای توابع بولینی که یک شرط ساده دارند، بهتر است به جای استفاده از ساختار if else، خود شرط را اصطلاحاً return کنید. برای روشن شدن این مسئله، تابع زیر را مد نظر قرار می‌دهیم:

function isStringEmpty(str)
{
    if(str === "") { 
        return true;
    }
    else {
        return false;
    }
}

و حال اگر کد بالا را کمی اصلاح کنیم، خواهیم داشت:

function isStringEmpty(str)
{
    return (str === "");
}

همان‌طور که مشاهده می‌شود، کدی به مراتب ساده‌‌تر، زیباتر و همچنین کم‌حجم‌‌تری داریم.

از اینتر استفاده کنید
شاید باور این مسئله دشوار باشد، اما دولوپرهایی وجود دارند که خیلی کم از اینتر استفاده می‌کنند مگر در آخر هر خط و این در حالی است که استفاده‌ٔ به ‌موقع از اینتر در کدنویسی باعث می‌شود که بخش‌‌های مختلف کد از هم مجزا شوند و کد در آینده راحت‌‌تر قابل‌ تحلیل و بررسی باشد. به عنوان مثال داریم:

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 existsStudent(id, list) 
{
    for(i = 0; i < list.length; i++) {
       student = list[i];
       // Get the student's id
       thisId = student.getId();
       if(thisId === id) {
           return true;
       }
    }
    return false;   
}

بنابراین نیازی به کامنت‌گذاری برای این بخش از کد نخواهیم داشت.

کدهای کامنت‌شده را در وسط سورس‌کد نهایی رها نکنید
برخی مواقع برای همه‌ٔ ما پیش می‌آید که متوجه می‌شویم بخشی از کد به ‌درستی کار نمی‌کند و یا نیازی به آن‌ها نیست که در اکثر مواقع، به‌خصوص در مرحله‌ٔ دیباگینگ، کدهای زائد را کامنت کرده تا تغییرات حاصل را راحت‌تر بتوان مشاهده کرد اما باید توجه داشت که حتماً پس از اتمام کار، کدهای اضافه و کامنت‌شده را از سورس‌کد پاک‌ کرد تا خروجی تمیزتر داشته باشیم:

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

کوتاه کردن خطوط طولانی
همیشه سعی کنید تا کدهایی که استفاده می‌کنید و به بیشتر از یک خط فضا نیاز دارند را با استفاده از اینتر، در چند خط جای دهید. برای مثال، تا به ‌حال حتماً پیش‌ آمده که کدی مثل کد زیر دیده باشید:

public static EnumMap<Category, IntPair> getGroupCategoryDistribution(EnumMap<Category, Integer> sizes, int groups) {
        EnumMap<Category, IntPair> categoryGroupCounts = new EnumMap<Category,IntPair>(Category.class);
        for(Category cat : Category.values()) {
            categoryGroupCounts.put(cat, getCategoryDistribution(sizes.get(cat), groups));
        }

توصیه نمی‌کنیم که از استاندارد 70 کارکتری ترمینال‌‌های یونیکس استفاده کنید اما حداقل می‌توانید محدودیتی 120 کاراکتری برای خودتان بگذارید تا کدی زیباتر داشته باشید.

حذف توابع و متدهای طولانی
توصیه می‌کنیم توابع طولانی را به چند تابع با اندازهٔ استاندارد، برای مثال 50 تا 60 خط، تقسیم کنید چرا که با این روش نه تنها توابع قابلیت دسته‌‌بندی پیدا می‌کنند، بلکه دیباگینگ آن‌‌ها نیز آسان‌‌تر خواهد شد (برخی دولوپرها بر این باورند که در یک مانیتور استاندارد، کلیهٔ خطوط تابع بدون نیاز به اسکرول کردن می‌بایست دیده شود.)

حال نوبت به نظرات شما می‌رسد. از دید شما با دنبال کردن چه راه‌کارهای دیگری می‌توان زیباتر کدنویسی کرد؟ نظرات، دیدگاه‌های و تجربیات خود را با سایر کاربران سکان آکادمی به اشتراک بگذارید.



آتنا ستوده