برنامهنویسی شیءگرا (OOP) یکی از محبوبترین و پرکاربردترین روشهای طراحی نرمافزار است که در طی چند دهه گذشته بهطور گستردهای در صنعت نرمافزار استفاده شده است. این رویکرد به برنامهنویسی، نرمافزار را به واحدهایی به نام اشیاء تقسیم میکند که هر کدام دادهها و رفتارهای خاص خود را دارند. اصول شیءگرا همچون کپسولهسازی «Encapsulation»، وراثت «Inheritance»، انتزاع «Abstraction» و چندریختی «Polymorphism» به برنامهنویسان این امکان را میدهد که کدهایی قابل استفاده مجدد، مقیاسپذیر و قابل نگهداری بنویسند.
در این مقاله، به بررسی مفاهیم اصلی برنامه نویسی شی گرا در c++، مزایا و انتقادهای آن، و مقایسه آن با دیگر پارادایمهای برنامهنویسی پرداخته میشود. این مقاله به توسعهدهندگان و علاقهمندان به یادگیری برنامهنویسی شیءگرا کمک خواهد کرد تا با درک بهتری از این روش، بتوانند نرمافزارهایی کارآمد و پایدار بسازند.
برنامه نویسی شی گرا چیست؟
برنامهنویسی شیءگرا (OOP) یک مدل برنامهنویسی کامپیوتری است که طراحی نرمافزار را حول دادهها یا اشیاء سازماندهی میکند، نه توابع و منطق. یک شیء میتواند بهعنوان یک فیلد داده تعریف شود که ویژگیها و رفتارهای منحصر بهفردی دارد.
OOP بر روی اشیائی تمرکز دارد که توسعهدهندگان میخواهند آنها را دستکاری کنند، نه منطق مورد نیاز برای دستکاری آنها. این رویکرد به برنامهنویسی برای نرمافزارهایی که بزرگ، پیچیده و بهطور فعال بروزرسانی یا نگهداری میشوند، مناسب است. این شامل برنامههای مربوط به تولید و طراحی، همچنین اپلیکیشنهای موبایل میشود. بهعنوان مثال، OOP میتواند برای نرمافزار شبیهسازی سیستمهای تولید استفاده شود.
سازماندهی یک برنامه شیءگرا همچنین این روش را برای توسعه گروهی مفید میسازد، جایی که پروژهها به گروهها تقسیم میشوند. مزایای اضافی OOP شامل قابلیت استفاده مجدد از کد، مقیاسپذیری و کارایی است.
اولین گام در OOP جمعآوری تمام اشیائی است که یک برنامهنویس میخواهد آنها را دستکاری کند و شناسایی نحوه ارتباط آنها با یکدیگر — این تمرین بهعنوان مدلسازی دادهها شناخته میشود.
مثالهایی از یک شیء میتواند از موجودات فیزیکی، مانند یک انسان که با ویژگیهایی مانند نام و آدرس توصیف میشود، تا برنامههای کوچک کامپیوتری، مانند ویجتها، متفاوت باشد.
پس از شناسایی یک شیء، آن به یک کلاس از اشیاء نسبت داده میشود که نوع دادههایی که در آن قرار دارد و هر توالی منطقی که میتواند آن را دستکاری کند، تعریف میکند. هر توالی منطقی متمایز بهعنوان یک روش شناخته میشود. اشیاء میتوانند از طریق رابطهای تعریفشده بهخوبی که پیام نامیده میشوند با یکدیگر ارتباط برقرار کنند.
ساختار برنامه نویسی شی گرا چیست؟
ساختار یا بلوکهای سازنده برنامهنویسی شیءگرا شامل موارد زیر است:
- کلاسها «class» انواع دادهای تعریفشده توسط کاربر هستند که بهعنوان الگو برای اشیاء، ویژگیها و روشها عمل میکنند.
- اشیاء «Object» نمونههایی از یک کلاس هستند که با دادههای خاص تعریفشده ایجاد میشوند. اشیاء میتوانند به اشیاء دنیای واقعی یا یک موجودیت انتزاعی مربوط باشند. زمانی که کلاس ابتدا تعریف میشود، فقط توصیف آن شیء تعریفشده است.
- متدها «Metod» توابعی هستند که اشیاء میتوانند آنها را اجرا کنند. آنها درون یک کلاس تعریف میشوند که رفتارهای یک شیء را توصیف میکنند. هر روش موجود در تعاریف کلاس با ارجاعی به یک شیء نمونه شروع میشود. علاوه بر این، زیرروتینهای موجود در یک شیء بهعنوان متدهای نمونه شناخته میشوند. برنامهنویسان از متدها برای استفاده مجدد یا نگهداشتن عملکرد درون یک شیء بهصورت محصور استفاده میکنند.
- ویژگیها وضعیت یک شیء را نشان میدهند. به عبارت دیگر، آنها ویژگیهایی هستند که کلاسها را از یکدیگر متمایز میکنند. اشیاء دادههایی را در فیلد ویژگیها ذخیره میکنند. ویژگیهای کلاس متعلق به خود کلاس هستند و در الگوی کلاس تعریف میشوند.
برنامه نویسی شی گرا در c++
شیگرایی در C++ یکی از مفاهیم بنیادین این زبان برنامهنویسی است که با استفاده از اشیاء، به مدلسازی دقیقتر و ملموستر مسائل دنیای واقعی کمک میکند. برخلاف سبک برنامهنویسی رویهای که بیشتر بر نوشتن توابع برای پردازش دادهها تمرکز دارد، شیگرایی در C++ بر ایجاد ساختارهایی به نام شیء تأکید میکند که شامل هر دو بخش داده (ویژگیها) و توابع (رفتارها) هستند.
در شیگرایی C++، هر شیء دارای دو بخش اصلی است:
- ویژگیها «attributes» مانند: برند، مدل، اندازه، میزان مصرف سوخت
- رفتارها «behavior» مانند: رانندگی، شتابگیری، پارک کردن
برای مثال، اگر بخواهیم یک خودرو را با استفاده از شیگرایی در C++ مدلسازی کنیم، میتوانیم آن را به صورت یک کلاس تعریف کرده و خصوصیات و رفتارهای آن را به صورت اعضای داده و توابع عضو در نظر بگیریم.
کلاس در ++C
کلاس یک نقشهی اولیه (blueprint) برای شیء است. میتوان کلاس را مانند طرح فنی (نمونه اولیه) یک خودرو در نظر گرفت. این طرح شامل تمام جزئیات مربوط به برند، مدل، میزان مصرف سوخت و غیره است. سپس میتوان خودروهای مختلفی را بر اساس این توصیفات ساخت. در اینجا، هر خودروی متمایز یک شیء محسوب میشود.
مثالی برای این موضوع میتواند به صورت زیر باشد:
class Car { public: // class data string brand, model; int mileage = 0; // class function void drive(int distance) { mileage += distance; } }
در کد بالا، از کلیدواژهی class برای ایجاد کلاسی به نام Car استفاده کردهایم. در اینجا:
- brand و model ویژگیهای کلاس هستند که برای ذخیرهسازی داده به کار میروند.
- تابع ()drive یک تابع عضو کلاس است که برای انجام عملیاتی خاص استفاده میشود.
- کلیدواژهی public یک تعیینکنندهی سطح دسترسی (access modifier) است.
اشیاء در ++C
یک شیء، نمونهای از یک کلاس است.
برای مثال، کلاس Car مدل، برند و میزان مصرف سوخت را تعریف میکند. حال، بر اساس این تعریف، میتوانیم اشیائی مانند موارد زیر ایجاد کنیم:
Car suv; Car sedan; Car van;
در اینجا، suv، sedan و van اشیائی از کلاس Car هستند. بنابراین، نحو پایه برای ایجاد اشیاء به صورت زیر است:
Class_Name object_name;
مثال
#include <iostream> using namespace std; class Car { public: // class data string brand, model; int mileage = 0; // class function to drive the car void drive(int distance) { mileage += distance; } // class function to print variables void show_data() { cout << "Brand: " << brand << endl; cout << "Model: " << model << endl; cout << "Distance driven: " << mileage << " miles" << endl; } }; int main() { // create an object of Car class Car my_car; // initialize variables of my_car my_car.brand = "Honda"; my_car.model = "Accord"; my_car.drive(50); // display object variables my_car.show_data(); return 0; }
خروجی
Brand: Honda Model: Accord Distance driven: 50 miles
در این برنامه، کلاسی به نام Car با اعضای داده «data members» و یک تابع عضو «member function» ایجاد کردهایم. همچنین، شیئی به نام my_car از کلاس Car ساختهایم.
my_car.brand = "Honda"; my_car.model = "Accord"; my_car.drive(50); my_car.show_data();
توجه داشته باشید که برای دسترسی به اعضای کلاس، از عملگر نقطهای (.) همراه با شیء my_car استفاده کردهایم.
اصول پایه ای برنامه نویسی شی گرا در c++
اصول بنیادی برنامه نویسی شی گرا در c++ عبارتاند از:
- کپسوله سازی «Encapsulation»: گروهبندی دادهها و توابع مرتبط در یک موجودیت واحد.
- انتزاع «Abstraction»: نمایش فقط ویژگیهای ضروری یک کلاس و پنهانسازی جزئیات فنی از کاربر.
- وراثت «Inheritance»: استفاده از ویژگیهای یک کلاس موجود در یک کلاس جدید، بدون نیاز به تغییر کلاس اصلی.
- چندریختی «Polymorphism»: توانایی یک موجودیت (تابع یا عملگر) برای رفتار متفاوت در شرایط مختلف.
بیایید این اصول را با جزئیات بیشتری بررسی کنیم.
۱- کپسوله سازی در ++C
برنامه نویسی شی گرا در c++ به ما اجازه میدهد که اعضای داده (مانند متغیرها، آرایهها و غیره) و توابع مرتبط با آنها را در یک موجودیت واحد قرار دهیم. این ویژگی در برنامهنویسی، کپسولهسازی «Encapsulation» نامیده میشود.
در مثال ۱، ما متغیرهای مرتبط مانند brand، model و mileage را به همراه تابع ()show_data در یک کلاس به نام Car کپسوله کردیم.
class Car { public: // class data string brand; string model; int mileage = 0; // class function void show_data() { // code } }
کپسولهسازی تضمین میکند که تنها توابع عضو یک کلاس میتوانند به دادههای آن دسترسی داشته باشند که این امر منجر به پنهانسازی دادهها میشود.
در ++C، ما با استفاده از کلیدواژههای private و protected دادهها را پنهان میکنیم. در مقابل، با استفاده از کلیدواژهی public برای اعضای خاص کلاس، آن اعضا قابل دسترسی به تمامی توابع و کلاسهای دیگر خواهند بود.
۲- انتزاع در ++C
در برنامه نویسی شی گرا در c++، انتزاع «Abstraction» به مفهومی اشاره دارد که فقط اطلاعات ضروری به کاربر نمایش داده میشود، یعنی جزئیات پیچیده پیادهسازی و اجرای برنامه از کاربر پنهان میماند.
برای مثال، بیایید نسخهای کمی تغییر یافته از کلاس Car را در نظر بگیریم:
class Car { private: // class data int speed; // class function void show_car_status() { // code } }
فرض کنید که میخواهیم تابع ()show_car_status وضعیت خودرو را بر اساس مقدار متغیر speed نمایش دهد.
ما میتوانیم چنین دستورات شرطی را با استفاده از دستور if…else داخل تابع ()show_car_status پیادهسازی کنیم.
void show_car_status() { if (speed != 0) cout << "The car is being driven." << endl; else cout << "The car is stationary." << endl; }
در این مثال، نیازی نیست که تمام کدهایی که برای تعیین اینکه خودرو متوقف است یا خیر نوشتهایم را به کاربر نشان دهیم؛ ما فقط باید به آنها نشان دهیم که آیا خودرو در حال حرکت است یا خیر، بسته به سرعت آن.
به عبارت دیگر، ما تنها اطلاعات مفید و مرتبط را به کاربر میدهیم و تمامی جزئیات غیرضروری را پنهان میکنیم.
این همان انتزاع دادهها در برنامهنویسی شیگرا است.
نکته: انتزاع همانند پنهانسازی دادهها نیست. انتزاع نشان دادن فقط اطلاعات مرتبط است، در حالی که پنهانسازی دادهها دسترسی به اعضای داده (متغیرها، آرایهها، ساختارها و غیره) را محدود میکند بهطوری که نتوان آنها را از خارج کلاس دسترسی داشت.
۳- وراثت در ++C
وراثت «Inheritance» در ++C به ما این امکان را میدهد که یک کلاس جدید (کلاس مشتقشده) را از یک کلاس موجود (کلاس پایه) ایجاد کنیم.
کلاس مشتقشده ویژگیهایی از کلاس پایه به ارث میبرد و میتواند ویژگیهای اضافی خود را نیز داشته باشد.
مثال استفاده از وراثت در ++C
#include <iostream> using namespace std; // base class class Vehicle { public: string brand; void show_brand() { cout << "Brand: " << brand << endl; } }; // derived class class Car : public Vehicle { public: string model; void show_model() { cout << "Model: " << model << endl; } }; int main() { // create an object of Car class Car my_car; // initialize variables of my_car my_car.brand = "Honda"; my_car.model = "Accord"; // display variables of my_car my_car.show_brand(); my_car.show_model(); return 0; }
خروجی
Brand: Honda Model: Accord
در مثال بالا، Vehicle کلاس پایه است. Car کلاس مشتقشده است.
کلاس مشتقشده ویژگیهای کلاس پایه را به ارث میبرد. این موضوع را میتوانیم از متغیر brand و تابع ()show_brand مشاهده کنیم، زیرا شیء Car به نام my_car میتواند به آنها دسترسی داشته باشد.
علاوه بر ویژگیهای کلاس پایه، کلاس مشتقشده ویژگیهای خود را نیز دارد. ویژگیهای منحصر به فرد کلاس Car عبارتند از:
- model – یک متغیر رشتهای
- ()show_model – یک تابع که متغیر model را چاپ میکند.
- همچنین میتوانیم ببینیم که کلاس Vehicle توسط کلاس مشتقشده تغییر نکرده است.
۴- چندریختی در ++C
چندریختی «Polymorphism» توانایی استفاده از یک تابع (یا عملگر) مشترک به روشهای مختلف است.
در ++C، چندریختی با استفاده از بارگذاری توابع «function overloading»، بارگذاری عملگرها «operator overloading»، بازنویسی توابع «function overriding» و توابع مجازی «virtual functions» پیادهسازی میشود.
بیایید بازنویسی توابع را به عنوان یک مثال بررسی کنیم.
#include <iostream> using namespace std; // base class class Shape { public: // function of base class void shape_name() { cout << "Shape" << endl; } }; // derived class class Square : public Shape { public: // overriding function of derived class void shape_name() { cout << "Square" << endl; } }; int main() { // create class objects Shape shape; Square square; // call function from Shape class shape.shape_name(); // call function from Square class square.shape_name(); return 0; }
خروجی
Shape Square
در مثال بالا، Shape کلاس پایه است. Square کلاس مشتقشده است.
- هر دو کلاس یک تابع به نام ()shape_name دارند، اما بدنه تابع ()shape_name در هر یک از این دو کلاس متفاوت است.
- زمانی که تابع ()shape_name توسط شیء shape فراخوانی میشود، کد داخل تابع کلاس Shape اجرا میشود.
- اما زمانی که ()shape_name توسط شیء square فراخوانی میشود، کد داخل بدنه کلاس Square اجرا میشود.
بنابراین، ما از همان تابع ()shape_name به روشهای مختلف در دو کلاس مختلف با استفاده از چندریختی در ++C استفاده کردهایم.
مزایای برنامه نویسی شی گرا
مزایای برنامهنویسی شیءگرا (OOP) عبارتند از:
- مدولار بودن: کپسولهسازی این امکان را میدهد که اشیاء خودکفا باشند، که این امر عیبیابی و توسعه گروهی را راحتتر میکند.
- قابلیت استفاده مجدد «Reusability»: کد از طریق وراثت قابل استفاده مجدد است، به این معنی که یک تیم نیازی به نوشتن کد مشابه چندین بار ندارد.
- افزایش بهرهوری «Productivity»: برنامهنویسان میتوانند با استفاده از کتابخانههای مختلف و کدهای قابل استفاده مجدد، برنامههای جدید را سریعتر بسازند.
- قابلیت ارتقا و مقیاسپذیری آسان: برنامهنویسان میتوانند عملکردهای سیستم را بهطور مستقل پیادهسازی کنند.
- توصیف رابطها: توصیف سیستمهای خارجی ساده است، بهدلیل تکنیکهای ارسال پیام که برای ارتباط اشیاء استفاده میشود.
- امنیت: با استفاده از کپسولهسازی و انتزاع، کد پیچیده پنهان میشود، نگهداری نرمافزار راحتتر میشود و پروتکلهای اینترنتی محافظت میشوند.
- انعطافپذیری: چندریختی این امکان را میدهد که یک تابع با توجه به کلاسی که در آن قرار دارد، سازگار شود. اشیاء مختلف همچنین میتوانند از طریق یک رابط مشترک عبور کنند.
- نگهداری کد: بخشهایی از یک سیستم میتوانند بهروزرسانی و نگهداری شوند بدون اینکه نیاز به تغییرات عمده باشد.
- هزینه کمتر: مزایای دیگر مانند نگهداری و استفاده مجدد از کد، هزینههای توسعه را کاهش میدهند.
انتقادها از برنامه نویسی شی گرا
توسعهدهندگان به مدل برنامهنویسی شیءگرا به دلایل مختلف انتقاد کردهاند. بزرگترین نگرانی این است که OOP بر روی مؤلفه داده در توسعه نرمافزار بیش از حد تأکید میکند و به اندازه کافی بر روی محاسبات یا الگوریتمها تمرکز ندارد. علاوه بر این، کد OOP ممکن است نوشتن آن پیچیدهتر باشد و زمان بیشتری برای کامپایل نیاز داشته باشد.
انتقادهای رایج دیگر شامل این است که وراثت با معایبی همراه است، مانند کلاسهای پایه شکننده. علاوه بر این، اشیاء گاهی اوقات در حالت ایزوله واضحتر هستند، اما زمانی که در برنامه واقعی عمل میکنند، درک آنها دشوارتر است.
روش های جایگزین برای شی گرایی
روشهای جایگزین برای OOP عبارتند از:
- برنامهنویسی تابعی: این شامل زبانهایی مانند ارلانگ «Erlang» و اسکالا «Scala» است که برای سیستمهای ارتباطی و سیستمهای مقاوم در برابر خطا استفاده میشوند.
- برنامهنویسی ساختاری یا مدولار: این شامل زبانهایی مانند پیاچپی (PHP) و سیشارپ (C#) است.
- برنامهنویسی دستوری: این جایگزین برای OOP بر روی تابع تمرکز دارد نه مدلها. زبانهای برنامهنویسی دستوری شامل C++ و جاوا «Java» هستند.
- برنامهنویسی اعلامی: این روش برنامهنویسی شامل بیانیههایی است که بیان میکنند وظیفه یا نتیجه مطلوب چیست، اما مشخص نمیکنند چگونه به آن دست یابیم. زبانهای برنامهنویسی اعلامی شامل پرولاگ «Prolog» و لیسپ «Lisp» هستند.
- برنامهنویسی منطقی: این روش، که عمدتاً بر اساس منطق رسمی است و از زبانهایی مانند پرولاگ «Prolog» استفاده میکند، مجموعهای از جملات را بیان میکند که واقعیتها یا قوانین مربوط به دامنه مشکل را توضیح میدهند. این روش بر روی وظایفی متمرکز است که میتوانند از پرسوجوهای منطقی مبتنی بر قواعد بهرهمند شوند.
بیشتر زبانهای برنامهنویسی پیشرفته به توسعهدهندگان این امکان را میدهند که مدلها را ترکیب کنند، زیرا میتوانند برای روشهای مختلف برنامهنویسی استفاده شوند. بهعنوان مثال، جاوااسکریپت «JavaScript» و اسکالا «Scala» میتوانند برای برنامهنویسی شیءگرا و برنامهنویسی تابعی استفاده شوند.
نتیجه گیری
در نتیجه، برنامه نویسی شی گرا در C++ بهعنوان یکی از الگوهای قدرتمند و پرکاربرد در توسعه نرمافزار شناخته میشود. این رویکرد با تکیه بر مفاهیم اساسی مانند کپسولهسازی، وراثت، انتزاع و چندریختی، امکان طراحی و پیادهسازی برنامههایی مدولار، قابل نگهداری، قابل استفاده مجدد و مقیاسپذیر را در اختیار برنامهنویسان قرار میدهد.
شی گرایی در C++ به توسعهدهندگان کمک میکند تا ساختاری انعطافپذیر و سازمانیافته برای پروژههای نرمافزاری خود ایجاد کرده و پیچیدگی سیستمها را بهتر مدیریت کنند. با این حال، مانند هر پارادایم دیگری، برنامهنویسی شی گرا در C++ نیز میتواند با چالشهایی همراه باشد؛ از جمله پیچیدگی بیشتر در طراحی کد و زمان بیشتر برای کامپایل نسبت به روشهای سادهتر.
در نهایت، انتخاب میان شی گرایی در C++ و سایر پارادایمهای برنامهنویسی به نیازهای خاص پروژه، مقیاس نرمافزار و اهداف کلی توسعه بستگی دارد. بنابراین، آشنایی عمیق با مفاهیم و مزایای برنامهنویسی شیگرا در C++ میتواند نقش کلیدی در موفقیت پروژهها و حل مسائل پیچیده ایفا کند.