تفاوت پایهای که بین یک فریم ورک IoC و سایر فریم ورکها وجود دارد، در معکوس شدن مسئولیتها است. در اینجا لایههای مختلف برنامه شما نیستند که فریم ورک IoC را فراخوانی میکنند؛ بلکه این فریم ورک IoC است که از جزئیات ارتباطات و وابستگیهای سیستم شما آگاه است و نهایتا کار کنترل وهله سازی اشیاء مختلف را عهده دار خواهد شد. طول عمر آنها را تنظیم کرده یا حتی در بعضی از موارد مانند برنامه نویسی جنبهگرا یا AOP، نسبت به تزئین این اشیاء یا دخالت در مراحل مختلف فراخوانی متدهای آنها نیز نقش خواهد داشت. نکتهی مهم در اینجا، نا آگاهی برنامه از حضور آنها است.
بنابراین در پروژه شما اگر ماژولها و لایههای مختلفی حضور دارند، تنها برنامه اصلی است که باید ارجاعی را به فریم ورک IoC داشته باشد و نه سایر لایههای سیستم. علت حضور آن در ریشه سیستم نیز تنها باید به اصطلاحا bootstrapping و اعمال تنظیمات مرتبط با آن خلاصه شود.
به عبارتی استفاده صحیح از یک فریم ورک IoC نباید به شکل الگوی Service Locator باشد؛ حالتی که در تمام قسمتهای برنامه مدام مشاهده میکنید resolver.Resolve، resolver.Resolve و الی آخر. باید از این نوع استفاده از فریم ورکهای IoC تا حد ممکن حذر شود و کدهای برنامه نباید وابستگی مستقیم ثانویهای را به نام خود فریم ورک IoC پیدا کنند.
var container = BootstrapContainer(); var finder = container.Resolve<IDuplicateFinder>(); var processor = container.Resolve<IArgumentsParser>(); Execute( args, processor, finder ); container.Dispose();
الف) در آغاز برنامه برای اعمال تنظیمات اولیه و bootstrapping
ب) پیش از اجرای عملی جهت وهله سازی وابستگیهای مورد نیاز
ج) پس از اجرای عمل مورد نظر جهت آزاد سازی منابع
نکته مهم اینجا است که در حین اجرای فرآیند، این فرآیند باید تا حد ممکن از حضور IoC container بیخبر باشد و کار تشکیل اشیاء باید خارج از منطق تجاری برنامه انجام شود: IoC container خود را صدا نزنید؛ او شما را صدا خواهد زد.
عنوان شد تا «حد ممکن». این تا حد ممکن به چه معنایی است؟ اگر کار وهله سازی اشیاء را میتوانید تحت کنترل قرار دهید، مثلا آیا میتوانید در نحوه وهله سازی کنترلرها در ASP.NET MVC دخل و تصرف کرده و در زمان وهله سازی، اینکار را به یک IoC Container واگذار کنید؟ اگر بلی، دیگر به هیچ عنوانی نباید داخل کلاسهای فراخوانی شده و تزریق شده به کنترلرهای برنامه اثری از IoC Container شما مشاهده شود. زیرا این فریم ورکها اینقدر توانمند هستند که بتوانند تا چندین لایه از سیستم را واکاوی کرده و وابستگیهای لازم را وهله سازی کنند.
اگر خیر (نمیتوانید کار وهله سازی اشیاء را مستقیما تحت کنترل قرار دهید)؛ مانند تهیه یک Role Provider سفارشی در ASP.NET MVC که کار وهله سازی این Role Provider راسا توسط موتور ASP.NET انجام میشود و در این بین امکان دخل و تصرفی هم در آن ممکن نیست، آنگاه مجاز است داخل این کلاس ویژه از متدهای container.Resolve استفاده کرد؛ چون چارهی دیگری وجود ندارد و IoC Container نیست که کار وهله سازی ابتدایی آنرا عهده دار شده است. باید دقت داشت به این حالت خاص دیگر تزریق وابستگیها گفته نمیشود؛ بلکه نام الگوی آن Service locator است. در Service locator یک کامپوننت خودش به دنبال وابستگیهای مورد نیازش میگردد. در حالت تزریق وابستگیها، یک کامپوننت وابستگیهای مورد نیاز را درخواست میکند.
یک مثال:
public class ExampleClass { private readonly IService _service; public ExampleClass() { _service = Container.Resolve<IService>(); } public void DoSomething(int id) { _service.DoSomething(id); } }
نمونه اصلاح شده کلاس فوق، تزریق وابستگیها در سازنده کلاس به نحو زیر است:
public class ExampleClass { private IService _service; public ExampleClass(IService service) { _service = service; } public void DoSomething(int id) { _service.DoSomething(id); } }
نمونه دیگری از کلاسی که خودش به دنبال یافتن و وهله سازی وابستگیهای مورد نیازش است مثال زیر میباشد:
public class Search { IDinner _dinner; public Search(): this(new Dinner()) { } public Search(IDinner dinner) { _dinner = dinner; } }