在开发中,我们经常会遇到这样一个场景:传入一个对象,经过不同的节点对这个对象做不同的操作,比如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();
}
}

如何构造请求处理对象链(Pipeline)的更多相关文章

  1. 构造Json对象串工具类

    import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.Property ...

  2. jQuery.buildFragment源码分析以及在构造jQuery对象的作用

    这个方法在jQuery源码中比较靠后的位置出现,主要用于两处.1是构造jQuery对象的时候使用 2.是为DOM操作提供底层支持,这也就是为什么先学习它的原因.之前的随笔已经分析过jQuery的构造函 ...

  3. 9.19.1 反射构造Class对象

    构造Class对象的三种方式:         第一种方式:                     (1)对于系统的类,必须写全名!                     (2)该方式会将.Cla ...

  4. js中的原型对象链

    由于原型对象也是一个对象,它也有自己的原型对象并继承对象中的属性,这就是原型对象链:对象继承其原型对象,而原型对象继承它的原型对象,以此类推. 我们创建的每一个函数都有一个prototype(原型)属 ...

  5. jquery 1.7.2源码解析(二)构造jquery对象

    构造jquery对象 jQuery对象是一个类数组对象. 一)构造函数jQuery() 构造函数的7种用法: 1.jQuery(selector [, context ]) 传入字符串参数:检查该字符 ...

  6. 4.构造Thread对象你也许不知道的几件事

    1.Thread类对象只有在调用了start()方法之后,JVM虚拟机才会给我们创建一个真正的线程!否则就不能说是创建了线程!也就是说new Thread()之后,此时实际上在计算机底层,操作系统实际 ...

  7. jQuery源码分析-03构造jQuery对象-源码结构和核心函数

    3. 构造jQuery对象 3.1源码结构 先看看总体结构,再做分解: (function( window, undefined ) { var jQuery = (function() { // 构 ...

  8. spring MVC之构造ModelAndView对象

    spring MVC之构造ModelAndView对象 ---------- 构造ModelAndView对象 当控制器处理完请求时,通常会将包含视图名称或视图对象以及一些模型属性的ModelAndV ...

  9. Django中构造响应对象的方式

    1 HttpResponse 可以使用django.http.HttpResponse来构造响应对象. HttpResponse(content=响应体, content_type=响应体数据类型, ...

随机推荐

  1. chain rule 到 Markov chain

    1. 联合概率(joint distribution)的链式法则 基于链式法则的 explicit formula: p(x1:n)===p(x)p(x1)∏i=2np(xi|x1,-,xi−1)∏i ...

  2. C++设计模式实现--备忘录(Memento)模式

    一. 备忘录模式 定义:在不破坏封装性的前提下,捕获一个对象的内部状态.并在该对象之外保存这个状态. 这样以后就可将该对象恢复到原先保存的状态. 结构图: 使用范围: Memento 模式比較适用于功 ...

  3. Android中的消息机制:Handler消息传递机制 分类: H1_ANDROID 2013-10-27 22:54 1755人阅读 评论(0) 收藏

    参考<疯狂android讲义>第2版3.5 P214 一.背景 出于性能优化考虑,Android的UI操作并不是线程安全的,这意味着如果有多个线程并发操作UI组件,可能导致线程安全问题.为 ...

  4. hadoop2.7全然分布式集群搭建以及任务測试

    要想深入的学习hadoop数据分析技术,首要的任务是必需要将hadoop集群环境搭建起来,本文主要讲述怎样搭建一套hadoop全然分布式集群环境. 环境配置:2台64位的redhat6.5 +  1台 ...

  5. [Grid Layout] Use auto-fill and auto-fit if the number of repeated grid tracks is not to be def

    What about the situation in which we aren’t specifying the number of columns or rows to be repeated? ...

  6. Word 2010/2013 菜单栏添加 MathType 菜单

    一般对于 office 的高级版本,比如 word 2010/2013,在手动安装 mathtype 之后,并不会再 word 的菜单栏,创建 mathtype 按钮,此时需要按照如下步骤,手动导入 ...

  7. 【64.22%】【codefoces round 382A】Ostap and Grasshopper

    time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...

  8. Struts2——(3)ValueStack(值栈)

    一.ValueStack 主要用于存储请求相关信息,内部结构如下 root区:被称为根存储区,是一个栈结构,栈顶元素为当前请求的Action对象. context区:被称为变量存储区,是一个Map结构 ...

  9. redis 学习笔记——持久化

    redis持久化 snapshot数据快照(rdb) 这是一种定时将redis内存中的数据写入磁盘文件的一种方案,这样保留这一时刻redis中的数据镜像,用于意外回滚.redis的snapshot的格 ...

  10. Linux命令list

    文件和目录 cd /home 进入 '/ home' 目录' cd .. 返回上一级目录 cd ../.. 返回上两级目录 cd 进入个人的主目录 cd ~user1 进入个人的主目录 cd - 返回 ...