دیزاین پترن ها(قسمت دوم)

در این قسمت به بررسی اجمالی یکی از پترن های Creational یا ایجادی می پردازیم.در قسمت قبلی گفتیم که الگوهای معروف به GoF یا Gang of Four، به سه دسته بندی تقسیم میشوند که یک دسته از این الگو ها، الگوهایی هستند که تمرکز آنها بر روی ایجاد شی از کلاس است. که به آنها Creational یا ایجادی میگویند.نکته مهمی که برای هر برنامه نویسی اهمیت داره، این نیست که بتونه یک پترن رو از نظر تئوری توضیح بده توی مصاحبه ها. بلکه نکته مهم در اینه که بدونه کجا باید از چه الگویی استفاده کنه؟ در واقع باید علت و چرایی انتخاب اون الگو رو به درستی درک کنه و هدف منم همینه که بتونم تا حد امکان این انتخاب کردن رو توضیح بدم.مقدمه ای بر Factory patternالگوی Factory، یکی از پرکاربرد ترین الگوهای Creational و در زمره یکی از بهترین های اونهاست. و هدف اصلیش اینه که به جای فراخوانی مستقیم خود concrete کلاس هایی که یک اینترفیس یکسان رو پیاده سازی کردند، برای ایجاد شی، از خود اینترفیس استفاده کنیم و یک مرجع واحد برای ایجاد شی داشته باشیم.نکته ی مهم جهت انتخاب الگوی Factory همینه که ما با یک مجموعه کلاس روبه رو هستیم که این کلاس ها از یک Implemention یکسانی بهره مند هستند.یعنی همه شون از یک اینترفیس مشترک ارث بری کردند.نکته مهم دیگه که ما رو به این نتیجه میرسونه که باید از این الگو استفاده کنیم، اینه که ما نمیدونیم کدوم شی دقیقا قراره ایجاد بشه. یعنی ما نمیدونیم کدوم کلاس قراره دقیقا new بشه. به بیان بهتر، ایجاد شی در زمان Run time ئه که داره تعیین میشه. و نکته آخر اینکه عموما ایجاد شی، یک سری عملیات لازم داره که این عملیات یک کاره هزینه بر است. یعنی ما عموما برای اشیا ایجاد شده بابت هر کلاس، یک سری کارهایی داریم انجام میدیم.بنابراین:سه شرط برای انتخاب الگوی Factory عبارتند از :کلاس هایی که از روی یک interface یکسان پیاده سازی یا Implement شدن.نوع شی ای که قرار است ایجاد شود، در لحظه Runtime مشخص میشود.ایجاد شی یک کاره هزینه بر است و باید یک سری عملیات بابتش انجام بشود.بزارید با یک مثال واقعی و ملموس شروع کنیم.فرض کنید که دارید یک سیستم بانکی پیاده سازی میکنید. در اونجا موجودیتی هست به اسم حساب بانکی. که خب نوع های مختلفی ازش هست ولی یک سری عملیات یکسان داره.مثل گرفتن اطلاعات حساب، بستن حساب، گرفتن جزئیات گردش حساب و ...پس من احتمالا با یک اینترفیس روبه رو هستم به اسم BankAccount و یک سری متد دارم داخل این اینترفیس که اومدم API شون رو مشخص کردم. و حالا هر حساب بانکی که بخواد این اینترفیس رو دنبال کنه، ملزم به پیاده سازی متدهای این قرارداد هست.مثلا متد GetAccount که برای گرفتن اطلاعات حسابه. یا متد GetAccountDetails که برای گرفتن جزئیات حسابه و غیره. خب طبیعتا ما نوع های حساب بانکی مختلفی داریم. مثلا حساب جاری، قرض الحسنه، ارزی و ...حالا چه اتفاقی می افته اگر شما بخواید بسته به ورودی کاربر، هر کدوم از این Concrete کلاس ها رو ایجاد کنید؟ مثلا یک صفحه وب جلوی کاربر باز کردید که کارش افتتاح حسابه و بعد چند تا گزینه برای انتخاب داره:افتتاح حساب جاریافتتاح حساب قرض الحسنهافتتاح حساب ارزیو ...این همون قضیه runtime ئه که اشاره کردم.یعنی ما عملا نمیدونیم چه input ای قراره بیاد و این ندونستن، ایجاد شی رو روال مند میکنه و هزینه بر.چون شما باید این انتخاب رو هندل کنید:یعنی بنویسید if حساب جاری بود، then برو از کلاس حساب های جاری یک شی نیو کن، برگردون.باز یک if دیگه که اگر حساب قرض الحسنه بود، then برو از کلاس حساب های قرض الحسنه یک شی نیو کن، و برگردونو همینطور تا اخر.و حالا چند مشکل خودشو داره نشون میده.اولا من اگر جای دیگری از بیزنس اپلیکیشنم، خواستم امکان افتتاح حساب بگذارم، اون وقت باز باید تمام این ایف و الس ها رو بردارم اونجا هم ببرم کپی کنم، که گزینه افتتاح حساب ام توی اون بخش برنامه ام، نره توی دیوار.دوما اگر یک کلاس جدیدی اضافه شد،یعنی یک نوع حساب جدید که بخواد از این اینترفیس ارث بری کنه، باز باید برم تک به تک بیزنس هایی که برای افتتاح حساب نوشتم رو تغییر بدم و یک ایف جدید بهشون اضافه کنم.اینجاست که شما باید برید به سمت الگوی Factory pattern.ایجاد الگوی Factory patternاین الگو فهم ساده ای داره.همونطور که از اسمش مشخصه، ما باید برای موجودیت خودمون، یک کارخونه کوچیک بسازیم! و ایجاد شی رو بر عهده اون کارخونه بگذاریم و اون کارخونه است که برای ما شی رو میسازه و میاره بهمون تحویل میده. و اصلا هم اهمیتی نداره اون روال ساختنش. اون چیزی که مهم اینه که ما هر چی سفارش دادیم، همونو بهمون بده. یعنی ما علاوه بر کلاس هایی که اومدن اون اینترفیس مورد نظر ما رو، یعنی BankAccount رو پیاده سازی کردن، یک کلاس مستقل دیگه هم میسازیم به اسم BankAccountFactory.این BankAccountFactory، یک متدی داره که کارش اینه که سفارش بگیره، خروجی رو بده. فقط چون نوع خروجی مشخص نیست. خروجی رو از نوع همون اینترفیس مشترک میده.(بر طبق اصل L اصول SOLID) یعنی یک متدی داره به اسم CreateBankAccount که یک پارامتر ورودی از نوع string داره به اسم accountType و بعد در خروجی BankAccount برمیگردونه. این پارامتر ورودی نوعش به خودتون بستگی داره. میتونه یک Enum باشه. میتونه یک عدد اینتیجر باشه.میتونه هر چیزی باشه، ولی باید در ورودی ما نوع ای که قراره ایجاد بشه رو مشخص کنیم.بعد تمام اون ایف و الس ها داخل این متده مینویسیم. یعنی دقیقا داخل متد ما یک نوع Concrete کلاس رو return میکنیم:_________________________________________________________________________ public interface IBankAccount { AccountDTO GetAccount(); َAccountDetailDTO GetAccountDetails(); } کلاس حساب های جاری _______________________________________________________ public class CheckingAccount: IBankAccount { #region IBankAccount Members public AccountDTO GetAccount() { ... } public AccountDetailDTO GetAccountDetails() { ... } #endregion } کلاس حساب های قرض الحسنه _________________________________________________ public class LoanAccount: IBankAccount { #region IBankAccount Members public AccountDTO GetAccount() { ... } public AccountDetailDTO GetAccountDetails() { ... } #endregion }تعریف BankAccountFactory به شکل زیر است :public class BankAccountFactory { public IBankAccount CreateBankAccout(string accountType) { if (accountType == &quotCheckingAccount&quot) return new CheckingAccount(); if (accountType == &quotLoanAccount&quot) return new LoanAccount(); } }نحوه استفاده در بیزنس برنامه هم به این شکل هست :BankAccountFactory accountFactory = new BankAccountFactory(); IBankAccount account = accountFactory.CreateBankAccout(inpute);