بو در کد جاوا: شما تشخیص میدهید؟

در این مطلب نگاهی به ده code smell یا کدهایی که اصول طراحی را در جاوا نقض میکنند میاندازیم. شما اینها را در برنامه تشخیص میدهید؟
بوها ساختارهایی در کد هستند که اصول طراحی را نقض میکنند و روی طراحی تاثیر منفی میگذارند. در این جا بعضی از این ها را در کدهای جاوا توضیح میدهیم:
۱. واسط کاربری ثابت
این واسطهای کاربری تنها اعضای final و استاتیک دارند و فاقد متدها میباشند. بازآرایی پیشنهادی برای این بو بستگی به نوع ثابتها در واسطهای کاربری دارد:ثابتها میتوانند به عنوان اعضای کلاس اضافه شوند یا میتوانند به شکل enum بازنویسی شوند. واسط java.io.ObjectStreamConstant یک مثال برای این بو است.
۲. کلاس با متغیر global
منظور کلاسی است که یک فیلد عمومی و استاتیک دارد. از آنجایی که امکان تغییر آن توسط هرکسی وجود دارد، مشابه متغیر جهانی سی میشود با این تفاوت که نام کلاس را به عنوان پیشوند همراه خود دارد. به عنوان مثال:
class Balls { public static long balls = 0; }
۳. کلاس با تابع global
این یک کلاس عمومی است که تنها یک تابع استاتیک عمومی دارد و فاقد هرگونه فیلد یا متدی است. میتواند یک private constructor اختیاری داشته باشد که اجازه نمونه برداری را ندهد. چنین چیزی مشابه توابع جهانی سی هست به جز اینکه لازم است تابع با پیشوند اسم کلاس همراه شود.
۴. فیلدهای عمومی
این یک کلاس عمومی با اعضای غیر استاتیک و غیر final است. مراقبت و نگهداری از کلاسهای عمومی مشابه زبان سی دشوار است. همانطور که در [2] گفته شده است که چند کلاس در کتابخانههای جاوا این توصیه که کلاسهای عمومی نباید مستقیم فیلدها را در دسترس بگذارند را نقض میکند. یک مثال برجسته آن کلاسهای Point و Dimension در بسته java.awt است. به جای اینکه از اینان الگو برداری شود باید به عنوان درس عبرتی در نظر گرفته شود.
۵. کلاس abstract بی پدر و مادر
از یک کلاس abstract هیچ کلاس concrete منشعب نمیشود. یک کلاس abstract در صورتی میتواند به درستی استفاده شود که توسط یک کلاس concrete پیاده سازی شود. اگر کلاس abstract مفید نباشد میتواند حذف شود و درصورتی که از آن نمونه برداری میشود لازم است concrete باشد. پس اگر میخواهید یک کلاس abstract مفید باشد یک یا چند کلاس concrete باید آن را پیادهسازی نمایند.
۶. واسط کاربری فراموش شده.
یک کلاس تمام توابعی که در واسط کاربری لیست شده است را باهمان امضا پیادهسازی میکند. یک اشتباه محتمل وجود دارد که یک کلاس قصد دارد یک واسط کاربری را پیادهسازی کند اما فراموش میکند که واسط کاربری را به عنوان نوع پایه خود یادداشت کند. یک پیامد این بو این است که اشیا کلاس به عنوان زیر نوع آن واسط کاربری به حساب نمیآیند و از مزایای زیرنوع بودن و چندریختی در زمان اجرا نمیتواند استفاده کند.
۷. کلاس clone
یک کلاس clone یک کپی دقیق از کلاسی دیگر است (هیچ ربطی به وراثت ندارد) و تنها نام کلاس متفاوت است اما همه اعضای آن، امضای آن و دسترسی به آن و … مشابه کلاس اول است. یک محدودیت این است که حداقل یکی از اعضا لازم است در کلاس حضور داشته باشند. یک استثنا این است که ترتیب اعضا ممکن است متفاوت باشد.
اگر دو یا چند کلاس داده و رفتار مشترک داشته باشند بایستی از یک کلاس مشترک ارث ببرند که آن رفتار و داده مشترک را شامل میشود. پس یک بهبود این است که چک کنیم آیا ممکن است یک کلاس پایه مشترک فراهم کنیم و کلاس های clone را از آن به ارث ببریم. کلاسهای clone اغلب به خاطر کد کپی/پیست به وجود میآیند. در این شرایط، بهتر است این کلاسهای تکراری را از بین برده و از یک کلاس منحصربه فرد استفاده کنیم.
۸. کلاس تنها
این کلاس وابسته به کلاس دیگر یا استفاده کننده از آن نیست. به علاوه هیچ کلاس دیگری به آن وابسته نیست و از آن استفاده نمیکند. ممکن است واقعا اشتباهی در طراحی رخ نداده باشد اما وجود چنینی کلاس مستقلی نادر است. پس ممکن است خطایی در طراحی مرتکب شده باشیم.
۹. برگ abstract
کلاسهای abstract وقتی مفید و معنادار هستند که نزدیک ریشه در درخت سلسله مراتب کلاسها ظاهر شوند. در غیر اینصورت حضور آنها در برگ در این سلسله مراتب بیمعنی خواهد بود، چرا که بیفایده هستند و میتوانند حذف شوند. کلاس های abtract در سطح برگ معمولا نشانه مشکلات طراحی هستند.
۱۰. کلاسهای برچسب دار
این بو اغلب در برنامههایی که توسط برنامهنویسانی با پیشینه برنامهنویسی ساختاریافته (structured) نوشته میشود دیده میشود. آنها اغلب کلاسها را مشابه struct تعریف میکنند و به جای آن که وراثت بین نوعهای مرتبط ایجاد کنند و از چندریختی زمان اجرا استفاده کنند، یک enum دارند که نوعهای مختلف را لیست میکنند و از عبارت switch-case یا if-else های تو در تو استفاده میکنند تا بتوانند نوعها را تشخیص داده و عملیاتهای مربوطه را انجام دهند.
عبارت کلاس برچسبدار از [2] آمده است که میگوید: این کلاس است که نمونههای آن در دو یا تعدادی بیشتر مزه(flavor) وجود دارند و یک برچسب، مزه هر نمونه را مشخص میکند.
یک روش تشخیص کلاسهای برچسب دار در کد،بررسی ویژگیهای زیر است:
- یک enum که مزه هر نمونه را مشخص میکند
- یک فیلد که مقدار enum/int را ذخیره میکند و به طور معمول constructor چنین کاری را میکند.
- یک عبارت switch در یک یا تعداد بیشتر تابع که کد بر اساس مقدار برچسب اجرا میشود.
یک بازآرایی بدیهی کلاس برچسبدار، سلسله مراتب کلاسی است.
آیا شما اینها را از روی تجربه خود تشخیص میدهید؟ چه بوهایی است که به طور معمول در کدهای جاوا میبینید که اینجا گفته نشدند؟
ابزارهای تحلیل زیادی برای تشخیص الگوی باگها در جاوا وجود دارند. اما جالب توجه است که ابزارهای زیادی برای تشخیص code smellها (بوها) وجود ندارند. امیدواریم چنین ابزارهایی در آینده منتشر شوند.
References:
[1] “Refactoring for Software Design Smells: Managing Technical Debt”, Girish Suryanarayana, Ganesh Samarthyam, Tushar Sharma, ISBN – 978-0128013977, Morgan Kaufmann/Elsevier, 2014.
[2] “Effective Java” (2nd Edition), Joshua Bloch, Addison-Wesley, 2008.
منبع: