آشنایی با چهار اصل برنامه نویسی شیء گرا


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

Abstraction
Polymorphism
Inheritance
Encapsulation

برای به خاطر سپردن این چهار اصل، می‌توان ابتدای هر یک از این مفاهیم را در کنار یکدیگر قرار داده و کلمه ی A PIE را به خاطر سپرد. اگرچه این مفاهیم در ابتدای امر کمی ترسناک و ناامید کننده به نظر می رسند، اما واقعیت این است که ما به صورت ناخودآگاه با این مفاهیم آشنا بوده و می‌شود گفت که هر روز با آن‌ها سروکار داریم. در ادامه، با تک تک این مفاهیم در قالب مثال‌هایی کاربردی از دنیای واقعی آشنا خواهیم شد.

Abstraction
فرض کنیم در اتاقی نشسته‌ایم و فردی از ما می‌پرسد که لپ تاپ کجاست؟ ما هم جواب فرد مد نظر را داده و می‌گوییم روی «میز» است. وقتی ما واژه ی میز را به زبان می آوریم، هرگز نمی‌گوییم که لپ تاپ روی میزی است که چوبی است، رنگ آن قهوه ای روشن است، روی آن یک شیشه قرار دارد، چهار پایه دارد، طول و عرض آن فلان اندازه است و … ما صرفاً واژه ی میز را به زبان آورده و طرف مقابل کاملاً متوجه می‌شود که منظور ما کدام میز است! هم ما و هم طرف مقابل مان که در اتاق حضور داریم تجربیاتی در برخورد با شیئ به نام «میز» داشته و داریم که به ما کمک کند تا منظور از میز را متوجه شویم. واژه ی Abstraction به معنی «انتزاعی بودن» است. یعنی چیزی که در حد یک ایده است و وجود خارجی ندارد یا این که نمی‌شود آن را لمس کرد.

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

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

Abstraction حاکی از آن است که ما اصلاً نیازی نیست تا یک کلاس برای BehzadAccount و کلاس دیگری برای AliAccount بسازیم بلکه یک کلاس مفهومی تحت عنوان BankAccount خواهیم ساخت که از روی آن می‌توان به تعداد بی نهایت آبجکت ایجاد کرد.

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

در برنامه نویسی، Encapsulation به زمانی اطلاق می‌شود که ما Attribute ها وBehavior های یک آبجکت را در کپسولی فرضی قرار داده و آن‌ها را کنار یکدیگر قرار دهیم. علاوه بر این، با به کارگیری مفهومی تحت عنوان Encapsulation ما خواهیم توانست دسترسی به بخش یا بخش‌هایی از یک کلاس یا آبجکتی که بر اساس آن کلاس نوشته شده را محدود کنیم. در مثال حساب بانکی، فرض کنیم ویژگی‌هایی داریم همچون شماره حساب و موجودی. از این دو صرفاً شماره حساب است که می‌تواند در اختیار سایر بخش‌های نرم‌افزار قرار گیرد و این در حالی است که سطح دسترسی به ویژگی موجودی با مفهومی همچون Encapsulation محدود شده است.

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

زبان‌های برنامه نویسی شیء گرای مختلف، تعریف مخصوص به خود را در ارتباط با مفهوم Encapsulation یا پنهان سازی بخش‌هایی از کلاس از دید سایر بخش‌های نرم‌افزار دارند. آنچه در اینجا حائز اهمیت می‌باشد این است که مفاهیمی همچون Abstraction و Encapsulation در کنار یکدیگر، این فضا را برای ما -به عنوان برنامه نویس- ایجاد می‌کنند تا به هر میزانی که تمایل داشته باشیم مفاهیمی انتزاعی ایجاد کرده تا در آینده به هر میزان سطح دسترسی که تمایل داشته باشیم از روی آن‌ها اشیائی خاص بسازیم.

Inheritance
در ساده‌ترین شکل ممکن، Inheritance را می‌توان به «استفاده ی مجدد از کدهای از قبل نوشته شده» ترجمه کرد. معنی لغوی Inheritance «وراثت» است. برای روشن‌تر شدن این مسأله مثالی عملی می زنیم. فرض کنیم که در نرم‌افزار خود، ما کلاسی ایجاد کرده‌ایم تحت عنوان Person به معنی «انسان» که دارای یکسری خصوصیات است که از آن جمله می‌توان به name, phoneNumber و email اشاره کرد که به ترتیب به معنی «نام»، «شماره تلفن»، «ایمیل» اند. این کلاس دارای یکسری عملکرد نیز هست که به طور مثال می‌توان به متدی تحت عنوان ()changeEmail یا قابلیت «تغییر آدرس ایمیل» اشاره کرد. اکنون قصد داریم اقدام به ساخت کلاس جدیدی کنیم مثلاً تحت عنوان Customer به معنی «مشتری» که این کلاس دقیقاً شبیه به کلاس Person است. به عبارت دیگر دارای خصوصیاتی مثل name, phoneNumber و email است و همچنین عملکردی تحت عنوان ()changeEmail نیز دارا است. علاوه بر این موارد، کلاس مشتری دارای Attribute یی تحت عنوان customerNumber به معنی «شناسه ی مشتری» است که کلاس انسان فاقد آن است.

در چنین شرایطی ما دو راه‌ کار پیش رو داریم: راه‌ کار اول این که یک کلاس جدید همچون کلاس Person ساخته سپس قابلیت customerNumber را به آن اضافه کنیم یا این که کلاس جدیدی تحت عنوان Customer ساخته، صرفاً قابلیت customerNumber را در آن قرار داده سپس کاری کنیم که این کلاس جدید سایر خصوصیات و قابلیت هایش را از کلاس Person اصطلاحاً «به ارث ببرد» که این همان چیزی است که زبان‌های برنامه نویسی شیء گرا برای آن ساخته شده اند.

از این پس، کلاس مشتری کلیه ی قابلیت‌های کلاس انسان را دارد و جالب است بدانیم در صورتی که تغییری در کلاس انسان ایجاد کنیم، کلیه ی آن تغییرات در کلاس مشتری نیز اعمال خواهند شد. حال ممکن است این سؤال برای شما پیش بیاید که چرا از همان ابتدا چیزی مثل customerNumber را داخل کلاس انسان قرار ندادیم. پیش از این هم توضیح دادیم که ما در برنامه نویسی شیء گرا می بایست تا حد ممکن به اولین اصل یا همان Abstraction احترام بگذاریم و کلاس‌هایی که ایجاد می‌کنیم می بایست تا حد ممکن کلی باشند. اگر ما چیزی مثل شناسه ی مشتری را داخل کلاس انسان قرار می دادیم، این کلاس را تا حدودی محدود می کردیم. فرض کنیم که در آینده شرایطی ایجاد می‌شد که مجبور می‌شدیم که کلاسی تحت عنوان Seller به معنی «فروشنده» ایجاد می کردیم. در چنین شرایطی اگر کلاس Seller از کلاس Person ارث بری می کرد، دارای قابلیتی همچون customerNumber بود که این ویژگی به هیچ وجه به درد اشیاء ساخته شده از روی کلاس Seller نمی خوردند.

به خاطر داشته باشید
در برنامه نویسی شیء گرایی، کلاس Person تحت عنوان Superclass یا «کلاس اصلی» و کلاس Customer تحت عنوان Subclass یا «کلاس زیرشاخه» شناخته می شود. از یک بعد دیگر، کلاس Person را می‌توان کلاس Parent یا «والد» و کلاس Customer را می‌توان کلاس Child یا «فرزند» تلقی کرد.

در زبان‌هایی همچون ++C این امکان در اختیار برنامه نویس قرار گرفته تا بتواند از بیش از یک کلاس والد ارث بری کند که چنین قابلیتی تحت عنوان Multiple Inheritance یا «وراثت چندگانه» شناخته می شود. اگر چه چنین قابلیتی دست برنامه نویسان را خیلی باز می گذارد، اما در عین حال چنین قابلیتی منجر به پیچیده‌تر شدن برنامه می‌گردد و به همین دلیل است که برخی زبان‌های برنامه نویسی همچون جاوا، سی شارپ، آبجکتیو سی، روبی و … به هیچ وجه اجازه ی ارث بری از بیش از یک کلاس والد را به برنامه نویس نمی دهند.

Polymorphism
آخرین اصل از اصول برنامه نویسی شیء گرا Polymorphism نام دارد. Poly به معنی «چندین» است و Morph هم به معنی «شکل» است که روی هم رفته می‌توان آن را به «چند شکلی» ترجمه کرد. این اصل در مقایسه با سه اصل اول اصول شیء گرایی کمی پیچیده‌تر است اما سعی می‌کنیم با مثال‌هایی ملموس، مفهوم Polymorphism را برای برنامه نویسان مبتدی روشن کنیم.

در ابتدا مثالی از این اصل می‌زنیم که شاید تا به حال خیلی در مورد آن فکر نکرده باشیم. علامت + را در نظر بگیرید. علامت + به چه معنا است؟ در بسیاری از زبان‌های برنامه نویسی این علامت دارای کاربردهای متنوعی است. اگر ما بخواهیم دو متغیر همچون a و b را با علامت + به صورت a + b در کنار یکدیگر قرار دهیم، بسته به این که این متغیرها چه مقداری داشته باشند، از علامت + می بایست انتظار عملکردی متفاوت داشته باشیم. به طور مثال، اگر مقدار متغیر a برابر با 7 و مقدار متغیر b برابر با 2 باشد، علامت + حاصل جمع جبری این دو متغیر که برابر با عدد 9 است را محاسبه خواهد کرد. حال فرض کنیم مقادیر مرتبط با این دو متغیر استرینگ باشند. به عبارت دیگر، مقدار متغیر a برابر با 'Behzad' و مقدار متغیر b برابر با 'Moradi' باشد که در چنین شرایطی علامت + به هیچ وجه حاصل جمع متغیرها را در اختیار ما قرار نخواهد داد بلکه این دو استرینگ را در کنار یکدیگر قرار داده و نتیجه ی 'BehzadMoradi' را در اختیار ما قرار می‌دهد که به چنین چیزی در برنامه نویسی اصطلاحاً Concatenation به معنی «الحاق» گفته می شود.

نکته
String (استرینگ به معنی رشته) به نوع خاصی از کلاس ها در برنامه نویسی گفته می شود که می توانند دربرگیرنده ی هر نوع داده ای از حروفی مثل a, F, GG گرفته تا علائمی همچون !, % و * باشند.

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

با در نظر گرفتن این مقدمه، حال قصد داریم ببینیم که اصل چند شکلی در شیء گرایی چگونه نمود عینی پیدا خواهد کرد. مجدد به مثال حساب بانکی باز می گردیم. فرض کنیم که یک Superclass داریم تحت عنوان BankAccount که دارای یکسری Attribute مثل accountNumber و accountType به ترتیب به معنی «شماره حساب» و «نوع حساب» است. این کلاس علاوه بر این دارای یکسری Bahavior نیز هست که از آن جمله می‌توان به متدهایی همچون ()open و ()close و یا ()withdraw به ترتیب به معنی «باز کردن حساب»، «بستن حساب» و «برداشتن موجودی» می باشد.

فرض کنیم که قصد داریم یکسری کلاس جدید ایجاد کنیم مثلاً با نام های InvestmentAccount و SavingAccount و CurrentAccount به معنی «حساب سرمایه گذاری»، «حساب پس انداز» و «حساب جاری». این حساب‌های بانکی همگی قابلیت‌هایی همچون باز و بسته کردن حساب، برداشت موجودی و یا شماره ی حساب و نوع حساب را دارند پس به سادگی می‌توانیم برنامه ی خود را طوری بنویسیم که این کلاس‌ها از کلاس اصلی قابلیت هایشان را به ارث ببرند اما توجه داشته باشیم که هر کدام از این کلاس‌ها دارای یکسری ویژگی‌های مخصوص به خود هستند. به طور مثال، حساب پس انداز می بایست دارای چیزی همچون interestRate یا «بهره ی بانکی» باشد که حساب‌های سرمایه‌گذاری و جاری فاقد آن هستند. اما قضیه به اینجا ختم نشده و شرایط تاحدودی پیچیده‌تری از آن چیزی است که در ظاهر به نظر می رسد. فرض کنیم بانکی که سفارش این نرم‌افزار را به ما داده است، قانونی گذاشته مبنی بر این که اگر یک مشتری از حساب سرمایه‌گذاری خود تا قبل از یک سال پولی برداشت کند، می بایست 30 درصد جریمه دهد اما مشتریانی که حساب پس انداز یا جاری دارند، به سادگی و بدون هیچ جریمه ای هر وقت که تمایل داشته می‌توانند Withdraw کنند یا «از حساب خود پول برداشت کنند».

متد ()withdraw در کلاس والد تعریف شده است اما این در حالی است که ما در کلاس حساب سرمایه گذاری، خواهیم توانست نوع خاصی از آن متد را ایجاد کنیم که چنین کاری اصطلاحاً در برنامه نویسی Override یا «رونویسی» گفته می شود. به عبارت دیگر، ما متد ()withdraw که در کلاس BankAccount قرار دارد را در کلاس InvestmentAccount اصطلاحاً Override کرده و انتظارات مد نظر خود را در آن می گنجانیم که صرفاً مخصوص کلاس InvestmentAccount اند. به طور خلاصه، زمانی که ما بخواهیم چیزی را از کلاس دیگری استفاده کنیم، به سادگی آن را به ارث می‌بریم اما اگر بخواهیم تغییری در این ارث بری ایجاد کنیم، Attribute یا Behavior مد نظر را Override می‌کنیم.

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

در پاسخ به

کاربر میهمان
کاربر میهمان
سلام خسته نباشید
میخواستم درباره abstraction layers بدونم؟؟؟؟؟؟
کاربر میهمان
کاربر میهمان
من آموزش‌های این بخش و از اول خوندم و عالی بودن ولی این یکی فوقِ عالی بود .... خیلی ممنون بابت این آموزش‌ها ... خدا قوت و پاینده باشید
کاربر میهمان
کاربر میهمان
سلام.یه سوال. در مفهوم ارث بری، یک آبجکت لزوما همه خصوصیات و رفتارهای کلاس رو به ارث میبره یا میتونه فقط قسمتی از اونها رو بگیره؟ تشکر
CIna Deuxshiri
CIna Deuxshiri
سلام. در ارث بری اصلا آبجکت چیزی و به ارث نمی‌بره، یک کلاس از کلاس دیگه ارث بری می‌کنه.

در پاسخ به

یزدان
یزدان
سلام.میخواستم بدونم علامت پرانتز یعنی () فقط بعد متد کاربرد داره؟؟؟؟؟
کاربر میهمان
کاربر میهمان
سلام.من میخوام برنامه نویسی اندروید رو بخونم که احتیاج به زبان جاوا داره.در این آموزش گفته شد که زبان جاوا هیچ وقت اجازه وراثت رو نمیده.یعنی من تو برنامه نویسی اندروید نیازی به دو اصل آخر که مربوط به وراثت بودن ندارم؟یعنی هیچ وقت بهشون احتیاج پیدا نمیکنم؟؟؟
CIna Deuxshiri
CIna Deuxshiri
سلام.
دوست عزیز مطلب و با دقت مطالعه کنید. ارث بری چندگانه در جاوا وجود نداره که البته با وجود اینترفیس این مسئله حل شده.

در پاسخ به

رضا بختیاری
رضا بختیاری
كاملا به زبان ساده .....


خيلي ممنون سكان جون
m.ali
m.ali
با سلام و تشکر از آموزش روان و عالی شما، پاراگراف آخر، اول خط 2، به اشتباه نوشته شده "حساب پس انداز" در حالی که (برای اشاره به ترجمه InvestmentAccount) باید نوشته می شد «حساب سرمایه گذاری»
ادمین سایت
ادمین سایت
سلام
ضمن تشکر از تذکر شما، احتراما به استحضار می رساند که اصلاح گردید


با تشکر
ارادتمند
تیم سکان آکادمی

در پاسخ به

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

در پاسخ به

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

در پاسخ به

بهزاد مرادی
بهزاد مرادی دولوپر، کپی‌رایتر و علاقمند به کامیونیتی اپن‌سورس
سلام
ممنون از شما
واقعیت امر آن است که کلیه ی آموزش های تالیفی بنده به صورت متنی خواهند بود.

در پاسخ به

محمد مهدی  رفیعی
محمد مهدی رفیعی
درود استاد .
میخواستم بگم توضیحتون عالی بود ولی امکانش نبود یه مثال عملی هم مثله بقیه اصولی که گفتید برای Encapsulation یا همان کپسوله سازی میزدید ؟

سپاس فراوان
CIna Deuxshiri
CIna Deuxshiri
سلام
درواقع وقتی ما دسترسی مستقیم به یک فیلدی و در یک کلاس از بین می بریم (با تعریف اون فیلد به صورت private)، عمل کپسول سازی انجام دادیم که برای دسترسی به اون فیلد باید از متد های Getter و Setter استفاده کنیم.

در پاسخ به

محمد مهدی  رفیعی
محمد مهدی رفیعی
بسیار سپاسگزارم جناب سینا دوشیری

در پاسخ به

محمد مهدی  رفیعی
محمد مهدی رفیعی
درود بر شما استاد بهزاد مرادی
این آموزشتون فــــــوق العــــاده بود .

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

با تشکر
ارادتمند
تیم سکان آکادمی

در پاسخ به

aref hosseini
aref hosseini
ببخشید خط آخر کلمه Encapsulation با Polymorphism اشتباه نشده؟
یا من اشتباه متوجه شدم؟
Milad Caesar
Milad Caesar
حرف شما درسته... اشتباه نوشته شده

در پاسخ به

مرتضی کارگر
مرتضی کارگر
جالب و مفید بود