Często zdarza się, że w dużej korporacji szeregowy pracownik zgłasza zapotrzebowanie zakupu jakiegoś drogiego urządzenia, np. tokarki CNC za cenę ponad 100 000 zł. Zgłoszenie to przekazuje swojemu kierownikowi. Kierownik rozważa pewne za i przeciw i może takie zgłoszenie odrzucić, lub przekazać do dyrektora działu. Dyrektor działu konsultuje pomysł ze specjalistami i gdy uzna, że takie urządzenia będzie przydatne, zgłasza zapotrzebowanie do prezesa firmy, a ten kontaktuje się z pracownikiem aby omówić szczegóły zakupu. Jednak w przypadku, gdy pracownik zgłosi zapotrzebowanie na coś tańszego, zgłoszenie zostanie zaakceptowane na niższym poziomie, np. zakup monitora może zatwierdzać kierownik, bez wiedzy przełożonych.
Zastosowanie:
Łańcucha odpowiedzialności używa się wszędzie tam, gdzie mamy do czynienia z wiadomością przekazywaną od nadawcy, do hierarchii odbiorców. Zależnie od treści wiadomości, pierwszy odbiorca może ją przekazać do następnego odbiorcy lub samemu przetworzyć i wysłać odpowiedź, nie informując o tym następnych odbiorców. Nadawca jest zawsze świadomy tylko pierwszego odbiorcy, natomiast każdy odbiorca wie tylko o następnym nadawcy. Nadawca nie wie, kto konkretnie przetworzył wiadomość, interesuje go tylko odpowiedź. Warto także pamiętać o tym, że kolejność jest ważna i należy także obsłużyć przypadek, gdy żaden odbiorca nie przetworzy wiadomości. Zastosowanie wzorca umożliwia dynamiczne zarządzanie MessageHandlerami a także ich hierarchizację.
Zasada działania:
W pierwszej kolejności należy utworzyć interfejs reprezentujący wiadomość, którą należy przetworzyć, a także zastanowić się nad tym, w jakiej postaci ma być przekazywana odpowiedź np. enum. Drugi interfejs będą implementowali odbiorcy wiadomości, którzy będą musieli przeładować metodę zwracają odpowiedź (np. enum) a przyjmującą obiekt typu wiadomości. Odbiorca musi także przechowywać referencję do następnego odbiorcy, który jest wywoływany, gdy odpowiedź nie może zostać przetworzona. Należy pamiętać o zaimplementowaniu w jakiś sposób ostatniego ogniwa z łańcucha.
Przykład implementacyjny:
public interface IPurchaseRequest { decimal Cost { get; set; } } public class PurchaseRequest : IPurchaseRequest { public string Name { get; set; } public PurchaseRequest(decimal cost) { Cost = cost; } public decimal Cost { get; set; } } public interface IPurchaseApprover { Response ConsiderRequest(IPurchaseRequest request); void SetNextSupervisor(IPurchaseApprover approver); } public enum Response { Granted, Denied, NotAllowedToDecide }
public class Supervisor : IPurchaseApprover { public decimal ApprovalLimit { get; set; } public IPurchaseApprover Next { get; set; } public string Name { get; set; } public Supervisor(string n, decimal l) { ApprovalLimit = l; Name = n; Next = EndOfChain.Instance; } public void SetNextSupervisor(IPurchaseApprover approver) { Next = approver; } public Response ConsiderRequest(IPurchaseRequest request) { if (request.Cost < ApprovalLimit) { return Response.Granted; } return Next.ConsiderRequest(request); } }
public static class EndOfChain { public static IPurchaseApprover Instance { get { return new Denier(); } } } public class Denier : IPurchaseApprover { public Response ConsiderRequest(IPurchaseRequest request) { return Response.Denied; } public void SetNextSupervisor(IPurchaseApprover approver) { throw new NotImplementedException(); } }
class Program { static void Main(string[] args) { var cncRequest = new PurchaseRequest(120000); cncRequest.Name = "CNC"; IPurchaseApprover chief = new Supervisor("Chief Tom", 1000); IPurchaseApprover cto = new Supervisor("CTO Mark", 50000); IPurchaseApprover owner = new Supervisor("Owner Mike", 500000); chief.SetNextSupervisor(cto); cto.SetNextSupervisor(owner); Console.WriteLine("We need to buy CNC"); var resp = chief.ConsiderRequest(cncRequest); if(resp == Response.Granted) Console.WriteLine("OK, Let's do it"); else { Console.WriteLine("We do not need it"); } Console.ReadKey(); } }
Brak komentarzy:
Prześlij komentarz