ورود

View Full Version : مبتدی: کلاس های تو در تو و ارث بری



قله بلند
شنبه 14 آذر 1388, 14:51 عصر
با سلام
سوال من راجع به ارث بری و کلاس های تو در توست.در ارث بری، زیر کلاس، تمام صفات کلاس پایه را به ارث می برد و خودش هم می تواند صفات ویژه ای داشته باشد. کلاس های تو در تو نیز همینگونه هستند. در C++‎‎‎ خواندم که چون ارث بری وجود دارد دیگر استفاده از کلاس های تو در تو ضرورتی ندارد. در جاوا نیز همینگونه است؟
لطفاً به قطعه کد زیر توجه کنید:
class A
{
int i;
private int j;
A(int a)
{
j=a;
}
void showij()
{
System.out.println("i and j are= "+ i+" "+j);
}
}
class B extends A
{
int k;
void showk()
{
System.out.println("k is= "+ k);
}
}
public class SimpleInheritancePrivate
{
public static void main(String args[])
{
A superOb=new A(8);
B subOb=new B();
subOb.i=7;
subOb.k=9;
System.out.println("Contents of subOb is: ");
subOb.showij();
subOb.showk();
}
}این کد کامپایل نمی شه. از خط class B extends A ایراد می گیره و می گه: کلاس سازنده برای کلاس A پیدا نمی شود. مگر وقتی شیئی از کلاس B ساخته می شود نباید سازنده کلاس پایه اول ایجاد شود؟ من این سازنده را برای کلاس A به این خاطر گذاشته ام که بتوانم مقدار خصوصی j را مقداردهی کنم.

قله بلند
شنبه 14 آذر 1388, 18:51 عصر
جواب را پیدا کردم. برای این راه حل فرقی نمی کند که متغیرهای کلاس بالا، عمومی باشند یا خصوصی. در هر حال تابع super برای مقداردهی متغیرهای کلاس بالاتر استفاده می شود.
لطفاً به کد اصلاح شده دقت کنید:
class A
{
int i;
private int j;
A(int a)
{
j=a;
}
void showij()
{
System.out.println("i and j are= "+ i+" "+j);
}
}
class B extends A
{
int k;
B(int p)
{
super(p);
}
void showk()
{
System.out.println("k is= "+ k);
}
}
public class SimpleInheritancePrivate
{
public static void main(String args[])
{
B subOb=new B(8);
subOb.i=7;
subOb.k=9;
System.out.println("Contents of subOb is: ");
subOb.showij();
subOb.showk();
}
}

قله بلند
شنبه 14 آذر 1388, 19:51 عصر
حالا فرض کنید می خواهیم این سه متغیر را با هم جمع کنیم. تابع sum نیز در زیر کلاس B قرار دارد و از طرفی j نیز خصوصی است و در کلاس A قرار دارد. من دو راه حل برای پیاده سازی تابع sum ارائه می کنم. آیا راه های دیگری نیز وجود دارد؟
راه اول:
int temp;
B(int p)
{
super(p);
temp=p;
}
void sum()
{
System.out.println("i+j+k is= "+ (i+temp+k));
}راه دوم:
کدهای زیر به برنامه پست دوم اضافه می شود:
in class A:
int getj()
{
return j;
}in class B:
void sum(int t)
{
System.out.println("i+j+k is= "+ (i+t+k));
}in main()
subOb.sum(subOb.getj());

cups_of_java
شنبه 14 آذر 1388, 21:26 عصر
حالا فرض کنید می خواهیم این سه متغیر را با هم جمع کنیم. تابع sum نیز در زیر کلاس B قرار دارد و از طرفی j نیز خصوصی است و در کلاس A قرار دارد. من دو راه حل برای پیاده سازی تابع sum ارائه می کنم. آیا راه های دیگری نیز وجود دارد؟
در مورد راه اول:
اساسن این راه درست نیست. چون شما اطلاعی نداری که حتمن متغیری که در سازنده کلاس B هست در کلاس A برای مقداری دهی متغیر private به نام j داره استفاده میشه.
با این کار شما داری کلاس B رو به داده ای private در کلاس A وابسته می کنی! اونم به شکلی خطرناک! یعنی فردا روزی که شما بخوای سازنده رو تغییر بدی یا مقداری که رو J می نویسی رو در سازنده قرار ندی یا ... برنامه شما از کار میافته! اصلن A به خاطر همین j رو private کرده بوده. شما با این کار داری Encapsulation رو نقض می کنی.

در مورد راه دوم: این راه، راهی اصولی برای انجام این کار هست. یعنی شما باید برای اعضای private توابع دسترسی (getter و setter ) تعریف کنید تا قابل دسترسی باشند. فقط یک نکته:
لازم نیست شما یک ورودی به نام t تو تابع sum تعریف کنید. چون تابع sum می خواد از j استفاده کنه پس داخل خودش باید getJ رو صدا بزنه و ورودی نداشته باشه.
به این اصل توجه کنید:
شی گرایی باید باعث بشه شما تا حای ممکن تعداد آرگومان های ورودی توابعتون رو کم کنید.

دقت کنید که هر آرگومان ورودی برای هر تابع یعنی امکان ایجاد Bug و لزوم debug و ردیابی کد در زمان اجرا...! خودتون کار خودتون رو سخت نکنید!

نکاتی به همین سادگی در برنامه نویسی هستند که می تونند بین یک نرم افزار انعطاف پذیر با نرم افزاری غیر قابل نگهداری فرق بزارن.

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

همیشه عملیات باید در کنار داده مربوط به خودش باشه. (یا اینکه عملیات در جایی قرار بگیره که نزدیک ترین مکان به داده های مورد نیازش باشه!)

قله بلند
شنبه 14 آذر 1388, 23:45 عصر
با سلام
جناب cups_of_java عزیز، اولاً خوشحالم که اینگونه جواب سوالم را دادید. وقتی یک فرد متخصص پاسخ یک فرد مبتدی را می دهد، باعث رشد و پیشرفت او می شود. پاسخ شما را 3 بار خواندم و در هر دفعه بیشتر متوجه شدم ولی راه حل سوم شما تخصصی تر بود و من هنوز کاملاً آنرا درک نکردم. اگر قرار باشد تابع sum به کلاس A برود بازهم مشکل دسترسی به K مطرح می شود چون کلاس A از زیر کلاس خود یعنی B خبر ندارد. پس راه درست در این مساله چیست؟
شما فرمودید که این موضوع به طراحی شی گرا بر میگرده. شما باید اصول شی گرایی رو بدونید اول بعدش این موضوع رو استفاده کنید.
من هم با خواندن مبتدیانه این موضوعات دارم سعی می کنم تا دانشم را در این موارد زیاد کنم و در این راه از ابزار پرسش و تحقیق استفاده می کنم و امید دارم که متخصصان نیز ایراداهای من را گوشزد کنند تا راه پیشرفت را یاد بگیرم وگرنه همیشه در گرداب سطحی نگری به مسائل باقی خواهم ماند.
باز هم از شما و اساتید این بخش تشکر می کنم و خواهش می کنم که به من در این زمینه کمک کنید.

cups_of_java
یک شنبه 15 آذر 1388, 19:02 عصر
با سلام
اگر قرار باشد تابع sum به کلاس A برود بازهم مشکل دسترسی به K مطرح می شود چون کلاس A از زیر کلاس خود یعنی B خبر ندارد. پس راه درست در این مساله چیست؟


راه حل دوم برای این مثالی که شما زدید کافیه.
در هر مسئله باید شرایط رو دونست و پارامتر های مورد نیاز در طراحی رو ملاک قرار داد تا بشه در موردش حرف زد.
تو این مثال اینکه شما sum رو به همین شکل در کلاس A بزارید منطقی به نظر نمی رسه. اما شاید sum مربوط به کلاس دیگری هست که نیاز به این عملیات جمع هست.
می خوام بگم نمی شه همین طوری گفت این راه درسته اون راه غلطه! باید دید جمع مورد نظر شما چی هست اصلن؟ کاربرانش (استفاده کنندگانش) کیا هستن؟ آیا این عمل باید polymorphic انجام بشه؟ آیا جزییات این عمل می تونه به مرور زمان تغییر کنه؟ و ...
جواب این سوال ها در این که sum کجا باشه و اصلن چه شکلی تعریف بشه تاثیر میذاره.
نهایتن درمسائل کوچیک، همیشه ساده ترین راه درست ترین راهه!

قله بلند
یک شنبه 15 آذر 1388, 23:58 عصر
با سلام
استاد گرامی جناب cups_of_java
اجازه دهید من از گفتار شما کمی گیج شوم. شما خیلی بالاتر هستید و من پایین. شما در این زمینه پر تجربه و من در اول راه.
ولی چیزی که می دانم این است که باید تجربه کرد. تا من دچار بحران در برنامه نویسی نشوم مطالب جدید را نخواهم آموخت. مثلاً اصلاً فکر نمی کردم که این دو راه حل پیشنهادی باعث تولید این بحث جالب شود. من عیناً از راهی که شما پیشنهاد دادید استفاده کردم در حالیکه فکر می کردم راه اول بهترین راه است در صورتیکه اشتباه فکر می کردم. پس با پیشروی در یادگیری و پرسیدن سوال انشاء الله راه های مطلوب را خواهم آموخت.
من در پست اول، سوال دیگری نیز مطرح کرده بودم که در مورد کلاس های تو در تو بود. من کلاس B را در درون کلاس A قرار دادم دیدم که کلاس B به راحتی به عضو اختصاصی کلاس A دسترسی دارد.
در C++ نیز خواندم که با وجود وراثت، دیگر نیازی به کلاس های تو در تو نیست در حالیکه کلاس های تو در تو عیناً مانند وراثت عمل نمی کنند.
پس مورد کاربرد کلاس های تو در تو در چیست؟

cups_of_java
دوشنبه 16 آذر 1388, 02:10 صبح
بحث بالا و پایین نیست دوست عزیز،
گاهی میشه کسی که در حال یادگیری هست اینقدر با دقت مطالب رو بررسی می کنه که از زاویه دید خودش نکته ای رو مطرح می کنه که فرد با تجربه هم ممکنه بهش برخورد نداشته بوده باشه.
هدف ما اینه که از هم یاد بگیریم و بهم یاد بدیم :)





در C++‎ نیز خواندم که با وجود وراثت، دیگر نیازی به کلاس های تو در تو نیست در حالیکه کلاس های تو در تو عیناً مانند وراثت عمل نمی کنند.
پس مورد کاربرد کلاس های تو در تو در چیست؟


اینکه با وجود وراثت نیازی به کلاس های تو در تو (nested classes) نیست حرف درستی به نظر نمی رسه. نمی دونم اینرو کجا خوندید ولی من نمی پسندم این جمله رو.
ببینید درسته که وراثت و کلاس های تودرتو از جهاتی به هم شبیهند. اون هم اینه که در هر دو کلاس درونی (یا فرزند) می تونه به اطلاعات کلاس بیرونی (پدر) دسترسی داشته باشه. اما این خیلی غیر دقیق هست. پس تکلیف داده های private چی میشه؟...
بنابراین این دو رو نمی شه به هم ربط داد. شما زمانی که رابطه is-a دارین از وراثت استفاده می کنید. اما زمانی که می خواهید به اطلاعات یک کلاس پایه دسترسی داشته باشید ولی رابطه is-a ندارید می تونید از کلاس های تودرتو برای اینکار استفاده کنید. البته من ندیدم خیلی از این کلاس ها در کد استفاده بشه چون می تونن کد رو پیچیده کنن و شاید بشه گفت به نوعی Encapsulation رو نقض می کنند.
البته کلاس های تودرتو خیلی منطعف هستند و گاهی کارهای ما رو به سادگی راه میندازن اما یک طرح شی گرا که کلاس های تودرتو استفاده نکرده باشه طرح به مراتب تر و تمیزتری خواهد بود. از کلاس های تودرتو میشه برای کپسوله کردن وظایف تکمیلی در حیطه یک کلاس خاص استفاده کرد.

قله بلند
دوشنبه 16 آذر 1388, 13:06 عصر
با عرض سلام
عین مطلب از کتاب درسی ام:
می توان یک کلاس را در یک کلاس دیگر تعریف کرد. اما به علت اینکه در C++‎ برای کلاسها خاصیت ارث بری
وجود دارد نیازی معمولاً به تعریف نمودن یک کلاس در کلاس دیگر نیست و از کلاسهای تو در تو به ندرت استفاده
می گردد.جناب cups_of_java من جمله زیر را نفهمیدم:

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

cups_of_java
دوشنبه 16 آذر 1388, 13:57 عصر
با عرض سلام
عین مطلب از کتاب درسی ام:جناب cups_of_java من جمله زیر را نفهمیدم:
واقعاً من را ببخشید. نمی دانم من نمی فهمم یا مطلب سنگین است

ببین یه موقع هایی هست که شما چند تا عملیات داری که در حاشیه مسولیت اصلیه کلاستون هست. مثلن می خواین کلاستون یک کاری رو به صورت multiThread انجام بده. یا مثلن می خواین یک listener و یا observer به کلاستون اضافه کنید. این موارد اکثرن وظیقه مندی های جانبی هستند نه وظیفه اصلی کلاس مد نظر شما! از طرفی موقعی که کوچیک هستن و ارزش نداره یک کلاس public براشون تعریف کنید (در واقع کاربرشون فقط همون کلاس مد نظر شما هست) پس میاین و از کلاس تودرتو استفاده می کنید.
شما چه در سی پلاس پلاس چه در جاوا اگه مثال های کلاس های تودرتو رو ببینید با کاربرد هاش آشنا میشید. این کلاس ها خیلی کاربرد وسیعی ندارند.

cups_of_java
دوشنبه 16 آذر 1388, 14:07 عصر
می توان یک کلاس را در یک کلاس دیگر تعریف کرد. اما به علت اینکه در C++‎‎ برای کلاسها خاصیت ارث بری
وجود دارد نیازی معمولاً به تعریف نمودن یک کلاس در کلاس دیگر نیست و از کلاسهای تو در تو به ندرت استفاده
می گردد.

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

قله بلند
سه شنبه 17 آذر 1388, 18:55 عصر
با سلام
در کد زیر، BoxWeight زیر کلاس Box است و قرار است دو شیء زیر از آنها ساخته شود:
BoxWeight weightbox=new BoxWeight(3,5,7,8.37);
Box plainbox=new Box(); همانطور که می دانیم، کلاس BoxWeight از کلاس Box باخبر است ولی کلاس Box از کلاس BoxWeight باخبر نیست. یعنی کلاس Box نمی داند که متغیری به نام Weight در BoxWeight وجود دارد. حالا با دستور plainbox=weightbox، کاری می کنیم که ارجاعی اتفاق بیافتد.
سوال: اگر به آخرین خط از قطعه کد زیر نگاهی بیاندازید، کلمه Error را می بینید. یعنی کامپایلر، این کد را با خطا مواجه می کند و می گوید که کلاس Box چیزی از متغیر Weight نمی داند.
مگر نه اینکه به هر جا که weightbox اشاره کند، با دستور plainbox=weightbox، plainbox نیز به همانجا اشاره خواهد کرد؟ پس چرا این قضیه در مورد کلاس ها صادق نیست و همچنان قوانین کلاس ها بر سر این موضوع سایه می اندازد؟
class Box
{
double width;
double height;
double depth;
Box(double w, double h, double d)
{
width=w;
height=h;
depth=d;
}
Box()
{
width=-1;
height=-1;
depth=-1;
}
double volume()
{
return width*height*depth;
}
}

class BoxWeight extends Box
{
double Weight;
BoxWeight(double w,double h,double d,double m)
{
super(w,h,d);
Weight=m;
}
}

public class RefDemeo
{
public static void main(String args[])
{
BoxWeight weightbox=new BoxWeight(3,5,7,8.37);
Box plainbox=new Box();
double vol;
vol=weightbox.volume();
System.out.println("Volume of weightbox is= "+vol);
System.out.println("Weight of weightbox is= "+weightbox.Weight);
System.out.println();
plainbox=weightbox;
vol=plainbox.volume();
System.out.println("Volume of plainbox is= "+vol);
//System.out.println("Weight of plainbox is= "+plainbox.Weight); hass Error!
}
}

cups_of_java
سه شنبه 17 آذر 1388, 22:48 عصر
سوالی که پرسیدی نکته ظریف و مهمی در بحث تایپ ها در زبان های شی گراست که اتفاقن همه براشون این سوال پیش میاد یا بهتر بگیم این اشتباه رو می کنن.
حالا جواب:

مگر نه اینکه به هر جا که weightbox اشاره کند، با دستور plainbox=weightbox، plainbox نیز به همانجا اشاره خواهد کرد؟
چرا! و بعد از این دستور متغیر plainbox داره به شی ای که توسط weightbox ایجاد شده بود ارجاع میکنه.


پس چرا این قضیه در مورد کلاس ها صادق نیست و همچنان قوانین کلاس ها بر سر این موضوع سایه می اندازد؟
بزار اینجوری بهت بگم:
این که متغیر شما داره به یه چیزی (همون شی شما) اشاره می کنه درست اما از کجا مشخص میشه که اون چیزی که داره بهش اشاره میشه اصلن چی هست؟ یعنی جنسش چیه؟ یعنی تایپش چیه؟ یعنی کلاسش چیه؟ از کجا...؟ خب در زبان جاوا از تایپ اون متغیر این مشخص میشه و این طبیعی هست. کامپایلر نمیره اون جایی که متغیر شما داره بهش اشاره میکنه رو وارسی کنه تا ببینه چی هست چس نیست و کلاسش واقعیش چیه! (همین جوری هم باید باشه، اگه این طور نباشه نمیشه پلیمورفیزم رو پیاده سازی کرد) خب!؟ پس میره جایی که plainbox تعریف شده و میبینه که تایپش از کلاس Box هست! دیگه کاری نداره که الان واقعن داره به چی اشاره میکنه! همین باعث میشه شما بتونی اشیای در یک سلسله مراتب وراثتی رو بتونی یک جور ببینی! این یعنی شما میتونی به به یک شی weightbox به دید یک شی box نگاه کنی! و قدرت شی گرایی همین جاست! چون برات چندریختی (polymorphism) میاره.
پس! زمانی که شما یک متغیر از نوع (نوع مهمه) box رو داری به یک شی از نوع weightbox اشاره میدی در واقع قسمتی از اون شی که در کلاس واقعیش یعنی WeightBox داری رو از دست میدی (فراموش میکنی!) این مسئله هم برا همین هست که شما نمی دونی اون شی واقعی که داری بهش اشاره میکنی واقعن کدوم یک از بچه هات میتونه باشه و اینکه چه داده ها و متد هایی داره... پس از متغیر Weight دیگه از این طریق نمی تونی استفاده کنی و این کد پلیمورفیک شما رو safe میکنه.
ضمنن بحث تایپ بحث مهمی در زبان های شی گرای قوی (Strongly-typed) هست و همون طور که تو این مثال هم میبینی مهم تایپ استاتیک یک متغیر هست نه تایپ زمان اجراش!

در نهایت به این کار که شما انجام دادی تو این مثال Upcasting هم میگن و هیچ ایرادی که نداره هیچ، تازه کد پلیمورفیک هم هست.

خیلی مفصل توضیح دادم امیدوارم گیج نشده باشی، اگه سوالی داشتی در این مورد بپرس تا شفاف شه.

قله بلند
چهارشنبه 18 آذر 1388, 17:05 عصر
سلام</p>تقریباً می تونم بگم که الان از سرم داره بخار بلند می شه!
اجازه بدید مساله را کمی ساده تر و به گونه ای دیگر مطرح کنم. شاید با پاسخ شما، متن کنونی بیشتر برایم قابل فهم باشد.

در ابتدا گذری می کنم به C++‎‎ با کد زیر:
int main()
{
int i=5,*pi,*temp;
pi=&i;
temp=pi;
//disply i
//disply pi
//disply temp
//disply *pi
//disply *temp;
return 0;
} اگر قطعه کد بالا را در C++‎‎ اجرا کنیم، خواهیم داشت:(البته این کد برای کامپایلر جاوا ناشناخته است و مطمئن نیستم که جاوا نیز از اینگونه ارجاعات داشته باشد یا خیر)

I=5 pi=0xfff4 temp=0xfff4 *pi=5 *temp=5سوال: در اینجا رفتار کامپایلر در مورد این ارجاع چگونه است؟

حال برگردیم به بحث خودمان در جاوا:
در جایی خواندم که: وقتی شیئی را به تابعی ارسال می کنید، call-by-reference اتفاق می افتد. وقتی متغیری از نوع کلاس ایجاد می کنید، تنها نوعی نشانی به یک شیء ایجاد می شود. از این رو، وقتی این نشانی را به تابعی ارسال می کنید، پارامتر دریافت کننده آن به همان شیء ارجاع خواهد شد که آرگومان متناظرش به آن ارجاع دارد. این بدان معناست که شیء ها با روش call-by-reference به توابع ارسال می شوند.
سوال مافوق برنامه: یعنی وقتی شیئی از کلاسی ایجاد شد، فقط یک آدرس تولید می کند و بلوکی از حافظه برای شیء اختصاص نمی یابد؟
حالا اگر شما می دانید که با افزودن مطلبی، توضیح قبلی روشنتر می شود ممنون می شوم که بفرمایید. ممنون

cups_of_java
چهارشنبه 18 آذر 1388, 18:18 عصر
در اینجا رفتار کامپایلر در مورد این ارجاع چگونه است؟
سوالت رو متوجه نمیشم!؟ کامپایلر چیکار باید بکنه؟ دو اشاره گر تعریف کردید و مقدار اون ها رو برابر آدرس متغیر i قرار دادید. چیزی که چاپ شده هم درست و طبیعی هست.
در جاوا شما اشاره گر نداری ولی چیزی داری به نام ارجاع (reference) که می تونی با ارجاع توی سی پلاس پلاس مقایسش کنی. (همون متغیر هایی که با & تعریف میشن) با جاوا هم این کار رو با کلاس ها می تونی بکنی. یعنی چند تا ارجاع داشته باشی به یک شی. اما تو جاوا این موضوع در مورد نوع های ساده یا primitiveها (غیر کلاسی ها) صدق نمی کنه. چون اون ها از نوع ارجاع نیستند اصلن و خود حافظشون محتویات رو نگه می داره مثلن تو جاوا وقتی می نویسی

int i = 5;
یک خونه 4 بایتی برات گرفته میشه تو حافظه و مقدار 5 توش نوشته میشه و ارجاعی وجود نداره و شما هم نمی تونی بهش ارجاع بدی.


یعنی وقتی شیئی از کلاسی ایجاد شد، فقط یک آدرس تولید می کند و بلوکی از حافظه برای شیء اختصاص نمی یابد؟
چرا این حرف رو میزنید؟ مشخص هست که وقتی از دستور new استفاده میکنید فضا برای شی شما گرفته اختصاص داده میشه توی حافظه! و بعد آدرس همین فضاست که بر میگرده و توی متغیر شما قرار میگیره. اگه حافظه گرفته نشه آدرس کجا می خواد برگرده پس؟

نهایتن اینکه این بحث ها بین سی پلاس پلاس و جاوا (به خاطر پیاده سازی هاشون که کمی فرق داره) کمی با هم تفاوت داره. البته اصول هر دو یکی هست و تفاوت ها ظریف هستند که نباید شما رو به اشتباه بندازه.
حرفی که زدید در بحث call by reference هم در جاوا درسته. البته باز یک نکته ظریفی در موردش هست که من دیگه بهش اشاره نمی کنم تا گیج نشید.
اگه در اصول مشکل دارید و احساس می کنید براتون مطالب گنگ می مونه باید از یه استاد خوب استفاده کنید.

قله بلند
چهارشنبه 18 آذر 1388, 19:15 عصر
خواهش می کنم اینجوری نگویید. اون وقت نا امید می شوم.
اجازه دهید یک بار توضیحات شما را مرور کنم:
1-جناب شیء plainbox ، شما جایی را نگاه کن که شیء weightbox آنجا را نگاه می کند.
2-جناب plainbox، شما بدان که از کلاس Box به وجود آمده ای که پدر کلاس BoxWeight است. و بدان که طبق قوانین، نمی توانی به نوه اش دسترسی داشته باشی. چون شما نمونه ای از او هستی و باید هر چه او گفت گوش دهی!
3-حالا از اینجا به بعد رو اصلاً نفهمیدم. من دارم از ارث بری صحبت می کنم ولی شما از چند ریختی.
کامپایلر نمیره اون جایی که متغیر شما داره بهش اشاره میکنه رو وارسی کنه تا ببینه چی هست چس نیست و کلاسش واقعیش چیه! (همین جوری هم باید باشه، اگه این طور نباشه نمیشه پلیمورفیزم رو پیاده سازی کرد) خب!؟ پس میره جایی که plainbox تعریف شده و میبینه که تایپش از کلاس Box هست! دیگه کاری نداره که الان واقعن داره به چی اشاره میکنه! همین باعث میشه شما بتونی اشیای در یک سلسله مراتب وراثتی رو بتونی یک جور ببینی! این یعنی شما میتونی به به یک شی weightbox به دید یک شی box نگاه کنی! و قدرت شی گرایی همین جاست! چون برات چندریختی (polymorphism) میاره. اگر شما لطفی بکنید و قسمت داخل نقل قول را توضیح دهید ممنون می شوم. اگر این قسمت واضح شود شاید دیگر نیازی به شفاف سازی بقیه صحبت های شما نباشد.

cups_of_java
چهارشنبه 18 آذر 1388, 19:48 عصر
خواهش می کنم اینجوری نگویید. اون وقت نا امید می شوم.

نه! نا امید نشید. هیچ وقت برای یادگیری دیر نیست. مخصوصن اگه اشتیاق یادگیری داشته باشید.


بزار جملاتت رو تصحیح کنم:


1-جناب شیء plainbox ، شما جایی را نگاه کن که شیء weightbox آنجا را نگاه می کند.
plainbox شی نیست بلکه یک متغیر ارجاع هست که به یک شی ارجاع میکنه! پس باید بگی:
جناب متغیر plainbox شما به آدرس شی ای ارجاع کن که متغیر weightbox بهش ارجاع میکند.


2-جناب plainbox، شما بدان که از کلاس Box به وجود آمده ای که پدر کلاس BoxWeight است. و بدان که طبق قوانین، نمی توانی به نوه اش دسترسی داشته باشی. چون شما نمونه ای از او هستی و باید هر چه او گفت گوش دهی!

جناب plainbox چون شما موقعی که تعریف شدی پشتت اسم کلاس Box اومده پس همیشه به هر چی اشاره میکنی اون رو به دید یک Box می بینی! پس به هر شی ای از نوع Box (یادمون باشه تمام فرزندان کلاس Box هم از نوع Box هستند!) می تونی اشاره کنی ولی هر آنچه در کلاس Box هست رو میفهمی! نه کمتر نه بیشتر!


3-حالا از اینجا به بعد رو اصلاً نفهمیدم. من دارم از ارث بری صحبت می کنم ولی شما از چند ریختی.اگر شما لطفی بکنید و قسمت داخل نقل قول را توضیح دهید ممنون می شوم. اگر این قسمت واضح شود شاید دیگر نیازی به شفاف سازی بقیه صحبت های شما نباشد.
اگه متوجه نشدید شاید به خاطر این هست که مفاهیم شی گرایی رو اصولی نمی دونید. اینجا کلاس درس نیست که من برای شما کتاب بنویسم. پس وارد جزییات نمی شم فقط اینو میگم:
زمانی که شما وراثت ایجاد میکنی همه چیز عادیه. (مثل کلاس Box و WeightBox)
اما زمانی که شما همچین دستوری می نویسی:

Box box=new BoxWeight(3,5,7,8.37);

(به برنامت نگاه کن! شما هم همین کار رو کردی فقط در دو خط اینکار رو انجام دادی! فزقی نمی کنه. من مختصرشو نوشتم برات.)
وقتی شما چنین دستوری می نویسی داری وارد محدوده پلیمورفیزم میشی! و این کدی هست که زمانی می نویسنش که می خوان نگاه پلیمورفیک داشته باشن.
نهایتن، وراثت به تنهایی در شی گرایی موجود مفیدی نیست! چون چیزی جز reuse رو نمی ده به ما. اگر شما کدت رو پلیمورفیک ننویسی و فقط وراثت استفاده کنی، متخصصین شی گرایی می گن کد شما شی گرا نیست! مثالی که زدی مربوط می کرد بحث رو به این بحث ها، وگرنه پلی مورفبزم رو شما جدا از این مباحث باید بلد باشی.

قله بلند
چهارشنبه 18 آذر 1388, 20:56 عصر
جناب cups_of_java عزیز
حق با شماست. من در درک پایه مشکل دارم و تا مشکلم حل نشود فقط با سوال هایم شما را خسته می کنم و خودم هم گیج تر می شوم.
1-شما خودتان چگونه این مفاهیم را درک کردید؟ اگر کتاب خواندید چه کتابی یا کتاب هایی چون واقعاً پول کلاس رفتن برایم مقدور نیست.
2-اگر لطف کنید و فقط به این سوال من هم دوباره پاسخ دهید ممنون می شوم:(عدم درک صحیح از شیء، نمونه و متغیر ارجاع)
BoxWeight weightbox=new BoxWeight(3,5,7,8.37);
Box plainbox=new Box();در اینجا مگر weightbox و plainbox نمونه هایی از کلاس های خود نیستند؟ مگر شیء همان نمونه نیست؟
حالا در plainbox=weightbox ، تغییر نام به متغیر ارجاع صورت می گیرد؟

cups_of_java
چهارشنبه 18 آذر 1388, 21:34 عصر
1-شما خودتان چگونه این مفاهیم را درک کردید؟ اگر کتاب خواندید چه کتابی یا کتاب هایی

این مفاهیم رو باید در برنامه نویسی پیشرفته (اصولن با زبان سی پلاس پلاس خوب بفهمید. بعد هم در یادگیری زبان های دیگه ازش استفاده کنید. کتاب های مرجع سی پلاس پلاس همه در مورد اشاره گر ها، ارجاع ها و متغیر های معمولی local حرف می زنند. اینکه تخصیص حافظه پویا چیه!؟ new چیه؟ اشاره گر چیه؟ اینکه پشته و متغیر های local چیه؟ و و و
کتاب انگلیسی باید بخونید. کتاب Thinking In Java کتابه فوق العاده ای هست اما باید به انگلیسی بودنش عادت کنید.



BoxWeight weightbox=new BoxWeight(3,5,7,8.37);
Box plainbox=new Box();در اینجا مگر weightbox و plainbox نمونه هایی از کلاس های خود نیستند؟ مگر شیء همان نمونه نیست؟

بله. منظور از نمونه (instance) همون شی هست.
در مورد اون سوال قبلش هم: درسته! اصطلاحن همین رو میگیم! چون weightbox و plainbox ارجاع هایی برای اون دو شی ایجاد شده در حافظه هستند پس اسم اون اشیا هم میشن و ما میگیم شی plainbox و شی weightbox و این درسته! اما نباید فراموش کنی که plainbox و weightbox در واقع چیزی جز دوتا ارجاع نیستند و شی واقعی (همون چیزی که اینا بهش ارجاع میکنن) پشت پرده در حافظه هست! درست شد!؟ اصلن اینجوری فکر کن که plainbox و weightbox چیزی جز آدرس حافظه نیستند! چون plainbox آدرس یک شی (مثلن از نوع Box) رو داره پس میگیم شی یا نمونه plainbox اما در عمل باید بگیم شی ای که plainbox بهش ارجاع می کنه!
یه مثال ساده برات میزنم!
شما یک انسانی! یک نمونه هستی دیگه! یک شی هستی چون وجود داری! (فکر کن تو حافظه)
حالا زمانی که من بخوام شما رو صدا بزنم چی میگم؟ وقتی منظورم شما باشی چه طوری ارجاع می کنم به شما؟ معلومه دیگه! آیدیت رو میگم: قله بلند!
این چیه؟ فقط یک اسمه که اندازه تعداد حروفش جا میگیره! اما منظورش یک انسان دیگست! داره به یک انسان ارجاع میکنه! که اونم شمایی. خود قله بلند که شی نیست! اون ارجاعه شما شی هستی! اما ما میگیم آقای قله بلند!!! درسته؟ گرفتی؟ خیلی ساده بود! نه؟


حالا در plainbox=weightbox ، تغییر نام به متغیر ارجاع صورت می گیرد؟
سوالتو نمی فهمم. با این دستور مقدار آدرسی که در weighbox بوده در plainbox کپی میشه! پس plainbox هم میشه ارجاعی به اون شی ای که weightbox بهش ارجاع میکرده. به عبارت ساده تر: الان plainbox و weightbox هر دو یک شی هستند!

قله بلند
پنج شنبه 19 آذر 1388, 20:54 عصر
سلام
داشتم فصل 10 از کتاب C++‎ جعفر نژاد قمی رو می خوندم که به یک مطلب جالب بر خورد کردم:
چند ریختی، هم در زمان ترجمه و هم در زمان اجرا در C++‎ امکان پذیر است. چند ریختی زمان ترجمه با تعریف
مجدد توابع و عملگرها صورت می گیرد و چند ریختی زمان اجرا با استفاده از وراثت و توابع مجازی انجام می
شود.منظور شما نیز در پست 13 همین بود. درست است؟
من تصوری که از چند ریختی داشتم، چند ریختی زمان ترجمه بود یعنی توابعی که هم نام هم هستند. همان چیزی که در کتاب های درسی می خواندم. پس می شود در زمان اجرا نیز چند ریختی داشت.
عجب بحثیه این شیء گرایی. هم ناامیدکننده است و هم جالب! ناامیدکننده از این جهت که در ابتدا خیلی پیچیده به
نظر می رسه و جالب از این نظر که وقتی معماش حل می شه شیرین می شه. خدا کنه یادگیریش پوست آدم رو
نکّنه.

mazdadoost
پنج شنبه 19 آذر 1388, 23:49 عصر
با سلام :
استفاده از inner class ها در جاوا کاربرد های خاص خودش رو داره و مزایای خاص خودش رو داره وقتی که مثل سایر چیز ها درست و به ج ارش استفاده بشه .
1-وقتی می خواهید بدون شلوغ کردن class با bean notation های java متغیر های private اون class رو در اختیار کلاسی دیگر که فقط کارش کار روی اون متغبر ها ست استفاده از inner class به نظرم بهتره.این ناقض شی گرایی نیست.صرفا مدیریت یک کلاس با شکلی متفاوته.
2-وقتی رفتار ما در یک framework به شکل پیاده سازی یک interface یا abstract class مورد انتظاره مثل قسمت عمده ای از کتابخانه java(Runable -Swing Framework-Cuncorency Framework-Collection framework )-
3-بعضی patter ها رو هم از نظر خوانایی هم از نظر امکان بهتره با inner class ها پیاده کرد:adaptor pattern
4-با anonymous nested class میشه ساختار هایی مثل Function Object ساخت و pattern هایی مثل startegy رو پیاده کرد . در واقع اگر چه در jave - fuction pointer نداریم اما با ترکیب inner class ها و دیگر ابزار می تونیم وقتی لازم بود از delegation استفاده بکنیم.
پس همونطور که گفته شد inner class ها ابزار بسیار انعطاف پذیری برای مقاصدی هستند که براشون طراحی شدن.
موفق باشید.

cups_of_java
پنج شنبه 19 آذر 1388, 23:51 عصر
شیرینه و اگه از اول باهاش درست آشنا شی پیچیده نیست.
بله دو نوع چند ریختی داریم. منظور از چند ریختی، همیشه چند ریختی پویا (زمان اجرا) هست مگر اینکه ذکر شه چند ریختی از نوع استاتیک (زمان کامپیایل). حرف های من همه در مورد چند ریختی زمان اجرا بود و در اصل شی گرایی اینرو بهمون میده. چند ریختی استاتیک بدون شی گرایی هم وجود داره و ربطی به شی گرایی نداره!

cups_of_java
پنج شنبه 19 آذر 1388, 23:54 عصر
با سلام :

می تونم بپرسم پاسخ من چرا حذف شده؟

قله بلند
جمعه 20 آذر 1388, 10:27 صبح
با سلام :
استفاده از inner class ها در جاوا کاربرد های خاص خودش رو داره و مزایای خاص خودش رو داره وقتی که مثل سایر چیز ها درست و به ج ارش استفاده بشه .
1-وقتی می خواهید بدون شلوغ کردن class با bean notation های java متغیر های private اون class رو در اختیار کلاسی دیگر که فقط کارش کار روی اون متغبر ها ست استفاده از inner class به نظرم بهتره.این ناقض شی گرایی نیست.صرفا مدیریت یک کلاس با شکلی متفاوته.
2-وقتی رفتار ما در یک framework به شکل پیاده سازی یک interface یا abstract class مورد انتظاره مثل قسمت عمده ای از کتابخانه java(Runable -Swing Framework-Cuncorency Framework-Collection framework )-
3-بعضی patter ها رو هم از نظر خوانایی هم از نظر امکان بهتره با inner class ها پیاده کرد:adaptor pattern
4-با anonymous nested class میشه ساختار هایی مثل Function Object ساخت و pattern هایی مثل startegy رو پیاده کرد . در واقع اگر چه در jave - fuction pointer نداریم اما با ترکیب inner class ها و دیگر ابزار می تونیم وقتی لازم بود از delegation استفاده بکنیم.
پس همونطور که گفته شد inner class ها ابزار بسیار انعطاف پذیری برای مقاصدی هستند که براشون طراحی شدن.
موفق باشید.

با سلام
من فقط قسمت اول فرمایشات شما رو فهمیدم و در مورد بقیه اش کاملاً گیج شدم. در مورد
فهمیدنش پیگیری نمی کنم چون الان برای من که مبتدی هستم زود هست که با این مفاهیم آشنا
باشم. انشاء الله وقتی بیشتر از جاوا فهمیدم درک این اصطلاحات ساده تر خواهد بود.
در صفحه دو، پست من هم پاک شده بود. دو پست جناب cups_of_java هم پاک شده بود. خوب شد که صفحه ها رو ذخیره کردم وگرنه آدرس اون کتاب از دستم می رفت.