设计模式是软件开发必备的技能,但是只有少部分小伙伴才会去研究这块的内容。设计模式相关的书籍也比较多,为了让我们的设计模式能在实际工作中得到实践,把工作中运用到的设计模式记录下来。以便日后精益学习。

业务场景说明

当我们在主线程上处理一些业务逻辑时,希望能把部分任务丢到一个异步线程中处理,防止卡UI。但是我们又希望任务能够按照特定的流程或要求执行,如:单任务顺序执行,仅单任务执行等。当然,我们直接在主流程中通过异步方法去实现相关的逻辑也是没有问题的,但是场景越多,情况越多,代码会显得越乱,维护性也会变得越差。于是,我们就需要对它进行一下改造了。

案例

当我们在处理人脸识别的开发过程中,我们需要从视频流中做抽帧处理。通过抽取的帧用来做人脸识别跟踪,活体检测,人脸比对等功能。但是为了避免算法的执行速度,影响视频播放流畅度,于是需要将算法放在异步线程中执行,同时为了避免不必要的运算和并行错误,我们需要将异步任务做串行处理。于是我们就有了以下方法来解决这个问题:

示例代码:单一异步任务调度器(模板类)


    /// <summary>
    /// 单一异步任务调度器
    /// </summary>
    public abstract class SingleAsyncTaskDispatcher
    {
        private CancellationTokenSource _cancellationTokenSource;

        public SingleAsyncTaskDispatcher()
        {
            Status = AsyncTaskStatus.Waiting;
        }

        /// <summary>
        /// 任务启动时执行
        /// </summary>
        protected virtual void OnStarting()
        {
            Status = AsyncTaskStatus.Starting;
        }

        /// <summary>
        /// 任务执行的主体
        /// </summary>
        protected virtual void OnRun()
        {
            Status = AsyncTaskStatus.Running;
        }

        /// <summary>
        /// 任务取消时执行
        /// </summary>
        protected virtual void OnCanceled()
        {
            Status = AsyncTaskStatus.Canceled;
        }

        /// <summary>
        /// 任务完成时执行
        /// </summary>
        protected virtual void OnCompleted()
        {
            Status = AsyncTaskStatus.Completed;
        }

        /// <summary>
        /// 当前任务状态
        /// </summary>
        public AsyncTaskStatus Status
        {
            get; private set;
        }

        /// <summary>
        /// 是否允许启动一个新任务
        /// </summary>
        bool CanStartNew
        {
            get
            {
                return Status.Equals(AsyncTaskStatus.Completed) || Status.Equals(AsyncTaskStatus.Canceled) || Status.Equals(AsyncTaskStatus.Waiting);
            }
        }

        /// <summary>
        /// 任务令牌,只有存在任务时才存在令牌
        /// </summary>
        public CancellationToken? TaskToken
        {
            get
            {
                return _cancellationTokenSource?.Token;
            }
        }

        /// <summary>
        /// 尝试运行一个新的任务,如果任务正忙,则任务不会启动
        /// </summary>
        /// <param name="action"></param>
        /// <returns></returns>
        public bool TryStartNew()
        {
            if (CanStartNew)
            {
                _cancellationTokenSource = new CancellationTokenSource();
                OnStarting();
                Task.Run(() =>
                {
                    OnRun();
                }, _cancellationTokenSource.Token)
                .ContinueWith((t) =>
                {
                    if (_cancellationTokenSource.Token.IsCancellationRequested)
                    {
                        _cancellationTokenSource = null;
                        OnCanceled();
                    }
                    else
                    {
                        _cancellationTokenSource = null;
                        OnCompleted();
                    }
                });
                return true;
            }

            return false;
        }

        /// <summary>
        /// 取消任务
        /// </summary>
        public void Cancel()
        {
            _cancellationTokenSource?.Cancel();
        }
    }

    /// <summary>
    /// 异步任务状态
    /// </summary>
    public enum AsyncTaskStatus
    {
        /// <summary>
        /// 任务空闲
        /// </summary>
        Waiting,
        /// <summary>
        /// 正在启动一个任务
        /// </summary>
        Starting,
        /// <summary>
        /// 任务正在执行
        /// </summary>
        Running,
        /// <summary>
        /// 任务已取消
        /// </summary>
        Canceled,
        /// <summary>
        /// 任务已完成
        /// </summary>
        Completed,
    }

示例代码:人脸比对任务调度器(实例类)


    /// <summary>
    /// 人脸比对任务调度器
    /// </summary>
    public class FaceMatchAsyncTaskExecutor : SingleAsyncTaskDispatcher
    {
        public FaceMatchAsyncTaskExecutor()
        {
            //参数根据实际情况设计
        }

        protected override void OnStarting()
        {
            base.OnStarting();
            //启动人脸扫描动画,此处代码省略
            Console.WriteLine("OnStarting");
        }

        protected override void OnRun()
        {
            //参数根据实际情况设计
            base.OnRun();
            Console.WriteLine("OnRun");
            //获取人脸比对图片
            while (!TaskToken.Value.IsCancellationRequested)
            {
                //检测人脸是否在指定区域内
                //开始人脸比对
                //…… 代码省略s
            }
        }

        protected override void OnCanceled()
        {
            base.OnCanceled();
            //关闭人脸扫描动画,此处代码省略
            Console.WriteLine("OnCanceled");
        }

        protected override void OnCompleted()
        {
            base.OnCompleted();
            //关闭人脸扫描动画,此处代码省略
            Console.WriteLine("OnCompleted");
        }
    }

至此,我们就可以使用这套模板,开发其他类似的业务逻辑了。


那什么时候会用到模板方法模式呢?

  • 我们在实现一个算法,但发现其中有些部分非常易变,或者很容易随着运行环境、后续开发的不同产生很多变化,所以把它们抽象出来,供子类完成。
  • 随着项目的展开,我们发现之前有些工作可以被独立出来形成公共的开发库。
  • 对一系列子类进行约束,要求它们必须实现算法要求的某些方法,便于其他客户程序按照这些方法操作子类。

未完待续……


本文会经常更新,请阅读原文: https://huchengv5.gitee.io//post/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E6%A8%A1%E6%9D%BF%E6%96%B9%E6%B3%95.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

知识共享许可协议 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名胡承(包含链接: https://huchengv5.gitee.io/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系