آموزش قدم به قدم جاوا – قسمت یازدهم

تعیین‌کننده‌های دسترسی (Access Modifiers)

همان‌طور که در مقدمه مبحث شی‌گرایی خواندید در اصل کپسوله‌سازی باید دسترسی به اعضای کلاس‌ها را مدیریت و محدود کنیم (به دلایلی که گفته شد) و این کار با استفاده از تعیین‌کننده‌های دسترسی امکان‌پذیر است. برای شروع کار در پروژه خود یک پکیج ایجاد کرده و کلاسی درون آن پکیج بسازید (کلاس خود را درون default package قرار ندهید). من پکیجی به نام myclasses و کلاسی به نام Printer با مشخصه‌های modelNumber (شماره مدل) و isOn (وضعیت خاموش و روشن بودن) و numberOfPapers (تعداد کاغذهای موجود در پرینتر) ایجاد کردم. طبق دانشی که تا الان به دست آوردیم کلاس ما به شکل زیر خواهد شد:

می‌دانید که الان می‌توانیم با ایجاد یک شی از این کلاس به تمام مشخصه‌های آن دسترسی داشته باشیم و هر مقداری که خواستیم به آن‌ها بدهیم. دقیقا به همین دلیل در برنامه‌نویسی شی‌گرا به‌هیچ‌وجه مشخصه‌ها را به این شکل تعریف نمی‌کنند (مگر موارد خاص).

واضح‌ترین مثالی که می‌توان بیان کرد این است: فرض کنید یک پرینتر فقط ظرفیت نگه‌داری 100 کاغذ را داشته باشد اما در کلاسی که نوشتیم محدودیتی در مقداردهی به مشخصه numberOfPapers نداریم و حتی می‌توانیم مقداری منفی به آن وارد کنیم و یا مقداری بیش از ظرفیت پرینتر در آن قرار دهیم! و با این کار ممکن است در روند کار کلاس اختلال ایجاد شود بنابراین باید کاری کنیم که نتوان به صورت مستقیم این مشخصه‌ها را دست‌کاری کرد و تغییر این مشخصه‌ها باید با واسطه یک متد انجام شود که در ادامه نحوه این کار را خواهید دید.

چهار نوع تعیین‌کننده دسترسی در جاوا وجود دارد:

package-private یا بدون تعیین‌کننده دسترسی

تا به‌حال برای کلاس‌ها، فیلدها (مشخصه‌ها) و متدهای خود هیچ تعیین‌کننده دسترسی مشخص نمی‌کردیم. به کلاس یا اعضای کلاس (فیلدها و متدها) که بدون تعیین‌کننده دسترسی هستند package-private گفته می‌شود به این معنی که فقط در همان پکیجی که در آن تعریف شده‌اند قابل دسترسی هستند. مثل همین کلاس Printer که ایجاد کردیم.

در قسمت قبل که با پکیج‌ها آشنا شدید گفتیم که برای استفاده از کلاس‌های موجود در پکیج‌های دیگر باید آن‌ها را import کنیم. اگر کلاسی package-private باشد نمی‌توان آن را در پکیج‌های دیگر import کرد مثل کلاس Printer که ساختیم.

private

محدودترین سطح دسترسی private می‌باشد. البته از این تعیین کننده دسترسی برای کلاس‌ها نمی‌توان استفاده کرد مگر کلاس‌های داخلی (با کلاس‌های داخلی در آینده آشنا خواهید شد). فیلدها و متدهایی که private باشند فقط و فقط در همان کلاسی که در آن تعریف شده‌اند قابل دسترسی هستند. برای اعمال یک تعیین‌کننده دسترسی به یک فیلد یا متد باید آن را قبل از شروع تعریف فیلد یا متد آورد. مثال:

همانطور که می‌بینید فیلد name به صورت private تعریف شده است، یعنی حتی اگر شیئی از این کلاس هم بسازید باز هم نمی‌توانید به این فیلد دسترسی داشته باشید. متد testMethod هم به همین شکل است و فقط از این متد می‌توان در همین کلاس استفاده کرد.

public

کلاس‌هایی که به صورت public تعریف می‌شوند در تمام پکیج‌ها قابل دسترسی هستند و می‌توان در پکیج‌های دیگر با import کردن کلاس از آن استفاده کرد. متدها و فیلدهای public هم در تمام پکیج‌ها قابل دسترسی هستند و با داشتن شی از کلاس آن‌ها می‌توان از فیلدها و متدهای public آن کلاس استفاده کرد. کلاسی که در ابتدا ایجاد کردید را از حالت package-private به public تغییر دهید:

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

protected

این تعیین‌کننده دسترسی چیزی بین public و private است. اعضای protected یک کلاس تنها در پکیجی که کلاس در آن قرار دارد قابل دسترسی هستند به علاوه‌ی کلاس‌هایی که از آن کلاس ارث‌بری دارند. (با این تعیین‌کننده دسترسی در قسمت وراثت کاملا آشنا خواهید شد.)

در جدول زیر سطح دسترسی فیلدها و متدهایی که با تعیین‌کننده‌های مختلف تعریف می‌شوند را مشاهده می‌کنید:

کل برنامه

کلاس‌های وارث

پکیج

داخل کلاس

تعیین کننده دسترسی

*

*

*

*

public

*

*

package-private

*

private

*

*

*

protected

با مفهوم وراثت در آینده آشنا خواهید شد.

کپسوله‌سازی کلاس

در کلاس Printer که ساختیم اصل کپسوله‌سازی رعایت نشده است. یعنی جزئیات درونی کلاس کاملا در دسترس کلاس‌های دیگر است. مثلا اگر شیئی از کلاس Printer داشته باشیم می‌توانیم مشخصه numberOfPapers آن را هر طور که خواستیم تغییر دهیم:

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

همانطور که در کد مشخص شده سطح دسترسی تمام فیلدهای کلاس خود را به private تغییر دهید. به متد loadPaper که برای بارگذاری کاغذ درون پرینتر به کار می‌رود دقت کنید: این متد مقداری را به عنوان تعداد کاغذ دریافت می‌کند سپس در بدنه متد این مقدار بررسی می‌شود. اگر منفی بود پیغام “عدد نامعتبر” و اگر بیشتر از 200 بود (فرض کردیم ظرفیت این پرینتر حداکثر 200 کاغذ است) پیغام “نمی‌توانید بیشتر از 200 کاغذ بارگذاری کنید” و اگر هیچ کدام از دو شرط اول برقرار نبود پس عدد وارد شده معتبر است و می‌توان آن را به فیلد numberOfPapers نسبت داد.

همچنین متد دیگری به نام turnOn برای روشن‌کردن پرینتر نوشتیم که اگر پرینتر روشن نبود (فیلد isOn برابر با false بود) آن را روشن کند و اگر روشن بود پیغام “پرینتر قبلا روشن شده است” چاپ شود.

همانطور که دیدید با مخفی سازی فیلدها توانستیم کار با مشخصه‌های کلاس را کاملا کنترل کنیم.

چند نکته مهم درباره تعیین‌کننده‌های دسترسی و کپسوله‌سازی

1 – کلاس‌ها تنها می‌توانند به صورت public یا بدون تعیین‌کننده دسترسی (package-private) تعریف شوند. البته کلاس‌های داخلی از این قاعده مستثنی هستند که در آینده با آن‌ها آشنا خواهید شد.

2 – به عنوان یک قاعده کلی کپسوله‌سازی همیشه فیلدهای کلاس خود را private کنید و با استفاده از متدها دسترسی به آن‌ها را فراهم کنید. به کلاس زیر دقت کنید:

در این کلاس به نظر می‌آید که استفاده از این دو متد الزامی نیست و این دو متد کاری به جز برگرداندن مقدار فیلد fuel و نسبت‌دادن یک مقدار به فیلد fuel انجام نمی‌دهند و این کار را می‌توان با دسترسی مستقیم به فیلد fuel هم انجام داد. اما به دلایل زیادی باز هم باید از این قاعده پیروی کنید که یکی از دلایل آن این است: فرض کنید که به جای این دو متد، فیلد fuel را public کردید و در 50 کلاس دیگر از این فیلد به صورت مستقیم استفاده کردید مثلا:

اما حالا قصد دارید تا محدودیتی برای نسبت‌دادن مقدار به فیلد fuel قرار دهید و این کار فقط با ایجاد یک متد امکان‌پذیر است، اما چون شما از اول از متد استفاده نکردید حالا باید در هر کدام از 50 کلاس کدی را که از این فیلد استفاده کرده است را تغییر دهید تا از متدی که ساختید استفاده کند. مثلا:

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

3 – یکی از اصول دیگر کپسوله‌سازی این است که اگر کلاسی در یک پکیج فقط قرار است در همان پکیج استفاده شود پس آن را به صورت package-private تعریف کنید نه public. برای متدها هم همین قانون وجود دارد یعنی اگر متدی از یک کلاس public فقط قرار است در همان پکیجی که کلاس در آن تعریف شده استفاده شود پس متد هم باید package-private باشد.

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

مصطفی نصیری

دانشجوی نرم افزار هستم و علاقه شدیدی به برنامه نویسی مخصوصا با زبان جاوا دارم! در حال حاضر تمرکزم روی اندرویده. دوست دارم چیزایی که یاد میگیرم رو با بقیه به اشتراک بگذارم :)

همچنین ممکن است دوست داشته باشید ...

۳ واکنش

  1. محمد گفت:

    آقا دمت گرم
    خدا خیرت بده خیلی خوب توضیح میدی

  2. iman313 گفت:

    اقا واقعا عالی مرسی تروخدا دوره جاوا پیشرفته هم برگذار کن تروخدا ! خیلی خوب یاد میدی!

پاسخ دهید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *