۲۲ اوت ۲۰۲۳

کد نینجا

@@@needs translation@@@ @@@old part@@@

یادگیری بدون تفکر، بی‌فایده است؛ تفکر بدون یادگیری، خطرناک. @@@old part@@@

@@@new part@@@

Learning without thought is labor lost; thought without learning is perilous. @@@new part@@@ @@@needs translation@@@

Confucius (Analects)
Confucius (Analects)

نینجاهای برنامه‌نویس در گذشته از این ترفندها برای باز کردن ذهن حافظان کد استفاده می‌کرده‌اند.

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

توسعه‌دهندگان تازه‌کار، بعضی اوقات در استفاده از این ترفندها بهتر از نینجاهای برنامه‌نویس عمل می‌کنند.

موارد زیر را با دقت بخوانید و مشخص کنید کدام هستید: یک نینجا، یک تازه‌کار، یا شاید یک بازبین کد؟

مزاح

افراد زیادی سعی می‌کنند مسیرهای نینجا شدن را طی کنند. تعداد اندکی موفق می‌شوند.

نکته مترجم

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

اختصار، روح شوخ‌طبعی است

کد را تا حد امکان کوتاه کنید. به همه نشان بدهید که چقدر باهوش هستید.

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

برای نمونه، به این عملگر سه‌گانه ('?') نگاه کنید:

// برگرفته از یک کتابخانه‌ی مشهور جاوااسکریپت
i = i ? i < 0 ? Math.max(0, len + i) : i : 0;

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

به او بگویید که «کوتاه‌تر، همیشه بهتر است». وی را به راه نینجا شدن هدایت کنید.

متغیرهای تک‌حرفی

دائو، در سکوت، پنهان است. تنها دائو به خوبی شروع شده و به خوبی کامل می‌شود.

لائوتسه (دائو دِ جینگ)

یک راه دیگر برای کدنویسی سریع‌تر، استفاده از متغیرهای تک‌حرفی در همه‌جاست. برای مثال: a، b یا c.

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

اما یک استثنا وجود دارد. یک نینجای واقعی هرگز از i برای شمارنده‌ی یک حلقه‌ی for استفاده نخواهد کرد. در هر جایی از این نام استفاده می‌کند، به جز این مورد خاص. به اطراف خود بنگرید، تعداد زیادی از حروف عجیب و غریب وجود دارند. برای مثال، x یا y. از آن‌ها استفاده کنید.

به‌خصوص استفاده از یک متغیر نامتعارف به عنوان شمارنده‌ی یک حلقه، وقتی حالتان را دگرگون می‌کند که بدنه‌ی حلقه، ۱-۲ صفحه باشد (اگر امکان طولانی‌تر کردن آن وجود دارد، دریغ نکنید). حال اگر شخصی با دقت به درون حلقه نگاه کند، به سرعت نخواهد توانست تشخیص دهد که متغیری با نام x، در واقع همان شمارنده‌ی حلقه است.

از مخفف‌ها استفاده کنید

اگر قوانین تیم، استفاده از اسامی تک‌حرفی و مبهم را ممنوع کرده است، آن‌ها را کوتاه کنید، مخفف بسازید.

همانند زیر:

  • listlst.
  • userAgentua.
  • browserbrsr.
  • و غیره

فقط افراد بابصیرت قادر خواهند بود این نوع اسامی را دریابند. سعی کنید همه‌چیز را کوتاه کنید. تنها یک فرد شایسته باید بتواند توسعه‌ی کد شما را تایید کند.

اوج بگیر. سبک باش.

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

لائوتسه (دائو دِ جینگ)

هنگامی که می‌خواهید نامی را انتخاب کنید، سعی کنید از خلاصه‌ترین کلمات استفاده کنید. برای مثال obj، data، value، item، elem و غیره.

  • نام ایده‌آل برای یک متغیر، data است. در هر جایی که توانستید از آن استفاده کنید. در واقع، هر متغیری داده را درون خود نگه می‌دارد، نه؟

    اما اگر data قبلا گرفته شده بود چه؟ value را امتحان کنید، این نام نیز جهانی است. گذشته از این‌ها، یک متغیر در نهایت یک مقدار به خود می‌گیرد.

  • یک متغیر را با نوعش نام‌گذاری کنید: str، num و غیره

    امتحان کنید. یک تازه‌وارد جوان ممکن است تعجب کند: آیا واقعا این نام‌ها برای یک نینجا مفید هستند؟ در واقع، بله!

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

    نوع مقدار را به راحتی می‌توان با اشکال‌زدایی به دست آورد. ولی مراد از خود متغیر چیست؟ کدام رشته/عدد را در خود ذخیره می‌کند؟

    بدون یک مراقبه‌ی طاقت‌فرسا، هیچ راهی برای فهمیدن آن وجود ندارد.

  • حال اگر نام دیگری به این شکل وجود نداشته باشد چه؟ کافی است یک عدد به آن‌ها اضافه کنید: data1، ‍item2، elem5 و غیره.

تست توجه

تنها یک برنامه‌نویس ملتفت باید قادر به درک کدتان باشد. اما چگونه از کیفیت کد خود مطمئن شویم؟

یکی از راه‌ها، استفاده از اسامی مشابه برای متغیرهاست، برای مثال date و data.

هر جایی توانستید، آن‌ها را در هم آمیزید.

خواندن سریع اینچنین کدی غیرممکن خواهد بود. و هنگامی که یک غلط تایپی در کدتان وجود داشته باشد، برای یک مدت طولانی درگیر خواهیم بود. در چنین شرایطی، چای بنوشید.

مترادف‌های خلاقانه

@@@needs translation@@@ @@@old part@@@

سخت‌ترین کار، یافتن یک گربه‌ی سیاه در یک اتاق تاریک است، به‌خصوص اگر هیچ گربه‌ای وجود نداشته باشد. @@@old part@@@ @@@new part@@@

The Tao that can be told is not the eternal Tao. The name that can be named is not the eternal name. @@@new part@@@ @@@needs translation@@@

Laozi (Tao Te Ching)
Laozi (Tao Te Ching)

استفاده از نام‌های مشابه برای مفاهیم یکسان زندگی را بسیار جذاب‌تر نموده و خلاقیت شما را در معرض دید عموم قرار می‌دهد.

برای نمونه، پیشوند توابع را در نظر بگیرید. اگر تابعی یک پیام را بر روی صفحه نمایش می‌دهد، نام آن را با display... آغاز کنید، مثلا displayMessage. حال اگر یک تابع دیگر، چیز دیگری (برای مثال نام یک کاربر) را بر روی صفحه نمایش می‌دهد، نام تابع را با show... شروع کنید، مثلا showName.

با این کار، این تلقین را ایجاد کنید که یک تفاوت ناچیز بین این توابع وجود دارد، در حالی که این طور نیست.

با یاران نینجای خود عهدی ببندید: اگر آرش توابع را در کد خود با display... نمایش می‌دهد، محمد می‌تواند از render... استفاده کند، آیدا از paint...و غیره. ملاحظه می‌کنید که با این کار، کدتان بسیار جالب و متنوع خواهد شد.

… و اینک برگ برنده!

برای توابعی که تفاوت‌های فاحش و مهمی دارند، از پیشوندهای یکسانی استفاده کنید!

برای نمونه، تابع printPage(page) از یک چاپگر استفاده خواهد کرد. و تابع printText(text) متن را بر روی صفحه نمایش خواهد داد. اجازه بدهید یک فرد اجنبی، با دیدن تابع printMessage به فکر فرو برود: «این تابع، پیام را به کجا می‌فرستد؟ به یک چاپگر یا به صفحه‌ی نمایش؟». برای این که کارتان واقعا بدرخشد، بهتر است printMessage(message) پیام را در پنچره‌ی جدید به کاربر نمایش دهد!

از نام‌ها دوباره استفاده کنید

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

لائوتسه (دائو دِ جینگ)

تنها زمانی که واقعا ضروری است از یک متغیر جدید استفاده کنید.

در عوض، از نام‌های موجود دوباره استفاده کنید. کافی است مقادیر جدید را به آن‌ها نسبت دهید.

در یک تابع، سعی کنید تنها از متغیرهایی که به عنوان پارامتر ارسال شده‌اند استفاده کنید.

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

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

برای نمونه:

function ninjaFunction(elem) {
  // ۲۰ خط از کد که با elem کار می‌کنند

  elem = clone(elem);

  // ۲۰ خط دیگر، که حال با کپی elem کار می‌کنند
}

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

این امر به دفعات مشاهده شده است. به شدت موثر است، حتی در مقابل یک نینجای باتجربه.

زیرخط‌ها برای سرگرمی

از زیرخط‌های _ و __ قبل از نام متغیرها استفاده کنید، برای مثال _name یا __value. عالی است اگر فقط شما معنی آن‌ها را بدانید. حتی بهتر، آن‌ها را صرفا برای سرگرمی اضافه کنید، بدون این که اصلا معنی خاصی داشته باشند. یا می‌توانند معانی مختلفی در مکان‌های متفاوت داشته باشند.

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

یک نینجای زیرک، زیرخط‌ها را در یک مکان از کد قرار داده، و در مکان‌های دیگر از آن‌ها پرهیز می‌کند. این کار، کد را شکننده‌تر می‌کند و احتمال خطاهای آتی را افزایش خواهد داد.

عشق خود را نشان دهید

بگذارید همه ببینند موجودیت‌های شما چقدر باشکوه هستند! نام‌هایی مانند superElement، megaFrame و niceItem قطعا مخاطب را روشن خواهند کرد.

از یک طرف، متغیرهایی به این شکل نام‌گذاری شده‌اند: super..، mega..، nice... ولی از طرف دیگر، هیچ جزئیاتی را نمی‌توان از نامشان استخراج نمود. ممکن است مخاطب به مدت یک یا دو ساعت از زمان کاری خود در جستجوی یک معنی مخفی به مراقبه بپردازد.

متغیرهای بیرونی را مخفی کنید

در روشنایی بایست، چیزی در تاریکی دیده نخواهد شد.
در تاریکی بایست، همه چیز در روشنایی دیده خواهد شد.

گوان یین زی

از اسامی یکسانی برای متغیرهای درون و بیرون یک تابع استفاده کنید. به همین سادگی. نیازی به تلاش برای اختراع نام‌های جدید نیست.

let user = authenticateUser();

function render() {
  let user = anotherValue();
  ...
  ...many lines...
  ...
  ... // <-- یک برنامه‌نویس می‌خواهد در این‌جا با user کار کند و ...
  ...
}

برنامه‌نویسی که تابع render را می‌خواند احتمالا متوجه نخواهد شد که یک user محلی وجود دارد که بر روی متغیر بیرونی سایه انداخته است.

با فرض این که user یک متغیر خارجی است کار را ادامه می‌دهند. خواهند پنداشت این متغیر، نتیجه‌ی تابع authenticateUser() است. تله پهن شده است. درود بر اشکال‌زدایی.

اثرات جانبی در همه‌جا!

توابعی وجود دارند که در ظاهر، چیزی را تغییر نمی‌دهند. مانند isReady()، checkPermission()، findTags() و … فرض بر این است که این توابع، محاسباتی را انجام داده، داده‌ای را پیدا کرده و برمی‌گردانند، بدون این که چیزی را در خارج از حوزه‌ی خود تغییر دهند. به عبارت دیگر، این توابع بدون اثرات جانبی هستند.

یک ترفند واقعا زیبا این است که در کنار وظیفه‌ی اصلی توابع، یک عمل مفید دیگر را نیز به آن‌ها اضافه کنیم،

حالت تعجب روی چهره‌ی یارانتان وقتی تابعی را می‌بینند که با is..، check.. یا find... نام‌گذاری شده است و چیزی را هم تغییر می‌دهد، قطعا مرزهای منطق و استدلال شما را پهناورتر خواهد کرد.

یک راه دیگر برای شگفت‌زده کردن، برگرداندن یک نتیجه‌ی غیراستاندارد است.

تفکر اصل خود را نشان دهید! بگذارید فراخوانی checkPermission مقدار true/false را برنگرداند، در عوض یک شی پیچیده با نتایچ بررسی را برگرداند.

توسعه‌دهندگانی که سعی کنند از if (checkPermission(..)) استفاده کنند، حیرت‌زده خواهند شد که چرا این تابع به درستی عمل نمی‌کند. به آن‌ها بگویید: «مستندات را بخوانید!». و این مقاله را به آن‌ها معرفی کنید.

!توابع قدرتمند

دائوی بزرگ در همه‌جا جاری است،
در چپ، و در راست.

لائوتسه (دائو دِ جینگ)

توابع را محدود به چیزی که در نامشان نوشته شده است نکنید. بازتر فکر کنید.

برای نمونه، تابع validateEmail(email) می‌تواند (در کنار بررسی صحت ایمیل) پیام خطایی را نمایش داده و از کاربر بخواهد تا ایمیل را دوباره وارد کند.

اعمال اضافی نباید از روی نام تابع به راحتی برداشت شوند. یک کدنویس نینجای واقعی به گونه‌ای کد خود را می‌نویسد که از روی خود کد هم نتوان اعمال اضافی را تشخیص داد.

ادغام چندین عمل در یکی، از کد شما در مقابل استفاده‌ی مجدد محافظت می‌کند.

تصور کنید، توسعه‌دهنده‌ی دیگری می‌خواهد تنها ایمیل را بررسی کنید، نه این که پیامی را برگرداند. تابع شما، validateEmail(email)، که هر دو کار را با هم انجام می‌دهد به کارش نخواهد آمد. در نتیجه وی، مراقبه‌ی شما را با پرسیدن سوالاتی در مورد آن تابع بر هم نخواهد زد.

خلاصه

تمام نصایح بالا برگرفته از کدهای واقعی هستند. گاهی اوقات، این کدها توسط توسعه‌دهندگان باتجربه نوشته شده‌اند، حتی شاید باتجربه‌تر از شما ;)

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