Register کردن و Resolve کردن Type ها با استفاده از تکنیک Type Mapping

در قسمت قبلی از این سری آموزشی نحوه ی نصب کردن Unity Container را در یک Console Application به شما نشان دادیم. در این قسمت نحوه انجام Register کردن و سپس Resolve کردن Type ها را با استفاده از تکنیک Type Mapping به شما نشان خواهیم داد. همانطور که در قسمت های قبل خدمتتان عرض کردیم هر IoC Container موجود باید بتواند روش هایی را برای Register کردن و سپس Resolve کردن Dependency ها در اختیار ما قرار بدهد. در Unity Container متدهای Register Type و Resolve به منظور انجام این کار طراحی شده اند. به منظور بررسی و پیاده سازی عملیات Register کردن و Resolve کردن از کلاس ها و اینترفیس هایی که در قسمت زیر مشاهده می کنید به عنوان مثال استفاده خواهیم کرد.1 public interface ICar2 {3 int Run();4 }56 public class BMW : ICar7 {8 private int _miles = 0;910 public int Run()11 {12 return ++_miles;13 }14 }1516 public class Ford : ICar17 {18 private int _miles = 0;1920 public int Run()21 {22 return ++_miles;23 }24 }2526 public class Audi : ICar27 {28 private int _miles = 0;2930 public int Run()31 {32 return ++_miles;33 }3435 }36 public class Driver37 {38 private ICar _car = null;3940 public Driver(ICar car)41 {42 _car = car;43 }4445 public void RunCar()46 {47 Console.WriteLine("Running {0} - {1} mile ", _car.GetType().Name, car.Run());48 }49 }همان طور که در این مثال مشاهده می کنید کلاس Driver به اینترفیس ICar وابستگی دارد. بنابراین زمانی که یک Object از کلاس Driver ایجاد می‌شود باید بتوانیم یک Object از کلاسی که اینترفیس ICar را پیاده سازی کرده است را به آن تحویل بدهیم. کلاس‌هایی که این Interface را پیاده سازی کرده اند عبارتند از BMW و Audi و Ford. کدی که در قسمت زیر مشاهده می کنید نحوه ی انجام این کار را نشان می دهد.1 Driver driver = new Driver(new BMW());23 driver.RunCar();خروجی این کد شبیه به تصویر زیر خواهد بود.Running BMW – 1 mileدر مثال بالا پس از ایجاد کردن یک Object از نوع کلاس Driver یک Object از کلاس BMW که اینترفیس ICar را پیاده سازی کرده است را به عنوان Dependency به Driver تحویل دادیم. این تزریق وابستگی به درون کلاس Driver به صورت دستی انجام شد. حال می خواهیم با استفاده از Unity Container روش‌های مختلف Register کردن و Resolve کردن Dependency ها و انجام عملیات تزریق به صورت خودکار را بررسی کنیم.استفاده کردن از Unity Containerبه منظور استفاده کردن از Unity Container در ابتدا باید یک Object از آن ایجاد کنیم. به منظور انجام این کار می توانید از هر کلاسی که اینترفیس IUnityContainer را پیاده سازی کرده است استفاده بفرمایید. در Unity Container کلاس UnityContainer وجود دارد که در Microsoft.Practices.Unity قرار گرفته است و اینترفیس IUnityContainer را پیاده سازی می کند.اگر نیاز داشته باشید که Container مربوط به فریمورک Unity را تغییر داده و یا گسترش بدهید می توانید یک کلاس ایجاد کرده و اینترفیس IUnity را بسته به نیاز خود در آن پیاده سازی کنید. کدی که در قسمت زیر مشاهده می کنید نحوه ی ایجاد کردن یک Object از کلاس UnityContainer را نشان می دهد.1 using Microsoft.Practices.Unity;23 IUnityContainer container = new UnityContainer();4 //or5 var container = new UnityContainer();پس از ایجاد کردن این Unity Container باید اقدام به Register کردن Type ها کنیم.رجیستر کردن Dependency هاقبل از اینکه Unity Container بتواند Dependency ها را Resolve کند باید ابتدا آنها را در Container مربوطه Register کنیم تا در زمان مورد نیاز این Dependency ها Resolve بشوند. برای انجام این کار از متد RegisterType() استفاده می‌کنیم. در واقع این متد به ما اجازه می‌دهد تا یک Type-Mapping و یا نگاشت بین Type های مختلف ایجاد کنیم. به بیان ساده با استفاده از متد RegisterType() می توانیم Container را طوری پیکربندی می‌کنیم تا بتواند در صورت وجود یک Dependency به یک Interface و یا Base Class آن را تشخیص دهد و Object ی از کلاس مورد نظر را ایجاد کند. به عنوان مثال اگر بخواهیم که Unity Container در زمان وجود یک Dependency به اینترفیس ICar یک Object از نوع BMW را ایجاد کرده و آن را اصطلاحاً Resolve کند باید شبیه به کد زیر عمل کنیم.1 IUnityContainer container = new UnityContainer();23 container.RegisterType<ICar, BMW>();همانطور که در این کد مشاهده می کنید با استفاده از Container و متد RegisterType() اینترفیس ICar را به کلاس BMW نگاشت کرده‌ایم. با انجام این کار Unity Container در زمان وجود یک Dependency از نوع ICar یک Object از کلاس BMW را ایجاد کرده و به صورت خودکار آن را از طریق تابع سازنده به درون Object مورد نظر تزریق می‌کند. متد RegisterType() دارای Overload های مختلفی می باشد که بررسی کردن تمامی آنها از حوصله این مقاله خارج است. در ادامه در رابطه با Resolve کردن صحبت خواهیم کرد.Resolve کردن Dependency هاپس از Register کردن Dependency ها و ایجاد یک نگاشت بین یک Interface و یا یک Base Class با یک کلاس دیگر، Unity Container به صورت خودکار Dependency ها را با استفاده از متد Resolve() پیدا کرده و سپس آنها را تزریق می نماید. در قسمت قبل اینترفیس ICar را به کلاس BMW نگاشت کردیم. حال زمانی که یک Object از نوع Driver ایجاد می‌شود Unity Container بدون نیاز به استفاده از کلمه New برای ساختن Dependency مورد نظر در این کلاس با استفاده از متد Resolve() این Dependency ها را کشف کرده و به درون Driver تزریق می‌کند. این موضوع در کد زیر نشان داده شده است.1 IUnityContainer container = new UnityContainer();23 container.RegisterType<ICar, BMW>();// Map ICar with BMW4 //Resolves dependencies and returns the Driver object5 Driver drv = container.Resolve<Driver>();6 drv.RunCar();خروجی این کد شبیه به تصویر زیر خواهد بود.Running BMW - 1 mileدر مثال بالا Unity Container یک Object از کلاس Driver را با استفاده از متد Resolve ایجاد می‌کند. این کلاس یک Dependency از نوع ICar دارد بنابراین متد Resolve یک Object از کلاس Driver را ایجاد کرده و به صورت خودکار و بر اساس نگاشتی که ما لحاظ کرده‌ایم برای وابستگی ICar از این کلاس یک Object از نوع کلاس BMW را ایجاد کرده و به درون آن تزریق می‌کند. به طور پیش فرض هر زمان که یک Object جدید از کلاس Driver را اصطلاحاً Resolve کنیم Dependency های جدید ساخته شده و به درون کلاس مورد نظر تزریق می‌شوند. این موضوع در کد زیر نشان داده شده است.1 var container = new UnityContainer();2 container.RegisterType<ICar, BMW>();34 Driver driver1 = container.Resolve<Driver>();5 driver1.RunCar();67 Driver driver2 = container.Resolve<Driver>();8 driver2.RunCar();خروجی این کد شبیه به تصویر زیر خواهد بود.Running BMW - 1 mile Running BMW - 1 mileهمانطور که در مثال بالا مشاهده می کنید Unity Container یک Object از کلاس BMW را در زمان Resolve کردن Driver ایجاد کرده و به درون آن تزریق می‌کند. بنابراین Object های Driver1 و Driver2 هر دو Reference هایی به دو Object جداگانه از کلاس BMW دارند در ادامه در رابطه با Register های چندگانه صحبت می کنیم.انجام Register های چندگانهاگر در Unity Container برای یک Dependency یکسان چندین Type مختلف را Map کنید فقط آخرین Type مورد نظر لحاظ خواهند شد. مثال زیر این موضوع را نشان می‌دهد.1 IUnityContainer container = new UnityContainer();2 container.RegisterType<ICar, BMW>();3 container.RegisterType<ICar, Audi>();45 Driver driver = container.Resolve<Driver>();6 driver.RunCar();خروجی این کد شبیه به تصویر زیر خواهد بود.Running Audi - 1 Mileدر مثال بالا مشاهده می کنید که اینترفیس ICar هم به BMW و هم به Audi نگاشت شده است اما Unity Container در زمان ایجاد شدن یک Driver جدید آخرین Type ایجاد شده یعنی Audi را ایجاد کرده و به درون کلاس تزریق می نماید.رجیستر کردن Type های نامگذاری شدهیکی از تکنیک هایی که می‌توانید با استفاده از Unity Container انجام بدهید استفاده کردن از Named Type ها برای کنترل کردن بیشتر متد Resolve می باشد. کدی که در قسمت زیر مشاهده می کنید این موضوع را نشان می دهد.1 IUnityContainer container = new UnityContainer();2 container.RegisterType<ICar, BMW>();3 container.RegisterType<ICar, Audi>("LuxuryCar");45 ICar bmw = container.Resolve<ICar>(); // returns the BMW object6 ICar audi = container.Resolve<ICar>("LuxuryCar"); // returns the Audi objectدر این کد ما اینترفیس ICar را به BMW و همچنین Audi نگاشت کرده‌ایم. اما برای نگاشت ICar به Audi از یک نام به نام LuxuryCar استفاده کرده‌ایم. حال می‌توانیم در متد Resolve و در یکی از Overload های آن بگوییم که این نگاشت خاص که نامگذاری شده است را در نظر بگیر و از آن استفاده کن. ضمناً به کد زیر نگاهی بیندازید.1 var container = new UnityContainer();2 container.RegisterType<ICar, BMW>();3 container.RegisterType<ICar, Audi>("LuxuryCar");45 // Registers Driver type6 container.RegisterType<Driver>("LuxuryCarDriver",7 new InjectionConstructor(container.Resolve<ICar>("LuxuryCar")));8 Driver driver1 = container.Resolve<Driver>();// injects BMW910 driver1.RunCar();11 Driver driver2 = container.Resolve<Driver>("LuxuryCarDriver");// injects Audi12 driver2.RunCar();خروجی این کد شبیه به تصویر زیر خواهد بود.Running BMW - 1 Mile Running Audi - 1 Mileدر این مثال ما کلاس Driver را با نام LuxuryCarDriver نگاشت و یا Register کردیم و از یک Object از کلاس InjectionConstructor استفاده نمودیم. با استفاده از این Object عملیات Constructor Injection برای کلاس Driver اتفاق می‌افتد. به عبارت دیگر یک Object از کلاس Audi ایجاد شده و در زمان Resolve شدن Driver به درون آن تزریق می شود. حال می توانیم به سادگی با لحاظ کردن کلمه ی LuxuryCarDriver در متد Resolve در زمان ساختن یک Driver جدید از یک Object از کلاس Audi استفاده کنیم. دقت کنید که این اتفاق هر چند که اینترفیس ICar باکلاس BMW رجیستر شده است رخ می‌دهد.Register کردن یک Object از قبل موجودبا استفاده از Unity Container که می‌توانیم یک Object از قبل موجود را با استفاده از متد RegisterInstance() در تعریف کردن نگاشت مربوط به Dependency ها مورد استفاده قرار بدهیم. این موضوع باعث می‌شود که در زمان Resolve کردن Dependency ها Object جدیدی ایجاد نشود و از همان Object موجود استفاده گردد. این موضوع در کد زیر نشان داده شده است.1 var container = new UnityContainer();2 ICar audi = new Audi();3 container.RegisterInstance<ICar>(audi);45 Driver driver1 = container.Resolve<Driver>();6 driver1.RunCar();7 driver1.RunCar();89 Driver driver2 = container.Resolve<Driver>();10 driver2.RunCar();خروجی این کد شبیه به تصویر زیر خواهد بود.Running Audi - 1 Mile Running Audi - 2 Mile Running Audi - 3 Mileبنابراین تا به اینجای کار توانسته ایم عملیات Register کردن و Resolve کردن Dependency ها را با استفاده از Unity Container انجام بدهیم.پرووید: مرکز آموزش تخصصی برنامه نویسی و توسعه نرم افزار