این سایت برای ie9 طراحی نشده است

لطفا دستگاه خود را بچرخانید.

الگوی طراحی – Chain of Responsibility یا زنجیره مسئولیت

۱۱ اردیبهشت ۱۳۹۶ حسین صداقت
بدون دیدگاه

به نام خدا

Four Part Chain Graphic

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

تعریفی که Gang of Four  از اینDesign Pattern کرده است به صورت زیر است:”اجتناب از وابستگی بین فرستنده Request و دریافت کننده آن با ایجاد قابلیت رسیدگی به Request توسط شی ها و سناریوهای های مختلف”.

در این الگو زنجیره request را دریافت کرده و سپس این Request را بین Chain (حلقه) های مختلف ارسال میکند تا زمانیکه یک chain این درخواست رو پردازش کند و هنگامی که این درخواست توسط یکی از Chain ها پردازش شود ارسال متوقف می شود.در حقیقت Request  وارد Chain های مختلف می شود و در زنجیره حرکت می کند. زمانیکه یک زنجیره قابلیت پردازش این Request را داشته باشد حرکت Request بین زنجیره های مختلف متوقف می شود.

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

۱-Handler :یک Interface می باشد که signature متد handleRequest را تعریف می کند.(signature یک متد در شامل نام متد وردی و خروجی متد می باشد.دقیقا اتفاقی که در یک  interface یا یک متد Abstarct می افتد) و همچنین امکان set کردن successor رو نیز در این کلاس دیده شده است.

۲-Concrete handler : که از کلاس Handler ارث می برد و وظیفه پیاده سازی متد handleRequest  را دارد و همچنین به successor نیز دسترسی دارد.اگر یک Concrete Handler نتواند یک request را handle کند آن request را از طریق Successor به Handler دیگری ارسال می کند.

۳-client :  وظیفه ارسال request به یک Concrete handler را دارد.

نمودار UML این design pattern  به صورت زیر است:

chain

اگر تا این لحظه از تعاریف و دیاگرام بالا چیز زیادی متوجه نشدید کاملا طبیعی هستش! در ادامه مثال می آوریم که این design pattern  را به خوبی توضیح میدهد.

سناریوی زیر را در نظر بگیرید:

فرض کنید ما یک شرکت داریم که این شرکت ۴ نوع ایمیل رو دریافت می کند:۱-ایمیل های درخواست سرویس جدید ۲- ایمیل درخواست پشتیبانی ۳-ایمیل از طرف شرکت های همکار و درخواست همکاری ۴-و ایمیل های هرزنامه یا اسپم. در شرکت ما با دریافت هر ایمیل باید پروسه ی خاصی طی شود.مثلا با دریافت ایمیل درخواست سرویس جدید ، یک نسخه از این ایمیل باید برای بخش فنی شرکت و یک نسخه برای بخش مالی شرکت فرستاده شود.ایمیل های در خواست پشتیبانی باید برای پشتیبان های شرکت ارسال شود و و ایمیل های از طرف شرکت های همکار باید برای مدیر بازاریابی شرکت فرستاده شود و ایمیل های هرزنامه هم بلافاصله حذف می شود.

حالا این سیستم رو بدون استفاده از design pattern بخواهم پیاده سازی کنیم:

public enum EmailType
{
    NewService,
    Support,
    Colleague,
    Spam
}

 

public class Email
{
    public string Title { get; set; }
    public string Body { get; set; }
    public EmailType EmailType { get; set; }
}

 

 public class Proccess
 {
 public void RequestProcess(Email email)
 {
 if (email.EmailType == EmailType.NewService)
 {
 //do NewService proccess
 }
 else if (email.EmailType == EmailType.Support)
 {
 //do Support proccess
 }
 else if (email.EmailType == EmailType.Colleague)
 {
 //do Colleague proccess
 }
 else
 {
 //do spam proccess
 }
 }
 }

 

روش بالا کاری که ما نیاز داریم رو به درستی انجام میده اما راه حل بهینه ی این مسئله نیست.

حالا اگر ما همین سناریو رو بخواهیم با استفاده از الگوی طراحی chain of responsibility پیاده سازی کنیم که به صورت زیر خواهد شد:

public enum EmailType
{
    NewService,
    Support,
    Colleague,
    Spam
}
public class Email
{
    public string Title { get; set; }
    public string Body { get; set; }
    public EmailType EmailType { get; set; }
}
public abstract class Handler
{
    protected Handler _successor;
    public void SetSuccessor(Handler successor)
    {
        _successor = successor;
    }
    public abstract void HandleRequest(Email email);
}
public class NewServiceHandler : Handler
{
    public override void HandleRequest(Email email)
    {
        if (email.EmailType == EmailType.NewService)
        {
            //do NewService proccess
        }
        else if (_successor != null)
        {
            _successor.HandleRequest(email);
        }
    }
}
public class SupportHandler : Handler
{
    public override void HandleRequest(Email email)
    {
        if (email.EmailType == EmailType.Support)
        {
            //do Support proccess
        }
        else if (_successor != null)
        {
            _successor.HandleRequest(email);
        }
    }
}
public class ColleagueHandler : Handler
{
    public override void HandleRequest(Email email)
    {
        if (email.EmailType == EmailType.Colleague)
        {
            //do Colleague proccess
        }
        else if (_successor != null)
        {
            _successor.HandleRequest(email);
        }
    }
}
public class SpamHandler : Handler
{
    public override void HandleRequest(Email email)
    {
        if (email.EmailType == EmailType.Spam)
        {
            //do Spam proccess
        }
        else if (_successor != null)
        {
            _successor.HandleRequest(email);
        }
    }
}
public class MainProgram
{
    public void Main(Email email)
    {
        Handler newServiceHandler = new NewServiceHandler();
        Handler newSupportHandler = new SupportHandler();
        Handler colleagueeHandler = new ColleagueHandler();
        Handler SpamHandler = new SpamHandler();

        newServiceHandler.SetSuccessor(newSupportHandler);
        newSupportHandler.SetSuccessor(colleagueeHandler);
        colleagueeHandler.SetSuccessor(SpamHandler);

        newServiceHandler.HandleRequest(email);

    }
}

 

احتمالا اساسی ترین سوالی که باهاش رو به رو میشیم این قضیه هستش که فایده استفاده از این desing pattern نسبت به استفاده از if-else چه چیزی است که در پایین به آن اشاره میکنیم:

۱-فرض کنید ما به جای یک حالت استاتیک ، به یک حالت دینامیک و پویا احتیاج داشته باشیم به این معنی که بخواهیم بر اساس یک سری پردازش و با یک منطق پویا و در run-time  زنجیره یا chain خود را تشکیل دهیم.مطمئنا در حالتی که از if-else  استفاده کرده ایم این امکان وجود ندارد ولی design pattern زنجیره مسئولیت این قابلیت رو برای ما فراهم میکند:

public class MainProgram
{
    public void Main(Email email)
    {
        Handler newServiceHandler = new NewServiceHandler();
        Handler newSupportHandler = new SupportHandler();
        Handler colleagueeHandler = new ColleagueHandler();
        Handler SpamHandler = new SpamHandler();

        if (true)//condition
        {
            newServiceHandler.SetSuccessor(newSupportHandler);
        }
        else
        {
            newServiceHandler.SetSuccessor(colleagueeHandler);
        }
        newSupportHandler.SetSuccessor(colleagueeHandler);
        colleagueeHandler.SetSuccessor(SpamHandler);

        newServiceHandler.HandleRequest(email);

    }
}

 

۲-استفاده از این الگوی طراحی سبب میشه که وابستگی بین شی های  Handler(که همان زنجیره های ما می شود) و  request که قرار است پردازش شود از بین برود.

۳-استفاده از این Desing pattern باعث می شود که کلاینت درگیر منطقی که در handler ها پیاده سازی شده است نشود.

۴-با استفاده از Chain of Responsibility Design Pattern اصل Open-Closed   که یکی از اصل های برنامه نویسی شی گرا می باشد رعایت میشود.(این اصل به این مورد اشاره میکند که زمانیکه نرم افزار فعلی را توسعه می دهیم کد های فعلی ما نباید تغییری نکنند و همچنین کد ها و functionality تکراری نیز نباید ایجاد شوند)

در مطلب بعدی قصد داریم که الگوی Command را با هم بررسی کنیم.

موفق باشید