5️⃣ اصل پنجم SOLID: وارونگی وابستگی (Dependency Inversion Principle)
تا حالا شده یه کلاس بنویسید که داخلش یه کلاس دیگه رو با new میسازید و بعداً برای تغییر اون کلاس داخلی، مجبور میشید کلاس اصلی رو هم دستکاری کنید؟ این یعنی کدهای شما به هم سفت و سخت (Tightly Coupled) وصل شدن.
اصل وارونگی وابستگی (DIP) برای حل همین مشکل و ایجاد کدهای انعطافپذیر طراحی شده.
این اصل چی میگه؟ 🎯
این اصل دو بخش مهم داره:
ماژولهای سطح بالا نباید به ماژولهای سطح پایین وابسته باشند. هر دو باید به انتزاع (Abstraction) وابسته باشند.به زبان ساده: کلاسهای اصلی و سطح بالای شما (مثلاً بیزنس لاجیک) نباید به جزئیات پیادهسازی (مثلاً نحوه کار با دیتابیس یا ارسال ایمیل) وابسته باشن. در عوض، هر دو باید به یک قرارداد مشترک (Interface) وابسته باشن.
انتزاعها نباید به جزئیات وابسته باشند. این جزئیات هستن که باید به انتزاعها وابسته باشند.
مثال عملی: سیستم اطلاعرسانی
فرض کنید یه سیستمی برای اطلاعرسانی به کاربر داریم.
مثال بد (نقض اصل وارونگی وابستگی):
اینجا کلاس سطح بالای Notification مستقیماً به کلاس سطح پایین EmailSender وابسته است. اگه فردا بخوایم به جای ایمیل، با SMS اطلاعرسانی کنیم، مجبوریم کلاس Notification رو تغییر بدیم. این یعنی وابستگی سفت و سخت.
// ❌ این کلاس سطح پایین است
public class EmailSender
{
public void Send() => Console.WriteLine("Email sent!");
}
// ❌ این کلاس سطح بالاست و مستقیماً به کلاس پایینی وابسته است
public class Notification
{
private readonly EmailSender _emailSender = new EmailSender();
public void SendNotification()
{
_emailSender.Send();
}
}
مثال خوب (رعایت اصل وارونگی وابستگی):
حالا با استفاده از یک Interface، این وابستگی رو "وارونه" میکنیم.
قدم اول: ساختن قرارداد (Interface)
این قرارداد در لایه سطح بالا تعریف میشه.
public interface IMessageSender
{
void SendMessage();
}
قدم دوم: کلاسهای سطح پایین از قرارداد پیروی میکنند
public class EmailSender : IMessageSender
{
public void SendMessage() => Console.WriteLine("Email sent!");
}
public class SmsSender : IMessageSender
{
public void SendMessage() => Console.WriteLine("SMS sent!");
}
قدم سوم: کلاس سطح بالا به قرارداد وابسته است، نه به جزئیات
public class Notification
{
private readonly IMessageSender _sender;
// وابستگی از طریق Constructor تزریق میشود (Dependency Injection)
public Notification(IMessageSender sender)
{
_sender = sender;
}
public void SendNotification()
{
_sender.SendMessage();
}
}
جادو اینجا اتفاق میفته: حالا کلاس Notification دیگه کاری نداره که پیام چطوری ارسال میشه (با ایمیل یا SMS). اون فقط "قرارداد" رو میشناسه. ما میتونیم موقع ساختن آبجکت Notification، هر نوع IMessageSender که دوست داریم رو بهش پاس بدیم، بدون اینکه یک کلمه از کدش رو تغییر بدیم!
🤔 حرف حساب و تجربه شما
این DIP قلب معماریهای تمیز و مدرنه و اساس کار تزریق وابستگی (Dependency Injection) هست. رعایت این اصل، کد شما رو فوقالعاده انعطافپذیر، قابل تست و توسعهپذیر میکنه.
شما چقدر به این اصل در پروژههاتون اهمیت میدید؟ بهترین مثالی که از کاربرد این اصل تو ذهنتون دارید چیه؟
💬 بحث و گفتگوی بیشتر در گروه کامیونیتی:
[C# Geeks Community]
🔖 هشتگها:
#CSharp #Programming #Developer #SOLID #SoftwareArchitecture #CleanCode #DIP