چگونه کیفیت سورس‌کد اپلیکیشن خود را بهبود بخشیم؟


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

اولین کسی باشید که به این سؤال پاسخ می‌دهید

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

هرگز کدهای فاقد یونیت‌ تست را ریفکتور نکنید
Unit Testing پروسه‌ای است که امکان بررسی عملکرد بخش به بخش کد را به صورت مجزا فراهم می‌آورد. در این ارتباط توصیه می‌شود که هرگز نباید ریفکتورینگ کدی را آغاز نمود که یونیت‌ تست‌های مناسبی ندارد و دلیل آن هم کاملاً واضح است؛ زیرا چنانچه چنین کدی را ریفکتور کنید، در پایان کدی خواهید داشت که درست کار نمی‌کند و به سختی می‌توان فهمید که کدام بخش آن باگ دارد و از همین روی اگر قصد ریفکتور نمودن چنین کدی را دارید، باید قبل از شروع کار بخش به بخش آن را تست کنید و مطمئن شوید که هر بخش قبل از اِعمال تغییرات به درستی کار می‌کند.

ریفکتورینگ را از عمیق‌ترین بخش کد آغاز کنید
به منظور درک بهتر این موضوع، ابتدا به بلوک کد زیر توجه کنید:

public function add() 
{
    if () {
        if () {

        } else {

        }
    }
}

در این بلوک متدی به نام ()add را مشاهده می‌کنید که عمیق‌ترین بخش این کد دستورات if و else احاطه‌شده در دستور شرطی بیرونی است. معمولاً درونی‌ترین سطح کد بر روی یک منطق واحد تمرکز دارد که این موضوع ریفکتور نمودن آن را آسان‌تر می‌کند و از همین روی اگر قرار باشد این بخش از کد را ریفکتور کنیم، باید کار را از دستورات if و else مذکور آغاز نماییم.

متدهای طولانی را با تقسیم آن‌ها به چند متد کوتاه‌ کنید
در مثال فوق می‌توان دستورات if و else را به صورت یک فانکشن از جنس private نوشت به طوری که خواهیم داشت:

private function someName()
{
    if () {

    } else {

    }
}

حال متد ()add بعد از ریفکتورینگ به صورت زیر خواهد بود:

public function add() 
{
    if () {
        $this->someName();
    }
}

همان‌طور که ملاحظه می‌شود، این متد بسیار تمیز‌تر،‌ خواناتر و آزمون‌پذیرتر شده است.

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

public function checkLogin() 
{
    if (! $userId)

        redirect("/login");
}

اما به شکلی اصولی‌تر خواهیم داشت:

public function checkLogin() 
{
    if (! $userId) {
        redirect("/login");
    }
}

می‌بینیم که استفاده از علائم {} خوانایی سورس‌کد را به مراتب بیشتر کرده است.

از اعداد/استرینگ‌های جادویی استفاده نکنید
اعداد/استرینگ‌های به اصطلاح Magic (جادویی) چیزهایی هستند که در کد مورد استفاده قرار گرفته‌اند اما مفهوم، کاربرد و هدف آن‌ها واضح نیست و توضیحی نیز دربارهٔ‌ آن داده نشده است! در مثال زیر اگر مقدار متغیر rooms$ به بیش از ۲۵۰ برسد یک پیام خطا نمایش داده خواهد شد اما نکتهٔ مهم اینجا است که ۲۵۰ در اینجا به عنوان یک عدد جادویی مورد استفاده قرار گرفته است و از همین روی اگر کسی به جز دولوپر اصلی این اپلیکشن با چنین خطایی مواجه شود، به سختی می‌تواند دلیل نمایش این خطا را دریابد:

public function availableRooms($rooms) 
{
    if ($rooms > 250) {
        return "No rooms availble";
    } else {
        return true;
    }
}

برای ریفکتور نمودن این متد می‌توان متغیر دیگری به نام maxAvailableRooms$ ایجاد نموده و مقدار آن را برابر با عدد ۲۵۰ قرار داد:

public function availableRooms($rooms) 
{
    $maxAvailableRooms = 250;
    if ($rooms > $maxAvailableRooms) {
        return "No rooms availble";
    } else {
        return true;
    }
}

اکنون همان‌طور که در کد فوق نشان داده‌ شده است، می‌توانیم در بخش‌های دیگر کد به جای عدد جادویی ۲۵۰ از متغیر maxAvailableRooms$ استفاده نموده و به این ترتیب درک آن را برای دیگران آسان‌تر نماییم.

اگر به عبارت else نیازی ندارید از آن استفاده نکنید
در ریفکتورینگ فانکشن ()availableRooms عبارت else را به صورت زیر حذف نمودیم:

public function availableRooms($rooms) 
{
    $maxAvailableRooms = 250;
    if ($rooms > $maxAvailableRooms) {
        return "No rooms availble";
    }
    return true;
}

زیرا بدون وجود آن نیز مشکلی برای کد پیش نمی‌آید و منطق این بخش از کد همچنان پابرجا است.

برای متد‌ها، متغیرها و تست‌ها از نام‌های معنی‌دار استفاده کنید
در مثال زیر دو متد به نام‌های ()index و ()roomM را مشاهده می‌کنید که هدف آن‌ها را نمی‌توان از روی نام‌شان دریافت:

public function index() 
{
    $rooms = $this->roomM->getRooms();
}

در حالی که اگر از نام‌های توصیفی برای این متدها استفاده می‌شد، برای خوانندهٔ کد نام آن‌ها می‌توانست بیانگر هدف‌شان باشد.

از حداکثر قابلیت‌های زبان برنامه‌نویسی خود استفاده کنید
بسیاری از دولوپرها از حداکثر قابلیت‌های زبان برنامه‌نویسی خود بهره نمی‌برند! در حالی که استفاده از این قابلیت‌ها می‌تواند زحمت دولوپر را کمتر نموده و کد تمیزتری را ایجاد نماید:

public function calDiscount(string $name, int $age): array 
{
    return [
        'name' => $name,
        'age' => $age
    ]
}

در مثال فوق توجه کنید که استفاده از قابلیت Type Hinting تا چه حد می‌تواند کار را ساده‌تر نماید و در عین حال نتیجهٔ مشابهی را پدید آورد. به عبارتی، دولوپر موظف است که پارامترهای ورودی فانکشن ()calDiscount را به ترتیب از جنس رشته و عدد صحیح وارد کند مضاف بر اینکه همواره خروجی این تابع یک آرایه خواهد بود (جهت آشنایی بیشتر با مفهوم تایپ هینتینگ می‌توانید به مقالهٔ‌ آشنایی با مفهوم Type Hinting در زبان PHP مراجعه نمایید.)

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

- همواره سعی کنید به جای ==  از اپراتور === استفاده کنید مگر اینکه عدم بررسی دیتا تایپ حائز اهمیت باشد.
- بهتر است برای متدهای پابلیک از نام‌های توصیفی کوتاه استفاده شود اما برای متدهای پرایوت، از آنجا که اهداف محدودتری دارند، می‌توان از نام‌های طولانی‌تر استفاده کرد.
- در کلاس‌ها همواره متدهای پابلیک را اول بنویسید و متدهایی که بدون کاربرد باقی مانده‌اند را حذف کنید.
- همراه با فانکشن‌هایی که مقادیر بولین را ریترن می‌کنند از پیشوندهایی همچون is یا has استفاده کنید (به عنوان مثال از نام‌هایی همچون (isAdmin($user و (hasPermission($user می‌توان استفاده نمود.)
- همواره از کلیدواژگان public و protected و همچنین مواقعی که نیاز به سطح دسترسی محدودتری دارید از private برای پراپرتی‌ها و متدها استفاده کنید و حتی اگر زبان برنامه‌نویسی مورد استفادهٔ شما اجازه درج نکردن برخی کیوردها همچون public را می‌دهد، کماکان در نوشتن آن کوتاهی نکنید. 
- همچنین برای هر کلاس تنها یک وظیفه/ هدف در نظر بگیرید (در رابطه با این نکته می‌توانید به آموزش درآمدی بر قانون Single Responsibility مراجعه کنید.)

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

منبع


رائفه خلیلی