برنامه نویسی شی گرا چیست ؟

banner

یکی از محبوب‌ترین، پرکاربردترین و قدرتمندترین روش های برنامه‌نویسی در دنیای امروز، برنامه نویسی شی‌گرا یا Object Oriented Programming هست که به اختصار OOP نیز نامیده می‌شود.

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

استفاده از این روش امکان بهینه‌سازی و کم‌تر کردن حجم کدها، راحت‌تر شدن رفع باگ‌ها و مشکلات و همچنین جلوگیری از اشتباهات در حین برنامه‌نویسی را برای شما فراهم می‌کند.

چرا شی‌گرایی مهم است؟

در ابتدا برنامه‌نویس ها برای پیاده‌سازی و توسعه برنامه‌ها از روش procedural یا رویه‌گرا استفاده می‌کردند( ممکن است این الگو با الگوی functional اشتباه گرفته شود در صورتی که برنامه‌نویسی فانکشنال یک الگوی مجزا هست.).

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

به همین علت بود که الگوهایی در برنامه‌نویس به وجود آمدند که شی‌گرایی را می‌توان از مهم‌ترین ‌آن‌ها معرفی کرد.

زبان ها و فریم‌ورک های متعددی بر اساس همین مفهوم شکل گرفته‌اند، مثلا #C و ++C تقریبا همان زبان C هستند که مفهوم شی‌گرایی به آن‌ها اضافه شده است و امروزه اکثر زبان‌های برنامه نویسی این مفهوم را درون خود دارند، مثل جاوا، پایتون، جاوا اسکریپت و… .

مفهوم شی‌گرایی در حوزه‌ها و ابزارهای مختلفی مورد استفاده قرار می‌گیرد، مثلا در دنیای بازی‌سازی خیلی مورد استفاده قرار می‌گیرد و همین‌طور فریم‌ورک‌ها و ابزارهای محبوبی مانند django و nest.js با همین الگو کار می‌کنند، خلاصه در اکثر ابزار‌ها با شی‌گرایی را خواهید دید.

شی‌گرایی چه مشکلاتی را حل می‌کند؟

قبل از به روی کار آمدن برنامه‌نویسی شی‌گرا برای تعریف متغیر ها از از data type های محدودی استفاده می‌شد مثل رشته‌ها،اعداد،آرایه‌ها و… که همه با آن‌ها آشنا هستیم استفاده می‌شد ولی سوال اینجاست که اگر بخواهیم چیزی مانند یک ماشین را تعریف کنیم چه خواهیم کرد؟

به عنوان مثال ماشین‌ها ویژگی های مختلفی مانند اسم، رنگ و تعداد سرنشین و کارکردهایی مانند روشن شدن، حرکت و توقف را دارند.

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

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

هیجان‌انگیز است! ما می‌توانیم نوع داده های زیادی به دلخواه خودمان بسازیم!

کلاس (class) چیست؟

کلاس‌ها در واقع یک قالب یا به اصطلاح یک blueprint برای ساخت اشیا هستند.

در واقع به کمک کلاس، ما یم ساختار برای اشیا تعریف می‌کنیم و در طول برنامه هر جایی که نیاز به ایجاد یک نمونه شی (object) بود با استفاده از کلاس تعریف شده آن را می‌سازیم.

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

C++


    class Car {
        public:
            string brand;
            string color;
            string model;
            void honk() {
            cout << "Beep!" << endl;
            }
    };

 به این قطعه کد توجه کنید، یک کلاس با نام Car ساخته ایم که متغیر ها و توابع را درون خود دارد، متغیر‌های آن بیانگر برند، رنگ و مدل خودرو هستند و تابع honk عمل بوق زدن خودرو را انجام می‌دهد.

با استفاده از این کلاس می‌توانیم به تعداد دلخواه object ها یا اشیائی را از این نوع تعریف کنیم.

شی یا Object چیست؟

می‌رسیم به object ها یا اشیا که در واقع نمونه هایی هستند که توسط کلاس ساخته شده اند.

مثال خودرو را در نظر داشته باشید، اگر کلاسی را به این صورت تعریف کنیم که هر خودرو ویژگی های رنگ و برند و کارکرد های روشن شدن و نحوه فرمان پذیری خودرو باشد، می توانیم دو object را با آن تعریف کنیم.

خودروی اول سفید رنگ و از برند بنز هست که به صورت بدون سوییچ و با دکمه روشن می‌شود و فرمان برقی دارد از سوی دیگر خودروی دیگر مشکی رنگ و از برند فورد می‌باشد که با سویچ روشن می‌شود و فرمان هیدرولیک دارد.

هر کدام از این خودرو ها یک object از class خودرو هستند که ویژگی ها و کارکرد های مختص به خود را دارند.

ویژگی‌ها و متد ها( attributes & methods)

 همانطور که درون کلاس ها تعریف کردیم هر object از متغیر ها و توابع مختلف ساخته می‌شود که به متغیر ها ویژگی، صفت یا attribute و به توابع روال یا روش و یا method گفته میشود.

صفات، ویژگی های یک شی و متد ها، انجام فعالیت ها و محاسبات شی را بیان می کنند.

در قطغه کد مثال قبلی ویژگی های brand ،color و model وجود داشت و همچنین متدی با نام honk نیز بود.

تابع سازنده یا constructor

تابع سازنده تابع ویژه ای است که درون کلاس ها تعریف می شود و هنگام ساختن object ها بدون نیاز به فراخوانی آن ها اجرا می شود و معمولا از آن برای مقداردهی های اولیه استفاده می‌شود.

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

برنامه‌نویسی شی‌گرا چهار اصل دارد که درباره آن‌ها بحث می‌کنیم.

کپسوله‌سازی یا Encapsulation

یک قرص کپسولی را تصور کنید، در برنامه‌نویسی شی‌گرا ویژگی‌ها و متد های هر object درون خود آن نگه‌داری می‌شوند، مانند قرص های کپسولی که همه محتویات آن‌ها داخل کپسول است که به اصطلاح می‌گویند کپسوله شده است.

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

با این وجود امنیت بیش‌تری فراهم خواهد شد و می‌توانیم دسترسی به محتویات یک object را محدود کنیم و یا از دید دیگران پنهان کنیم.

سه حالت برای تعریف نحوه دسترسی به ویژگی‌ها و متد های هر شی وجود دارد: public, private و protected.

  • حالت عمومی یا public :

به متغیرها و متدهایی که حالت public دارند می‌توان بیرون از کلاس دسترسی داشت.

  • حالت خصوصی یا private :

به متغیرها و متدهایی که حالت private دارند فقط درون کلاس خودشان می‌توان دسترسی داشت و هیچ‌گونه دسترسی بیرونی مجاز نخواهد بود.

  • حالت محافظت‌شده یا protected :

به متغیرها و متدهایی که حالت protected دارند می‌توان درون کلاس خودشان و همچنین کلاس هایی که از این کلاس ارث‎‌بری کرده‌اند که به آن‌ها sub class نیز گفته می‌شود دسترسی داشت. 

C++


    class MyClass {
        public:
            int publicData;
        private:
            int privateData;
        protected:
            int protectedData;
    };

انتزاع یا Abstraction :

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

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

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

به قول خارجی ها نیاز نیست بدانیم under the hood یا زیر کاپوت چه می گذرد.

همه جزئیات از دید بقیه مخفی می شود و با استفاده از متد ها می توان بدون توجه به ریزه کاری ها و پیچیدگی ها به نتایج مورد نظر رسید.

فرض کنید که درون یک object متدی به نام find_shortest_path داریم که طبق الگوریتمی کوتاهترین مسیر بین مبدا و مقصد را محاسبه می‌کند، برای استفاده از چنین متدی نیاز نیست که کسی که از آن استفاده می‌کند بداند که این متد چگونه و با چه الگوریتم و جزئیاتی پیاده‌سازی شده است و برای یافتن کوتاه‌ترین مسیر بین مبدا و مقصد کافی است که آن متد را فراخوانی کند و تمام! همه‌ی کارهای لازم انجام خواهد شد.

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

وراثت یا Inheritance :

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

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

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

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

کلاس های کارمند و استاد در این مثال هر یک می‌توانند خصوصیات مخصوص به خودشان را نیز داشته باشند.

ارث‌بری چند نوع دارد که محل بحث ما نیست و یادگیری مفهوم ارث‌بری مهم‌تر است اما با این وجود به هر کدام اشاره مختصری می‌کنیم. 

انواع ارث‌بری:

  • ارث‌بری تک‌گانه (single inheritance) :

در این نوع که ساده‌ترین نوع ارث‌بری نیز هست، یک کلاس فرزند تنها از یک کلاس والد ارث‌بری می‌کند.

  •  ارث‌بری چندگانه (multiple inheritance) :

این نوع از ارث‌بری، کلاس فرزند می‌تواند از چند کلاس والد ارث‌بری کنند و ترکیبی از محتویات هر دو را داشته باشد.

  • ارث‌بری سلسله مراتبی (hierarchical inheritance) :

در این نوع از ارث‌بری چند کلاس فرزند از یک کلاس والد ارث‌بری می‌کنند.

  • ارث‌بری چند سطحی (multi level inheritance) :

در این نوع از ارث‌بری یک کلاس فرزند از کلاس والدی ارث‌بری می‌کند که خود آن نیز از کلاس دیگری ارث برده است، مانند رابطه‌ی بین پدربزرگ، پدر و نوه.

  • ارث‌بری ترکیبی (hybrid inheritance) :

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

نکته: در ارث‎‌بری ترکیبی در بعضی موارد ممکن است با مشکلی رو‌به‌رو شوید که به اصطلاح به آن diamond problem گفته می‌شود.

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

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

چند ریختی یا Polymorphism :

کلمه‌ی Polymorphism برگرفته از کلمات یونانی poly به معنای "چند" و morph به معنای "شکل" تشکیل شده است.

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

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

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

می‌توان پیاده‌سازی های مختلفی از یک متد را در هر کلاس داشت.

 ساختار های پیاده‌سازی مختلفی برای این اصل در زبان‌های مختلف وجود دارد و ممکن است تفاوت‌هایی را بین زبان‌های برنامه‌نویسی مختلف مشاهده کنید، اما از انواع Polymorphism می توان compile time polymorphism , run time polymorphism را نام برد.

 جمع‌بندی

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

مهم یادگیری و درک مفهوم شی‌گرایی است چون بیش‌تر از این که به نحوه‌ی نوشتن کدها مربوط باشد یک چارچوب فکری و راه‌حل برای حل مسائل است، به همین دلیل هم هست که درک مفهوم کلی آن اهمیت دارد و در نهایت این را هم باید در نظر گرفت که فکر نکنید چون شی‌گرایی الگوی خوبی هست باید تمام برنامه‌ها را با این الگو پیش برد بلکه باید نسب به پروژه و مسئله، الگو و راه‌حل مناسب را انتخاب کنید.

موفق باشد؛

علی احمدیان