1. 程式人生 > 實用技巧 >設計模式之責任鏈模式

設計模式之責任鏈模式

職責鏈(責任鏈)模式 Chain of Responsibility

Intro

職責鏈(責任鏈)模式,很多物件由每一個物件對其下家的引用而連線起來形成一條鏈。請求在這個鏈上傳遞,直到鏈上的某一個物件決定處理此請求。
發出這個請求的客戶端並不知道鏈上的哪一個物件最終處理這個請求,這使得系統可以在不影響客戶端的情況下動態地重新組織和分配責任。

Sample

public class Request
{
    public string RequestContent { get; set; }

    public int RequestNum { get; set; }
}

public abstract class Manager
{
    protected readonly string ManagerName;
    protected Manager Superior;

    protected Manager(string managerName) => ManagerName = managerName;

    public void SetSuperior(Manager superior)
    {
        Superior = superior;
    }

    public abstract void RequestApplications(Request request);
}

public class CommonManager : Manager
{
    public CommonManager(string managerName) : base(managerName)
    {
    }

    public override void RequestApplications(Request request)
    {
        if (request.RequestNum <= 2)
        {
            Console.WriteLine($"{ManagerName}: {request.RequestContent} {request.RequestNum}天被批准");
        }
        else
        {
            Superior?.RequestApplications(request);
        }
    }
}

public class Majordomo : Manager
{
    public Majordomo(string managerName) : base(managerName)
    {
    }

    public override void RequestApplications(Request request)
    {
        if (request.RequestNum <= 5)
        {
            Console.WriteLine($"{ManagerName}: {request.RequestContent} {request.RequestNum}天被批准");
        }
        else
        {
            Superior?.RequestApplications(request);
        }
    }
}

public class GeneralManager : Manager
{
    public GeneralManager(string managerName) : base(managerName)
    {
    }

    public override void RequestApplications(Request request)
    {
        if (request.RequestNum <= 10)
        {
            Console.WriteLine($"{ManagerName}: {request.RequestContent} {request.RequestNum}天被批准");
        }
        else
        {
            Console.WriteLine($"{ManagerName}: {request.RequestContent} {request.RequestNum}天不被批准");
        }
    }
}


var manager = new CommonManager("金利");
var manager1 = new Majordomo("宗劍");
var manager2 = new GeneralManager("鍾精勵");

manager1.SetSuperior(manager2);
manager.SetSuperior(manager1);

var request = new Request()
{
    RequestNum = 4,
    RequestContent = "小菜請假"
};

manager.RequestApplications(request);

More

在職責鏈模式中,多個處理器(也就是剛剛定義中說的“接收物件”)依次處理同一個請求。一個請求先經過 A 處理器處理,然後再把請求傳遞給 B 處理器,B 處理器處理完後再傳遞給 C 處理器,以此類推,形成一個鏈條。鏈條上的每個處理器各自承擔各自的處理職責,所以叫作職責鏈模式(責任鏈模式)。

有沒有覺得 asp.net core 中請求管道中介軟體和上面的例項有點相像呢,

其實 asp.net core 中請求管道中介軟體的設計就是這種模式的變形,可以很靈活的處理請求

我封裝了一個 PipelineBuilder (https://www.cnblogs.com/weihanli/p/12700006.html

) 用來輕鬆的構建中介軟體模式的程式碼,
和 asp.net core 中介軟體的程式碼幾乎一摸一樣,可以看下面的示例:

private class RequestContext
{
    public string RequesterName { get; set; }

    public int Hour { get; set; }
}

public static void Test()
{
    var requestContext = new RequestContext()
    {
        RequesterName = "Kangkang",
        Hour = 12,
    };

    var builder = PipelineBuilder.Create<RequestContext>(context =>
            {
                Console.WriteLine($"{context.RequesterName} {context.Hour}h apply failed");
            })
            .Use((context, next) =>
            {
                if (context.Hour <= 2)
                {
                    Console.WriteLine("pass 1");
                }
                else
                {
                    next();
                }
            })
            .Use((context, next) =>
            {
                if (context.Hour <= 4)
                {
                    Console.WriteLine("pass 2");
                }
                else
                {
                    next();
                }
            })
            .Use((context, next) =>
            {
                if (context.Hour <= 6)
                {
                    Console.WriteLine("pass 3");
                }
                else
                {
                    next();
                }
            })
        ;
    var requestPipeline = builder.Build();
    foreach (var i in Enumerable.Range(1, 8))
    {
        Console.WriteLine();
        Console.WriteLine($"--------- h:{i} apply Pipeline------------------");
        requestContext.Hour = i;
        requestPipeline.Invoke(requestContext);
        Console.WriteLine("----------------------------");
        Console.WriteLine();
    }
}

Reference