پس از اینکه در درس قبلی با مفهوم کلاس و شیء آشنا شدید، در این جلسه میخواهم درباره اولین رکن از چهار رکن اصلی برنامهنویسی شیءگرا توضیحاتی بدم.
کپسولهسازی یا Encapsulation:
کپسولهسازی به معنی بستهبندی دادهها (ویژگیها) و رفتارها (متدها) توی یه
کلاس و کنترل دسترسی به اونهاست. فکر کنید مثل یه کپسول دارویی که مواد داخلش رو
محافظت میکنه و فقط از راه مشخصی (مثل خوردن) میشه بهش دسترسی داشت. یا باتری
ماشین که فقط با اتصال دو سیم میتونید ازش استفاده کنید، یا خود ماشین که با کمک
سوئیچ روشن میشه و هزاران مثال دیگه.
هدف Encapsulation در سیشارپ اینه که
دادهها رو از دسترسی یا تغییرات مستقیم و ناخواسته محافظت کنه، جزئیات داخلی
پیادهسازی رو مخفی نگه داره و دسترسی به دادهها رو فقط از طریق متدهای مشخص و
کنترلشده (مثل خواص یا متدها) فراهم کنه.
در واقع بدون کپسولهسازی، هر بخشی از کد (مثل متدها یا اشیاء دیگه) میتونه
مستقیماً به دادههای داخلی کلاس دسترسی داشته باشه و اونا رو تغییر بده. این
مسئله میتونه منجر به بروز خطاها یا رفتارهای غیرقابل پیشبینی در برنامه بشه، بهخصوص
توی پروژههای بزرگ و پیچیده که چندین توسعهدهنده روی کد کار میکنن. این اولین
قدم برای ساختن کد امن و قابل نگهداریه.
برای درک مفهوم کپسولهسازی فقط کافیه تا با مفهوم متغیرها و همچنین دو
مفهوم کلاس و شیء آشنا باشید که دو مورد آخر توی درس قبلی معرفی شدن.
چه مواقعی از کپسولهسازی در پروژه خود استفاده کنیم؟
کپسولهسازی را چگونه در پروژه خود
اعمال کنیم؟
در واقع برای این کار ابتدا از سطوح دسترسی مناسب برای جلوگیری از در دسترس
بودن برخی فیلدها و سپس استفاده از پراپرتیهای
(get/set)
برای خواندن و تغییر این فیلدها و همچنین استفاده از متدها برای تغییر یا
مشاهده قاعدهمند دادهها استفاده میکنیم. این یعنی اینکه ما دسترسی به فیلدها رو
محدود و طبق شرایط خاص مجاز کردیم.
مثال عملی: حساب بانکی
فرض کنید یه کلاس برای حساب بانکی داریم. میخواهیم موجودی حساب رو کنترل
کنیم تا مستقیم تغییر نکنه و منفی هم نشه. اینجوری کپسولهسازی پیاده سازی میشه:
public class BankAccount
{
private decimal _balance; // موجودی حساب مخفی شده
public decimal Balance // پراپرتی برای دسترسی کنترلشده
{
get { return _balance; }
set
{
if (value >= 0) // قانون: موجودی نمیتونه منفی باشه
_balance = value;
}
}
public void Deposit(decimal amount) // متد برای تغییر قاعدهمند
{
if (amount > 0)
_balance += amount;
}
}
توی این مثال، _balance با private مخفی شده و فقط از طریق پراپرتی Balance یا متد Deposit میشه بهش دسترسی داشت. اینجوری مطمئنیم که موجودی حساب طبق قوانین مشخص تغییر میکنه.
کاربرد as در سی شارپ :
روش صحیح استفاده از as در سیشارپ:
در سیشارپ، as برای تبدیل نوع (Casting) استفاده میشود. اگر تبدیل ممکن نباشد، null برمیگرداند. در زیر یک مثال صحیح از استفاده از as در سیشارپ آورده شده است:
کد صحیح در سیشارپ:
using System;
class Program
{
static void Main()
{
object obj = "Hello"; // مقدار obj یک رشته است
string str = obj as string; // تبدیل به string با استفاده از as
if (str != null)
{
Console.WriteLine(str); // خروجی: Hello
}
else
{
Console.WriteLine("Memory type mismatch"); // اگر obj قابل تبدیل به string نباشد
}
// مثال دیگر با یک عدد
object obj2 = 123; // مقدار obj2 یک عدد است
string str2 = obj2 as string; // تبدیل به string با استفاده از as
if (str2 != null)
{
Console.WriteLine(str2);
}
else
{
Console.WriteLine("Memory type mismatch"); // خروجی: Memory type mismatch
}
}
}
توضیحات کد:
تعریف متغیر obj:
object obj = "Hello";
در اینجا، obj از نوع object است و مقدار آن یک رشته ("Hello") است.
استفاده از as:
string str = obj as string;
اگر obj قابل تبدیل به string باشد، str مقدار "Hello" را میگیرد.
در غیر این صورت، str برابر null خواهد بود.
اگر str برابر null نباشد، مقدار آن چاپ میشود.
در غیر این صورت، پیام "Memory type mismatch" نمایش داده میشود.
در مثال دوم، obj2 یک عدد (123) است و نمیتوان آن را به string تبدیل کرد.
بنابراین، str2 برابر null خواهد بود و پیام "Memory type mismatch" چاپ میشود.
تفاوت as با Casting معمولی:
Casting معمولی ((type)variable):
اگر تبدیل ممکن نباشد، یک استثنا (InvalidCastException) پرتاب میشود.
مثال:
object obj = 123;
string str = (string)obj; // خطا: InvalidCastException
استفاده از as:
اگر تبدیل ممکن نباشد، null برمیگرداند و خطایی پرتاب نمیشود.
مثال:
object obj = 123;
string str = obj as string; // str = null
نکات مهم:
as فقط با انواع Reference Type کار میکند:
as فقط برای انواع Reference Type (مانند کلاسها، رشتهها و آرایهها) کار میکند و برای انواع Value Type (مانند int, bool, double و غیره) استفاده نمیشود)
برای انواع Value Type، میتوانید از is و سپس Casting معمولی استفاده کنید.
استفاده از is برای بررسی نوع:
قبل از استفاده از as، میتوانید از is برای بررسی نوع استفاده کنید.
مثال:
if (obj is string)
{
string str = (string)obj;
Console.WriteLine(str);
}
else
{
Console.WriteLine("Memory type mismatch");
}
جمعبندی:
در سیشارپ، از as برای تبدیل نوع (Casting) استفاده میشود و اگر تبدیل ممکن نباشد، null برمیگرداند.
روش دوم ارتباط کلاس و فرم :
استفاده از فرم به عنوان آرگومان
اگر صفحات آموزش های قبلی را خوانده باشید به یاد دارید که از روش استفاده از پراپرتی برای ایجاد ارتباط کلاس با فرم توضیحاتی داده شد در این جلسه بعنوان دومین روش برای ارتباط بین کلاس و فرم روش پاس دادن فرم بعنوان یک آرگومان به متد کلاس توضیحاتی ارائه می شود .این روش به شما امکان میدهد با کد نویسی کمتری به کنترلهای فرم دسترسی داشته باشید .
مثال:
internal class MyClass
{
public void ProcessForm(Form form)
{
CheckBox checkBox = (CheckBox)form.Controls["checkBox1"];
if (checkBox.Checked)
{
// کد مربوط به چکباکس تیک خورده
}
else
{
// کد مربوط به چکباکس تیک نخورده
}
}
}
نحوه استفاده در فضای فرم مثلا در رویداد یک باتن :
private void button1_Click(object sender, EventArgs e)
{
MyClass testClass = new MyClass();
testClass.ProcessForm(this); // this اشاره به فرم فعلی دارد
}
در این مثال، متد ProcessForm در کلاس MyClass به عنوان یک متد تعریف شده است که یک فرم را به عنوان آرگومان دریافت میکند. سپس میتوانید به کنترلهای فرم با استفاده از نام آنها در متد دسترسی پیدا کنید.
مقایسه دو روش
روش اول : استفاده از Property
مزیت: انعطافپذیر است و به شما امکان میدهد تا فقط اطلاعات مورد نیاز را بین کلاس و فرم منتقل کنید.
معایب: اگر بخواهید چندین کنترل را مدیریت میکنید نیاز به تعریف چندین Property دارید .
روش دوم : استفاده از فرم به عنوان آرگومان
مزیت: به شما امکان میدهد به تمام کنترلهای فرم دسترسی داشته باشید و کد کمتری نیاز دارید.
معایب: ممکن است به دلیل دسترسی مستقیم به کنترلهای فرم، کدهای شما کمتر کپسوله شده باشند و در صورت تغییرات در فرم، نیاز به تغییرات بیشتر در کلاس داشته باشید.
در هر صورت هر دو روش دارای مزایا و معایبی هستند و انتخاب بین آنها بستگی به نیاز و طراحی برنامه شما دارد.
انتزاع یا Abstraction:
پس از آشنایی با کپسولهسازی، حالا میخوام درباره دومین رکن مهم برنامهنویسی شیءگرا، یعنی انتزاع یا Abstraction صحبت کنم.
انتزاع به معنی سادهسازی و نمایش ویژگیهای اصلی یک شیء و پنهان کردن جزئیات غیرضروریه. فکر کنید به یک ریموت تلویزیون. شما فقط با دکمههای روی ریموت کار دارید و نیازی نیست بدونید داخلش چه اتفاقی میفته. یا وقتی ماشین میرونید، فقط با فرمون، پدالها و دنده سر و کار دارید و نیازی نیست بدونید موتور چطور کار میکنه.
هدف Abstraction در سیشارپ اینه که پیچیدگیهای غیرضروری رو پنهان کنه و فقط اطلاعات مهم و مرتبط رو نمایش بده. این کار باعث میشه کد سادهتر، قابل فهمتر و راحتتر برای استفاده بشه.
انتزاع به ما کمک میکنه تا:
چه مواقعی از انتزاع استفاده کنیم؟
انتزاع رو چطور در پروژهمون پیاده کنیم؟
در سیشارپ، میتونیم از
کلاسهای
abstract، interface ها، و متدهای virtual استفاده کنیم. این ابزارها به ما اجازه میدن تا
یک قالب کلی رو تعریف کنیم بدون اینکه جزئیات پیادهسازی رو مشخص کنیم.
مثال عملی: وسایل نقلیه
فرض کنید میخوایم یه
سیستم برای مدیریت انواع وسایل نقلیه بسازیم. میتونیم از انتزاع استفاده کنیم تا
یه رابط کلی برای همه وسایل نقلیه تعریف کنیم:
public abstract class Vehicle
{
public abstract void Start();
public abstract void Stop();
public virtual void Refuel()
{
Console.WriteLine("سوختگیری انجام شد");
}
}
public class Car : Vehicle
{
public override void Start()
{
Console.WriteLine("ماشین روشن شد");
}
public override void Stop()
{
Console.WriteLine("ماشین خاموش شد");
}
}
public class ElectricBike : Vehicle
{
public override void Start()
{
Console.WriteLine("دوچرخه برقی روشن شد");
}
public override void Stop()
{
Console.WriteLine("دوچرخه برقی خاموش شد");
}
public override void Refuel()
{
Console.WriteLine("باتری شارژ شد");
}
}
در این مثال، کلاس Vehicle یک قالب کلی برای همه وسایل نقلیه تعریف میکنه. کلاسهای Car و ElectricBike این قالب رو پیادهسازی میکنن. ما جزئیات داخلی هر وسیله نقلیه رو پنهان کردیم و فقط عملیاتهای اصلی رو نمایش دادیم.
انتزاع به ما کمک میکنه تا سیستمهای پیچیده رو سادهتر مدیریت کنیم و کد قابل فهمتر و انعطافپذیرتری بنویسیم.
از آنجایی که دو مفهوم انتزاع و ارثبری همیشه در مثالهایی که آورده می شود با همدیگر بکار می روند توضیحاتی در زیر اضافه می کنم تا این دو مفهوم از هم تفکیک داده شوند .
انتزاع (Abstraction) و ارثبری (Inheritance) دو مفهوم مرتبط اما متمایز در برنامهنویسی شیءگرا هستند.
بیایید این دو مفهوم را از هم تفکیک کنیم:
انتزاع (Abstraction)
انتزاع به معنای سادهسازی و پنهان کردن پیچیدگیهای غیرضروری است:
ارثبری (Inheritance)
ارثبری مکانیزمی برای استفاده مجدد از کد و ایجاد سلسله مراتب بین کلاسهاست:
تفکیک انتزاع و ارثبری
انتزاع میتواند بدون ارثبری وجود داشته باشد:
ارثبری میتواند بدون انتزاع وجود داشته باشد:
انتزاع و ارثبری اغلب با هم استفاده میشوند، اما هر یک مفهوم مستقلی دارند. انتزاع بر سادهسازی و پنهانسازی تمرکز دارد، در حالی که ارثبری بر به اشتراکگذاری و سازماندهی کد تأکید میکند.