Chain of Responsibility – chuỗi xử lý yêu cầu

    0

    Chain of Responsibility là mẫu thiết kế thuộc nhóm behavioral. Ý tưởng của mẫu thiết kế này là tạo ra một chuỗi các phương thức xử lý (handler) cho mỗi yêu cầu (request). Yêu cầu này sẽ chạy dọc theo chuỗi xử lý. Mỗi handler trong chuỗi có thể quyết định xử lý yêu cầu hoặc chuyển tiếp nó sang handler tiếp theo trong chuỗi.

    Chain of Responsibility

    Hãy tưởng tượng bạn đi đến ủy ban phường để làm một thủ tục hành chính nào đó. Bạn tiếp xúc với người phụ trách ở bộ phận một cửa và đưa ra yêu cầu công việc.

    Với những công việc đơn giản (như đóng dấu công chứng), người đó có thể thực hiện ngay cho bạn. Nhưng nếu yêu cầu phức tạp hơn mà người phụ trách phòng một cửa không trực tiếp làm được, người đó sẽ chuyển yêu cầu đó đến người phụ trách mảng công việc tương ứng.

    Nếu yêu cầu công việc có gì đó bất thường mà người phụ trách chính mảng công việc đó cũng không xử lý được, người đó sẽ chuyển tiếp đơn lên chủ tịch/phó chủ tịch phường để quyết định cách xử lý.

    Tương tự, nếu ở cấp độ phường không xử lý được, yêu cầu sẽ chuyển tiếp lên cấp quận/huyện.

    Đây là một ví dụ thực tế minh họa cho ý tưởng hoạt động của mẫu Chain of Responsibility: một yêu cầu có thể chuyển qua một chuỗi các phương thức xử lý (handler). Tùy vào đặc điểm của yêu cầu, một trong số các handler trong chuỗi sẽ quyết định xử lý nó. Nếu không thể xử lý, nó sẽ chuyển tiếp sang handler tiếp theo.

    Một ví dụ khác trong lập trình windows forms. Các control trên một form chứa đựng nhau tạo ra một chuỗi object. Giả sử bạn click vào một nút bấm, sự kiện “click” sẽ chạy ngược chuỗi object từ Button tới Window (cấp cao nhất) để tìm đến đúng control có khả năng xử lý nó.

    A chain can be formed from a branch of an object tree

    Sự kiện sẽ được xử lý ở Control đầu tiên trong chuỗi có khả năng xử lý “click”.

    Thiết kế Chain of Responsibility

    Khi bạn đã hiểu ý tưởng cơ bản của Chain of Responsibility, hãy cùng xem sơ đồ UML nguyên lý của mẫu thiết kế này.

    Trong sơ đồ trên,

    • Client là một class phát ra yêu cầu cần xử lý.
    • IHandler là giao diện chung cho tất cả các class xử lý cụ thể.
    • Handler1, Handler2 là các class xử lý cụ thể.
    • Successor là object trỏ tới handler tiếp theo trong chuỗi.
    • Request là cách các handler “đối xử” với yêu cầu từ client. Request có thể trực tiếp xử lý yêu cầu, hoặc gọi tới Request của handler tiếp theo (successor).

    Từ sơ đồ thiết kế trên bạn có thể tạm hình dung về cách thức hoạt động của nó như sau:

    Client phát đi một yêu cầu cần xử lý nhưng nó không cần biết ai sẽ xử lý và xử lý như thế nào. Lấy ví dụ, client code có thể phát đi yêu cầu rút 10000$ từ hệ thống.

    Chain of Responsibility chuyển yêu cầu đó qua một chuỗi các class handler. Client không hề biết gì về các handler này, và nó cũng không biết handler cụ thể nào sẽ xử lý yêu cầu của mình. Yêu cầu chạy dọc theo chuỗi handler.

    Khi qua mỗi một handler, handler sẽ kiểm tra xem mình có đủ tài nguyên, thông tin và thẩm quyền đối với yêu cầu đó hay không. Nếu đáp ứng yêu cầu, handler sẽ xử lý yêu cầu đó. Nếu không đáp ứng yêu cầu, handler sẽ chuyển tiếp yêu cầu sang handler tiếp theo trong chuỗi. Quá trình tiếp diễn cho đến khi tìm được một handler phù hợp sẽ xử lý yêu cầu đó.

    Thông qua biến thành viên #successor, mỗi handler sẽ biết được handler nào sẽ đứng sau mình trong chuỗi (để chuyển tiếp yêu cầu tới).

    Thông thường Handler cuối cùng trong chuỗi sẽ xử lý yêu cầu theo cách mặc định hoặc phát ra ngoại lệ, vì tất cả các handler khác đều không phù hợp.

    Thực thi mẫu Chain of Responsibility trong C#

    Với bản thiết kế như trên, giờ chúng ta cùng thực thi nó bằng C#.

    Dưới đây là một ví dụ rất đơn giản mô phỏng việc thực hiện lệnh rút tiền. Có 3 cấp độ rút do 3 class thực hiện: HandlerLow – dưới 10000$, HandlerMedium – dưới 20000$, HandlerHigh – dưới 30000$, và HandlerDefault – tất cả các trường hợp còn lại.

    4 Handler trên ghép với nhau thành chuỗi theo thứ tự Low – Medium – High – Default.

    Việc xử lý rút tiền luôn xuất phát từ Low.

    Để tiện lợi khi viết các chương trình nhỏ với giao diện dòng lệnh, trong các ví dụ sau sử dụng một bộ thư viện class tạo sẵn.

    Bạn có thể tải file thư viện từ đường link sau:
    https://1drv.ms/u/s!Ar_aj4rIJ2qGkZU4EYBdm4K3EptLTg?e=9ecAug
    Sau khi tải về bạn tham chiếu chương trình tới file thư viện Framework.dll.

    Bộ thư viện này hỗ trợ bạn nhanh chóng xây dựng ứng dụng console với khả năng tiếp nhận và xử lý lệnh/truy vấn từ người dùng + tham số của lệnh. Nó cũng có một số class hỗ trợ hiển thị dữ liệu trên console. Thêm vào đó, nếu ứng dụng phức tạp hơn, bạn có thể sử dụng một số class hỗ trợ để viết code theo mô hình MVC cho console.

    Nếu quan tâm, bạn có thể đọc thêm loạt bài về cách xây dựng bộ thư viện hỗ trợ console này.

    using System;
    using Framework;
    
    namespace P01_Concept
    {
        interface IHandler
        {
            IHandler Successor { get; set; }
    
            void RequestWidraw(int amount);
        }
    
        class HandlerLow : IHandler
        {
            public IHandler Successor { get; set; }
    
            public void RequestWidraw(int amount)
            {
                if (amount < 10000)
                {
                    ViewHelper.WriteLine($"Low handler: I can handle less than 10000$. DONE!", ConsoleColor.Green);
                }
                else
                {
                    ViewHelper.WriteLine($"Low handler: I received the request but I can handle only less than 10000$. Passed", ConsoleColor.Yellow);
                    Successor?.RequestWidraw(amount);
                }
            }
        }
    
        class HandlerMedium : IHandler
        {
            public IHandler Successor { get; set; }
    
            public void RequestWidraw(int amount)
            {
                if (amount < 20000)
                {
                    ViewHelper.WriteLine($"Medium handler: I can handle less than 20000$. DONE!", ConsoleColor.Green);
                }
                else
                {
                    ViewHelper.WriteLine($"Medium handler: I received the request but I can handle only less than 20000$. Passed", ConsoleColor.Yellow);
                    Successor?.RequestWidraw(amount);
                }
            }
        }
    
        class HandlerHigh : IHandler
        {
            public IHandler Successor { get; set; }
    
            public void RequestWidraw(int amount)
            {
                if (amount < 30000)
                {
                    ViewHelper.WriteLine($"High handler: I can handle less than 30000$. DONE!", ConsoleColor.Green);
                }
                else
                {
                    ViewHelper.WriteLine($"High handler: I received the request but I can handle only less than 30000$. Passed", ConsoleColor.Yellow);
                    Successor?.RequestWidraw(amount);
                }
            }
        }
    
        class HandlerDefault : IHandler
        {
            public IHandler Successor { get; set; }
    
            public void RequestWidraw(int amount)
            {
                ViewHelper.WriteLine($"Default handler: I received the request. Everything is DONE!", ConsoleColor.Cyan);
    
            }
        }
    
        class ChainOfHandlers
        {
            readonly IHandler _low = new HandlerLow();
            readonly IHandler _medium = new HandlerMedium();
            readonly IHandler _high = new HandlerHigh();
            readonly IHandler _default = new HandlerDefault();
    
            public ChainOfHandlers()
            {
                _low.Successor = _medium;
                _medium.Successor = _high;
                _high.Successor = _default;
            }
    
            public void Handle(int amount)
            {
                _low.RequestWidraw(amount);
            }
        }
    
    
        class Program
        {
            static void Main(string[] args)
            {
                var app = new Application()
                {
                    Title = "CHAIN OF RESPONSIBILITY",
                    Config = RegisterRoutes
                };
    
                app.Run();
            }
    
            private static void RegisterRoutes()
            {
                Router.Instance.Register(
                    "widraw",
                    (p) => { Widraw(p["amount"].To<int>()); },
                    "Request to widraw an amount.\r\nsyntax: widraw ? amount = ..."
                );
            }
    
            private static void Widraw(int amount)
            {
                var chain = new ChainOfHandlers();
                chain.Handle(amount);
            }
        }
    }

    Dưới đây là kết quả thực thi chương trình:

    Ở trên chỉ là cách thức thực thi đơn giản và trực tiếp nhất của mẫu Chain of Responsibility.

    Bạn có thể thực hiện nhiều cách khác nhau để ghép nối các handler thành chuỗi. Cách thực hiện như trong class ChainOfHandlers là đơn giản và trực tiếp nhất.

    Kết luận

    Trong bài học này bạn đã làm quen với tư tưởng, thiết kế và cách thực thi đơn giản của mẫu thiết kế Chain of Responsibility. Mẫu thiết kế này có nhiều ứng dụng trong giao diện đồ họa hoặc lập trình mạng.

    Nếu có thắc mắc hoặc cần trao đổi thêm, mời bạn viết trong phần Bình luận ở cuối trang. Nếu cần trao đổi riêng, hãy gửi email hoặc nhắn tin qua form liên hệ. Nếu bài viết hữu ích với bạn, hãy giúp chúng tôi chia sẻ tới mọi người. Cảm ơn bạn!

    Bình luận

    avatar
      Đăng ký theo dõi  
    Thông báo về