بررسی Bad code smell ها: کلاس بزرگ
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: سه دقیقه

این نوع کد بد بو در دسته بندی «کدهای متورم» قرار می‌گیرد. یکی از نتایج متورم شدن کدها، سخت شدن نگهداری آنهاست. بدیهی به نظر می‌رسد که نگهداری و اعمال تغییرات بر روی یک کلاس بزرگ، دشوار و زمان گیر خواهد بود. علارغم سادگی مفهوم این نوع کد بد بو، این مورد یکی از موارد پر تکرار درمحصول‌ها است.  
کلاس بزرگ کلاسی است که تعداد اعضای آن (فیلد، خصوصیت، متد) زیاد باشند و تعداد خطوط کد زیادی نیز داشته باشد. 

چرا چنین بویی به راه می‌افتد 

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

نشانه‌های این کد بد بو 

نشانه‌هایی که به تشخیص یک کلاس بزرگ کمک می‌کنند به صورت زیر هستند: 
  • تعداد خطوط زیاد: این معیار نسبت به فناوری و زبان برنامه نویسی مورد استفاده درمحصول متفاوت است؛ ولی در حالت کلی زمانیکه یک کلاس تعداد خطوط کدی بیشتر از 100 داشت، مشکلی بوجود آمده است. 
  • تعداد وضعیت‌های داخلی (در تعریف شیء گرایی) زیاد در یک کلاس، نشان دهنده بزرگی یک کلاس هستند.  
  • تعداد پارامترهای زیاد سازنده کلاس نشان دهنده متورم شدن کلاس هستند. معمولا مدیریت کردن تعداد وضعیت‌های داخلی زیاد منجر به دریافت تعداد زیاد پارامتر ورودی در سازنده می‌شوند. اگر قانون مربوط به تعداد پارامترهای یک متد را در نظر داشته باشیم و با فرض اینکه سازنده نیز یک متد است، حداکثر پارامترهای مناسب برای یک سازنده 4 خواهد بود. 
  • متغیرهایی وجود دارند که به صورت دسته‌ای پیشوند یا پسوند خاصی دارند. این پیشوندها یا پسوندها نشان دهنده مواردی هستند که احتمالا می‌توانند به کلاس مخصوص به خود انتقال داده شوند. زیرا از نظر منطقی ارتباطی بین آنها وجود دارد و مربوط به کلاس فعلی نمی‌شوند (زیرا اگر اینگونه بود نیازی به پیشوند یا پسوند نبود).


مشکل این کد بد بو چیست؟ 

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


چگونه این بو را رفع کنیم؟  

دیدگاه کلی برای رفع چنین بویی تقسیم مسئولیت‌های موجود در یک کلاس بزرگ است. این تقسیم می‌تواند به صورت‌های زیر انجام شود:
  • ایجاد کلاسی مستقل برای هریک از مسئولیت‌های موجود در کلاس بزرگ 
  • ایجاد کلاسی پایه (Base class) برای انجام برخی از امور مشترک در کلاس 


جمع بندی 

یکی از نکات مهم در مورد انواع کد بد بو متعلق به دسته کدهای متورم، توجه دایمی به کدهای نوشته شده در محصول است. زیرا کدهای متورم به مرور زمان و به آرامی ایجاد می‌شوند و معمولا توجه کافی به آنها نمی‌شود.  
  • #
    ‫۷ سال و ۲ ماه قبل، سه‌شنبه ۱۳ تیر ۱۳۹۶، ساعت ۰۰:۱۸
    مطلبی رو  اینجا دیدم که به نظر طنز هست و مرتبط به مطلب جاری
    In the C++ world we don’t create a class witch contains 5 methods and 2 fields like in Java or C#. We do more. A lot more. Our class are consequent and take a lot of stuff inside because it is interesting to have a project with 10 files of 100 KB rather than 300 files of 3 KB organized with fuzzy concepts. We do OOP better. It’s more compact, it’s real, it just works
  • #
    ‫۷ سال و ۲ ماه قبل، سه‌شنبه ۱۳ تیر ۱۳۹۶، ساعت ۱۳:۰۴
    ۱۰۰ خط کد برای یک کلاس به نظر خیلی کم میاد! کلاس‌های مدل گاهی به ۶۰ - ۷۰ خط کد میرسن و یک کلاس ساده که دو سه عملیات مرتبط به یک موضوع واحد رو انجام میدن به راحتی ۲۰۰ خط کد داره. 
  • #
    ‫۵ سال قبل، سه‌شنبه ۱۲ شهریور ۱۳۹۸، ساعت ۱۹:۵۱
    در مورد این موضوع ابهامی برام پیش اومده. اگر بخوام تعداد چندین سرویس رو به Controller خودم Inject کنم، این بوی بد کد ایجاد میشه.
    این قضیه رو چجور میشه هندل کرد؟
    • #
      ‫۵ سال قبل، سه‌شنبه ۱۲ شهریور ۱۳۹۸، ساعت ۲۳:۱۳
      - زمانیکه تعداد زیادی سرویس تزریق شده را به سازنده‌ی یک کلاس مشاهده می‌کنید، مشکل عدم رعایت اصل تک مسئولیتی نمایان می‌شود. یعنی این کلاس/کنترلر کارهای زیادی را قرار است انجام دهد. بنابراین می‌توان این کنترلر را به چند کنترلر کوچک‌تر و با مسئولیت‌های کمتر شکست.
      - و یا اگر یک چنین سازنده‌ای را دارید:
            public MyController(ITransactionRepository transRepo;
                                 IAccountRepository accountRepo;
                                 ISystemsRepository sysRepo;
                                 IScheduleRepository schRepo;
                                 IProfileRepository profileRepo)
      می‌توانید با استفاده از abstract factory pattern (^ و ^)، این Repositoryها را در لایه‌ی سرویس با هم ترکیب کنید و فقط یک سرویس ارائه دهنده‌ی این‌ها را در اختیار کنترلرهای مصرف کننده قرار دهید.