دسته: الگوهای طراحی

الگوی استراتژی (Strategy Pattern)

در زندگی روزمره خود به مسائلی برخورد می کنیم و برای غلبه بر این مسائل ممکن است چندین راه حل پیش رو داشته باشیم. ما با توجه به شرایط خود یا به اجبار یکی از این راه حل ها را انتخاب می کنیم. در طراحی و پیاده سازی یک نرم افزار نیز این شرایط وجود دارد یعنی جهت انجام یک عمل خاص می توانیم از چندین الگوریتم خاص استفاده کنیم در این حالت می توانیم برنامه را طوری طراحی کنیم که خود برنامه به صورت پویا الگوریتم با بازدهی بالاتر را اتنخاب کند یا این امکان را به برنامه کلاینت یا  کاربر بدهیم تا یکی از الگوریتم ها را جهت انجام آن عمل انتخاب کند.

برای مثال فرض کنید که ما در حال طراحی یک برنامه مسیریابی برای یک شبکه هستیم. همانطوریکه می دانیم برای مسیر یابی الگوریتم های مختلفی وجود دارد که هر کدام دارای مزایا و معایبی هستند. و با توجه به وضعیت موجود شبکه یا عملی که قرار است انجام پذیرد باید الگوریتمی را که دارای بالاترین کارائی است انتخاب کنیم. همچنین این برنامه باید امکانی را به کاربر بدهد که کارائی الگوریتم های مختلف را در یک شبکه فرضی بررسی کنید. حالا طراحی پیشنهادی شما برای این مسله چست؟

دوباره فرض کنید که در مثال بالا در بعضی از الگوریتم ها نیاز داریم که گره های شبکه را بر اساس فاصله ی آنها از گره مبداء مرتب کنیم. دوباره برای مرتب سازی الگوریتم های مختلف وجود دارد و هر کدام در شرایط خاص، کارائی بهتری نسبت به الگوریتم های دیگر دارد. مسئله دقیقا شبیه مسئله بالا است و این مسله می توانند دارای طراحی شبیه مسله بالا باشد. پس اگر ما بتوانیم یک طراحی خوب برای این مسئله ارائه دهیم می توانیم این طراحی را برای مسائل مشابه به کار ببریم.

هر کدام از ما می توانیم نسبت به درک خود از مسئله و سلیقه کاری، طراح های مختلفی برای این مسئله ارائه دهیم. اما یک طراحی که می تواند یک جواب خوب و عالی باشد، الگوی استراتژی است که توانسته است بارها و بارها به این مسئله پاسخ بدهد.

الگوی استراتژی گزینه مناسبی برای مسائلی است که می توانند از چندین الگوریتم مختلف به مقصود خود برسند.

نمودار UML این الگو بصورت زیر است:

اجازه بدهید، شیوه کار این الگو را با مثال مربوط به مرتب سازی بررسی کنیم. فرض کنید که ما تصمیم گرفتیم که از سه الگویتم زیر برای مرتب سازی استفاده کنیم.

1

Shell Sort

2

Quick Sort

3

Merge Sort

ما برای مرتب سازی در این برنامه دارای سه استراتژی هستیم. که هر کدام را به عنوان یک کلاس جداگانه در نظر می گیریم (همان کلاس های ConcreteStrategy). برای اینکه کلاس Client  بتواند به سادگی یک از استراتژی ها را انتخاب کنید بهتر است که تمام کلاس های استراتزی دارای اینترفیس مشترک باشند. برای این کار می توانیم یک کلاس abstract تعریف کنیم و ویژگیهای مشترک کلاس های استراتژی را در آن قرار دهیم و کلاس های استراتژی آنها را به ارث ببرند(همان کلاس Strategy) و پیاده سازی کنند.

کلاس abstract که کلاس های استراتژی آنرا به ارث می برند.

abstract class SortStrategy

    {

        public abstract void Sort(ArrayList list);

    }

 

کلاس مربوط به QuickSort

class QuickSort : SortStrategy

    {

        public override void Sort(ArrayList list)

        {

          // الگوریتم مربوطه   

        }

    }

 

کلاس مربوط به ShellSort

    class ShellSort : SortStrategy

    {

        public override void Sort(ArrayList list)

        {

          // الگوریتم مربوطه

        }

    }

 

کلاس مربوط به MergeSort

    class MergeSort : SortStrategy

    {

        public override void Sort(ArrayList list)

        {

          // الگوریتم مربوطه

        }

    }

 

کلاس Context که یکی از استراتزیها را برای مرتب کردن لیست به کار می برد.

    class SortedList

    {

        private ArrayList list = new ArrayList();

        private SortStrategy sortstrategy;

 

        public void SetSortStrategy(SortStrategy sortstrategy)

        {

            this.sortstrategy = sortstrategy;

        }

 

        public void Add(string name)

        {

            list.Add(name);

        }

 

        public void Sort()

        {

            sortstrategy.Sort(list);

        }

    }

 

  

الگوی Template method

بعضی ار مردم نمی توانند زندگی را بدون قهوه تصور کنند و بعضی دیگر زندگی را بدون چای نمی توانند تصور کنند. ولی جزء تشکیل دهنده اصلی هر دو کافئین است. شباهت های دیگر نیز بین این دو مورد وجود دارد. روش ساخت هر دوی آنها تقریبا مشابه است. شما اغلب موارد به ترتیب زیر عمل می کنید.

دستوالعمل ساخت قهوه:

1.       مقداری آب را می جوشانید.

2.       مقدار قهوه در آب جوشانده شده می ریزد تا دم بکشد.

3.       فهوه را در فنجان می ریزد.

4.       مقداری شکر و یا شیر به آن اضافه می کنید.

دستورالعمل ساخت چای:

1.       مقداری آب را می جوشانید.

2.       مقدار چای خشک در آب جوشانده شده می ریزد تا دم بکشد.

3.       چای را در فنجان می ریزد.

4.       مقداری شکر به آن اضافه می کنید.

 

اگر بخواهیم برنامه ای برای تهیه چای و قهوه بنویسیم کلاس های را به صورت زیر خواهیم داشت.

 

کلاس قهوه

Public Class Coffee

    هر مرحله از دستورالعمل ساخت قهوه به عنوان یک متد در نظر گرفته شده است.

    Public Sub prepareRecipe()

        مراحل تهیه قهوه

        boilWater()

        brewCoffeeGrinds()

        pourInCup()

        addSugereAndMilk()

    End Sub

    Public Sub boilWater()

        Console.WriteLine(جوشاندن آب)

    End Sub

    Public Sub brewCoffeeGrinds()

        Console.WriteLine(ریختن پودر قهوه به داخل آب جوشانده شده)

    End Sub

    Public Sub pourInCup()

        Console.WriteLine(ریختن قهوه به فنجان)

    End Sub

    Public Sub addSugereAndMilk()

        Console.WriteLine(اضافه کردن شیر و شکر)

    End Sub

End Class

 

 

کلاس چای

Public Class Tea

    Public Sub prepareRecipe()

        boilWater()

        brewTea()

        pourInCup()

        addSugere()

    End Sub

    Public Sub boilWater()

        Console.WriteLine(جوشاندن آب)

    End Sub

    Public Sub brewTea()

        Console.WriteLine(ریختن چای به داخل آب جوشانده شده)

    End Sub

    Public Sub pourInCup()

        Console.WriteLine(ریختن چای به فنجان)

    End Sub

    Public Sub addSugere()

        Console.WriteLine(اضافه کردن شکر)

    End Sub

End Class

 

 با مشاهده دو کلاس بالا مشاهده می شود که دو متد boilWater و pourInCup دقیقا مشابه یکدیگر هستند. پس طراحی ما دارای اشتباه هست چون بعضی از کدها تکرار شده اند. پس ما باید طراحی خود را تغییر دهیم. ما می توانیم طراحی خود را به صورت زیر تغییر دهیم. دو متد کاملا مشابه در کلاس پایه پیاده سازی می شود. اما چون متد prepareRecipe در هر کلاس به صورت نتفاوت عمل می کند. این متد در زیر کلاس های مربوطه پیاده سازی می شود.

طراحی بالا یک طراحی خوب است اما نمی شود طراحی را بهتر از این کرد؟   اگر توجه کنیم متوجه می شویم که برای تهیه هر دو مورد الگوریتم یکسانی را به کار می بریم:

1.       جوشاندن آبی

2.       اضافه کردن چای یا قهوه به آب جوشیده شده

3.       ریختن نوشیدنی به دست امده در فنجان

4.       اضافه کردن چاشنی مورد نظر به نوشیدنی

پس ما می توانیم با کمی تغییرات متد prepareRecipe را در کلاس پایه پیاده سازی کنیم.

 Public Sub prepareRecipe()

        boilWater()

        brewTea()

        pourInCup()

        addSugere()

 End Sub

 

Public Sub prepareRecipe()

        boilWater()

        brewCoffeeGrinds()

        pourInCup()

        addSugereAndMilk()

End Sub

 

 همانطوریکه می توانید مشاهده کنید کلاس مر بوط به قهوه متدهای به نام brewCoffeeGrinds و addSugereAndMilk را استفاده می کند در حالیکه کلاس مربوط به چای از متد های به نام brewTea و addSugere استفاده می کند. یک راه حل این است که برای هر مرحله غیر مشابه یک نام مشترک در نظر بگیریم. نام متدهای brewTea و brewCoffeeGrinds را به brew تغییر دهیم. و نام متدهای addSugereAndMilk و addSugere را به addCondiments  تغییر می دهیم. پس متد prepareRecipe برای هر دو کلاس به صورت زیر تغییر می کند.

Public Sub prepareRecipe()

        boilWater()

        brew ()

        pourInCup()

        addCondiments()

End Sub

 

 پس حالا می توانیم این متد را بطور کامل به کلاس پایه منتقل کنیم و طراحی را به صورت زیر تغییر دهیم.

حالا ما برای این مسله به یک طراحی ائده ال دست یافتیم. کاری که ما برای حل این مسله انجام دادیم به نام الگوی Template method  شناخته می شود. دلیل نامگذاری این الگو را می توانیم با مشاهده متد prepareRecipe از کلاس پایه درک کنیم. اولین اینکه prepareRecipe یک متد است و دوما به عنوان یک قالب برای الگوریتم به کار می رود. برای نمونه در این مثال شامل الگوریتم تهیه یک نوشیدنی می باشد.

الگوی Template method، مراحل انجام یک الگوریتم را در یک متد در کلاس پایه تعریف می کند و اجازه می دهد زیر کلاس ها یک یا چند مرحله از الگوریتم را پیاده سازی کنند. در واقع اسکلت یک الگوریتم در یک متد تعریف می شود.

هدف ما در این الگو، ایجاد یک قالب برای یک الگوریتم است. یک قالب یک الگوریتم را به صورت مجموعه ای از مراحل تعریف می کند. هر کدام از این مراحل به عنوان یک متد در نظر گرفته می شود. که بعضی از متدها در همان کلاس پایه پیاده سازی می شود و بعضی از متدها به صورت abstract در کلاس پایه در نظر گرفته می شود و در زیر کلاس ها پیاده سازی می شود.

نمودار UML :

نمودار UML این الگو به صورت بالا است. که در آن کلاس AbstractClass یک کلاس Abstract  است. که الگوی قالب در آن تعریف می شود. و متد primitiveOperation یک متد Abstract  است (یک مرحله از الگوریتم) که در زیر کلاس پیاده سازی خواهد شد. همانطوریکه در مثال بالا مشاهده کردیم می تواند بیش از یک کلاس ConcerteClass وجود داشته باشد. این حالت زمانی رخ می دهد که بعضی از مراحل الگوریتم بتواند در روش های مختلف پیاده سازی شود.

الگوی زنجیره مسئولیت (Chain of Responsibility pattern)

یک سرویس دهنده اینترنتی را در نظر بگیرید. این شرکت دارای سه سطح پشتیبانی به شرح زیر برای کاربران است:

سطح 1: در این سطح به مشکلات ابتدایی کاربران مانند فراموش کردن کلمه عبور کاربران و … رسیدگی می شود.

سطح 2: اگر اعضای گروه سطح 1 نتوانند مشکل مورد نظر را حل کنند. مشکل برای حل به گروه سطح 2 ارجاع داده می شود.

سطح 3: اگر اعضای گروه 2 نیز نتوانند مشکل مورد نظر را حل کنند.مشکل برای حل به گروه سطح 3 ارجاع داده می شود. در این سطح برای حل مشکل یک قرار ملاقات در محل مشکل با مشتری گذاشته می شود.

همانطوریکه در مثال بالا مشخص است. ممکن است هر کدام از گروه ها به در خواست ارائه شده پاسخ دهند. ولی دقیقا مشخص نیست کدام گروه به در خواست ارائه شده پاسخ خواهد داد. اما همه گروه ها باید شانس این را داشته باشند که به درخواست ارائه شده پاسخ دهند. این شانس اولویت دار است. برای نمونه در مثال بالا ابتدا به تیم سطح 1 این شانس داده می شود تا مشکل را حل کند. در صور ت عدم حل مشکل به ترتیب، شانس به تیمهای 2 و 3 داده می شود. این امکان نیز وجود دارد که هیچ یک از تیم ها نتوانند این مشکل را حل کنند و درخواست ارائه شده بدون پاسخ بماند.

مثالی که در بالا ارائه شده تقریبا تعریفی از الگوی Chain of responsibility (زنجیره مسئولیت) می باشد.

در این الگو به یک شی اجازه داده می شود در خواستی را ارسال کند بدون اینکه بداند کدام شی یا اشیاء آنرا دریافت خواهند کرد و به آن پاسخ خواهند داد. این کار با ارسال درخواست به زنجیره ای از اشیاء صورت می گیرد. هر شی در این زنجیره می تواند به درخواست ارائه شده پاسخ دهد یا آن را به شی بعدی در زنجیره ارسال کند. در واقع هر شی در زنجیره بعنوان یک پاسخگو برای درخواست ارائه شده عمل می کند . اگر شی مورد نظر بتواند درخواست راجواب دهد، پاسخ آن را می دهددر غیر اینصورت آن را به شی جانشین اش (شی بعدی در زنجیره) ارسال می کند.

برای ادامه موضوع یک مثال دیگر را در نظر می گیریم. فرض کنید شما مسول نوشتن یک برنامه برای شبیه سازی فرآیند تائید یک درخواست سفارش از یک شرکت هستید. در این شرکت، بعد از اینکه درخواستی توسط مشتری ارائه می شود باید در ابتدا توسط مدیر  تائید شود و در صورت تائید توسط مدیر، جنس به مشتری فروخته خواهد شد. این شرکت دارای چهار سطح مدیریت می باشد و مشخص شده است که هر مدیر سفارشاتی با مبلغ چقدر را می تواند تائید کند.که حد مبالغی که می توانند تائید کنند در جدول زیر مشخص شده است.

سطوح مدیریت

مبلغ سفارش

مدیر نمایندگی

25000

مدیر ناحیه

100000

نايب‌ رئيس‌

200000

رئيس‌

400000

 

شما برای اینکار چه الگوی را پیشنهاد می کنید؟

اجاز ه بدهید کار را با الگوی زنجیره مسولئیت ادامه دهیم. در ابتدا یک کلاس برای سفارش به نام سفارش خرید در نظر می گیریم.

سپس یک کلاس انتزاعی به نام مدیریت سفارش تعریف می کنیم که دارای یک اینترفیس مشترک برای هر کدام از سطوح مدیریت است. که نمودار کلاس  مربوط به مدیریت سفارش و سطوح مدیریت به صورت زیر خواهد بود. که متد تعیین شی جانشین (SetSuccessor)، برای مشخص کردن شی بعدی (شی جانشین) در زنجیره عمل می کند.

برای تائید یک سفارش، کارهای زیر صورت خواهد گرفت:

1.    ایجاد یک مجموعه از اشیای که می توانند به درخواست های رسیده شده پاسخ بدهند (سطوح مدیریت). و قرار دادن آنها در یک زنجیره به طور مرتب بر حسب مقادیری که می توانند تائید کنند. هر یک از این اشیاء توسط متد SetSuccessor به شی بعدی متصل می شود.(کلاس Main)

2.       یک درخواست تائید به اولین شی موجود در زنجیره ارسال می شود که این کار توسط فراخوانی متد   authorize  آن شی انجام می شود. اگر مبلغ سفارش از مقدار مبلغی که ان شی نی تواند تائید کند کوچکتر باشد، آنرا تائید می کند در غیر اینصورت آن درخواست را به شی بعدی در زنجیره ارسال می کند. (برای مثال در کد زیر می توانید کلاس BranchManager را بررسی کنید. ) 

 

کلاس مدیریت سفارش

abstract class RHandler

    {                                     

        protected RHandler successor;

 

        public void SetSuccessor(RHandler successor)

        {

            //تعیین شی جانشین

            this.successor = successor;

        }

 

        public abstract void authorize (PurchaseRequest purchase);

    }

کلاس مدیر نمایندگی

 

class BranchManager: RHandler
  {
   

 public override void authorize (PurchaseRequest purchase)
    {
   // تائید سفارش

      if (purchase.Amount <=25000)
      {

       

      }
      else if (successor != null)
      {
        successor.ProcessRequest(
RegionalDirector);
      }
    }
  }

کلاس Main

class MainApp

    {

        static void Main()

        {

            BranchManager A = new BranchManager();

            RegionalDirector B = new RegionalDirector();

            VicePresident C = new VicePresident();

            President D = new President();

            A.SetSuccessor(B);

            B.SetSuccessor(C);

            C.SetSuccessor(D);

 

            Purchase p = new Purchase( 1, “Order 1”,15000);

            A.authorize(p);

 

            Purchase p = new Purchase( 2, “Order 2”,110000);

            A.authorize(p);

        }

    }

 

 

دیاگرام کلاس الگو:

State Pattern

حال شما چطور است؟، سوالی است که در یک روز بارها از شما پرسیده می شود یا شما از دیگران می پرسید. حال شما یک ویژگی ثابت شما نیست بلکه یک ویژگی پویا است که ممکن است با توجه به رویدادهای خارجی یا درونی تغییر کند. و شما می توانید در مقابل یک رویداد مشابه با توجه به حالتان رفتار متفاوتی از خود نشان دهید.

علاوه بر خودمان اکثر اشیای که در اطراف ما قرار دارند دارای حالت های متفاوتی هستند و با توجه به رویداد های بیرونی و داخلی از حالت به حالت دیکر می روند و رفتار متفاوتی را در هر حالت از خود بروز می دهند.

برای مثال فرضی کنید با یک برنامه مانند Windows Media Player، در حال دیدن یک فیلم هستید. این برنامه دکمه های مانند Play، Pause و Stop دارد که با کلیک روی آنها می توانید برنامه را به حالت همنام با آن دکمه ببرید. پس برای این شی، سه حالت بالا را می توانید در نظر بگیرید. حال تصویر کنید که یک برنامه شبیه به مثال بالا را خودمان می خواهیم بنویسیم. یکی  از شیوه ها برای انجام این کار می تواند به این ترتیب باشد که ما در ابتدا تمام حالاتی را که یک شی Media Player، می تواند قبول کند را جمع آوری می کنیم. در مرحله دوم برای هر حالت یک متغییر تعریف می کنیم و مقادیر مربوط به آنها را مشخص می کنیم و علاوه بر اینها یک متغییر برای نگهداری حالت فعلی شی در نظر می گیریم.

const int play=0;

const int stop=1;

const int pause=2;

int state = stop; //حالت اولیه

سپس تمام اعمالی و رویدادهای که در سیستم می تواند رخ دهد را جمع آور می کنیم. و در انتها یک کلاس ایجاد می کنیم که به عنوان یک ماشین حالت عمل کند، برای هر عمل یک متد ایجاد می کنیم که جملات شرطی را به کار می برند تا رفتار مناسبی را که در هر حالت نیاز است  را پیاده سازی کنند.

public void  Stop()

{

if (state == play)

   {

    //عملیات مورد نظر

   }

if (state == pause)

   {

    //عملیات مورد نظر

   }

if (state == stop)

   {

    //عملیات مورد نظر

   }

}

روش بالا می تواند  برای پیاده سازی یک شی که دارای چندین حالت است به کار برود. ولی تصور کند که یک حالت جدید را بخواهیم به شی اضافه کنیم و یا در نحوه عملکرد شی در یک حالت خاص تغییر دهیم مطمئنا کار مشکلی را خواهیم داشت.

یک روش خوب و کارا برای پیاده سازی اشیای که در طول حیات خود می توانند چندین حالت مختلف داشته باشند، می تواند چگونه باشد؟ شاید شما پاسخ های خوب و بهتری بتوانید ارائه کنید. ولی یکی از روش ها می تواند به صورت زیر باشد:

1.       در ابتدا یک اینترفیس به نام state تعریف می کنیم که شامل یک متد برای هر عملی است که شی مورد نظر می تواند انجام دهد.

2.       در مرحله دوم برای هر حالت شی یک کلاس تعریف می کنیم که اینترفیس state را پیاده سازی می کند. در واقع این کلاس مسول پاسخگویی به رویدادها است. زمانیکه  شی در این حالت قرار دارد.

 

برای نمونه در همان مثال Media Player،  در ابتدا ما یک کلاس اینترفیس به نام state تعریف می کنیم. سپس برای هر حالت یک کلاس تعریف می کنیم که اینترفیس state را پیاده سازی می کند. برای پیاده سازی هر حالت ما نیاز داریم  تا رفتار هر کلاس را مشخص کنیم هنکامیکه هر عمل فرا خوانی می شود.

 

 

بعد از پیاده سازی تمام حالات شی مورد نظر، به کلاس اصلی یا همان کلاس شی مورد نظر (Media Player) می رسیم. این کلاس همان متد های روش اول را خواهد داشت اما با چندین تفاوت:

1.       به جای استفاده از متغییرهای عددی برای هر حالت و حالت فعلی، از اشیاء که برای حالت ها تعریف کردیم استفاده می کنیم.

 

کد جدید

کد قدیمی

public class MediaPlayer{

State MediaState;

PlayState playstate;

PauseState pausestate;

StopState stopstate;

MediaPlayer{

playstate = new PlayState();

 new PauseState ();=  pausestate

stopstate = new StopState();

MediaState = stopstate;

}

 

Public class MediaPlayer{

const int play=0;

const int stop=1;

const int pause=2;

MediaPlayer{

const int state = stop; //حالت اولیه

}

 

2.       در پیاده سازی عمل ها دیگر از جملات شرطی استفاده نمی کنیم. بلکه وقتی عملی فراخوانی می شود آن عمل به کلاس حالت فعلی برای انجام واگذار می شود.

public void  Stop()

{

MediaState.stop();

}

چون MediaState اشاره گر به حالت فعلی است. متد stop حالت فعلی اجاره خواهد شد بدون هیچ جمله ی شرطی.

  

نمودار الگوی حالت (State Pattern):

ما  تا اینجا یک الگوی حالت را بطور کامل پیاده سازی کردیم. ولی دوباره نکات بالا را باهم بررسی می کنیم.

1.       رفتار یک شی توسط حالت داخلی آن شی مشخصی می شود که در مقابل رخ دادن یک رویداد می تواند تغییر کند.

2.       تعداد حالات یک شی را باید بتوانیم افزایش بدهیم بدون اینکه مجبور باشیم قسمت های زیادی از کد را مرور کنیم یا تغییر دهیم.

الگوی حالت، به یکی شی اجازه می دهد رفتارش را زمانیکه حالت داخلی شی تغییر کند، تغییر دهد. و همچنین با در نظر گرفتن هر حالت به عنوان یک شی جداگانه، حالات شی را نهان سازی می کند. و ما می توانیم هر تعداد حالات مورد نظر را به شی مربوطه اضافه کنیم بدون اینکه در پیاده سازی کلاس context تغییر انجام دهیم.

  

 

کلاس Context کلاسی است که می تواند دارای چندین حالت داخلی باشد در مثال ما همان Media Player.

اینترفیس State یک اینترفیس مشترک برای همه حالت ها تعریف می کند.

کلاس های Concrete، عمل های را که شی در آن حالت انجام می دهد، پیاده سازی می کنند.(کلاسهای PlayState, StopState,PauseState)

 

Adapter Pattern

Adapter Pattern:

الگوی سازگارساز (وفق دهنده، مبدل، Adapter Pattern)، الگوی است که در دنیا واقعی نمونه های زیادی از این الگو وجود دارد و به همین خاطر درک این الگو زیاد مشکل نخواهد بود.

شکل زیر را در نظر بگیرید و فرض کنید که کامپیوتر laptop شما دارای دو شاخه ای به شکل زیر است ولی پریز های برقی که در دیوار نصب شده است به شکلی است که در سمت چپ تصویر نشان داده شده است. شما برای استفاده از لپ تاپ خود در این محل چه کاری را انجام می دهید؟

شما می توانید برای حل این مشکل از یک مبدل استفاده کنید. که بین دو شاخه، لپ تاپ شما و پریز برق قرار می گیرد و شما می توانید با قرار دادن دو شاخه در مبدل و زدن مبدل به پریز از برق استفاده کنید. در واقع مبدل، اینترفیس پریز را به گونه ای تغییر می دهد که با اینترفیسی که لپ تاپ شما نیاز دارد همخوانی داشته باشد.

مثال بالا یک مثال از دنیا واقعی بود. ولی یک وفق دهنده شی گرا چست؟ وفق دهنده های شی گرا دقیقا کار مشابهی را انجام می دهند. آنها یک اینترفیس را به گونه ای تغییر می دهند که سرویس گیرنده ها انتظار آنرا دارند.

وفق دهند های شی گرا:

فرض کنید که شما یک سیستم نرم افزاری دارید که نیاز دارد با کتابخانه ی از کلاس های جدید که مربوط به فروشندگان است کار کند، اما کلاس های جدید به گونه ای طراحی شده اند که اینترفیس متفاوتی را نسبت به اینترفیس قبلی دارند.

برای حل این مشکل شما چیکار می کنید؟ می توانید سیستم نرم افزاری موجود را به گونه ای باز نویسی کنید که با کلاس هاس جدید همخوانی داشته باشد. یا کلاس های جدید را به گونه ای تغییر می دهید که با سیستم موجود همخوانی داشته باشد. اما آیا راه حل کم هزینه تری وجود ندارد که بدون تغییر هیچ کدام از سیستم ها، سیستم کار خود را انجام دهد. شما می توانید یک کلاس بنویسید که اینترفیس فروشندگان را به گونه ای تغییر بدهد که سیستم موجود انتظار دارد.

 

 

Adapter(سازگار ساز، وفق دهنده) مانند یک واسطه عمل می کند و درخواست های را که از سیستم موجود می رسد دریافت می کند و آنها را به شکلی تغییر می دهد و برای کلاس مورد نظر در سیستم فروشندگان ارسال می کند که قابل درک و اجرا برای آن کلاس باشد.

 

برای نمونه در شکل بالا کلاس کلاینت متدی به نام Max را که در اینترفیس Math1 تعریف شده است را نیاز دارد. ولی کلاس های که این اینترفیس را پیاده سازی می کنند. عملی را که کلاس کلاینت در نظر دارد انجام نمی دهند. ولی اینترفیس Math2 دارای عملی به نام Maxvalue است که توسط کلاس Imp2 پیاده سازی شده است و دقیقا خواسته کلاس کلاینت را انجام می دهد ولی با یک اینترفیس دیگر. برای حل این مشکل چه پیشنهادی دارید؟

برای حل این مشکل ما از یک مبدل (Adapter)، استفاده می کنیم. یعنی یک کلاس جدید تعریف می کنیم که اینترفیسی را که کلاس کلاینت می شناسد پیاده سازی می کند. ولی عمل Max را طوری سر بار گذاری می کنیم که وقتی آن فراخوانی می شود. عمل Maxvalue از کلاس Imp2 را فراخوانی کند.

 

 

نمودار کلاس Adapter:

نمودار کلاس الگو به صورت زیر است:

 

 

 

الگوی Façade (نما)

الگوی Façade (نما):

الگوی نما در سیستم های استفاده می شود که توسعه دهندگان می خواهند پیچیدگی یک سیستم را در یک کلاس (در بعضی مواقع چند کلاس) مخفی کنند و با استفاده از این کلاس و یا کلاس ها به عنوان واسط و میانجی، پیچیدگی سیستم را کاهش دهند.

اگر زیر سیستمی حاوی چند کلاس باشد و این کلاس ها با همدیگر همکاری کنند تا سرویس های را که قرار است زیر سیستم ارائه دهد را تداراک ببینند، ریسکی در ایجاد واسط به آن زیر سیستم وجود دارد. هر کلاس ممکن است چندین عملیات داشته باشد. زیر سیستم های دیگر باید  توانایی ارسال پیام به نمونه هایی از هر کلاس را داشته باشند. به این ترتیب، یک اتصال قوی بین زیر سیستم ها به وجود می آید و اگر تغییر در کلاس های یک زیر سیستم ارائه دهنده سرویس بوجود بیاید، نیازمند ردیابی تمامی مکان ها در زیر سیستم های هستیم که عملیات این کلاس ها را فراخوانی کرده اند.

یک روش غلبه بر این وضعیت، ایجاد کلاس façade می باشد، که واسطی را به زیر سیستم ایجاد می کند و این کلاس به عنوان نماینده زیرسیستم مورد نظر با زیر سیستم های دیگر عمل می کند. به این ترتیب، تغییر در پیاده سازی کلاس های زیر سیستم اثر محدودی بر زیر سیستم های دیگر دارد. و اگر تغییری در کلاس ها صورت بگیرد، فقط لازم است نقاطی در زیر سیستم های دیگر پیدا شود که در آنها، پیامی به نمونه های از کلاس façade ارسال شده است.

حالا با بررسی چند مثال می توانیم تصویری بهتری از این الگو به دست بیاوریم.

تصور کنید شما به عنوان مدیر پروژه برای یک پروژه نسبتا بزرگ انتخاب شده اید. در حالت اول شما خودتان وظیفه دارید مسئولیت تک و تک افراد پروژه را به آنها بگوید و وظایف واگذر شده به آنها را تحویل بگیرید. در این حالت شما وظیفه دارید با تک و تک افراد ارتباط برقرار کنید و همچنین برای بازخواست وظایف شما باید نحوه ارتباط این افراد را با یکدیگر بدانید. در حالت دوم یک نفر به عنوان سر گروه انتخاب می شود و شما مسئولیت ها واگذر شده را به او اعلام می کنید و برای تحویل وظایف فقط از او بازخواست می کنید. حالا دو حالت را باهم مقایسه کنید در کدام حالت پیچیدگی و حجم کاری شما کم است؟ البته که حالت دوم. در حالت دوم در واقع ما از الگوی façade استفاده کردیم. سر گروه در این حالت به عنوان کلاس façade است.

برای مثال دوم در نظر بگیرید شما مسئول طراحی یک فروشگاه الکترونیکی هستید که در آن مشتری باید قادر باشد برای پرداخت مبلغ خرید خود از کارت های اعتباری استفاده کند. شما به یکی از شرکت های صادر کننده کارت اعتباری مراجعه می کنید و می خواهید نحوه استفاده و موارد مورد نیاز برای اینکار را به شما ارائه دهد. شرکت مورد نظر هیچ وقت ساختار و کلاس های مورد نظر را که در داخل برنامه خود برای اینکارها استفاده می کنند دقیقا به شما ارائه نمی دهد. چون این نه به نفع شرکت مورد نظر است نه به نفع شما، چون با این کار  پیچیدگی سیستم پرداخت دقیقا به برنامه شما نیز اضافه خواهد شد. پس چاره کار چیست؟ استفاده از کلاس façade. شرکت مورد نظر یک کلاس façade را به عنوان میانجی بین برنامه شما و برنامه سیستم پرداخت خودشان طراحی و در اختیار مشتریان خود قرار می دهند و شما پیام های خود را به این کلاس ارسال می کند و آن نیز برای گرفتن و ارسال پاسخ به شما، پیام مورد نظر را به کلاس اصلی ارسال می کند و پاسخ را از آن دریافت و به شما ارسال می کند.

برای مثال آخر فرض کنید شما وظیفه دارید یک برنامه برای ایجاد و ارسال ایمیل بنویسید کلاس های که ممکن است برای اینکار در نظر بگیرید می تواند به صورت شکل زیر باشد.

·         کلاس MessageBody: این کلاس شامل بدنه پیام خواهد بود.

·         کلاس Attachment: این کلاس نمونه سازی می شود تا یک کلاس MessageBody در صورت نیاز شامل فایل پیوست باشد.

·         کلاس MessageHeader: این کلاس نمونه سازی می شود تا شامل اطلاعات سرآیند فایل باشد مانند From,To,Subject,….

·         کلاس Message: این کلاس نمونه سازی می شود تا دو کلاس MessageBody و MessageHeader را به هم متصل کند (پیوند بزند).

·         کلاس Security: این کلاس نمونه سازی می شود تا در صورت نیاز پیام ارسالی رمز گذاری شود.

·         کلاس MessageSender: این کلاس نونه سازی می شود تا در آخر پیام مورد نظر را به سرور مربوطه ارسال کند.

شکل زیر نمودار کلاس های بالا را همراه با ارتباطات آنها نشان می دهد.

 

 

 

با مشاهده تصویر بالا می تواند حدس بزند که کار با کلاس های که برای اینکار در نظر گرفته اید، کلاس client را می تواند بیش از حد پیچیده کند. برای استفاده از این کلاس ها، کلاس کلاینت باید با این 6 کلاس و همچنین باید با روابط بین آنها آشنا باشد. دوباره برای کاهش پیچیدگی می توانیم از الگوی façade استفاده کنیم یعنی برای کاهش پیچیدگی از یک کلاس جدید اتفاده می کنیم. طرح جدید می تواند به شکل زیر باشد.

 

 

نمودار کلاس الگوی نما:

نمودار کلاس الگو به صورت زیر می باشد.

اجزاء تشکیل دهنده الگو:

 

کلاس ها و اشیاء تشکیل دهنده این الگو عبارتند از:

·         کلاس façade :

  • این کلاس می داند که کلاس های زیر سیستم باید به پیام دریافت شده پاسخ دهند.
  • پیام های دریافت شده را به شی مناسب برای پاسخگوئی ارسال می کند.

·         کلاس های زیر سیستم:

  • کلاس های زیر سیستم از وجود کلاس façade آگاه نیستند.
  • به پیام های ارسال شده توسط کلاس façade پاسخ می دهند.

Factory Patterns (قسمت اول)

مفهوم Factory (کارخانه)، دقیقا از معنی لغوی آن مشخص است. کارخانه مکانی است که در آن کالاها و محصولاتی تولید می شود. و در بحث الگوهای طراحی، یک Factory کلاسی است که نمونه های از کلاس های دیگر را با توجه به پارامترهای که برای آن ارسال می شود، ایجاد می کند.

برای ایجاد یک ذهنیت در خودتان می توانید تصور کنید که ما یک فروشگاه پوشاک داریم هر وقت که فروشگاه به لباس خاصی نیاز داشته باشید. اسامی آن را به تولید کننده(Factory) ارسال می کند و تولید کننده با توجه به اسامی ارسال شده آن لباس ها را تولید کرده و به فروشگاه ارسال می کند. در این مثال تولید کننده همان کلاس Factory ما است که با توجه به پارامترهای ارسال شده (لیست پوشاک مورد نظر فروشگاه) لباس مورد نظر (شی موزد نظر) را تولید می کند و به مشتری ارسال می کند.

نمونه کد اول در VB.NET

‘Class Shirt

Public Class Shirt

Protected _Color As String

Protected _Size As String

Public ReadOnly Property Color()

Get

Return _Color

End Get

End Property

Public ReadOnly Property Size()

Get

Return _Size

End Get

End Property

End Class

‘Class Shrit_A

Public Class Shrit_A

Inherits Shirt

Public Sub New()

Me._Color = “Blue”

Me._Size = “XL”

End Sub

End Class

‘Class Shrit_B

Public Class Shirt_B

Inherits Shirt

Public Sub New()

Me._Color = “Red”

Me._Size = “M”

End Sub

End Class

‘Class Factory

Public Class Factory

Public Function GetShirt(ByVal Type As String) As Shirt

If Type = “A” Then

Return New Shrit_A()

Else

Return New Shirt_B()

End If

End Function

End Class

‘Class Store

Public Class Store

Private _OrderType As String

Public Property OrderType()

Get

Return _OrderType

End Get

Set(ByVal value)

_OrderType = value

End Set

End Property

Public Function Order() As Shirt

Dim Ins1 As New Factory

Dim Ins2 As Shirt = Ins1.GetShirt(_OrderType)

Return Ins2

End Function

End Class

نحوه استفاده

Private Sub Cmd_Order_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)

Dim Ins1 As New Store

Dim INs2 As New Shirt

Ins1.OrderType = “A”

INs2 = Ins1.Order()

End Sub

Singleton Pattern

Singleton Pattern:

اشیاء زیادی وجود دارند که ما فقط و فقط به یک نمونه آن از اشیاء نیاز داریم. برای مثال سیستم شما فقط می تواند فقط یک نمونه از پورت COM1 را داشته باشید یا در اداره شما فقط می تواند یک مدیر عامل وجود داشته باشد و …. حال فرض کنید که شما کلاسی دارید که فقط باید یک نمونه از آن ایجاد شود. شما این مسئله را چگونه حل می کنید؟

Singleton Pattern، الگوی است که برای اینکار ایجاد شده است. یعنی اطمینان از اینکه از کلاس ما فقط یک نمونه ایجاد شده است. این الگو برای این به کار می رود تا ایجاد نمونه از یک کلاس را محدود کند.

 

نحوه پیادسازی:

آسانترین راه برای ایجاد کلاسی که فقط می تواند یک نمونه از آن ایجاد شود استفاده از یک متغییر استاتیک در داخل کلاس می باشد. اولین نمونه ای که از این کلاس ایجاد می شود ما متغییر استاتیک را مقدار True می دهیم و در سایر دفعات این متغییر استاتیک در سازنده تست می شود  اگر مقدارش False باشد یک نمونه از شی ایجاد خواهد شد در غیر این صورت نمونه جدیدی از آن شی ایجاد نخواهد شد.

 

نمونه کد اول در VB.NET

تعریف کلاس

Public Class Singleton

    Private Shared instance_flag As Boolean = False

    Private _Id As Integer

 

    Public Property Id() As Integer

        Get

            Return _Id

        End Get

        Set(ByVal value As Integer)

            _Id = value

        End Set

    End Property

 

    Public Shared Function GetInstance() As Singleton

        If (Not instance_flag) Then

            instance_flag = True

            Return New Singleton

        Else

            Return Nothing

        End If

    End Function

 

End Class

نحوه استفاده

        Dim Ins1, Ins2 As Singleton

        Ins1 = Singleton.GetInstance

        Ins2 = Singleton.GetInstance

        Ins1.Id = 200

 

        Console.WriteLine(Ins1.Id)

        Console.WriteLine(Ins2.Id)

 

        Ins2.Id = 123

        Console.WriteLine(Ins1.Id)

        Console.WriteLine(Ins2.Id)

 

        Console.ReadKey()

خروجی: هنگام استفاده از کلاس بالا به شیوه ای که نوشته شد نمونه دوم از آن هیچ وقت ایجاد نخواهد شد پس تمام عملیاتی که نمونه دوم می خواهد انجام دهد با خطا همراه خواهد بود. برای حل این مشکل می توانیم به صورت زیر عمل کنیم.

 

 

نمونه کد دوم در VB.NET

تعریف کلاس

Public Class Singleton

    Shared m_instance As Singleton

    Private _Id As String

    Public Property Id() As Integer

        Get

            Return _Id

        End Get

        Set(ByVal value As Integer)

            _Id = value

        End Set

    End Property

    Public Shared Function GetInstance() As Singleton

        If (m_instance Is Nothing) Then

            m_instance = New Singleton()

        End If

        Return (m_instance)

    End Function

 

    Private Sub New()

    End Sub

End Class

نحوه استفاده

        Dim Ins1, Ins2 As Singleton

        Ins1 = Singleton.GetInstance

        Ins2 = Singleton.GetInstance

        Ins1.Id = 200

 

        Console.WriteLine(Ins1.Id)

        Console.WriteLine(Ins2.Id)

 

        Ins2.Id = 123

        Console.WriteLine(Ins1.Id)

        Console.WriteLine(Ins2.Id)

 

        Console.ReadKey()

خروجی:

       

200

200

123

123

 

الگو های طراحی (Design Pattern)

کسی وجود دارد که قبلاً مسله شما را حل کرده است.

در مهندسی نرم افزار یک الگوی طراحی، یک روش حل قابل تکرار برای مسائلی هست که عموماً در طراحی نرم افزار با آن برخورد می کنیم. یک الگوی طراحی یک قالب یا شرح برای چگونگی حل مسائلی است که می تواند در شرایط مختلف استفاده شود.یک الگوی طراحی، راه حلی است که برای مستند سازی ارزشمند تشخیص داده شده است، بطوریکه توسعه دهند گان دیگر می توانند آن را در حل مسائل مشابه به کار ببرند.همانگونه که طراحی شی گرا ادعا می کند که استفاده مجدد از کتابخانه ها و قطعات را افزایش می دهد، ادعا می شود که استفاده از الگو های طراحی، استفده مجدد از کتابخانه ها و قطعات را افزایش می دهد.

الگو ها تکنیک هایی هستند که افراد زمانی از آنها برای حل مسائل خاص استفاده کردند. و به عنوان راه حل های خوب شناخته شده اند. سپس این تکنیک ها مستند سازی شده اند تا توسعه دهندگان هنگام برخورد با مسائل مشابه از این مستندات استفاده کنند و مسائل خود را حل کنند.

تاریخچه الگو های طراحی :

استفاده از الگوها برای اولین بار به ذهن یک معمار به نام الکساندر خطور کرد. الکساندر با این مشکل روبرو شد که یک طرح خوب و با کیفیت برای یک ساختمان چگونه می تواند باشد. او برای حل مشکل خود، ساختمانها، خیابانها، شهرک ها و هر مکانی که یک انسان برای خودش می سازد را مورد مطالعه و بررسی قرار داد. او کشف کرد که بناهای خوب از نظر طراحی دارای ویژگیهای مشترک هستند. او کشف کرد که بناهای خوب دارای ویژگیهای مشابه هستند واین ویژگیهای مشابه را الگو نامید.

هر الگو بیانگر یک مسئله و مشکل است که می تواند بارها و بارها روی بدهد همراه با راه حل آن مسئله، وشما می تواند از این راه حل برای میلیون ها بار استفاده کند بدون نیاز مجدد برای پیدا کردن راه حل.

در اوایل دهه 1990، بعضی از توسعه دهندگان نرم افزار با کارهای الگساندر برخورد کردند.آنها با این سوال روبرو شدند که اگر الگو های طراحی معماری در این رشته به صورت صحیح جواب می دهد آیا می توان الگوهای را برای طراحی نرم افزار بوجود آورد.

  • در نرم افزار چه مسائل وجو دارد که بارها رخ می دهد و تقریبا با روشهای مشابه می توان آنها را حل کرد؟
  • آیا امکان استفاده از مفهوم الگوها در طراحی نرم افزار وجود دارد، آیا می توان راه حل های را برا اساس الگوها بعد از شناسایی الگوها ایجاد کرد.

سوالات بالا، سوالاتی بودند که توسعه دهندگان مطرح کردند و پاسخ آن را یافتند. پاسخ سوال بلی بود. مرحله بعدی شناسایی الگوها بود و توسعه استانداردهای برای مستند سازی الگوها.

در اوایل دهه 1990، افرادی زیادی روی الگوهای طراجی کار می کردند. اما چهار نفر به نام های، گاما، جاکوبسون، هلم و ولسایدز بیشترین تاثیر را در این زمینه با نوشتن کتابی به نام

“Design Pattern: Elements of Reusable Object-Oriented Software”، داشتند. این چهار نویسنده بهGang of Four مشهور است. آنها در این کتاب ائده استفاده از الگوها را در طراحی نرم افزار به کار بردند.و یک فرمت استاندارد را برای مستندسازی الگوها ایجاد کردند. 23 نوع از الگوها را دسته بندی کردند و …. به مرور زمان فرمت های استاندارد دیگری برای مستند سازی الگوها پیشهناد شد.

قالب مستند سازی برای الگوهای طراحی :

نام الگو یک نام خوب و مفید برای الگو
هدف (intent) یک جمله کوتاه و مختصر درباره چیزی که الگو انجام می دهد. (تعریف مسله و راه حل به صورت مختصر و مفید)
نام مستعار نام های دیگری که الگو با آن شناخته می شود.
ساختار یک نمایش گرافیکی از الگو
اجزاء تشکیل دهنده (Participants) کلاس ها و اشیائ که در الگو شرکت دارند (وجود دارند).
همکاریها (Collaborations) چگونه اجزای تشکیل دهنده با هم همکاری می کنند تا وظایفشان را انجام دهند.
نتايج‌ (Consequences) نتایج استفاده از الگوی مورد نظر
پیاده سازی تکنیک های برای پیاده سازی الگوی مورد نظر
نمونه کد تکه کدی برای پیاده سازی یک نمونه
الگو های مرتبط الگوهای طراحی دیگری که ارتباط نزدیگ با الگوی مورد نظر دارند.

دسته بندی الگو ها :

1-   الگوهای بوجود آورنده(Creational Pattern): همه الگو های که در این دسته قرار می کیرند در ارتباط با روش های ایجاد اشیاء هستند.

2-   الگوهای ساختاری(Structural Patten): این نوع الگوها شرح می دهند چگونه اشیاء و کلاس ها می توانند در ساختارهای بزرگتر باهم ترکیب شوند.

3-      الگوهای رفتاری(Behavioral Pattern): این نوع الگو ها روی ارتباط اشیاء با یکدیگر تمرکز دارند.

Creational Structural Behavioral
Factory Method

Abstract Factory

Builder

Prototype

Singleton

Adapter

Bridge

Composite

Decorator

Flyweight

Façade

Proxy

Interpreter

Template Method

Chain of Responsibility

Command

Iterator

Mediator

Memento

Observer

State

Strategy

Visitor

فهرست الگوهای طراحی