"نه مرگ آنقدر ترسناک است و نه زندگی آنقدر شیرین که انسان برای این دو شرفش را بدهد" نامتان جاوید باد

درس ۰۵: مفهوم شی‌گرایی

مفهوم شی گرایی (Object-Oriented Programming)

Photo by Lucas Benjamin

این درس به توضیح مفاهیم پایه «برنامه‌نویسی شی‌گرا» (Object-Oriented Programming) اختصاص یافته است و آخرین درس از سطح «پایه» در این کتاب می‌باشد. هدف از این درس آشنایی خوانندگان با مفاهیم عمومی شی‌گرایی بوده و نه آموزش آن؛ جزییات بیشتر از برنامه‌نویسی شی‌گرا به همراه آموزش پیاده‌سازی مفاهیم آن در زبان پایتون از درس هفدهم تا بیست و دوم به صورت کامل بررسی خواهد شد. البته در این درس کمی به ساختار اشیا و کلاس‌ها در زبان پایتون اشاره‌ شده است که پیش‌نیاز دروس آتی خواهد بود.

سطح: پایه



برنامه‌نویسی شی‌گرا

«برنامه‌نویسی شی‌گرا» (Object-Oriented Programming) یا به اختصار OOP یک الگو یا شیوه تفکر در برنامه‌نویسی است که برگرفته از دنیای واقعی بوده و از دهه ۱۹۶۰ میلادی مطرح گشته است. به زبانی که از این الگو پشتیبانی کند، «زبان شی‌گرا» گفته می‌شود؛ Simula 67 و Smalltalk نخستین زبان‌های برنامه‌نویسی شی‌گرا هستند. ایده شی‌گرایی در پاسخ به برخی از نیازها که الگوهای موجود پاسخ‌گو آن‌ها نبودند به وجود آمد؛ نیازهایی مانند: توانایی پیاده‌سازی مسائل پیچیده (Complex)، «پنهان‌سازی داده» (Data Hiding)، «قابلیت استفاده مجدد» (Reusability) بیشتر، وابستگی کمتر به توابع، انعطاف بالا و...

رویکرد برنامه‌نویسی شی‌گرا «از پایین به بالا» (Bottom-Up) است؛ یعنی ابتدا واحدهایی کوچک از برنامه ایجاد می‌شوند و سپس با پیوند این واحدها، واحدهایی بزرگتر و در نهایت شکلی کامل از برنامه به وجود می‌آید. برنامه‌نویسی شی‌گرا در قالب دو مفهوم «کلاس» (Class) و «شی» (Object) ارایه می‌گردد. هر کلاس واحدی از برنامه است که بخشی از عملیات‌ها و داده‌های یک برنامه را تعریف می‌کند و هر شی نیز یک نمونه پیاده‌سازی شده از یک کلاس است. می‌توان هر تعداد شی از یک کلاس ایجاد کرد. هر شی مستقل از دیگر اشیای ایجاد شده خواهد بود که می‌تواند مقادیر متفاوتی را در خود نگهداری کند (در چهارچوبی که توسط کلاس آن تعریف شده است). در برنامه‌نویسی شی‌گرا، تمامی عملیات‌های قابل انتظار یک برنامه از تعامل اشیای گوناگون با یکدیگر حاصل می‌شود.

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

مفاهیم شی‌گرایی

گفته شد که هر کلاس ساختاری است که تعدادی داده و عملیات مرتبط را تعریف می‌کند. از دید برنامه‌نویسی، هر کلاس از دو بخش «اعضای داده» (Data Members) و «توابع عضو» (Member Functions) تشکیل شده است. اعضای داده در واقع همان متغیر‌های درون کلاس هستند که خصوصیات یا صفات شی را بیان می‌کنند و در شی‌گرایی با عنوان «فیلد» (Field) یا «صفت» (Attribute) از آن‌ها یاد می‌شود. توابع عضو نیز عملیات یا کارهایی هستند که یک شی از کلاس قادر به انجام آن‌ها می‌باشد؛ در شی‌گرایی به این توابع «متد» (Method) گفته می‌شود.

تا به اینجا با مفاهیم «کلاس»، «صفت»، «متد» و «شی» آشنا شده‌ایم؛ در ادامه به توضیح مفاهیم اصلی از برنامه‌نویسی شی‌گرا خواهیم پرداخت.

نمونه‌سازی

به هر شی از یک کلاس، یک نمونه (Instance) از آن کلاس گفته می‌شود و به عمل ایجاد شی از کلاس در شی‌گرایی «نمونه‌سازی» (Instantiation) گفته می‌شود. در این مفهموم، هدف نهایی از تعریف کلاس‌ها در برنامه، تنها تولید شی نیست. گاهی نیز وجود یک کلاس تنها برای درج تعاریف مشترک بین چند کلاس دیگر کاربرد پیدا می‌کند. بر همین اساس دو نوع کلاس در شی‌گرایی وجود دارد: ۱- کلاس‌های عادی که توانایی نمونه‌سازی دارند و به آن‌ها ”Concrete Class“ گفته می‌شود ۲- کلاس‌هایی که توانایی نمونه‌سازی ندارند و به آن‌ها ”Abstract Class“ گفته می‌شود.

همانطور که در دروس مربوط به پیاده‌سازی کلاس خواهید دید؛ با ایجاد هر نمونه از کلاس یک متد خاص در آن به صورت خودکار اجرا می‌گردد. این متد «سازنده» (Constructor) نام دارد و کار آن «مقداردهی اولیه» (Initialization) شی است. این کار موجب اطمینان از مقداردهی تمامی اعضای داده پیش از استفاده شی در برنامه می‌گردد.

برای مثال یک کلاس خودرو ساده را در نظر بگیرید که در آن صفات: رنگ بدنه، ظرفیت باک، بیشینه سرعت و متدهای: راندن، دریافت میزان سوخت، سوخت گیری، تنظیم سرعت، توقف تعریف شده است. در همان ابتدا یعنی زمان نمونه‌سازی می‌توانیم با تنظیم صفات توسط متد سازنده، اشیا هم نوع ولی با صفات متفاوتی را ایجاد نماییم. برای مثال: دو خودروی آبی با ظرفیت باک ۲۰ لیتر و بیشینه سرعت ۸۰ کیلومتر-ساعت یا یک خودروی صورتی با ظرفیت باک ۴۰ لیتر و بیشینه سرعت ۱۶۰ کیلومتر-ساعت که البته هر سه آن‌ها تمام متدهای کلاس را در خود دارند:

../_images/l05-car-class-sample.jpg ../_images/l05-car-class-object-sample.jpg

کپسوله‌سازی

یک مفهوم دیگر در برنامه‌نویسی شی‌گرا، «کپسوله‌سازی» (Encapsulation) است. کپسوله‌سازی به معنی پنهان‌سازی داده‌ها یا عملیات‌های درون یک شی است که با محدودسازی دسترسی به آن‌ها به دست می‌آید. در این شرایط اشیا باید بدون آگاهی از ساختار درونی و چگونگی انجام عملیات یکدیگر به تعامل بپردازند. این امر از پیچیدگی برنامه جلوگیری و تغییر را ساده‌تر می‌کند.

وراثت

وراثت (Inheritance) یکی از شکل‌های «قابلیت استفاده مجدد» کد بوده که برنامه‌نویس را قادر می‌سازد تا با ارث‌بری صفات و متدهای یک یا چند کلاس موجود، کلاس‌های جدیدی را ایجاد نماید.

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

به کلاسی که از آن ارث‌بری می‌شود ”Parent Class“ یا ”Base Class“ (کلاس پایه) یا ”Superclass“ و به کلاسی که اقدام به ارث‌بری می‌کند ”Child Class“ (کلاس فرزند) یا ”Derived Class“ یا ”Subclass“ گفته می‌شود.

ارث‌بری توسط «نسبت هست-یک» (IS-A Relationship) بیان می‌شود؛ این نسبت می‌گوید کلاس فرزند یک نوع از چیزی است که کلاس پایه هست. کلاس A از کلاس B ارث‌بری دارد؛ در این حالت می‌گوییم: A is a type of B، یعنی درست است اگر بگوییم: «سیب» یک نوع «میوه» است یا «خودرو» یک نوع «وسیله نقلیه» است ولی توجه داشته باشید که این یک ارتباط یک‌طرفه از کلاس فرزند به کلاس پایه است و نمی‌توانیم بگوییم: «میوه» یک نوع «سیب» است یا «وسیله نقلیه» یک نوع «خودرو» است.

کلاس‌ها می‌توانند مستقل باشند ولی هنگامی که وارد رابطه‌های وراثت می‌شوند، یک ساختار سلسله مراتب (Hierarchy) به شکل درخت را تشکیل می‌دهند. برای نمونه به ساختار سلسله مراتب وراثت پایین که مربوط به برخی اشکال هندسی است توجه نمایید، پیکان‌ها نشانگر نسبت is-a هستند.

../_images/l05-Inheritance-Hierarchy-Sample.png

ترکیب