نقل قولهای زیادی، در مورد کیفیت کد وجود دارند. دستور العملهای فراوانی نیز در این راستا وجود دارند. یکی از ابزارهایی که برای نوشتن کدهایی با کیفیت مطلوب وجود دارد، مجموعه الگوهای بد کد نویسی است که به Code smell یا بوی بد کد مشهور هستند.
بوی بد کد، نشانههایی در کد هستند که حکایت از مشکلات عمیقتری دارند. بوی بد کد مساوی با باگ نیست. ولی خطر افزایش باگها و یا مشکلاتی را در آینده، به دنبال خواهند داشت. بوی بد کد معمولا حاصل رعایت نکردن یک سری اصول اولیه برنامه نویسی و یا طراحی شیء گرا هستند.
برای بهبود کیفیت نرم افزار در دراز مدت نیاز است موارد بوی بد کد به دقت بررسی و رفع شوند. رفع شدن آنها ریسک انباشته شدن بوی بد کد را در پروژه کم خواهد کرد. یکی از فواید جلوگیری از انباشته شدن چنین الگوهای بدی در پروژه، بهبود فرآیند نگهداشت آن میباشد که موضوعی بسیار مهم برای چابکی یک تیم نرم افزاری است.
هنگام مشاهدهی بوی بد، در بخشی از کدها، معمولا اولین اقدام، رفع آن است (Refactoring). در فرآیند رفع آن ممکن است الگوهای بد دیگری در کد یافت شوند که با آنها نیز به همین صورت برخورد خواهد شد.
انوع بوهای بد کد به دستههای زیر طبقه بندی میشوند.
کدهای متورم (Bloaters)
این دسته در واقع تکه کدهایی (متد، کلاس و ...) هستند که به دلیل بزرگی بیش از اندازه عملا امکان کار با آنها وجود ندارد. این بخشهای بزرگ کد معمولا با توسعه تدریجی محصول ایجاد و روی هم انباشته میشوند. بوهای بد این دسته بندی به صورت زیر هستند:
1 - متدهای بلند (Long method): در این الگوی بد، متدها تعداد خطهای زیادی از کد را شامل میشوند. به طور معمول متدهایی با تعداد خطوط بیشتر از 10 خط، متدهای بلند محسوب میشوند. نکته قابل توجه این است که هیچ کس متدی را با تعداد خطوط زیاد طراحی نمیکند! معمولا به مرور زمان تعداد خطهای یک متد افزایش مییابند.
2 - کلاسهای بزرگ (Large class): کلاسی که تعداد فیلدها، متدها و خطوط کد زیادی دارد.
3 - وسواس استفاده از متغیرهای دادهای اولیه (Primitive obsession): این بوی بد معمولا به سه شکل بروز میکند.
- استفاده از متغیرهای اولیه بجای ساختارهای کوچک برای کارهای اولیه مانند Currency, DateTime, PhoneNumber
- استفاده از constantها برای کد کردن اطلاعات مانند USER_ADMIN_ROLE = 1
- استفاده از constantهای رشتهای به عنوان نام فیلدها در آرایههای داده
4 - تعداد پارامترهای زیاد متد (Long parameter list): تعداد پارامترهای بیشتر از سه یا چهار عدد در یک متد.
5 - توده داده (Data clumps): در بعضی موارد ممکن است از متغیرها به صورت دستهای در مکانهای مختلف کد استفاده شود. مانند استفاده از دستهای از متغیرها برای نگه داشتن اطلاعات مربوط به اتصال پایگاه داده. این دستهها باید به کلاسهای حمل کننده داده خود تغییر کنند.
بد استفاده کنندگان از شیء گرایی (Object orientation abusers)
تکه کدهای این بخش در واقع بد استفاده کنندگاه یا ناقص استفاده کنندگان از اصول شیء گرایی هستند. در این دسته بندی موارد زیر وجود دارند:
1 - گذارههای switch: وجود یک گذاره switch پیچیده یا دنبالهای از گذارههای if
2 - درخواست رد شده (Refused request): در این حالت یک کلاس مجموعه محدودی از اعضای کلاس پدر خود را پیاده سازی میکند و باقی اعضای کلاس پدر یا بدون استفاده میمانند یا با استفاده از پرتاب کردن استثناء (Exception throwing) از کار انداخته میشوند.
3 - فیلد موقتی (Temporary field): در این حالت متغیرها مقدار خود را در شرایط خاصی میگیرند و در بقیه شرایط خالی هستند.
4 - کلاس هایی دقیقا مشابه در کارایی ولی متفاوت در مشخصات (Alternative Classes with Different Interfaces): دو کلاس دقیقا یک کار را انجام میدهند ولی نام اعضای آنها (متد و ...) متفاوت است.
جلوگیری کنندگان از تغییر(Change preventers)
این نشانهها حاکی از این دارند زمانیکه تغییری در یک بخش کد نیاز باشد، در راستای آن حتما باید دیگر بخشهای کد نیز به مقدار زیادی تغییر کنند. در این حالات اعمال تغییرات و نگهداری کد به شدت سخت خواهد شد.
مواردی که در این دسته بندی قرار دارند به صورت زیر میباشند:
1 - تغییر واگرا (Divergent change): این حالت زمانی اتفاق میافتد که برای اعمال یک تغییر به کلاس نیاز است متدهای زیادی را تغییر دهید. به طور مثال به ازای هر نوع محصولی که به محصولات شما اضافه میشود باید متدهای ذخیره، بازیابی، جستجو را تغییر دهید.
2 - Shotgun Surgery: این حالت شباهت زیادی به تغییر واگرا دارد. تنها تفاوت آن این است که در این حالت شما به ازای هر تغییر نیاز است کلاسهای زیادی را تغییر دهید. تغییر واگرا در بدنه یک کلاس اتفاق میافتد.
3 - سلسله مراتب موازی ارث بری (Parallel inheritance hierarchy): این مورد یکی کمتر درک شدهترین موارد است. در این حالت زمانی که یک زیر کلاس برای یک کلاس ایجاد میکنید به ازای آن ناخودآگاه مجبور میشوید یک زیر کلاس برای کلاس دیگری ایجاد کنید.
کدهای غیر ضروری (Dispensables)
این دسته از کدها معمولا کدهایی هستند بی دلیل و بی استفاده. کدهایی که نبودنشان بهتر از بودنشان است! حذف کردن این کدها به خوانایی و قابلیت نگهداری کد خواهد افزود. بوهای بدی که در این دسته بندی قرار دارند به صورت زیر میباشند:
1 - کامنت: یک متد، با مقادیر فراوانی از کامنتهای توضیحی پر شده است.
2 - کد تکراری: در این بوی بد، دو قطعه کد دقیقا مانند یکدیگر هستند.
3 - کلاس داده (ِData class): کلاسهایی که تنها فیلدهای اطلاعاتی در آنها وجود دارند و متدهای خامی که جهت دریافت یا ذخیره اطلاعات در آنها استفاده میشوند. این کلاسهای معمولا هیچ روال منطقی ای در خود ندارند. یکی از قدرتهای شیء گرایی افزودن رفتار به کلاسها در کنار اقلام اطلاعاتی موجود در آن است.
4 - کلاس تنبل (Lazy class): اگر کلاس کار چندانی که درخور نگهداری و توسعه باشد، انجام نمیدهد بهتر است از بین برود.
5 - کد مرده (Dead code): متغیر، پارامتر، متد یا کلاسی که دیگر هیچ استفادهای از آن متصور نیست و هیچ استفادهای در حال حاضر از آن وجود ندارد.
6 - کلی نگری بیش از اندازه (Speculative Generality): این الگو نیز کدهایی را شامل میشود که بلااستفاده هستند. ولی دلیل بلااستفاده بودن آن کلی نگری و دور اندیشی بدون دلیل است. معمولا کدهای تولیدی برای شرایط فعلی و پیشبینی آینده تولید میشوند. اگر این پیشبینی آینده به درستی و بر مبنای واقعیات انجام نشود، معمولا نتیجه کار، طراحی و پیاده سازی ای بی فایده و بلااستفاده خواهد بود.
کدهایی بیش از اندازه وابسته به هم (Couplers)
کدهایی که در این دسته قرار میگیرند معمولا یا خود درگیر یک وابستگی شدید هستند یا به ایجاد وابستگی بین کلاسها کمک میکنند. بویهای بدی که در این دسته بندی قرار میگیرند به صورت زیر هستند:
1- متد حسود (Feature envy): متدی که از اعضای یک شیء دیگر بیشتر از اعضای کلاس خود استفاده میکند! این اتفاق معمولا زمانی میافتد که فیلدهایی به یک "کلاس داده" منتقل میشوند. وقتی این اتفاق میافتد یکی از راه حلها، انتقال روالهای استفاده کننده از فیلدها به "کلاس داده" مربوطه است.
2 - کلاسهای بیش از اندازه صمیمی (Inappropriate Intimacy): کلاسها از اعضای internal یکدیگر بیش از اندازه استفاده میکنند. کلاسهای خوب کلاسهایی هستند که کمترین اطلاعی را از وضعیت داخلی یکدیگر دارند.
3 - کتابخانههای ناقص (Incomplete Library Class): زمانیکه کتابخانهای آماده میشود، بالاخره روزی میرسد که این کتابخانه نیازهای پروژه را رفع نمیکند و نیاز به توسعه خواهد داشت. ولی از آنجایی که کتابخانهها به صورت فقط خواندنی در اختیار پروژهها قرار میگیرند، در صورتیکه توسعه دهنده اصلی آن از توسعه کتابخانه سر باز بزند، مشکلاتی بوجود خواهد آمد.
4 - زنجیره فراخوانیها (Message chain): زمانیکه یک متد در بدنه خود پیامی به شیء دیگری میفرستد که آن شیء نیز به خودی خود پیامی به شیء دیگری میفرستد (و الی آخر) یک زنجیره فراخوانی بوجود آمده است. در این روش بیرونیترین استفاده کننده از متد در واقع وابسته به یک زنجیرهای از فراخوانیها است که تغییر در هر قدمی از آن باعث خرابی خواهد شد.
5 - دلال (Middle man): اگر کلاسی تنها کاری که انجام میدهد انتقال فراخوانی به کلاس دیگری است، دیگر نیازی به این کلاس وجود نخواهد داشت.
اطلاع از الگوهای بد کد نویسی به همان اندازه اطلاع از الگوهای خوب کد نویسی در کیفیت محصول تولیدی اثر مثبت خواهند داشت. یادگیری طبقه بندی شده این الگوها کار را برای استفاده روزمره از آنها آسانتر خواهد کرد.