Mohsen Farokhi - محسن فرخی
Mohsen Farokhi - محسن فرخی
خواندن ۳ دقیقه·۲ سال پیش

الگوی Chain of Responsibility

الگوی chain of responsibility، به تفکیک ارسال کننده و دریافت کننده یک پیام می پردازد. به این شکل که به objectهای مختلف امکان پردازش آن پیام را می دهد.

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

Chain of Responsibility
Chain of Responsibility

مفهومی به نام Implicit Receiver وجود دارد که در برخی از الگوها از جمله chain of responsibility دیده می شود. زمانی که یک client، درخواست انجام کاری را به یک object می دهد، اگر client این دانش را داشته باشد که چه کلاسی قرار است جواب را به آن برگرداند، به آن explicit receiver گفته می شود. در implicit receiver به این صورت است که client اطلاعاتی در مورد receiver ندارد و متوجه این نمی شود که چه کسی آن کار را انجام می دهد.

سه المان اصلی Client, Handler, Concrete Handler در این الگو وجود دارد. وجود successor به این معنی است که هر handler می تواند به handler بعدی برای ارسال پیام، point داشته باشد.

جزء Handler، یک abstraction می باشد که برای مثال class زیر را برای آن در نظر بگیرید.

public abstract class Handler<TRequest, TResponse> { protected Handler<TRequest, TResponse> Successor; public void SetSuccessor(Handler<TRequest, TResponse> successor) { Successor = successor; } public abstract TResponse HandleRequest(TRequest request); }

به عنوان concrete handlers، برای مثال پیاده سازی های زیر را در نظر بگیرید.

public class TrimHandler : Handler<string, string> { public override string HandleRequest(string request) { request = request.Trim(); if (Successor != null) return Successor.HandleRequest(request); else return request; } }
public class UppercaseHandler : Handler<string, string> { public override string HandleRequest(string request) { request = request.ToUpper(); if (Successor != null) return Successor.HandleRequest(request); else return request; } }

به این ترتیب این قابلیت در client وجود دارد که به طریقی یک abstract از handlerها دریافت شود و ازین طریق کاری را انجام می دهد.

var str = &quot test &quot var handler = GetHandler(); var result = handler.HandleRequest(str); static Handler<string,string> GetHandler() { var trimHandler = new TrimHandler(); var upperHandler = new UppercaseHandler(); trimHandler.SetSuccessor(upperHandler); return trimHandler; }

بنابراین، زمانی که ممکن است بیش از یک object، درخواستی را دریافت کنند و پردازشی انجام دهند و همچنین مشخص نیست که در مورد کدام یک از handlerها صحبت می کنیم، می توانیم از این الگو استفاده کنیم.

در این الگو به طور مشخصی بین ارسال کننده و دریافت کننده، یک decoupling وجود دارد. همچنین وارد کردن یک ساختار dynamic که می تواند در runtime قابل تغییر باشد، به ما اجازه می دهد که یک زنجیره از handlerها ایجاد کنیم و درخواست را برای پردازش، داخل این زنجیره قرار دهیم.


موضوع successor نهایی، می تواند در این الگو مساله ساز باشد. کسانی که درگیر این موضوع هستند، به نحوی در مورد نبود successor نگرانی دارند.

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

برای مثال در این مساله می توینم به این شکل یک null object ایجاد کنیم و در handlerها، درگیر چک کردن null بودن successor نباشیم.

public class NullHandler : Handler<string, string> { public override string HandleRequest(string request) { return request; } }

ساختن یک chain، معمولا با پیچیدگی هایی همراه می باشد. برای این موضوع می تونیم از الگوی builder استفاده کنیم.

public class ChainBuilder<TRequest, TResponse> { private readonly IList<Handler<TRequest, TResponse>> _handlers = new List<Handler<TRequest, TResponse>>(); public ChainBuilder<TRequest, TResponse> With<THandler>() where THandler : Handler<TRequest, TResponse>, new() { var handler = new THandler(); _handlers.Add(handler); return this; } public Handler<TRequest, TResponse> Build() { _handlers.Aggregate((a, b) => { a.SetSuccessor(b); return b; }); return _handlers.First(); } }
static Handler<string, string> GetHandler() { var handler = new ChainBuilder<string, string>() .With<TrimHandler>() .With<UppercaseHandler>() .With<NullHandler>() .Build(); return handler; }

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

به طور خلاصه، موضوع receiver و handler به عنوان کسی که یک کار را انجام می دهد و همچنین ترتیب آن ها را به عنوان variability در نظر می گیریم و شناختن concept آن handler توسط client را به عنوان commonality در نظر می گیریم.

design patternsالگوهای طراحی
شاید از این پست‌ها خوشتان بیاید