
برنامه های امروزی، برنامه های پیچیده ای هستند که اطلاعات را به صورت گرافیکی به کاربران نشان می دهند. برنامه ها در پشت پرده از روابط پیچیده ای همچون روابط مابین مشتری ها، کالاها و سفارش ها تشکیل شده اند. برای مدیریت این پیچیدگی عظیم، تکنیکی به نام برنامه نویسی شئ گرا توسعه داده شد. که در آن با مدل کردن جنبه های ضروری سیستم، از پیچیدگی سیستم کاسته می شود. برنامه نویسی شئ گرا یا Object Oriented Programming که به اختصار OOP شناخته می شود، روشی نوین برای توسعه ی برنامه هاست.
تا قبل از ظهور برنامه نویسی شئ گرا، برنامه ها به شکل Procedural یا رویه ای نوشته می شدند. برنامه نویسی رویه ای مشکلات زیادی به همراه داشت که در ادامه به صورت خلاصه تعدادی از آنها را ذکر می کنیم:
- در برنامه نویسی رویه ای، برنامه ها به توابع تقسیم می شدند.
- با بزرگ شدن نرمافزار، برنامه نویسان مجبور به تکرار کدها می شدند.
- توابع به صورت نامرتب و تو در تو یکدیگر را صـدا می زدند، و با تغییر در یک قسمت برنامه، برنامه نویس مجبور به تغییر کدها در قسمت های دیگر برنامه می شد.
- برنامه ها در برنامه نویسی رویه ای تبدیل به کد اسپاگتی می شدند.
کد اسپاگتی
کد اسپاگتی، به کدی اطلاق می شود که در آن توابع و قطعات کد به هم وابسته و چسبیده اند و ایجاد تغییر در یک تابع باعث به هم ریختن سایر قسمت های برنامه می شود. در شکل زیر یک نمونه کد اسپاگتی آورده شده است.

هدف برنامه نویسی شئ گرا
هدف از برنامه نویسی شئ گرا، افزایش قابلیت توسعه پذیری سیـستم و کارآمد شدن آنهاست. همچنین نگهداری و تغییر کد ها نیز آسانتر می شود.
مدل سازی
مدل ها برای مدیریت پیچیدگی و کمک به فهم مسائل برای حل آنها ایجاد می شوند. برای مثال نقشه ها مدل هایی از جاده ها هستند. گوی ها مدل هایی از زمین هستند. در اصل مدل ها نوعی ساده سازی به شمار می آیند.

کلاس ها و اشیاء
دنیا ترکیبی از اشیاء است. این اشیاء از یکدیگر متمایز هستند. انسان بدون آنکه متوجه شود خیلی از این اشیاء را در گروه هایی دسته بندی می کند. برای مثال فرض کنید که دو مداد رنگی به رنگ های قرمز و سبز دارید. هر دو مداد رنگی جزء دسته ی مدادرنگی هستند. یعنی مداد رنگی های قرمز و سبز نمونه ای از دسته بندی مداد رنگی هستند. پس می توان گفت مداد رنگی های قرمز و سبز هر کدام یک شیء یا Object هستند و گروه مدادرنگی یک Class است. هر Object نمونه ای از یک Class است.
در OOP ، توابـع و متـغیر های مرتبط در واحد هایی به اسم Class تجمـیع می شوند. در ادامه به معرفی تک تک مفاهـیم شئ گرایی می پردازیم.
مفاهیم شئ گرایی
در شئ گرایی با چهار مفهوم اصلی سروکار داریم:
- Object
- Property
- Method
- Class
Object
یک نمونه از کلاس است که دارای هویت بوده و قادر به بروز رفتار و ثبت حالت است. و موجودیت ها به صورت شئ یا object تعریف می شوند. برای مثال یک آهو نمونه ای از دسته بندی حیوانات (کلاس حیوان) است که گونه ای منحصر به فرد است و قادر به بروز رفتار است.

Property یا Attribute
هر شئ یک سری خصوصیات دارد که به آن صفت می گویند. به عنوان مثال هر مداد رنگی دارای خصوصیاتی نظیر رنگ، طول، قطر و … است.

Method
هر شئ یک سری رفتار دارد که به آن متد می گویند. به عنوان مثال، در کلاسی که مربوط به تعامل با دیتابیس است رفتار هایی نظیر Delete, Update, Create و … را Method می گویند.

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

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

در شکل بالا، کلاسی به نام Human وجود دارد که دارای Attribute ها و Method هایی است. نمونه ای از روی این کلاس ساخته می شود که به عنوان مثال نامش اصغر، سنش 64 سال و ایمیلش asghar@mail.com است. همچنین متد Walk باعث می شود که اصغر راه برود و متد Talk باعث سخن گفتن اصغر می شود.
تعریف کلاس در #C
کلاس در #C با کلمه ی کلیدی Class شناخته می شود. و بدنه ی کلاس داخل {} قرار می گیرد. در بدنه ی کلاس Method ها و Attribute ها قرار می گیرند. در مثال زیر کد کلاس اتومبیل با متغیری به نام رنگ را می بینید.
1 2 3 4 |
class car { string color = "red"; } |
در مثال زیر، خط اول طریقه ی ساخت یک Object از روی کلاس را نشان می دهد. و در خط بعد می خواهد مقدار attribute ای به نام Color از Object ساخته شده را در خروجی چاپ کند.
1 2 |
car obj = new car(); Console.WriteLine(obj.color); |
خروجی به شکل زیر خواهد بود:
red
در مثال زیر، کد کلاس با دو متغیر و یک متد آورده شده است.
1 2 3 4 5 6 7 8 9 10 11 12 |
class MyClass { //class members string color = "red"; int maxSpeed = 200; //method public void fullThrotle() { Console.WriteLine("The car is going as fast as it can!"); } } |
Constructor یا سازنده ی کلاس
کلاس ها می توانند دارای Constructor (متد سازنده) باشند. سازنده متدی است که در هنگام ساخت یک نمونه شئ از یک کلاس، اجرا می شود. متد سازنده باید همنام کلاس باشد. مثال زیر کلاسی را نشان می دهد که Constructor دارد:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class Car { public string model; public string color; public int year; //create a class constructor with multiple parameters public Car(string modelName,string modelColor,int modelYear) { model = modelName; color = modelColor; year = modelYear; } } // in main static void Main(string[] args) { Car peugeot = new Car("405","red",2003); Console.WriteLine(peugeot.model + " " + peugeot.color + " " + peugeot.year); } |
اگر دقت کنید در کلاس Car متدی به نام Car وجود دارد، پس آن متد، متد Constructor است. یکی از مزایای استفاده از Constructor خلاصه تر شدن کد هاست. به مثالهای زیر توجه کنید، اولین مثال، چگونگی استفاده از کلاس بدون Constructor را نشان می دهد و دومین مثال استفاده از کلاس با Constructor را نشان می دهد. .
مثال اول استفاده از کلاس بدون Constructor
1 2 3 4 5 6 7 8 9 |
static void Main(string[] args) { Car peugeot = new Car(); peugeot.model = "405"; peugeot.color = "blue"; peugeot.year = 2003; Console.WriteLine(peugeot.model + " " + peugeot.color + " " + peugeot.year); } |
مثال دوم استفاده از کلاس با Constructor
1 2 3 4 5 |
static void Main(string[] args) { Car peugeot = new Car("405", "red", 2003); Console.WriteLine(peugeot.model + " " + peugeot.color + " " + peugeot.year); } |
همانطور که پیداست استفاده از کلاسی که Constructor دارد به مراتب بهینه تر و خلاصه تر از کلاس ساده است.
ارکان چهارگانه برنامه نویسی شئ گرا
- Abstraction (انتزاع)
Abstract Class
تمرکز بر نمایش موجودیت های ضروری دارد و نه تمام موجودیت ها. انتزاع می تواند با Abstract Class و یا Interface حاصل شود. به عنوان مثال برای روشن کردن تلویزیون تنها کافی ست که دکمه ی On روی ریموت کنترل را فشار دهیم. نیازی نیست که بدانیم امواج Infrared دقیقا چه کاری را انجام می دهند تا تلویزیون روشن شود.
کلاس Abstract همانند کلاس معمولی است با این سه تفاوت:
- با کلمه ی کلیدی Abstract قبل از واژه ی Class شناخته می شود.
- متد هایش می توانند بدنه داشته یا نداشته باشند. متد هایی که بدنه ندارند با عبارت کلیدی Abstract قبل از نام متد شناخته می شوند.
- نمی توان از روی آنها نمونه ساخت.
مثال زیر یک کلاس Abstract را نشان می دهد که دو متد دارد، یکی با بدنه و دیگری بدون بدنه
1 2 3 4 5 6 7 8 |
abstract class Animal { public abstract void AnimalSound(); public void Sleep() { Console.WriteLine("Zzz"); } } |
همانطور که اشاره شد نمی توان از روی آن نمونه ساخت.در عوض می توان از آن ارث بری کرد. (در مورد وراثت بعدا توضیح داده خواهد شد.)
1 2 3 4 5 6 7 8 9 |
//Derived class (inherit from Animal) class Pig : Animal { public override void AnimalSound() { //The body of animalSound() is provided here. Console.WriteLine("the Pig says: wee wee"); } } |
قطعه کد زیر نحوه ی استفاده از کلاس مشتق شده از کلاس Abstract را نشان می دهد.
1 2 3 4 5 6 7 8 9 |
class Program { static void Main(string[] args) { Pig myPig = new Pig(); //create a pig object myPig.AnimalSound(); //call teh abstract method myPig.Sleep(); //call the regular method } } |
اما انتزاع چه کاربردی دارد؟ با وجود کلاس معمولی چه نیازی به استفاده از کلاس Abstract وجود دارد؟ در اینجا ذکر یک مثال واقعی به درک مسئله کمک می کند. فرض کنید که افراد های درون یک دانشگاه به سه دسته تقسیم می شوند. دانشجویان، اساتید و کارمندان. این سه دسته بندی را می توان سه کلاس در نظر گرفت که خود از کلاس والدی به نام کلاس انسان ساخته شده اند. به عبارت دیگر سه دسته بندی وجود دارد. انسان هایی که دانشجو هستند، انسان هایی که استاد هستند و انسان هایی که کارمند هستند. یک فرد در دانشگاه نمی تواند فقط انسان باشد، حتما باید جزء یکی از این دسته بندی ها باشد. پس نیازی به ساخت یک نمونه از کلاس انسان نیست. در اینجا بهتر است که کلاس انسان از جنس Abstract Class باشد. (چون تنها تمرکز بر اطلاعات ضروری دارد و نه تمام اطلاعات)
Interface
راه دیگری برای رسیدن به Abstraction وجود دارد و آن اینترفیس است. اینترفیس همان Abstract Class است با این تفاوت که متدهایش بدنه ندارند و پیاده سازی نشده اند در عوض پیاده سازی آن متد ها به کلاسی واگذار می شود که آن اینترفیس را پیاده سازی می کند.
طبق قرارداد، ابتدای نام اینترفیس با حرف I شروع می شود. کلاسی که اینترفیس را پیاده سازی می کند باید تمام متد هایش را نیز پیاده سازی کند. بر خلاف کلاس، تمام اعضاء اینترفیس به صورت پیش فرض Public هستند. یکی از کاربرد های اینترفیس برای زمانی است که می خواهیم کلاینت را ملزم به پیاده سازی متد های معینی کنیم.
در مثال زیر اینترفیس IAnimal آورده شده است.
1 2 3 4 5 |
interface IAnimal { void animalSound(); void Run(); } |
با دو نقطه می توان تعیین کرد که یک کلاس، کدام اینترفیس را پیاده سازی کند:
1 2 3 4 5 6 7 8 9 10 11 12 |
class Pig : IAnimal { public void animalSound() { Console.WriteLine("The Pig says: wee wee"); } public void Run() { Console.WriteLine("The Pig runs."); } } |
در C# یکی دیگر از تفاوت های اینترفیس با کلاس در این است که تنها از یک کلاس می توان ارث بری کرد ولی همزمان می توان چندین اینترفیس را پیاده سازی کرد.
1 2 3 4 5 6 7 8 9 10 11 12 |
public class myClass : IFristInterface, ISecondInterface { public void myFirstMethod() { Console.WriteLine("my first method is invoked"); } public void mySecondMethod() { Console.WriteLine("my second method is invoked"); } } |
2. Encapsulation (کپسوله سازی)
با محصور کردن دیتا و متدها در یک بسته منطقی، از دسترسی به جزئیات پیاده سازی جلوگیری به عمل می آورد. با استفاده از Access Modifiers یا سطوح دسترسی، کپسوله سازی حاصل می شود. به سطح مشخصی از مجوز برای دسترسی به خصوصیات و متد ها، سطوح دسترسی می گویند. انواع سطوح دسترسی:
- Public
متد ها و صفات در تمام کلاس ها قابل دسترسی هستند. کد زیر نشان می دهد که در داخل کلاس Program می توان به صفت Model کلاس Car دسترسی داشت چون به صورت Public تعریف شده است.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Car { public string Model = "Peykan"; } class Program { static void Main(string[] args) { Car myCar = new Car(); Console.WriteLine(myCar.Model); } } |
- Private
متد ها و صفات فقط در کلاسی که در آن تعریف می شوند قابل دسترسی هستند. شایان ذکر است که تمام اعضاء یک کلاس به صورت پیش فرض Private هستند. به مثال زیر دقت کنید.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class Car { private string Model = "Peykan"; } class Program { static void Main(string[] args) { Car myCar = new Car(); Console.WriteLine(myCar.Model); } } |
این کد با خطا مواجه می شود. چون صفت Model در کلاس Car به صورت Private تعریف شده است و در کلاس های دیگر امکان استفاده از آن وجود ندارد.
- Protected
متدها و صفات فقط در کلاسی که در آن تعریف می شوند و کلاسی که از آنها مشتق می شود قابل دسترسی هستند.
- Internal
متدها و صفات در تمام کلاس های داخل یک فضای نام یا Assembly قابل دسترسی هستند.
- Protected Internal
ترکیبی از protected و internal است. متدها و صفات تنها در همان اسمبلی یا فضای نام قابل دسترسی هستند و همزمان محدود به خود آن کلاس و کلاس های مشتق شده از آن هستند.
3. Inheritance (وراثت)
کلاس ها می توانند صفات و متد های یکدیگر را به ارث ببرند. این ویژگی می تواند به کاهش زمان توسعه و بهینه تر شدن سیستم کمک کند. به کد زیر دقت کنید، کلاس Car از کلاس Vehicle مشتق شده است یعنی صفات و متدهای آن را به ارث برده است.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// base class (parent) class Vehicle { public string brand = "Ford"; //vehicle field public void honk() //vehicle method { Console.WriteLine("Beep Beep"); } } //Derived class (child) class Car { private string ModelName = "Mustang"; //car field } |
4. Polymorphism (چندریختی)
یک کلاس می تواند چندین ریخت و شکل داشته باشد. در لغت کلمه ی Polymorphism خود از دو کلمه ی یونانی تشکیل شده است، Poly به معنای چند و Morph به معنای شکل و شمایل است. و در شئ گرایی به آن معناست که یک کلاس باید بتواند چندین شکل داشته باشد. مثال دانشگاه را به یاد دارید؟ این بار فرض کنید متدی به نام کارکردن در کلاس انسان وجود دارد، کلاس های دانشجویان، استاتید و کارمندان هر کدام از کلاس انسان مشتق شده اند اما هر کدام متد کارکردن را به شیوه ی خودشان پیاده کرده اند.
در ادامه دو مثال زده خواهد شد که در یکی Polymorphism وجود دارد و در دیگری وجود ندارد. مثال اول بدون Polymorphism است.
کلاس Dog از کلاس Animal مشتق شده است. در کلاس Animal یک متد به نام AnimalSound وجود دارد که متنی را در خروجی چاپ می کند. کلاس مشتق شده همان متد را دوباره تعریف می کند و می خواهد به شیوه ی خودش آن را پیاده سازی کند.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class Animal // base class (parent) { public void animalSound() { Console.WriteLine("The Animal makes a sound."); } } class Dog : Animal // derived class (child) { public void animalSound() { Console.WriteLine("The dog say: bow wow"); } } |
نحوه ی استفاده از آن:
1 2 3 4 5 6 7 8 9 10 11 |
class Program { static void Main(string[] args) { Animal myAnimal = new Animal(); Animal myDog = new Dog(); myAnimal.animalSound(); myDog.animalSound(); } } |
اما در کمال تعجب می بینیم که با فراخوانی متد کلاس فرزند، متد کلاس پدر اجرا شده است.
The animal makes a sound
The animal makes a sound
چرا؟ در مثال زیر با استفاده از Polymorphism این مشکل را حل می کنیم. تنها کافی است که متدی را که می دانیم کلاس فرزند قرار است آن را جور دیگری پیاده سازی کند به صورت Virtual تعریف کنیم و در کلاس فرزند آن متد را به صورت Override تعریف کنیم. به مثال زیر دقت کنید:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class Animal // base class (parent) { public virtual void animalSound() { Console.WriteLine("The Animal makes a sound."); } } class Dog : Animal // derived class (child) { public override void animalSound() { Console.WriteLine("The dog say: bow wow"); } } |
نحوه ی استفاده از آن همانند مثال قبل است و با اجرای برنامه، خروجی زیر به نمایش درخواهد آمد:
The animal makes a sound
The dog says: bow wow