مفهوم تزریق وابستگی (Dependency Injection) در سی شارپ
در این مقاله با چند مثال ساده به بررسی مفهوم الگوی تزریق وابستگی یا همان Dependency Injection در زبان برنامه نویسی سی شارپ می پردازیم.
تزریق وابستگی چیست؟
Dependency Injection یک الگوی طراحی است که با هدف حذف وابستگی های موجود بین دو کلاس با استفاده از یک Interface یا همان رابط ایجاد شده است. به عبارت دیگر تزریق وابستگی به معنی تزریق وابستگی های یک کلاس به جای استفاده مستقیم از آن ها درون کلاس است.
الگوری تزریق وابستگی شامل سه نوع کلاس زیر است:
- کلاس کلاینت (Client Class): کلاسی است که به کلاس سرویس وابسته است.
- کلاس سرویس (Service Class): کلاسی است که خدماتی را به کلاس کلاینت ارائه می کند.
- کلاس تزریق کننده (Injector Class): کلاسی است که شیء از کلاس سرویس را به کلاس کلاینت تزریق می کند.
تصویر زیر ارتباط بین سه کلاس فوق را نشان می دهد:
همانطور که مشاهده می کنید، کلاس تزریق کننده یک شیء از کلاس سرویس ایجاد می کند و آن را به کلاس کلاینت تزریق می کند. با این روش الگوی DI مسئولیت ایجاد شیء از کلاس سرویس را خارج از کلاس کلاینت انجام می دهد.
انواع تزریق وابستگی
همانگونه که در بالا یاد گرفتید، کلاس تزریق کننده، سرویس (dependency) را به کلاینت (dependent) تزریق می کند. کلاس Injector به طور کلی می تواند به سه روش زیر وابستگی را تزریق کند.
- تزریق سازنده (Constructor Injection)
- تزریق پراپرتی (Property Injection)
- تزریق متد (Method Injection)
مثال
برای درک بهتر مطالب گفته شده به مثال زیر توجه کنید که بدون الگوی وابستگی نوشته شده است.
public interface ICustomerDataAccess { string GetCustomerName(int id); } public class CustomerDataAccess: ICustomerDataAccess { public CustomerDataAccess() { } public string GetCustomerName(int id) { return "Dummy Customer Name"; } } public class DataAccessFactory { public static ICustomerDataAccess GetCustomerDataAccessObj() { return new CustomerDataAccess(); } } public class CustomerBusinessLogic { ICustomerDataAccess _custDataAccess; public CustomerBusinessLogic() { _custDataAccess = DataAccessFactory.GetCustomerDataAccessObj(); } public string GetCustomerName(int id) { return _custDataAccess.GetCustomerName(id); } }
مشکلی که در کلاس بالا وجود دارد این است که ما از DataAccessFactory در داخل کلاس CustomerBusinessLogic استفاده کرده ایم. فرض کنید پیاده سازی دیگری از رابط ICustomerDataAccess وجود دارد و ما می خواهیم از آن استفاده کنیم. در این صورت باید کد کلاس CustomerBusinessLogic را تغییر دهیم. هنگامی که از الگوی تزریق وابستگی استفاده شود، این مشکل با تزریق اشیاء از طریق سازنده، پراپرتی و یا متد حل می شود.
تصویر زیر پیاده سازی الگوی تزریق وابستگی برای مثال بالا را نشان می دهد:
همانطور که مشاهده می کنید، کلاس CustomerService به عنوان تزریق کننده عمل می کند و یک شیء از کلاس CustomerDataAccess (سرویس) را به کلاس CustomerBusinessLogic (کلاینت) تزریق می کند.
تزریق سازنده (Constructor Injection)
در این نوع تزریق، کلاس Injector سرویس (dependency) را از طریق سازنده کلاس کلاینت تزریق می کند.
در مثال زیر الگوی تزریق وابستگی با استفاده از سازنده پیاده سازی شده است:
public class CustomerBusinessLogic { ICustomerDataAccess _dataAccess; public CustomerBusinessLogic(ICustomerDataAccess custDataAccess) { _dataAccess = custDataAccess; } public CustomerBusinessLogic() { _dataAccess = new CustomerDataAccess(); } public string ProcessCustomerData(int id) { return _dataAccess.GetCustomerName(id); } } public interface ICustomerDataAccess { string GetCustomerData(int id); } public class CustomerDataAccess: ICustomerDataAccess { public CustomerDataAccess() { } public string GetCustomerName(int id) { //get the customer name from the db in real application return "Dummy Customer Name"; } }
در مثال بالا CustomerBusinessLogic شامل سازنده ای با پارامتری از نوع ICustomerDataAccess است و اکنون هر متدی که بخواهد از این کلاس استفاده کند باید یک شیء از نوع ICustomerDataAccess را به آن تزریق کند.
ماننده مثال زیر:
public class CustomerService { CustomerBusinessLogic _customerBL; public CustomerService() { _customerBL = new CustomerBusinessLogic(new CustomerDataAccess()); } public string GetCustomerName(int id) { return _customerBL.GetCustomerName(id); } }
در کد فوق، کلاس CustomerService یک شیء از کلاس CustomerDataAccess را ایجاد و به کلاس CustomerBusinessLogic تزریق می کند. بنابراین دیگر نیازی به ایجاد یک شیء از CustomerBusinessLogic در داخل کلاس CustomerDataAccess نیست.
تزریق پراپرتی (Property Injection)
در این نوع تزریق، کلاس Injector سرویس (dependency) را از طریق پراپرتی عمومی کلاس کلاینت تزریق می کند.
در مثال زیر الگوی تزریق وابستگی با استفاده از Property پیاده سازی شده است:
public class CustomerBusinessLogic { public CustomerBusinessLogic() { } public string GetCustomerName(int id) { return DataAccess.GetCustomerName(id); } public ICustomerDataAccess DataAccess { get; set; } } public class CustomerService { CustomerBusinessLogic _customerBL; public CustomerService() { _customerBL = new CustomerBusinessLogic(); _customerBL.DataAccess = new CustomerDataAccess(); } public string GetCustomerName(int id) { return _customerBL.GetCustomerName(id); } }
در کد بالا، کلاس CustomerBusinessLogic شامل یک پراپرتی عمومی به نام DataAccess است که برای تنظیم یک شیء پیاده سازی شده بر اساس ICustomerDataAccess استفاده می شود. بنابراین کلاس CustomerService با استفاده از این پراپرتی شیء از نوع CustomerDataAccess را به کلاس CustomerBusinessLogic تزریق می کند.
تزریق متد (Method Injection)
در این نوع تزریق، کلاس کلاینت برای مشخص کردن وابستگی ها یک interface پیاده سازی می کند و کلاس تزریق کننده با استفاده از این interface وابستگی های کلاس کلاینت را تزریق می کند.
در مثال زیر الگوی تزریق وابستگی با استفاده از Method پیاده سازی شده است:
interface IDataAccessDependency { void SetDependency(ICustomerDataAccess customerDataAccess); } public class CustomerBusinessLogic : IDataAccessDependency { ICustomerDataAccess _dataAccess; public CustomerBusinessLogic() { } public string GetCustomerName(int id) { return _dataAccess.GetCustomerName(id); } public void SetDependency(ICustomerDataAccess customerDataAccess) { _dataAccess = customerDataAccess; } } public class CustomerService { CustomerBusinessLogic _customerBL; public CustomerService() { _customerBL = new CustomerBusinessLogic(); ((IDataAccessDependency)_customerBL).SetDependency(new CustomerDataAccess()); } public string GetCustomerName(int id) { return _customerBL.GetCustomerName(id); } }
در مثال فوق، کلاس CustomerBusinessLogic رابط IDataAccessDependency را که شامل متد SetDependency است را پیاده سازی کرده است. بنابراین کلاس CustomerService (تزریق کننده) می تواند با استفاده از متد SetDependency کلاس CustomerDataAccess را به کلاس مشتری تزریق کند.
نوشته مفهوم تزریق وابستگی (Dependency Injection) در سی شارپ اولین بار در سورس سرا - آموزش برنامه نویسی. پدیدار شد.