换个角度理解设计模式之责任链模式

系列文章的目录:https://blog.csdn.net/hjkl950217/article/details/89490709

记住:设计模式重理解轻照搬

业务背景

想象一下你在做一个简单的电商系统,电商系统中核心一定会有商品订单这2个东西,而商品必然会有数量、品牌、商品描述信息这些方面。而商品上架之后才会有订单这个东西,那么我们第一件事就是进行商品管理。一个简单的商品创建模型如下:
在这里插入图片描述

随着业务变化,你发现商品的不同品牌之间有着关联。比如AMD(中国)和AMD(美国)可以看做一个公司,但在具体商品上又要区分开,记录上主公司与分公司的关系。那么我们的模型发生了一点变化:

在这里插入图片描述

设计

在开始之前,建议先停下来想想,如果是你,你会如何设计? 然后再向下看。

观察上面整理出来的业务模型发现特点:一环扣一环,审批处如果审批不过会退回到起始点

我们这里简单对比下传统思路与责任链模式:

  1. 如果用传统的业务思想去开发:可能会是一个方法中调用不同的子方法,然后判断每个子方法的返回结果并做出不同的处理方式。随着业务变化,这个方法会变的越来越庞大,修改一处都有可能影响整套代码
  2. 如果使用责任链模式:把每一环拆开进行开发,然后在使用处组装成我们的业务处理链。这样在开发时我们并不关心整体如何,而是我们这一处地方应该如何。并且业务变化时,中断整体调用,由具体的环节给出对应的方案。

下面是实现的代码,你也可以下载查看 1

首先是我们的实体类:

    /// <summary>
    /// 商品信息
    /// </summary>
    public class ItemInfo
    {
        /// <summary>
        /// 商品名
        /// </summary>
        public string Name { get; set; }
    }

设计一个处理者接口(也可以使用父类):

    /// <summary>
    /// 商品创建处理者
    /// </summary>
    public interface IItemCreateHandler
    {
        /// <summary>
        /// 下一个处理者
        /// </summary>
        IItemCreateHandler NextHandler { get; }

        /// <summary>
        /// 设置下一个处理者
        /// </summary>
        /// <param name="handler"></param>
        void SetNextHandler(IItemCreateHandler handler);

        /// <summary>
        /// 处理商品创建
        /// </summary>
        /// <param name="itemInfo"></param>
        bool HandleItemCreate(ItemInfo itemInfo);
    }

实现的代码就很简单了:

  1. 处理录入的信息
  2. 审批
  3. 持久化到数据库
    代码:
    /// <summary>
    /// 商品信息录入处理器
    /// </summary>
    public class ItemInfoEntryHandler : IItemCreateHandler
    {
        public IItemCreateHandler NextHandler { get; protected set; }

        public bool HandleItemCreate(ItemInfo itemInfo)
        {
            itemInfo = itemInfo ?? this.GetItemInfoFromUI();

            return this.NextHandler.HandleItemCreate(itemInfo);
        }

        public void SetNextHandler(IItemCreateHandler handler)
        {
            this.NextHandler = handler;
        }

        //模拟从UI上获取数据
        private ItemInfo GetItemInfoFromUI()
        {
            return new ItemInfo() { };
        }
    }

    /// <summary>
    /// 商品创建审批处理者
    /// </summary>
    public class ItemCreateApproveHandler : IItemCreateHandler
    {
        public IItemCreateHandler NextHandler { get; protected set; }

        public bool HandleItemCreate(ItemInfo itemInfo)
        {
            //做一些检查,比如名字、信息是否匹配等
            //实际中,这些检查是由管理者在UI上审核的,这里只需要接受审核结果即可

            if (string.IsNullOrWhiteSpace(itemInfo.Name))
            {
                Console.WriteLine("商品名不能为空,请重新输入");
                return false;
            }
            else
            {
                return this.NextHandler.HandleItemCreate(itemInfo);
            }
        }

        public void SetNextHandler(IItemCreateHandler handler)
        {
            this.NextHandler = handler;
        }
    }

    /// <summary>
    /// 商品信息持久化处理者
    /// </summary>
    public class ItemInfoPersistenceHandler : IItemCreateHandler
    {
        public IItemCreateHandler NextHandler { get; protected set; }

        public bool HandleItemCreate(ItemInfo itemInfo)
        {
            //把数据写到数据库中,并且不再调用NextHandler

            return true;
        }

        public void SetNextHandler(IItemCreateHandler handler)
        {
            this.NextHandler = handler;
        }
    }

然后由调用者组装:


    internal class Program
    {
        private static void Main(string[] args)
        {
            Console.WriteLine("==开始创建商品==");

            IItemCreateHandler handler1 = new ItemInfoEntryHandler();
            IItemCreateHandler handler2 = new ItemCreateApproveHandler();
            IItemCreateHandler handler3 = new ItemInfoPersistenceHandler();

            handler1.SetNextHandler(handler2);
            handler2.SetNextHandler(handler3);

            bool createSusess = false;
            while (!createSusess)
            {
                Console.Write("请输入商品名:");
                string itemName = Console.ReadLine();
                createSusess = handler1.HandleItemCreate(new ItemInfo() { Name = itemName });
            } 

            Console.WriteLine("==商品创建结束==");
            Console.ReadLine();
        }
    }

至此,我们的第一阶段已经设计完成。

应对增加处理环节

上面我们看到,业务变化后还增加了一个映射环节。只需要加一个类,并且简单修改下调用处的代码即可搞定:

增加映射代码:

    /// <summary>
    /// 商品信息映射处理者
    /// </summary>
    internal class ItemInfoMapHandler : IItemCreateHandler
    {
        public IItemCreateHandler NextHandler { get; protected set; }

        public bool HandleItemCreate(ItemInfo itemInfo)
        {
            //在这个类中记录下子公司与分司的关系

            return this.NextHandler.HandleItemCreate(itemInfo);
        }

        public void SetNextHandler(IItemCreateHandler handler)
        {
            this.NextHandler = handler;
        }
    }

修改调用代码:

    internal class Program
    {
        private static void Main(string[] args)
        {
            Console.WriteLine("==开始创建商品==");

            IItemCreateHandler handler1 = new ItemInfoEntryHandler();
            IItemCreateHandler handler2 = new ItemCreateApproveHandler();
            IItemCreateHandler handler3 = new ItemInfoPersistenceHandler();
            IItemCreateHandler newHandler = new ItemInfoMapHandler();//新加代码

            handler1.SetNextHandler(newHandler);//修改代码
            newHandler.SetNextHandler(handler2);//修改代码
            handler2.SetNextHandler(handler3);

            bool createSusess = false;
            while (!createSusess)
            {
                Console.Write("请输入商品名:");
                string itemName = Console.ReadLine();
                createSusess = handler1.HandleItemCreate(new ItemInfo() { Name = itemName });
            }

            Console.WriteLine("==商品创建结束==");
            Console.ReadLine();
        }
    }

优缺点分析

代码写了一些了,我们来分析下目前的优点和缺点吧!

优点

  1. 通过代码能很好的看出我们的业务模型。
  2. 代码耦合度低

缺点

  1. 需要使用者自己组装并调用,不清真。
  2. 中间环节出现异常时,可以中断时还好。但如果是返回到非起始点,怎么办呢?

针对缺点中的第1点,可以使用一个帮助类或简单工厂封装一下就好了。
针对第2点,当A环节需要跳到B环节时,这里的对象也只有2个。 那么我们这里可以在A环节的代码中手动new一个B环节的代码来搞定。如果你觉得这种比较Low,也可以使用其它方式,核心就在于A环节调用B环节而已。

结合建造者模式

上面说了这么多,都是在讲责任链模式和它的应用,在某些层面可以与建造者模式很好的结合。

看下面的分析:
在这里插入图片描述

发现有3个固定步骤:

  1. 获取数据
  2. 检查
  3. 持久化 ,并且顺序也是固定的。

这是不是可以改造成建造者模式呢? 但现在的这个业务不一定会改造成建造者模式,因为需求变化还不足以让我们添加一个新的模式进来!

但这种场景就适合了:

  1. 针对不同的商品、有不同的审批环节。比如:普通的商品由操作员查看、虚拟类商品由系统查看卖家的资格自动审批、奢侈品需要主管审批等等。
  2. 获取数据的来源有多种。比如:从文件中批量获取、用爬虫在网络上爬、UI上手动输入等等
  3. 持久化到其它的数据库
  4. 其它…

不管场景怎么变,但我们的核心业务模型没有变化,只有具体的实现变了。当你有这种需求的时候,就可以结合建造者模式去快速应对需求变化了。

如果你想详细了解建造者模式,可去系列教程目录查找。

总结

责任链模式强调:环节或模块之间的顺序调用关系。.net core中的中间件思想、http处理管道都可以看成这种思想的一种延伸。
建造者模式强调:执行的代码是固定顺序固定步骤,但实现的细节多变。工厂模式就可以看成一个单步骤的建造者模式。


  1. 责任链模式.7z ↩︎

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值