اندازه‌گیری پیچیدگی نرم‌افزار

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

تعداد مسیرها که آیسا می‌توانست برای رسیدن به هدفش را طی کند میزان پیچیدگی شرایط‌ش در نظر گرفتیم. در سال ۱۹۷۶ شخصی به Thomas McCabe متریکی را برای اندازه‌گیری پیچیدگی در نرم‌افزار به نام Cyclomatic complexity معرفی کرد. او نیز تعداد مسیرهای مستقلی که کل یک ماژول یا متد را پوشش بدهد به عنوان پیچیدگی آن در نظر گرفت. برای محاسبه تعداد مسیرهای مستقل در متد روشهای مختلفی وجود دارد، رسمی‌ترین روش برای این کار رسم گراف کنترل جریان آن متد می‌باشد. در تصویر زیر گراف کنترل جریان متد حستجوی باینری رسم شده است.

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

Cyclomatic complexity = E – N + 2

تعداد یالهای گراف = E

تعداد گره‌های گراف = N

بر اساس تصویر بالا گراف کنترل جریان متد دارای ۱۲ گره و ۱۴ یال می‌با‌شد پس :

CC = 14 – ۱۲ + ۲ = ۴

پس متد جستجوی باینری دارای پیچیدگی ۴ می‌باشد، یعنی ۴ مسیر مستقل وجود دارد که کل متد را پوشش می‌دهد.

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

if | while | for | for each | case | default | continue | goto | && | || | catch | ?: | ??

اگر حوصله رسم گراف و شمردن نقاط تصمیم‌گیری را ندارید، استفاده از ابزارهای که متریک‌های کد را محاسبه کرده و نمایش می‌دهند بهترین گزینه هست. یکی از بهترین گزینه‌ها برای اینکار می‌تواند XDepend باشد برای دات نت NDepend، برای جاوا JDepend و برای پی‌اچ‌پی می‌توانید از PDepend استفاده کنید.  البته اگر از ویژوال استودیو استفاد می‌کنید نیازی به نصب ابزار دیگری نیست می‌توانید از منوی ANALYAZ بر حسب نیاز خود یکی از دو گزینه مشخص شده در تصویر را انتخاب کنید و نتیجه را مشاهده کنید.

مقدار قابل قبول برای Cyclomatic Complexity یک متد چقدر هست؟

یک مقدار ایده‌ ال برای محدود کردن Cyclomatic Complexity در یک متد وجود ندارد. تا ۱۰ را یک مقدار قابل قبول برای یک متد میدانند. و با افزایش آن میزان پیچیدگی افزایش و در نتیجه آن تست پذیری و قابلیت نگهداری کاهش می‌یابد. بازه‌های زیر را SEI برای مقدار  Cyclomatic Complexity منتشر کرده است.

Cyclomatic Complexityسطح پیچدگیریسک
۱-۱۰سادهکم
۱۱-۲۰نسبتا پیچیدهمتوسط
۲۱-۵۰پیچیدهزیاد
بزرگتر از ۵۰غیرقابل تستخیلی زیاد

آیا ارتباطی بین مقدار Cyclomatic Complexity و تعداد تست‌های واحد (unit test)  وجود دارد؟

مقدار Cyclomatic Complexity برابر حداقل تعداد تست‌های هست که می‌توان نوشت تا تمام مسیر‌های برنامه را پوشش بدهد. یعنی اگر برای هر مسیری که مشخص شده هست یک تست بنویسیم می‌توانیم از پوشش تمام مسیرهای برنامه توسط تست‌هایمان مطمئن شویم. در پست بعدی سعی می‌کنم در مورد میزان پوشش تست‌ها (test coverage) بنویسم و درباره انواع پوشش تست‌ها توضیح خواهم داد.

به‌عنوان نمونه تست‌های نوشته شده برای مسیر ۱ و ۲ در متد جستجوی باینری در پایین نمایش داده شده است. سطرهای که با رنگ سبز مشخص شده است مسیر اجرای آن تست را نشان می‌دهد و سطرهای که با رنگ قرمز مشخص شده است سطرهای هست که آن مسیر پوشش نمی‌دهد.

مسیر ۱:  ۰->1->2->3->11

مسیر ۲: ۰->1->2->4->5->6->11