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

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

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

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


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

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

۲. در توابع بولینی، خود شرط را return کنید 
برای توابع بولینی که یک شرط ساده دارند بهتر است به جای استفاده از 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
}

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


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 کاراکتری برای خودتان بگذارید تا کدی زیباتر داشته باشید.

۷. حذف توابع و متدهای طولانی
چند سال پیش برخی از افراد معتقد بودن که ++Visual C یک ‌زبان افتضاح است چرا که به شما توانایی ساخت توابعی با بیش از 10 هزار خط را نمی‌دهد! با این تفاسیر چه کسی می‌تواند به توضیح این مسئله بپردازد که به چه دلیلی ما به یک تابع 10 هزار خطی نیاز داریم؟ حتی بزرگ‌‌ترین توابع هم قابل گنجاندن در استانداردهای 50 خطی می‌باشند. به طور حتم، 50 خط هم طولانی است اما حدی قابل‌قبول است اما هرچه تعداد خطوط تابع کمتر، بهتر (برخی دولوپرها بر این باورند که در یک مانیتور استاندارد، کلیهٔ خطوط تابع بدون نیاز به اسکرول کردن می‌بایست دیده شود).

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

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