خدمت شما علاقه مندان به آموزش جاوا سلام عرض می کنم. در جلسه قبل با نحوه تعریف کلاس در جاوا آشنا شدید و یاد گرفتید که چه طور برای آن کلاس یک سری ویژگی و توانایی تعریف کنیم و اینکه از این موجودیت نمونه بسازیم. در این جلسه در موردکپسولهسازی موجودیتها صحبت میکنیم.
کپسوله سازی موجودیت ها در جاوا
جلسه قبل موجودیت Student رو به این صورت نوشتیم:
در برنامهنویسی شیگرا قانوی وجود دارد با این مفهوم که همه چیز باید کپسوله و مخفی شود مگه اینکه خلافش ثابت شود. یعنی هر جزیی باید مخفی و سطح دسترسی نداشته باشد مگه اینکه واقعا لازم داشته باشیم.
خوب قبل از اینکه در مورد این قانون حرف بزنیم اول در مورد سطح دسترسیها صحبت کنیم.
سطح دسترسی در جاوا به چه معنی است ؟
هر field یا method در داخل کلاس خود همیشه در دسترس هست. و اعضای یک کلاس به همه field یا method های کلاس خود بدون هیچ محدودیتی دسترسی دارند.
مثلا در عکس قبل داخل متد printName شما میتونید هرکدوم از fieldها را فراخوانی کنید و استفاده کنید.
public و private دو مورد از سطح دسترسیها هستن که این جلسه در مورد آنها صحبت میکنیم.
به طور کلی اگر یک field یا method به صورت public تعریف شود به این معنی هست که دسترسی به آن از بیرون هم ممکن هست. یعنی اگر من داخل کلاس Student مقدار id را به این صورت تعریف کرده باشم:
public int id;
میتونیم به ویژگی id به این صورت دسترسی داشته باشم:
Student ali = new Student();
ali.id = 1;
خوب ببینیم که اگه یک field یا method به صورت private تعریف شود چه اتفاقی میافتد. مثلا ما ویژگی name موجودیت Student رو به صورت private مینویسیم:
private String name;
وقتی یک field یا method به صورت private تعریف میشود در واقع دسترسی آن رو ما محدود کردیم به اجزای داخلی آن کلاس و از بیرون قابل دسترسی نیست یعنی نمیتوانیم به این صورت بنویسیم:
Student ali = new Student();
ali.name = “ali”;
سطح دسترسیهای دیگری هم وجود دارد که در جلسات بعدی درموردشان صحبت میکنیم.
برگردیم سراغ قانون که اول جلسه گفتیم در مورد اینکه همه چیز باید مخفی شود پس طبق این قانون ما باید همه field یا method ها را به صورت private تعریف کنیم.
پس به این صورت میشود:
خوب ولی همان طور که میدانید ما متد printName() را برای این نوشتیم که بتونیم اسم و فامیل را برای یک نمونه چاپ کند و لازم است از بیرون کلاس هم فراخوانی شود پس نمیتواند private باشد پس باید تبدیل به public شود.
در واقع نوع سطح دسترسی method ها بر میگردد به منطق برنامه ای که مینویسیم. شاید method وجود داشته باشد که لازم نباشد شما public تعریف کنید.
ولی در مورد field ها همه باید private باشند!! خوب الان دوتا سوال پیش می آید و آن این است که چرا اصلا باید private باشد؟! و دوم اینکه چطور آنها را مقداردهی کنیم یا مقدارشان را بخوانیم.
در مورد سوال اول فکر کنید آخر جلسه در موردش صحبت خواهیم کرد ولی در مورد سوال بعدی اینکه چون field ها به صورتprivate هستند پس لازم است برای آن ها یک متد نوشته شود که مقدارشان را بخوانیم و یک متد برای تغییر مقدار آنها داشته باشیم به این صورت:
خوب اینگونه میتوانیم از بیرون کلاس با متد a مقداری را برای id مشخص کنیم (set کنیم) و با متد b مقدار id را بخونیم (getکنیم).
Student ali = new Student();
ali.a(1);
int i = ali.b();
پس تا اینجا مشکل سطح دسترسی را حل کردیم ولی همین طور که میبیند از اسمهای خوبی برای این متدها استفاده نکردیم. و خیلی متد a و b گویا نیستند! خوب بهترین کار این است که اسم این متدها را هم استاندارد بنویسیم. پس ما از الان استاندارد میکنیم برای خود که متدهایی که قرار است یک مقداری را برای یک field مشخص کند را با اسم set بنویسیم و برای متدهای که قرار یک field را بخواند از کلمه get استفاده کنیم.
شما میتوانید هر اسمی برای آن انتخاب نمایید ولی بهتر است همیشه استاندارد ها را رعایت کنید.
پس به این صورت شد:
خوب بیایم یکم استانداردتر بکنیم! بهتر آرگومان ورودی متد setId رو هم از اسم معنا داری استفاده کنیم!
public void setId(int id){
this.id = id;
}
خوب چون هم اسم آرگومان ورودی ما id هست و هم یک field به نام id داریم، برای اینکه بتوانیم تمایزی بین آن ها ایجاد کنیم هر موقع به فیلد ها نیاز داشته باشیم از کلمه this استفاده میکنیم. در واقع this.id به متغییر id که به صورت private در بالا تعریف کردیم اشاره میکند.
خوب الان برای همه field ها باید این get و set ها را بنویسیم به همین صورت ولی خوب میتوانید کار را ساده تر کنید و رو صفحه کلیک راست کنید و از گزینه Generate گزینه getter and Setter را انتخاب کنید و همه field ها را انتخاب کنید و ok رو بزنید. خودش همه رو کامل میکنه!
خوب برای اتمام این بحث لازم است در مورد مفهومی به نام سازنده یا constructor صحبت کنیم. constructor کاربردهای زیادی دارد ولی دوتا از کاربردهای آن خیلی خیلی زیاد لازم میشود.
ابتدا سازنده یا constructor را تعریف می کنیم
شما هر بار یک نمونه از یک کلاس میسازید (وقتی new میکنید) در واقع اولین اتفاقی که میافتد این است که قسمتی از حافظه برای آن نمونه اختصاص داده میشود و سازنده آن کلاس فراخوانی میشود. پس هر نمونه به ازای هر بار new شدن سازنده آن در ابتدا صدا زده میشود.
سازنده هر کلاس در واقع یک متد است که برخلاف همه متدهای موجود هیچ خروجی ندارد حتی void! و هم نام با اسم کلاس! مثلا برای کلاس Student به این صورت میشود:
public Student(){
}
یکی از کاربردهای سازنده این هست که اگه بخواهیم در زمان ساخته شدن یک نمونه یک فرایند همیشه اجرا شود آن را در سازنده مینویسیم. مثلا برای کلاس Student من میخوام هربار که نمونهای ساخته میشود یک متن ابتدا چاپ شود و مقدار name برای همه مقدار "ali" مشخص شود.
public Student(){
System.out.println(“create new student”);
name = “ali”;
}
خوب یعنی شما هر نمونهای بسازید به ازای هر نمونه یکبار عبارت “create new student” چاپ میشود و اسم همه نمونه ها “ali” میشود!!!!!
خوب کاربرد دوم این است که شما میخواهید این محدودیت را وضع کنید که هر نمونهای که قرار است از کلاس Student ساخته شود حتما id و name آنها را در زمان ساخته شدن وارد شود! در واقع این محدودیت این تضمین را میکندکه هیچ نمونهای وجود نخواهد داشت که این دو ویژگی را نداشته باشند!
خوب پس به این صورت میشود:
public Student(int id, String name){
System.out.println(“create new student”);
this.id = id;
this.name = name;
}
خوب برای نمونه ساختن هم دیگه نمیتونیم به این صورت نوشت:
Student std1 = new Student();
چون ما الان سازندهای داریم که حتما باید مقدار id و name را بگیرد:
Student std1 = new Student(1, “ali”);
System.out.println(std1.getId());
System.out.println(std1.getName());
خوب نکته بعدی این هست که شما میتوانید مقدار id یا name رو هم عوض کنید:
Student std1 = new Student(1, “ali”);
System.out.println(std1.getId());
System.out.println(std1.getName());
std1.setId(2);
System.out.println(std1.getId());
نکته بعدی این که شما میتونید چند سازنده هم زمان داشته باشید:
public Student(int id, String name){
System.out.println(“create new student”);
this.id = id;
this.name = name;
}
public Student(int id, String name, String family){
System.out.println(“create new student”);
this.id = id;
this.name = name;
this.family = family;
}
و به این صورت هم ازشون استفاده کنید:
Student std1 = new Student(1, “ali”);
Student std2 = new Student(2, “mohammad”, “ch”);
آخرین نکته این جلسه اینکه شما میتوانید برای اینکه حجم کدنویسی پایین بیاد و یک فرایند را چندین بار ننویسید میتونید یک سازنده را بر اساس سازنده دیگه بسازید (روش استاندارد برای چند سازنده همزمان)
public Student(int id, String name){
System.out.println(“create new student”);
this.id = id;
this.name = name;
}
public Student(int id, String name, String family){
this(id, name);
this.family = family;
}
این کد در واقع هیچ تفاوتی با کد بالا ندارد فقط استانداردتر است و کدنویسی کمتری دارد و این به معنی این هست خطای کدنویسی کمتری دارید.
فقط چند نکته اینکه this() در واقع به یک سازنده از همان کلاس اشاره میکند و در واقع دارد آن سازنده را مقدار دهی میکند پس ما ابتدا داریم مقدار id و name را مشخص میکنیم و بعد هم مقدار family را. و مقداردهی سازنده توسط this() حتما باید در اولین خط سازنده انجام شود!
قواعد نگارشی:
۱- نام field ها همیشه باید اسم باشند و نه فعل!
۲- نام method ها همیشه باید فعل باشند!
۳- حتی الامکان اسمها را با معنی و چند کلمهای استفاده کنید حتی اگه لازم هست نام به اندازه یک خط هم شود!
۴- اگه از کلمهای مخفف در نامگذاری استفاده میکنید هم اول هر کلمه را با حروف بزرگ بنویسید:
exportHtmlSource();
// NOT: exportHTMLSource();