منظور از Refactor کردن کد، پیرایش، تغییر چیدمان و یا حذف و اضافه بخش‌هایی به کد است، به طوری که بهبودی از قبیل افزایش سرعت یا خوانایی در آن حاصل شود. در این مقاله با بررسی برخی مثال‌ها روش بازسازی یا Refactor کردن کد را خواهیم آموخت.

فرض کنید کدی مانند زیر داریم که در حالت نامرتب قرار دارد:

منطق فوق، URL مربوط به «نقاط انتهایی» (endpoints) را مشخص می‌سازد و به چند چیز بستگی دارد. این موارد شامل سرویسی که استفاده می‌شود، رندر شدن روی سرور یا کلاینت و این که در محیط توسعه یا پروداکشن قرار داریم و غیره هستند. یکی از دلایل این که این قطعه کد می‌تواند چنین آشفته باشد، این است که ممکن است فراموش کنیم که تکرار کد بسیار کم‌هزینه‌تر از تجرید نادرست است.

اما خبر خوب این است که می‌توان برخی تکنیک‌های آسان برای ساده‌سازی گزاره‌های تودرتوی if-else اعمال کرد. خبر بد این است که این قطعه کد برای کارکرد اپلیکیشن ضروری است، چون همه درخواست‌ها به آن ارسال می‌شوند و همچنین تست نشده است. در ادامه روش بازسازی این کد را بررسی می‌کنیم.

تأمین پوشش تست 100% برای کد

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

امروزه غالب توسعه‌دهندگان از رویه TDD پیروی می‌کنند، اما این بخش خاص از کدبیس در زمان‌های بسیار قبل نوشته شده است و اهمیت آن موجب شده که از بازسازی کد در طی زمان اجتناب کنیم.

مزیت اصلی TDD امکان بازسازی کد بدون ترس و بدون هزینه است.

همچنین می‌خواهیم مطمئن شویم که پوشش تست 100% است، ‌بنابراین از فلگ Jest به صورت coverage– استفاده می‌کنیم که خروجی زیر را در اختیار ما قرار می‌دهد:

کارکرد دقیق

اکنون که به لطف تست‌ها، ‌اعتماد بیشتری به دست آورده‌ایم، می‌توانیم شروع به تجزیه کد بکنیم. کار را از ابتدا آغاز می‌کنیم. می‌بینیم که بسته به سرویس‌های مختلف، محیط، سمت کلاینت یا سرور بودن و غیره مقادیر متفاوتی به protocol انتساب یافته‌اند و سپس بقیه UTL به انتهای تابع اضافه شده است.

بنابراین می‌توانیم کدی که پروتکل را تعیین می‌کند در تابع خاص خود قرار دهیم و آن را صرفاً یک بار فرا بخوانیم:

همین کار که در مورد ()getProtocol انجام دادیم، روی باقی بخش‌های URL نیز قابل اجرا است. هر چه کارکردها بیشتر تجزیه شوند، بخش‌های if-else بیشتر ساده‌سازی می‌شوند و امکان جداسازی بقیه موارد آسان‌تر می‌شود. از این رو توصیه ما این است که کار را از بخشی آغاز کنید که پیچیدگی کمتری دارد، ‌چون باعث می‌شود پیچیدگی بقیه بخش‌ها هم کاهش یابد.

استخراج این تابع‌ها کار چندان پیچیده‌ای نیست، اما دلیل این امر آن است که کابوس if-else را جدا کرده‌ایم. بنابراین آشفتگی زیادی وجود ندارد، اما هنوز مقداری آشفتگی برجا مانده که غیر قابل قبول است و لذا در بخش بعد آن را نیز حذف می‌کنیم.

ساده‌سازی

علاوه بر بحث «جداسازی دغدغه‌ها» (Separation of Concerns)، ‌مزیت استخراج بخش‌های مختلف URL در تابع‌های گوناگون این است که گزاره‌های شرطی را می‌توان هر چه بیشتر ساده‌سازی کرد. در حالت قبلی، ‌برخی بخش‌های URL مانع ساده‌سازی می‌شدند، زیرا به شرایط مختلفی وابسته بودند. اکنون آن‌ها از هم جداسازی شده‌اند و لذا این دغدغه رفع شده است.

این نوع ساده‌سازی به صورت چشمی قابل اجرا است و همچنین می‌توانید از یک «جدول ارزش» (truth table) نیز کمک بگیرید. در مورد تابع ()getProtocol جدول ارزش به صورت زیر است:

Service is Webclient Server Side Env is Local Returns
TRUE TRUE TRUE http
TRUE TRUE FALSE http
TRUE FALSE TRUE https
TRUE FALSE FALSE https
FALSE TRUE TRUE http
FALSE FALSE TRUE http
FALSE TRUE FALSE http
FALSE FALSE FALSE https

اما آن را می‌توان کمی ساده‌تر نیز نوشت. علامت (-) به این معنی است که مقدار مربوطه اهمیتی ندارد.

Service is Webclient Server Side Env is Local Returns
TRUE TRUE http
TRUE FALSE https
FALSE TRUE http
FALSE TRUE FALSE http
FALSE FALSE FALSE https

ما صرفاً دو مقدار قابل قبول داریم که شامل http و https است. بنابراین می‌توانیم یکی که ردیف‌های کمتری دارد (https) را انتخاب کنیم و شرایط را برای آن بررسی کنیم و سپس دیگری (http) را بازگشت دهیم:

این وضعیت در عمل بهتر از چیزی است که در بخش قبل داشتیم. اما می‌توانیم باز هم آن را بهبود ببخشیم. با بررسی دو شرط نخست متوجه می‌شویم که تنها زمانی https می‌گیریم که در سمت سرور نباشیم. بنابراین می‌توانیم شرط اول را برای http بنویسیم و در بقیه بخش‌های تابع نیز isServerSide را حذف کنیم.

همچنان جا برای بهبود وجود دارد. می‌توان شرط دوم و سوم را با هم ادغام کرد تا کوچک‌تر شوند:

اما کد فوق کمی احمقانه به نظر می‌رسد. اگر سرویس webclient باشد، شرط باید True باشد. در غیر این صورت به بخش دوم OR می‌رویم، اما چرا باید اصولاً بررسی کنیم که webclient نباشد؟ اگر در سمت صحیحِ OR باشیم، در این صورت مطمئن هستیم که webclient نیست. نتیجه نهایی این فرایند کدی کاملاً ساده است، به خصوص زمانی که با کد اولیه بررسی می‌کنیم، میزان تفاوت را بهتر متوجه می‌شویم:

نتیجه

به لطف استخراج کارکردها در نهایت یک تابع endpoint()‎ به دست می‌آید که نوشتن آن لذت‌بخش است:

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

بدین ترتیب به پایان این راهنما می‌رسیم. یک اصلی در بین توسعه‌دهندگان وجود دارد که بیان می‌کند: «مهم نیست چه نوع کدی در اختیار شما قرار می‌گیرد، همیشه کاری کنید که کدی که تحویل می‌دهید، بهتر از کدی باشد که تحویل گرفته‌اید.»

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

میثم لطفی (+)

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

آیا این مطلب برای شما مفید بود؟

نظر شما چیست؟

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