在开发中,我们经常会遇到这样一个场景:传入一个对象,经过不同的节点对这个对象做不同的操作,比如ASP.NET Core 中的pipeline,IIS中的HTTPpipeline等。在这类问题中,往往我们允许用户可以自己定义自己的处理节点(Processor Node),能够实现这一目的的方式很多。本文中分享2种方式,欢迎大家交流。
责任链模式(Chain of Responsibility Pattern)
关于责任链模式的介绍园中已经有很多了,这里我就不再多余的来介绍了,直接上代码:
1. 创建抽象类
public abstract class ProcessorExecution { protected ProcessorExecution Next { get; private set; } protected ProcessorExecution(ProcessorExecution next = null) { Next = next; } public virtual void Execute() { Next?.Execute(); } public ProcessorExecution SetNext(ProcessorExecution next) { Next = next; return next; } }
2. 创建具体实现类
public class Node1ProcessorExecution : ProcessorExecution { public Node1ProcessorExecution(ProcessorExecution next = null) : base(next) { } public override void Execute() { Console.WriteLine("Node1 Processor"); base.Execute(); } } public class Node2ProcessorExecution : ProcessorExecution { public Node2ProcessorExecution(ProcessorExecution next = null) : base(next) { } public override void Execute() { Console.WriteLine("Node2 Processor"); base.Execute(); } } public class Node3ProcessorExecution : ProcessorExecution { public Node3ProcessorExecution(ProcessorExecution next = null) : base(next) { } public override void Execute() { Console.WriteLine("Node3 Processor"); base.Execute(); } } public class TerminalProcessorExecute : ProcessorExecution { public TerminalProcessorExecute(ProcessorExecution next = null) : base(next) { } public override void Execute() { Console.WriteLine("Terminal Processor"); base.Execute(); } }
3. 调用对象链
class Program { static void Main(string[] args) { var header = new Node1ProcessorExecution(); header.SetNext(new Node2ProcessorExecution()) .SetNext(new Node3ProcessorExecution()) .SetNext(new TerminalProcessorExecute()); header.Execute(); Console.Read(); } }
委托链
有时候我们很不情愿地为一个Processor Node 去创建一个新类,因为那样显得类文件特别多,毕竟每一个类中只有一个或很少的方法需要重写。这里,我们可以尝试第二种方法 ---- 委托链,帮助我们减少类的创建。
在C#中,委托是一种特殊的类的方式存在,所以我们可以使用委托的方式实现。话不多说,直接上代码:
1. 创建一个委托
我们需要创建一个委托用来替代 ProcessorExecution中的Execute方法(Processor Node 处理的逻辑核心),.NET 4.5提供了async的编程方法,这里我使用Task来作为该委托的返回值:
public delegate Task ProcessorExecutionDelegate();
2. 创建一个Builder用于构造委托链
Builder中包括三个方法:
- ProcessorExecutionBuilder Add(Func<ProcessorExecutionDelegate, ProcessorExecutionDelegate> execution): 用来维护Current -> Next的关系。我们需要创建一个表示 Func<ProcessorExecutionDelegate, ProcessorExecutionDelegate> 的集合 _components。
- ProcessorExecutionBuilder Add(Func<Func<Task>, Task> middleware):用来表述NodeXProcessorExecution类(Func<Func<Task>, Task> middleware),内部调用Add(Func<ProcessorExecutionDelegate, ProcessorExecutionDelegate> execution)
- ProcessorExecutionDelegate Build():构造委托链,并返回Header节点
public class ProcessorExecutionBuilder { private readonly List<Func<ProcessorExecutionDelegate, ProcessorExecutionDelegate>> _components = new List<Func<ProcessorExecutionDelegate, ProcessorExecutionDelegate>>(); private ProcessorExecutionBuilder Add(Func<ProcessorExecutionDelegate, ProcessorExecutionDelegate> execution) { _components.Add(execution); return this; } public ProcessorExecutionBuilder Add(Func<Func<Task>, Task> middleware) { Add((next) => () => { Func<Task> func = () => next(); return middleware(func); }); return this; } public ProcessorExecutionDelegate Build() { var next = (ProcessorExecutionDelegate)(() => Task.Run(() => { Console.WriteLine("Terminal Processor"); })); _components.Reverse(); foreach (var item in _components) { next = item(next); } return next; } }
3. 调用对象链
class Program { static void Main(string[] args) { var builder = new ProcessorExecutionBuilder(); builder.Add((next) => { Console.WriteLine("Node1 Processor"); return next(); }) .Add((next) => { Console.WriteLine("Node2 Processor"); return next(); }).Add((next) => { Console.WriteLine("Node3 Processor"); return next(); }); builder.Build().Invoke(); Console.Read(); } }