راهنمای Refactor یا بازسازی کد – به زبان ساده
منظور از Refactor کردن کد، پیرایش، تغییر چیدمان و یا حذف و اضافه بخشهایی به کد است، به طوری که بهبودی از قبیل افزایش سرعت یا خوانایی در آن حاصل شود. در این مقاله با بررسی برخی مثالها روش بازسازی یا Refactor کردن کد را خواهیم آموخت.
فرض کنید کدی مانند زیر داریم که در حالت نامرتب قرار دارد:
منطق فوق، URL مربوط به «نقاط انتهایی» (endpoints) را مشخص میسازد و به چند چیز بستگی دارد. این موارد شامل سرویسی که استفاده میشود، رندر شدن روی سرور یا کلاینت و این که در محیط توسعه یا پروداکشن قرار داریم و غیره هستند. یکی از دلایل این که این قطعه کد میتواند چنین آشفته باشد، این است که ممکن است فراموش کنیم که تکرار کد بسیار کمهزینهتر از تجرید نادرست است.
اما خبر خوب این است که میتوان برخی تکنیکهای آسان برای سادهسازی گزارههای تودرتوی if-else اعمال کرد. خبر بد این است که این قطعه کد برای کارکرد اپلیکیشن ضروری است، چون همه درخواستها به آن ارسال میشوند و همچنین تست نشده است. در ادامه روش بازسازی این کد را بررسی میکنیم.
تأمین پوشش تست 100% برای کد
بازسازیهای کد غالباً فینفسه توجیهی ندارند. با این حال معمولاً کسی در مورد افزایش پوشش تست کد به خصوص اگر مربوط به چنین کارکرد مهمی باشد، شکایتی نمیکند. بنابراین کار را از همین افزایش پوشش تست آغاز میکنیم و دلیل آن افزایش اعتماد به نفس به بازسازی نبوده، بلکه چون زمانی که تست پایان مییابد، ایده بهتری برای میزان دشواری بازسازی کد حاصل میشود.
امروزه غالب توسعهدهندگان از رویه TDD پیروی میکنند، اما این بخش خاص از کدبیس در زمانهای بسیار قبل نوشته شده است و اهمیت آن موجب شده که از بازسازی کد در طی زمان اجتناب کنیم.
مزیت اصلی TDD امکان بازسازی کد بدون ترس و بدون هزینه است.
همچنین میخواهیم مطمئن شویم که پوشش تست 100% است، بنابراین از فلگ Jest به صورت coverage– استفاده میکنیم که خروجی زیر را در اختیار ما قرار میدهد:
-------------------|----------|----------|----------|----------|-------------------| File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s | -------------------|----------|----------|----------|----------|-------------------| ... | ... | ... | ... | ... | ...| endpoint.js | 100 | 100 | 100 | 100 | | ... | ... | ... | ... | ... | ...| -------------------|----------|----------|----------|----------|-------------------| Test Suites: 1 passed, 1 total Tests: 12 passed, 12 total
کارکرد دقیق
اکنون که به لطف تستها، اعتماد بیشتری به دست آوردهایم، میتوانیم شروع به تجزیه کد بکنیم. کار را از ابتدا آغاز میکنیم. میبینیم که بسته به سرویسهای مختلف، محیط، سمت کلاینت یا سرور بودن و غیره مقادیر متفاوتی به protocol انتساب یافتهاند و سپس بقیه UTL به انتهای تابع اضافه شده است.
const url = `${protocol}://${domain}`
بنابراین میتوانیم کدی که پروتکل را تعیین میکند در تابع خاص خود قرار دهیم و آن را صرفاً یک بار فرا بخوانیم:
همین کار که در مورد ()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 در کد فوق به قدر کافی کوچک است تا به راحتی متوجه بشوید. البته همه موارد به این سادگی نیستند، اما بسیار بهتر از کد اصلی شدهاند. فایل ما در نهایت به صورت زیر در میآید:
بدین ترتیب به پایان این راهنما میرسیم. یک اصلی در بین توسعهدهندگان وجود دارد که بیان میکند: «مهم نیست چه نوع کدی در اختیار شما قرار میگیرد، همیشه کاری کنید که کدی که تحویل میدهید، بهتر از کدی باشد که تحویل گرفتهاید.»
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای برنامهنویسی
- ویژوال استودیو کد — ۱۰ نکته ضروری برای افزایش بهرهوری
- مجموعه آموزشهای JavaScript (جاوا اسکریپت)
- بازسازی کد با عملگر سه تایی در جاوا اسکریپت — راهنمای کاربردی
- تبدیل اپلیکیشن اندروید به Jetpack — به زبان ساده
==