الگوی طراحی Adapter، یک الگوی ساختاری است که این امکان را میدهد تا اینترفیسهای ناسازگار، با یکدیگر کار کنند. با این کار، ما به آبجکتها از اینترفیسهای مختلف، اجازه میدهیم تا با یکدیگر، داده ها را تبادل کنند.
در این مقاله، ما قصد داریم تا نحوه پیاده سازی الگوی Adapter در پروژه و زمان مناسب استفاده از آن را یاد بگیریم.
این مقاله، بخشی از مجموعه آموزشی زیر است:
- الگوی طراحی Builder و Fluent Builder
- اینترفیس Fluent Builder به همراه Generic بازگشتی
- Facated Builder
- متد Factory
- Singleton
- Adapter
- Composite
- Decorator
- Command
- Strategy
- Facade
سورس کد در این لینک موجود است: الگوی طراحی Adapter – سورس کد
برای مشاهده لیست کامل مقالات این مجموعه آموزشی، الگوهای طراحی #C را بررسی کنید.
این مقاله، به قسمتهای زیر تقسیم میشود:
آماده سازی پروژه
تصور کنیم که ما یک قابلیتی داریم که در آن لیست تولیدکنندگان خودرو را به فرمت JSON تبدیل کرده و آن را بر روی صفحه نمایش میدهیم. اما به جای لیست، یک API در اختیار ما قرار گرفته است که همه تولیدکنندگان را در قالب XML در اختیار ما قرار می دهد.
فرض کنید نمی توانیم عملکرد API موجود را تغییر دهیم (به دلیل محدودیت های فنی مانند import شدن به پروژه ما از یک solution دیگر که نباید آن را تغییر دهیم یا اینکه به عنوان یک NuGet package به پروژه ما import شده است) بنابراین باید راهی برای حل آن پیدا کنیم.
و مناسبترین راه برای حل این مسئله، پیاده سازی الگوی Adapter است.
بیایید با ایجاد مدل Manufacturer و یک مثال مبدل یک آبجکت ساده به XML، کارمان را شروع کنیم:
1 2 3 4 5 6 |
public class Manufacturer { public string Name { get; set; } public string City { get; set; } public int Year { get; set; } } |
1 2 3 4 5 6 7 8 9 10 11 12 |
public static class ManufacturerDataProvider { public List<Manufacturer> GetData() => new List<Manufacturer> { new Manufacturer { City = "Italy", Name = "Alfa Romeo", Year = 2016 }, new Manufacturer { City = "UK", Name = "Aston Martin", Year = 2018 }, new Manufacturer { City = "USA", Name = "Dodge", Year = 2017 }, new Manufacturer { City = "Japan", Name = "Subaru", Year = 2016 }, new Manufacturer { City = "Germany", Name = "BMW", Year = 2015 } }; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public class XmlConverter { public XDocument GetXML() { var xDocument = new XDocument(); var xElement = new XElement("Manufacturers"); var xAttributes = ManufacturerDataProvider.GetData() .Select(m => new XElement("Manufacturer", new XAttribute("City", m.City), new XAttribute("Name", m.Name), new XAttribute("Year", m.Year))); xElement.Add(xAttributes); xDocument.Add(xElement); Console.WriteLine(xDocument); return xDocument; } } |
همانطور که می بینیم، این یک کد بسیار ساده است. ما اینجا داده های مربوط به manufacturer را جمع آوری میکنیم، یک root element به نام Manufacturers و سپس تمام sub element های Manufacturer به همراه attribute هایشان را میسازیم.
پس از آن، ما نتایج را در پنجره کنسول چاپ می کنیم تا نشان دهیم که XML نهایی چطور به نظر می رسد.
xDocument باید به این شکل باشد:
حالا یک کلاس JsonConverter
را پیاده سازی کنیم:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public class JsonConverter { private IEnumerable<Manufacturer> _manufacturers; public JsonConverter(IEnumerable<Manufacturer> manufacturers) { _manufacturers = manufacturers; } public void ConvertToJson() { var jsonManufacturers = JsonConvert.SerializeObject(_manufacturers, Formatting.Indented); Console.WriteLine("\nPrinting JSON list\n"); Console.WriteLine(jsonManufacturers); } } |
این کد ساده تر است زیرا ما لیست تولید کنندگان خود را فقط در قالب serialize ،JSON می کنیم.
البته، برای کار serialize سازی، ما باید کتابخانه Newtonsoft.Json را نصب کنیم، بنابراین فراموش نکنید که این کار را انجام دهید.
بسیار عالی، در حال حاضر ما قابلیت تبدیل به JSON و اینترفیس XML مورد نظر را در اختیار داریم. اما در حال حاضر باید یک مشکل واقعی را حل کنیم. چگونه این دو اینترفیس را برای انجام task مورد نظر یعنی تبدیل تولیدکنندگان از فرمت XML به JSON ترکیب کنیم.
پیاده سازی Adapter
همانطور که می بینیم، راهی برای پاس دادن xDocument به کلاس JsonConverter وجود ندارد و نباید هم وجود داشته باشد، بنابراین ما باید یک کلاس adapter ایجاد کنیم که باعث شود این دو اینترفیس با یکدیگر کار کنند.
برای این منظور، ما قصد داریم جهت تعریف رفتار کلاس adapter خود، با اینترفیس IXmlToJson
شروع کنیم:
1 2 3 4 |
public interface IXmlToJson { void ConvertXmlToJson(); } |
سپس با کلاس XmlToJsonAdapter
ادامه میدهیم که اینترفیس IXmlToJson
را پیاده سازی میکند:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class XmlToJsonAdapter : IXmlToJson { private readonly XmlConverter _xmlConverter; public XmlToJsonAdapter(XmlConverter xmlConverter) { _xmlConverter = xmlConverter; } public void ConvertXmlToJson() { var manufacturers = _xmlConverter.GetXML() .Element("Manufacturers") .Elements("Manufacturer") .Select(m => new Manufacturer { City = m.Attribute("City").Value, Name = m.Attribute("Name").Value, Year = Convert.ToInt32(m.Attribute("Year").Value) }); new JsonConverter(manufacturers) .ConvertToJson(); } } |
بسیار عالی. ما کتابخانه adapter خود را ایجاد کردیم که آبجکت Xml document را در قالب لیست تولیدکنندگان درآورده و آن لیست را به کلاس JsonConverter
ارائه میدهد.
بنابراین همانطور که میبنید، ما همکاری بین دو اینترفیس کاملا متفاوت را فقط با معرفی یک کلاس adapter به پروژه خود، برقرار کردیم.
حال میتوانیم از کلاس client خود، این کلاس adapter را فراخوانی کنیم:
1 2 3 4 5 6 7 8 9 |
class Program { static void Main(string[] args) { var xmlConverter = new XmlConverter(); var adapter = new XmlToJsonAdapter(xmlConverter); adapter.ConvertXmlToJson(); } } |
زمانی که اپلیکیشن را اجرا کنیم باید نتیجه زیر را ببینیم:
عالی بود. ما پیاده سازی این الگو را به پایان رساندیم.
زمان استفاده از Adapter
هر زمان که بخواهیم از کلاسی استفاده کنیم که اینترفیس آن با مابقی کد ما سازگار نیست، باید از کلاس Adapter استفاده کنیم. در اصل، الگوی Adapter یک لایه میانی است که به عنوان یک مترجم بین کد پیاده سازی شده در پروژه و کلاس third party یا هر کلاس دیگر با یک اینترفیس متفاوت عمل می کند.
علاوه بر این، هنگامی که می خواهیم از کلاس های موجود در پروژه خود استفاده مجدد کنیم اما آنها فاقد عملکرد مشترک هستند، باید از Adapter استفاده کنیم.
با استفاده از الگوی Adapter در این مورد، نیازی نیست هر کلاسی را جداگانه گسترش دهیم و باعث ایجاد کد اضافی شویم.
نتیجه گیری
الگوی Adapter در جهان #C بسیار رایج است و هنگامی که مجبور به تطبیق برخی از کلاسهای موجود با یک اینترفیس جدید هستیم، بسیار مورد استفاده قرار می گیرد. این می تواند پیچیدگی کد را با افزودن کلاس های اضافی ( Adapter ها) افزایش دهد، اما مطمئناً ارزش آن را دارد.