zoukankan      html  css  js  c++  java
  • Pipeline模式与Factory+Provider模式的应用

    前言

    我正在写FastGithub这个小麻雀项目,里面主要涉及了Pipeline模式和Factory+Provider模式,这两种设计模式,让这个项目在"ip扫描"和"ip查找"两个核心功能上如鱼得水,在此分享给大家。

    Pipeline

    Pipeline模式也叫管道模式或流水线模式。通过预先设定好的一系列的阶段来处理输入的数据,每个阶段的输出即是下一个阶段的输入,每个阶段可以选择是否继续执行一下阶段。

    上下文对象

    在实现上,我们把所需的所有数据封装在上下文对象,每个阶段可以共享到同一份上下文对象。

    中间件

    在实现上,我们把每个阶段的处理封装为中间件,一个中间件可以访问到上下文对象和下一个阶段的处理对象,在执行时可以访问或修改上下文对象的数据。

    实现详解

    完整的Pipeline构建代码,详见https://github.com/xljiulang/FastGithub/tree/master/FastGithub.Core

    阶段处理对象

    /// <summary>
    /// 表示所有中间件执行委托
    /// </summary>
    /// <typeparam name="TContext">中间件上下文类型</typeparam>
    /// <param name="context">中间件上下文</param>
    /// <returns></returns>
    public delegate Task InvokeDelegate<TContext>(TContext context);
    

    委托中间件

    Func<InvokeDelegate<TContext>, InvokeDelegate<TContext>>为一个委托中间件,第一个InvokeDelegate<TContext>表示传入的下一个处理阶段,第二个InvokeDelegate<TContext>表示当前处理阶段。

    /// <summary>
    /// 定义中间件管道创建者的接口
    /// </summary>
    /// <typeparam name="TContext">中间件上下文</typeparam>
    public interface IPipelineBuilder<TContext>
    {
        /// <summary>
        /// 使用中间件
        /// </summary>
        /// <param name="middleware">中间件</param>
        /// <returns></returns>
        IPipelineBuilder<TContext> Use(Func<InvokeDelegate<TContext>, InvokeDelegate<TContext>> middleware);
    
        /// <summary>
        /// 创建所有中间件执行处理者
        /// </summary>
        /// <returns></returns>
        InvokeDelegate<TContext> Build();
    }
    

    强类型中间件

    我们可以把委托中间件,转换为如下的强类型中间件,InvokeAsync方法是本处理阶段,next参数,是委托中间件的下个阶段包装。

    /// <summary>
    /// 定义中间件的接口
    /// </summary>
    /// <typeparam name="TContext"></typeparam>
    public interface IMiddleware<TContext>
    {
        /// <summary>
        /// 执行中间件
        /// </summary>
        /// <param name="context">上下文</param>
        /// <param name="next">下一个中间件</param>
        /// <returns></returns>
        Task InvokeAsync(TContext context, Func<Task> next);
    }
    

    使用详解

    扫描任务分为完整扫描和历史结果扫描,使用的中间件有点差异,但都是把需要的中间件串起来就可以了。

    /// <summary>
    /// github扫描服务
    /// </summary>
    /// <param name="domainAddressFactory"></param>
    /// <param name="scanResults"></param>
    /// <param name="appService"></param>
    /// <param name="logger"></param>
    public GithubScanService(
        DomainAddressFacotry domainAddressFactory,
        GithubContextCollection scanResults,
        IServiceProvider appService,
        ILogger<GithubScanService> logger)
    {
        this.domainAddressFactory = domainAddressFactory;
        this.scanResults = scanResults;
        this.logger = logger;
    
        this.fullScanDelegate = new PipelineBuilder<GithubContext>(appService, ctx => Task.CompletedTask)
            .Use<ConcurrentMiddleware>()
            .Use<StatisticsMiddleware>()
            .Use<TcpScanMiddleware>()
            .Use<HttpsScanMiddleware>()
            .Build();
    
        this.resultScanDelegate = new PipelineBuilder<GithubContext>(appService, ctx => Task.CompletedTask)
            .Use<StatisticsMiddleware>()
            .Use<HttpsScanMiddleware>()
            .Build();
    }
    

    Factory+Provider

    多个Provider可以使用不同手段获取到github的ip,Factory再把各Provider得到的ip进行整合,他们都是得到相同的功能:拿到github的ip,只是各个Provider才是具体干活的,而且Provider之间没有任何有关系。

    IDomainAddressProvider

    /// <summary>
    /// 定义域名的ip提值者
    /// </summary>
    interface IDomainAddressProvider
    {
        /// <summary>
        /// 创建域名与ip的关系
        /// </summary>
        /// <returns></returns>
        Task<IEnumerable<DomainAddress>> CreateDomainAddressesAsync();
    }
    

    DomainAddressFacotry

    /// <summary>
    /// 域名与ip关系工厂
    /// </summary>
    [Service(ServiceLifetime.Singleton)]
    sealed class DomainAddressFacotry
    {
        private readonly IEnumerable<IDomainAddressProvider> providers;
    
        /// <summary>
        /// 域名与ip关系工厂
        /// </summary>
        /// <param name="providers"></param>
        public DomainAddressFacotry(IEnumerable<IDomainAddressProvider> providers)
        {
            this.providers = providers;
        }
    
        /// <summary>
        /// 创建域名与ip的关系
        /// </summary>
        /// <returns></returns>
        public async Task<IEnumerable<DomainAddress>> CreateDomainAddressesAsync()
        {
            var hashSet = new HashSet<DomainAddress>();
            foreach (var provider in this.providers)
            {
                var domainAddresses = await provider.CreateDomainAddressesAsync();
                foreach (var item in domainAddresses)
                {
                    hashSet.Add(item);
                }
            }
            return hashSet;
        }
    }
    

    模式优势分析

    FastGithub同时使用了上述两种模式,其工作流程很简单:使用DomainAddressFacotry创建要扫描的ip,然后使用pipeline创建得到的扫描委托进行扫描即可。想得到更多的ip,增加一个DomainAddressProvider即可,不影响到其它任何代码流程,想在扫描过程中做其它扫描逻辑,增加一个扫描中间件并安装到合适位置即可。

  • 相关阅读:
    【注册验证】 控制器(添加数据+后台验证)
    AJAX 简介
    我的博客生涯
    windows xp sp3 下载地址
    get、set、add、remove → (字段、属性)+(委托、事件)
    ArcObjects整体介绍
    委托
    对字符串的处理的又一个强大的工具,正则表达式
    对象序列化存在的原因
    关于文件操作的几个类
  • 原文地址:https://www.cnblogs.com/kewei/p/14899070.html
Copyright © 2011-2022 走看看