یکی از مفاهیمی که الگوی اصلی فکری برنامهنویسان را در اکثر زبانهای برنامهنویسی شکل میدهد، شییٔگرایی است که در این آموزش قصد داریم تا با ذکر یکسری مثال ساده و کاربردی با اصول این الگوی برنامهنویسی آشنا شویم.
در دههٔ 1960 میلادی، برنامهنویسان بدین نتیجه رسیدند که برای بهکارگیری سرعت و قدرت محاسباتی کامپیوترها در حل مسائل پیچیدهٔ دنیای واقعی لازم است تا برخی اجزای محیطی که رویدادها در آن اتفاق میافتند را به صورت مجازی برای کامپیوترها شبیهسازی کنند؛ به عبارت دیگر، برای هر چیزی که در دنیای واقعی وجود دارد، مفهومی مشابه به عنوان نمایندۀ آن چیز در محیط مجازی کامپیوتر تعریف کنند به طوری که از یکسو مفهومی کلی بوده و به هیچ چیز خاصی محدود نشود و از سوی دیگر برای اشاره به تمامی چیزهایی که در دنیای واقعی وجود دارند مورد استفاده قرار بگیرد که در نهایت دانشمندان علوم کامپیوتر به این نتیجه رسیدند که در سادهترین شکل ممکن نمایندهٔ هر چیز را در دنیای مجازی Object به معنای «شییٔ» بنامند و از همین روی شیوهٔ برنامهنویسی بر اساس این رویکرد را Object Oriented Programming یا به اختصار OOP نامیدند.
نخستین زبان برنامهنویسی طراحیشده بر اساس الگوی شیئگرایی تحت عنوان Simula-67 شناخته میشود و برای نامگذاری آن نیز از واژهٔ Simulation به معنی «شبیهسازی» الهام گرفته شده است چرا که برای شبیهسازی اجرای عملیات بانکی مورد استفاده قرار میگرفت و پس از آن هم زبانی به نام Smalltalk طراحی شد که به عنوان یک زبان برنامهنویسی موفق در حوزۀ شییٔگرایی به شمار میآید.
در حقیقت، برنامههای پیادهسازیشده بر اساس اصول شیئگرایی مجموعهای از آبجکتها را شامل میشوند به طوری که هر یک از این آبجکتها خصوصیات و رفتارهای منحصربهفرد خود را دارند و با توجه به این خصوصیات و رفتارها نیز دولوپرها میتوانند کارهای خاصی را برای هر آبجکت در نظر بگیرند مضاف بر اینکه در چنین برنامههایی آبجکتهای مختلف میتوانند همچون اشیاء دنیای واقعی با یکدیگر تعامل داشته باشند که به عنوان یک مثال ساده اما در عین حال ملموس در رابطه با چنین اشیائی میتوان انسان را نام برد.
در واقع، هر فرد نامی مختص به خود دارا است که به عنوان Identity (هویت) وی به شمار میرود و همچنین خصوصیات متفاوتی همچون جنسیت، سن، ملیت و غیره میتواند داشته باشد که در اصطلاح برنامهنویسی تحت عنوان Attribute به معنای «خصوصیت» شناخته میشوند به علاوه اینکه هر یک از افراد توانایی انجام یکسری عمل خاص را دارند که از آن جمله میتوان به راه رفتن، غذا خوردن، صحبت کردن، رانندگی کردن و غیره اشاره کرد که این اَعمال نیز اصطلاحاً Behaviour به معنای «رفتار» آن شیئ نامیده میشوند.
برای درک بهتر مفهوم آبجکتها و خصوصیات هر یک از آنها مثالی فرضی را مد نظر قرار میدهیم که در آن قصد داریم تا برنامهای بنویسیم که شبیهساز رانندگی انسانی باشد که ماشین خود را از نقطهٔ الف به نقطهٔ ب میراند. برنامۀ مذکور در سادهترین حالت دو آبجکت به نام راننده و ماشین دارا است که به منظور شروع پیادهسازی، باید این دو آبجکت را برای کامپیوتر تعریف کنیم به طوری که آبجکت راننده میباید خصوصیاتی همچون نحوۀ رانندگی، چگونگی روشن کردن ماشین، سرعت در هنگام رانندگی و نحوۀ ارسال فرمان توقف به آن را داشته باشد و به همین ترتیب آبجکت ماشین نیز یکسری خصوصیت به منظور شروع حرکت با دریافت دستورات راننده، پیمایش مسیر و توانایی توقف را داشته باشد. بنابراین میتوان گفت که برای تعریف چنین آبجکتهایی نیاز است تا برای هر یک خصوصیات مد نظر، نحوۀ عملکرد و رفتارشان را برای سیستم تعریف کنیم.
پس از مطرح شدن ایدهٔ برنامهنویسی شیئگرا، مسئلهای که برنامهنویسان با آن مواجه شدند این بود که برخی از آبجکتهای برنامه یکسری خصوصیت و رفتار مشترک داشتند. برای مثال، در شبیهسازی مسابقهٔ رانندگی تعداد زیادی آبجکت راننده و ماشین داریم و علیرغم برخی تفاوتها میتوان گفت که تمامی رانندهها میدانند که چگونه باید رانندگی کنند و همچنین همهٔ ماشینها نیز خصوصیات مشترکی به منظور تعیین میزان سرعت و چگونگی توقف دارند.
بنابراین مسئلۀ شباهتِ برخی خصوصیات و رفتارهای مابین آبجکتها، برنامهنویسان را به سمت تعریف یک طرح و نقشۀ کلی برای ساخت یکسری آبجکت مشابه یکدیگر هدایت کرد تا در صورت نیاز بتوانند آبجکتی بر اساس طرح مربوطه در برنامه تعریف کنند یا به عبارتی یک به اصطلاح Instance (نمونه) از روی یک طرح از پیش تعریفشده بسازند و به نوعی میتوان گفت که این طرح همچون یک فرم تکمیل مشخصات فردی است که گزینههای پیشفرضی نظیر سن، جنس، تاریخ تولد، محل تولد، میزان تحصیلات، تواناییهای ورزشی، مهارتهای هنری و غیره را دارا است.
نکته |
طرح کلی فوقالذکر در برنامهنویسی شییٔگرا تحت عنوان Class شناخته میشود که از روی کلاس مد نظر میتوان آبجکتی به عنوان Instance ساخت به طوری که کلیهٔ خصوصیات کلاس مربوطه را به ارث برد. |
ایدۀ تعریف کلاس و استفاده از آبجکتهای ساختهشده از روی آن کار برنامهنویسان را در فرآیند توسعۀ نرمافزار بسیار راحتتر از گذشته کرده و منجر به کاهش پیچیدگی مسائل و برنامههای مربوطه میشود مضاف بر اینکه مهمترین مشخصۀ تمایز یک کلاس از کلاسهای دیگر پیامهایی است که در قالب کدهای برنامه به نمونههای ساختهشده از روی آن کلاس ارسال میشود به طوری که متناسب با پیام دریافتی کار خاصی را انجام میدهند مضاف بر اینکه آبجکتهای ساختهشده از روی یک کلاس یکسان میتوانند پیامهای یکسانی را دریافت کنند.
به طور کلی، میتوان گفت که در برنامههای بزرگ و پیچیدۀ نوشتهشده به زبان پایتون برای شبیهسازی مسائل دنیای واقعی تکتک اجزای آن برنامه به صورت شیئی تعریف میشوند که هر یک هویتی منحصربهفرد داشته اما متعلق به یک کلاس با برخی خصوصیات و رفتارهای مشترک است.
در آموزشهای گذشته تا حدودی با مفهوم آبجکت و کلاس آشنا شدیم و همچنین با برخی کلاسهای از پیش تعریفشده در زبان پایتون کار کردیم. در واقع، کلاسهای از پیش تعریفشده همچون یک جعبۀ سیاه عمل میکنند که برای کار با چنین کلاسهایی نیازی به آگاهی از ساختار داخلی آنها نداریم بلکه یک نمونه از روی کلاس مد نظر ساخته و متد مورد نیاز خود را روی آبجکت مذکور فراخوانی کرده و در ادامه یکسری دیتا به عنوان آرگومان ورودی به آن میدهیم تا پس از انجام پردازشهای لازم خروجی را در اختیارمان قرار دهد.
برای مثال، به منظور فراخوانی متدی همچون ()append
از کلاس list
لازم نیست که چگونگی طراحی و پیادهسازی این فانکشن را بدانیم بلکه تنها آرگومانهای مورد نیاز را به این فانکشن پاس داده و آن را روی آبجکتی از کلاس list
ترتیب اثر میدهیم که در چنین شرایطی کلاس list
در پشت پرده پردازشهای لازم را انجام داده و نتیجه را در خروجی ریترن میکند. بنابراین میتوان گفت که کلاس همچون ظرفی است که مجموعهای از دادهها و کدهای مرتبط را کنار هم و در یک جا نگهداری کرده و از سایر قسمتهای برنامه امکان ساخت نمونه از روی کلاس مد نظر را برای دولوپرها فراهم میکند.
در عین حال، در فرآیند کدنویسی یک اپلیکیشن گاهی نیاز است تا کلاسی اختصاصی به منظور انجام تسکهای مد نظر خود ایجاد کنیم. در واقع، بهکارگیری مفهوم کلاس در فرآیند توسعهٔ نرمافزار از پیچیدگی کدهای برنامه جلوگیری میکند و این امکان را در اختیار دولوپرها قرار میدهد تا بتوانند یکسری کد مرتبط را در قالب کلاسهای مختلف نگهداری کرده و در صورت نیاز از آنها نمونهسازی کنند و یا از طریق نمونۀ ساختهشده از روی یک کلاس خاص به اتریبیوتها و متدهای داخل آن دسترسی پیدا کنند که در ادامۀ این آموزش قصد داریم تا نحوۀ ساخت کلاسهای مد نظر خود را در زبان برنامهنویسی پایتون بررسی کنیم و ببینیم چگونه میتوان آبجکتهای مختلفی از روی آنها ایجاد کرد.
آشنایی با نحوۀ ایجاد یک کلاس
پیش از هر چیز میباید به این سؤال پاسخ دهیم که «در دنیای واقعی به چه چیزی یک شیئ گفته میشود؟» که در پاسخ میتوان گفت هر چیزی که ماهیت فیزیکی داشته باشد یک شیئ است به طوری که بتوان از یکسو برخی کارها روی اشیاء مد نظر انجام داد و از سوی دیگر هر یک از این اشیاء شکل، رنگ، اندازه و سایر خصوصیات مختص خود را داشته باشند و از روی صفات یا خصوصیاتشان قابلشناسایی باشند. در صنعت توسعۀ نرمافزار نیز خصوصیاتی که اشیاء با بهکارگیری آنها توصیف میشوند Attribute نامیده شده و کارهایی که روی هر یک از آنها انجام میشوند Method نام دارند.
برای مثال، فرض کنید قصد داریم تا شیئی همچون توپ را در زبان برنامهنویسی پایتون شبیهسازی کنیم به طوری که شیئ مذکور صفاتی نظیر اندازه، جنس، رنگ و وزن داشته و متدهایی مانند شوت کردن، باد کردن، پرتاب کردن و غیره را بتوان بر روی آن اجرا کرد. برای نشان دادن آبجکت یا شیئ مذکور از شناسۀ ball
استفاده میکنیم و از همین روی ویژگیهای توپ مد نظر به صورت زیر خواهد بود:
ball.color
ball.size
ball.weight
همچنین متدهای زیر را میتوانیم برای آبجکت توپ در نظر بگیریم:
ball.kick()
ball.throw()
ball.inflate()
همانطور که اشاره کردیم، هدف این است که آبجکتهایی با انواع مختلف از روی کلاس مد نظر را در برنامۀ خود ساخته و از آنها استفاده کنیم. به طور کلی، ساخت یک آبجکت در زبان برنامهنویسی پایتون دو مرحله دارد که در مرحلۀ اول مشخص میکنیم که یک آبجکت خاص چه خصوصیاتی دارا است و چه کارهایی میتواند انجام دهد؛ به عبارت دیگر، ابتدا باید اتریبیوتها و متدهای آن را تعریف کنیم اما این در حالی است که تعریف موارد فوق منجر به ایجاد یک آبجکت نمیشود بلکه همچون کشیدن نقشۀ یک خانه است که بدین طریق شکل ظاهری آن را مشخص کردهایم. در حقیقت، نقشۀ مذکور یک خانۀ قابلسکونت نبوده و تنها یک تکه کاغذ است که میتوان برای ساخت چندین نمونه خانۀ واقعی از آن استفاده کرد.
یک کلاس در زبان برنامهنویسی پایتون نیز دقیقاً مانند نقشهای است که میتوان از روی آن به تعداد مورد نیاز آبجکت ایجاد کرد به طوری که داریم:
class MyClass:
myVar = 0
در کد فوق یک کلاس بسیار ساده به نام MyClass
ایجاد کردهایم بدین صورت که دستور مربوطه را با کلمۀ کلیدی class
شروع کرده و بدین ترتیب به مفسر پایتون میگوییم که قصد تعریف یک کلاس را داریم سپس شناسۀ MyClass
را آوردهایم که یک نام دلخواه برای کلاس مد نظر است و در ادامه علامت :
را قرار داده و کدهای داخلی کلاس را با رعایت تورفتگی نسبت به دستور سطر اول نوشتهایم. در مثال فوق، کلاس MyClass
حاوی متغیری تحت عنوان myVar
است که مقدار اولیۀ آن را برابر با ۰ قرار دادهایم و بدین ترتیب تمامی نمونههای ساختهشده از این کلاس شامل متغیر مذکور با مقدار اولیۀ ۰ خواهند بود.
اسکریپت فوق را در فایلی به نام MyClass.py
ذخیره میکنیم. حال پس از تعریف کلاس فوق وارد مرحلۀ دوم نمونهسازی از روی کلاس مذکور میشویم که به منظور ساخت نمونۀ دلخواه خود، کدی همانند اسکریپت زیر خواهیم داشت:
myInstance = MyClass()
در کد فوق نمونهای از کلاس MyClass
ایجاد کرده و آن را به متغیری تحت عنوان myInstance
ارجاع دادهایم (توجه داشته باشیم که این کد خارج از بدنۀ کلاس MyClass
نوشته شده است.) حال اگر متغیر myVar
را یک اتریبیوت از کلاس MyClass
در نظر بگیریم، نمونۀ ساختهشده از روی این کلاس تحت عنوان myInstance
نیز اتریبیوت myVar
را دارا است که برای دسترسی به آن کافی است تا نام شیئ را بیاوریم و در ادامه علامت .
قرار داده سپس نام اتریبیوت مذکور را میآوریم:
print(myInstance.myVar)
به عنوان خروجی آبجکت فوق داریم:
0
همانطور که میبینید، در آبجکت myInstance
(به عنوان نمونهای از کلاس MyClass
) نیز مقدار منتسب به اتریبیوت myVar
برابر با ۰ میباشد اما این در حالی است که در صورت نیاز میتوانیم مقدار اولیۀ فوق را برای این آبجکت تغییر دهیم که برای این منظور به صورت زیر عمل میکنیم:
myInstance.myVar = 100
print(myInstance.myVar)
در کد فوق مقدار منتسب به اتریبیوت myVar
در آبجکت myInstance
را به عدد 100 تغییر داده و آن را توسط فانکشن ()print
چاپ میکنیم که خروجی حاصل به صورت زیر میباشد:
100
در واقع، با این کار مقدار اولیۀ اتریبیوت myVar
را اصطلاحاً Override (رونویسی) کرده و مقدار جدیدی معادل عدد 100 را به آن منتسب کردهایم.