در این مقاله خواهیم آموخت که چگونه مشکلات احتمالی در برنامه خود را که با عنوان “Code Smell” شناخته شده است، شناسایی و ارزیابی کنیم. در برنامه نویسی code smell به چالش هایی گفته می شود که ممکن است برنامه را با خطا مواجه کند و در بازنگری کد ها (Code Review) کارایی بسیار زیادی دارد.
Code smell چیست؟
یک نشانه سطحی است که احتمالا نشان دهنده ی یک مشکل عمیق تر در برنامه است، بوی کد (code smell) مانع نگهداری، توسعه و تکامل برنامه می شود. گاهی اوقات بوی کد ناشی از طراحی و سبک برنامه نویسی ضعیف است. این اصطلاح اولین بار توسط آقای کنت بِک (Kent Beck) زمانی که در نوشتن کتاب Refactoring به مارتین فولر (Martin Fowler) کمک می کرد ابداع شده است. کنت بِک در اواخر دهه ی 1990 به تاکید بر کیفیت طراحی نرم افزار اشاره کرد و استفاده از اصطلاح بوی کد را رواج داد. این اصطلاح پس از ارائه در کتاب Refactoring مارتین فولر که در مورد بهبود طراحی کدِ موجود است، در برنامه نویسی به یک کلمه ی معمول و پر استفاده تبدیل شد. تعریف کوتاه و سریع بالا حاوی چند نکته ی ظریف است.
اول اینکه "بو (smell)" همانطور که از نامش پیداست بو می دهد پس چیزی است که سریعا تشخیص داده می شود. یک متد طولانی یا یک کلاس با داده زیاد ولی بدون رفتار، مثال های خوبی در این مورد هستند.
دومین نکته اینکه، بو همیشه نشان از یک مشکل نیست. برای مثال برخی از متد های طولانی بسیار خوب هستند. باید عمیق تر به آنها نگاه کرد تا متوجه شد که آیا یک مشکل اساسی وجود دارد یا خیر. بو ها به ذات خود بد نیستند. آنها اغلب نشان دهنده ی یک مشکل هستند نه خود مشکل.
بهترین نوع بو چیزی است که به راحتی قابل تشخیص باشد و شما را به سمت مشکلات واقعاً جالبی سوق بدهد.
یکی از نکات خوب در مورد بوها این است که افراد بی تجربه هم می توانند آنها را تشخیص بدهند، حتی اگر به اندازه کافی اطلاعاتی برای ارزیابی مشکل و یا اصلاح آن نداشته باشند.
مارتین فولر می گوید: هر احمقی می تواند کدی بنویسد که کامپیوتر آن را درک کند، برنامه نویسان خوب کدی می نویسند که هر انسانی بتواند آن را درک کند.
وجود بوی بد در کد قطعا به این معنی نیست که نرم افزار کار نمی کند، نرم افزار با وجود بوی بد کد باز هم خروجی خود را می دهد. اما این بوی بد ممکن است باعث کند شدن پردازش، افزایش خطر خرابی و خطاهای نرم افزار شود.
کد بد بو باعث پایین آمدن کیفیت کد و در نتیجه افزایش بدهی فنی می شود. با توجه به استانداردهای طراحی که توسط سازمان ها تعیین می شود، بوی کد در پروژه های مختلف و بین یک توسعه دهنده با توسعه دهنده ی دیگر متفاوت است.
رایج ترین Code Smell ها
• Bloaters
متد ها و کلاس هایی هستند که به حدی گسترده و بزرگ شده اند که کار با آنها سخت است. معمولا این بوها بلافاصله ایجاد نمی شوند، بلکه با تکامل برنامه و گذشت زمان بوجود می آیند. موارد زیر نشانگر این نوع بو هستند:
Long Method
متدی است که دارای خطوط زیادی کد بوده و چندین وظیفه را به عهده دارد. این که دقیقا چه متدی طولانی است قابل بحث است و تا حدودی به زبان برنامه نویسی بستگی دارد، به طور کلی هر متدِ طولانی تر از ده خط باید بویایی شما را بیدار کند.
Large Class
کلاسی که دارای تعداد زیادی خطوطِ کد، متد و فیلد باشد.
Primitive Obsession
استفاده از پایه های (Primitive) یک زبان به جای استفاده از object های کوچک در کارهای ساده.
استفاده از ثابت ها (constansts) برای رمز گذاری اطلاعات.
استفاده از ثابت های رشته ای به عنوان نام یک فیلد برای استفاده در داده های آرایه ای.
Long Parameter List
استفاده بیش از 3 یا 4 پارامتر برای ورودی یک متد.
Data Clumps
گاهی قسمتهای مختلف کد شامل گروهی از فیلدهای یکسان است (مانند فیلد های اتصال به پایگاه داده). این گروه ها باید به کلاس های خاص خود تبدیل شوند.
• Object-Orientation Abusers
این بوها مربوط به استفاده ی درست یا نادرست از اصول برنامه نویسی شیء گرا هستند. موارد زیر نشانگر این نوع بو هستند:
Switch Statements
وقتی یک عملگر switch پیچیده یا دنباله ای از دستورات if در کد وجود دارد.
Temporary Field
وقتی فیلد هایی در یک کلاس فقط در شرایط خاصی مقدار دهی می شوند و خارج از این شرایط خالی هستند.
Refused Bequest
وقتی یک زیر کلاس فقط برخی از متد ها و فیلد هایی که از والدین خود به ارث برده است را استفاده می کند و باعث از بین رفتن سلسله مراتب ارث بری می شود.
Alternative Classes with Different Interfaces
زمانی که دو کلاس با متد های غیر همنام وجود دارند اما هر دوی آنها کار یکسانی را انجام می دهند.
• Change Preventers
این بو ها به این معنا هستند که اگر نیاز به تغییر بخشی از کد را دارید، باید در بخش های دیگر نیز تغییرات زیادی را ایجاد کنید. در نتیجه توسعه برنامه بسیار پیچیده و پر هزینه خواهد بود. موارد زیر نشانگر این نوع بو هستند:
Divergent Change
هنگام تغییر یک کلاس، مجبور به تغییر بسیاری از متد های غیر مرتبط می شوید. مثلا: هنگام اضافه کردن متدی برای افزودن نوع جدید محصول، باید متد های مربوط به یافتن، نمایش و سفارش محصول را نیز تغییر دهید.
Shotgun Surgery
انجام هر گونه تغییر مستلزم ایجاد تغییرات کوچکی در کلاس های متفاوتی است.
Parallel Inheritance Hierarchies
هر زمان که بخواهید برای یک کلاس یک زیر کلاس تعریف کنید متوجه می شوید که باید برای کلاس دیگری نیز یک یا چند زیر کلاس تعریف کنید.
• Dispensable
چیزی بیهوده و غیر ضروری است که عدم وجود آن در کد باعث تمیزتر شدن، کارآیی و فهم آسان تر کد می شود. موارد زیر نشانگر این نوع بو هستند:
Comments
وجود کامنت توضیحی زیاد در یک متد یا کلاس.
Duplicate Code
یکسان شدن دو قطعه کد در برنامه.
Lazy Class
درک، حفظ و نگهداری کلاس ها همیشه نیازمند وقت و هزینه بوده. بنابراین اگر کلاسی به اندازه ای استفاده نشده است که توجه را به خود جلب کند باید حذف شود و یک lazy class است.
Data Class
کلاس داده ای فقط شامل فیلد ها و متدهایی ساده برای دست یابی به این فیلدها هستند (getters and setters). این کلاس ها به سادگی تبدیل به ظرف هایی برای داده های مورد استفاده توسط کلاس های دیگر می شوند. این کلاس ها هیچ قابلیت دیگری ندارند و نمی توانند به طور مستقل از داده هایی که متعلق به خود آنها است استفاده کنند.
Dead Code
وقتی یک متغیر، پارامتر، فیلد، متد یا کلاس مدت زیادی مورد استفاده قرار نگیرد (معمولا به دلیل منسوخ بودن) باید حذف شود.
Speculative Generality
اگر در یک کلاس، متد، فیلد یا پارامتر استفاده نشده وجود داشته باشد باید حذف شود.
• Couplers
همه ی بو های این گروه به جفت شدن و وابستگی بیش از حد بین کلاس ها اشاره دارد. وابستگی باعث می شود که ایجاد تغییر در یک مکان مستلزم ایجاد انبوهی از تغییرات در مکان های دیگر باشد. موارد زیر نشانگر این نوع بو هستند:
Feature Envy
وقتی یک متد به داده های یک object دیگر علاوه بر داده های خود دسترسی پیدا کند.
Inappropriate Intimacy
وقتی یک کلاس از فیلد ها و متد های داخلی کلاس دیگری استفاده کند.
Message Chains
وقتی در کد یک سری تماس شبیه به a()->b()->c()->d$ مشاهده شود.
Middle Man
وقتی یک کلاس فقط یک کار را انجام می دهد و آن کار را نیز به کلاس دیگری محول می کند.
چگونه می توان Code smell را از بین برد؟
پس از شناسایی انواع بو ها، روند بازبینی کد (code review) آغاز می شود. دو یا چند توسعه دهنده ممکن است با استفاده از یکی از روش های اصلی بازبینی کد مانندad-hoc code review ، برای یافتن چنین بوهایی به صورت دستی تلاش کنند. بسیاری از بوها با بازبینی دستی یافت نمی شوند و برای شناسایی آن ها از ابزار های بازبینی خودکار استفاده می شود.
پس از اینکه توسعه دهندگان کد بد بو را پیدا می کنند، قدم بعدی آنها بازسازی است. فرآیند بازسازی و تغییر سیستم نرم افزاری به گونه ای است که رفتار خارجی نرم افزار را تغییر نمی دهد اما ساختار داخلی آن را بهبود می بخشد.
بوی کد حتی در کدی که توسط برنامه نویسان با تجربه نوشته شده است هم وجود دارد. این بوها می توانند طول عمر نرم افزار را کاهش دهد و نگهداری آن را دشوار کند. در صورت وجود کد های بد بو، توسعه قابلیت های نرم افزار نیز دشوار می شود. بوی کد می تواند پس از بارها بازبینی کد شناسایی نشود، به همین دلیل استفاده از ابزار های خودکار بازبینی کد، برای آسان تر شدن تشخیص بوی کد، پیشنهاد می شود.
منابع
https://martinfowler.com/bliki/CodeSmell.html
https://refactoring.guru/refactoring/smells
https://sourcemaking.com/refactoring/smells
https://apiumhub.com/tech-blog-barcelona/code-smells