به حریم شخصی کدتان احترام بگذارید
به نظر شما، در پروژههای جاوایی از کدام سطح دسترسی بیش از سایرین استفاده میشود؟ public یا protected یا private و یا default؟ آیا هنگام کد زدن، به سطح دسترسی متدها و کلاسهایتان توجهی میکنید و برای انتخاب مناسبترین سطح دسترسی، فکر میکنید؟
در این مقاله ابتدا میبینیم که میزان استفاده از هر یک از این سطوح دسترسی در پروژههای جاوایی توسط توسعهدهندگان چقدر است و سپس با هم میبینیم که چرا توجه به سطوح دسترسی مهم است.
اخیرا، یکی از توسعهدهندگان جاوا کنجکاو شد بداند که برنامهنویسان جاوا بیشتر از کدام کلیدواژه در برنامههای خود استفاده میکنند. مثلا final یا return یا class یا شاید هم یک چیز دیگر. متاسفانه از آنجایی که بر روی اینترنت یا حتی GitHub چنین آماری وجود نداشت، شخصا یک خزشگر (crawler) ساده نوشت و آن را بر روی تعدادی پروژه بزرگ موجود در GitHub اجرا کرد. نتیجه کار وحشتناک بود.
طبق این نتایج، سه پراستفادهترین کلیدواژهها به ترتیب عبارت بودند از public و final و return.
موافقیم که final و return ممکن است در تقریبا تمام متدها و کلاسها استفاده شوند. اما غیرقابل باور است که public، این چنین پراستفاده باشد.
اینطور شد که او با خود فکر کرد قطعا یک جای کار اشتباه است! بنابراین یک بار دیگر خزشگر را بر روی پروژهها اجرا کرد و این بار تنها سطوح دسترسی را در نظر گرفت. نتیجه به این صورت بود:
public: هشتاد درصد
private: هفده درصد
protected: سه درصد
البته متاسفانه برای سطح دسترسی default چون هیچ کلیدواژهای ندارد، این آمار قابل محاسبه نبود. اما با توجه به تعداد کلاسهای موجود در پروژهها، به صورت نسبی میتوان گفت که تعدادش حدودا برابر protected بود. که البته بسیار کم (بسیار بسیار کم) است.
این که تقریبا هر چیزی در کد ما public باشد نشاندهنده چه چیزی است؟ کار درستیه یا کار اشتباهیه؟ خب، جواب اینه که قطعا چیز خوبی نیست. در واقع افتضاح است و میتواند باعث سواستفادههای بسیاری شود.
چرا public چیز بدی است؟
ابتدا، باید بدانیم که اصلا چرا این مشکل به وجود میآید. حدس اصلی، IDEهای مدرن مانند intelliJ و eclipse است که به صورت پیشفرض کلاس را به صورت public ایجاد میکنند. اما آیا این کار درست است؟ مگر سطح دسترسی default نداریم که در واقع هیچ کلیدواژه و modifierای ندارد؟ در واقع احتمالا نویسندگان زبان جاوا با تعریف سطح دسترسی default قصد صرفهجویی در زمان ما را داشتهاند. حدس بعدی هم این است که برنامهنویسان به درستی نمیدانند چرا و چگونه باید از سطوح دسترسی مختلف استفاده کنند.
حال بر میگردیم به پرسش اصلی.
چرا public اینقدر بد است؟ چرا نباید خیلی راحت همه چیز را به صورت public تعریف کنیم؟
چون همیشه باید به حریم شخصی کدمان احترام بگذاریم. دقیقا همانطور که به حریم شخصی خودمان، دوستان و همکلاسیهایمان احترام میگذاریم و سعی میکنیم مسائل زندگیمان را خصوصی حفظ کنیم. به همین سادگی.
اما به هر حال بعضی چیزها باید عمومی شوند. درست است؟ بله، همانطور که میدانید در برنامهنویسی مفهومی به نام API وجود دارد. هر API باید public باشد زیرا پارامترهای API مجبورند public باشند. اما هر چیزی پشت آن API به هر شکلی نباید public باشد. البته قطعا الان در مورد API کل برنامه صحبت نمیکنیم و صحبت تنها بر سر یک ویژگی (feature) است.
حالا چرا سطح دسترسی default در جاوا وجود دارد؟ سطح دسترسی default، دید package-private دارد. به این معنی که هیچ کس خارج از آن بسته نمیتواند مستقیما از این فیلد، متد یا کلاس استفاده کند. این رویکرد معمولا در معماری package-by-feature استفاده میشود.
برای ایجاد یک ویژگی جدید، چه مراحلی باید طی شود؟
لازم است سه مرحله زیر به ترتیب طی شوند:
- تعریف API عمومی ویژگی
- نوشتن تست برای آن API
- پیادهسازی API با استفاده از تعداد زیادی کلاس و متدهای داخلی (با سطح دسترسی default یا private)
فرقی نمیکند که در مورد اپلیکیشنهای مبتنی بر سرویس حرف بزنیم یا اپلیکیشنهای شیگرا. تقریبا همیشه میتوانیم این روند را انجام دهیم.
بیایید یک مثال ببینیم: فرض کنید میخواهید با استفاده از عملیات SELECT بر روی پایگاهداده SQL، یک ویژگی جدید را پیادهسازی کنید. در روش استاندارد، یک driver عمومی وجود خواهد داشت که دسترسی مستقیم به پایگاهداده دارد و از طریق آن میتوانید هر چیزی را از هر جایی فراخوانی کنید. طبیعتا اگر این API را به صورت کامل در اختیار front-end قرار دهید، خطرناک است.
بنابراین بهتر است یک API عمومی برای ویژگی جدید خود داشته باشید و هر کسی که بخواهد از این ویژگی استفاده کند باید از طریق این API اقدام کند. به این ترتیب اگر در داخل این API دارید از پایگاهداده استفاده میکنید، مسالهای نیست؛ چراکه هیچ کسی مستقیما به آن دسترسی نخواهد داشت و باعث تغییر وضعیت پایگاهداده نخواهد شد.
مزیت این رویکرد چیست؟
با استفاده از این رویکرد، بر روی تمام کدتان کنترل دارید و تا زمانی که API عمومی به درستی کارش را انجام دهد، هر کاری که بخواهید میتوانید داخلش انجام دهید. به راحتی میتوانید در پشت API، کدتان را بازآرایی کنید، میتوانید کلاسها را به کلاسهای کوچکتر بشکنید، متدهای بیشتری بنویسید، مسئولیتها را خرد کنید و در این بین اصلا نگران دنیای بیرون نباشید.
با این رویکرد، کدتان کاملا قابل تست است؛ برای API تست نوشتهاید که مانع از ایجاد مشکلات در آینده خواهد شد. علاوه بر این، تستهایتان در واقع یک مستندات برای چگونگی استفاده از آن API هم است. در نظر داشته باشید که کسی نوشتن تست برای کلاسهای غیرعمومی را ممنوع نکرده است و در صورت نیاز کاملا درست است که این کار را انجام دهید. به عنوان مثال، زمانی که باید الگوریتم یا عبارت منظم (Regex) بسیار پیچیدهای را تست کنید، اگر بخواهید آن را از طریق API تست کنید، کارتان پیچیدهتر خواهد شد. فقط حواستان باشد که به این کلاسهای داخلی بیش از حد وابسته نشوید. شاید زمانی فرا برسد که لازم باشد حذفش کنید و یا کلا فراموش کنید که یک زمانی چنین کلاسی نوشته بودید.
از آنجایی که کلاسهای داخلی، قابل دسترسی از دنیای بیرونی نیستند، راه حلتان دارای همبستگی (cohesion) بالایی خواهد بود و آنچه که لازم است، این است که بر تعریف API پایبند بمانید.
با استفاده از این روش، از دید یک کاربر، تمام امکانات به راحتی قابل دسترس و استفاده خواهند بود و هیچ کسی مجبور نیست کد اصلی را چک کند تا بفهمد چه خبر است و فقط کافی است به بستهها و APIهای عمومی نگاهی بیندازد.
در هر صورت، نمیدانیم چرا اکثر کلاسها public هستند. اما حالا میدانیم که چرا تعریف تمام کلاسها به به صورت public، کار اشتباهی است. به یاد داشته باشید، به محض این که تمام کلاسهایتان را در دید عموم قرار دهید، به دلیل اینکه هر کسی میتواند مستقیما از آنها استفاده کند، حذف کردنشان در آینده بسیار دشوار خواهد بود. دفعه بعد که دارید یک کلاس جدید ایجاد میکنید، فکر کنید و در نظر داشته باشید که آیا public کردن آن ضرورت دارد یا نه؟