专栏首页GreenLeaves领域驱动系列二策略模式的应用

领域驱动系列二策略模式的应用

一、简介

随着模型的不断扩大,发现模型中不单单只有"名词",还有许多"谓词",简言之,就是领域知识中,会参杂者许多的业务规则,他们和实体一样,都扮演者领域模型中的核心角色.

所以我们在建立领域模型的时候,不单单只关注实体和值对象,业务规则也被纳入到了领域模型中,如果业务规则变化不频繁,我们可以使用硬编码来解决,但是实际开发中业务规则的变化往往是变化的非常频繁的.当然你可以使用大量的If else来解决这个问题,但是这种代码是很难维护的.而且会影响原先的业务,所以这个时候策略模式就能很好地应对这种变化.

二、实战

现在我们需要设计一套计价系统,分别给Vip用户和普通用户计算每笔订单的总金额,前提,是VIP用户每笔订单有8折优惠.因此我们可以得到如下的领域模型:

C#实现代码如下:

    class Program
    {
        static void Main(string[] args)
        {
            //模拟用户
            var nUser = new NormalUser() {Name="普通用户",Age=23 };
            var vUser = new VipUser() { Name = "Vip用户", Age = 23 };

            //模拟订单
            var order = new Order()
            {
                Product = new Product() { Name = "电吹风", Price = 20, Amount = 2 },
                Amount = 2
            };

            //计算用户的总花费
            var cal = new CalcultePrice();
            Console.WriteLine("普通用户买两件电吹风的花费是:{0}元", cal.Calculate(nUser, order));
            Console.WriteLine("Vip用户买两件电吹风的花费是:{0}元", cal.Calculate(vUser, order));
            Console.ReadKey();
        }
    }

    /// <summary>
    /// 用户基类
    /// </summary>
    public abstract class User
    {
        public abstract string Name { get; set; }

        public abstract int Age { get; set; }
    }

    /// <summary>
    /// 普通用户
    /// </summary>
    public class NormalUser : User
    {
        public override string Name { get; set; }
        public override int Age { get; set; }
    }

    /// <summary>
    /// Vip用户
    /// </summary>
    public class VipUser : User
    {
        public override string Name { get; set; }

        public override int Age { get; set; }
    }

    /// <summary>
    /// 商品
    /// </summary>
    public class Product
    {
        /// <summary>
        /// 商品名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 价格
        /// </summary>
        public double Price { get; set; }

        /// <summary>
        /// 数量
        /// </summary>
        public int Amount { get; set; }
    }

    /// <summary>
    /// 订单类
    /// </summary>
    public class Order
    {
        public Product Product { get; set; }

        /// <summary>
        /// 商品数量
        /// </summary>
        public int Amount { get; set; }

        /// <summary>
        /// 订单总金额
        /// </summary>
        public double TotalFee { get { return Product.Price * Amount; } }

        /// <summary>
        /// 订单的实际花费
        /// </summary>
        public double ActuallyFee { get; set; }

        /// <summary>
        /// 下单的用户
        /// </summary>
        public User User { get; set; }
    }

    /// <summary>
    /// 计价类
    /// </summary>
    public class CalcultePrice
    {
        /// <summary>
        /// 计算价格
        /// </summary>
        /// <returns></returns>
        public double Calculate(User user, Order order)
        {
            double fee;
            var o = order;
            //这里存在一条业务规则,Vip用户享受8折优惠
            if (user is VipUser)
            {
                fee = o.TotalFee * 0.8;
            }
            else
            {
                fee = o.TotalFee;
            }
            return fee;
        }
    }

ok,上面设计的代码很好的满足了需求.但是问题来了,如下图:

这个逻辑是很脆弱的,可能会频繁的变化,而且虽然我的代码你可能一眼就看明白了,但是如果有些开发习惯不好的程序员,这里使用常量来表示用户身份,如0、1之类的数字来区分,我想大多数程序员可能看不懂这个代码。所以我们必须要将这个规则转换成一个领域对象,让代码可阅读的同时,同时符合领域驱动设计的规范.所以策略模式就出场了,修改代码如下:

    class Program
    {
        static void Main(string[] args)
        {
            //模拟用户
            var nUser = new NormalUser() {Name="普通用户",Age=23 };
            var vUser = new VipUser() { Name = "Vip用户", Age = 23 };

            //模拟订单
            var order = new Order()
            {
                Product = new Product() { Name = "电吹风", Price = 20, Amount = 2 },
                Amount = 2
            };

            //计算用户的总花费
            var cal = new CalcultePrice();
            Console.WriteLine("普通用户买两件电吹风的花费是:{0}元", cal.Calculate(new VipUserPrice(nUser, order)));
            Console.WriteLine("Vip用户买两件电吹风的花费是:{0}元", cal.Calculate(new VipUserPrice(vUser, order)));
            Console.ReadKey();
        }
    }

    /// <summary>
    /// 用户基类
    /// </summary>
    public abstract class User
    {
        public abstract string Name { get; set; }

        public abstract int Age { get; set; }
    }

    /// <summary>
    /// 普通用户
    /// </summary>
    public class NormalUser : User
    {
        public override string Name { get; set; }
        public override int Age { get; set; }
    }

    /// <summary>
    /// Vip用户
    /// </summary>
    public class VipUser : User
    {
        public override string Name { get; set; }

        public override int Age { get; set; }
    }

    /// <summary>
    /// 商品
    /// </summary>
    public class Product
    {
        /// <summary>
        /// 商品名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 价格
        /// </summary>
        public double Price { get; set; }

        /// <summary>
        /// 数量
        /// </summary>
        public int Amount { get; set; }
    }

    /// <summary>
    /// 订单类
    /// </summary>
    public class Order
    {
        public Product Product { get; set; }

        /// <summary>
        /// 商品数量
        /// </summary>
        public int Amount { get; set; }

        /// <summary>
        /// 订单总金额
        /// </summary>
        public double TotalFee { get { return Product.Price * Amount; } }

        /// <summary>
        /// 订单的实际花费
        /// </summary>
        public double ActuallyFee { get; set; }

        /// <summary>
        /// 下单的用户
        /// </summary>
        public User User { get; set; }
    }

    /// <summary>
    /// 计价类
    /// </summary>
    public class CalcultePrice
    {
        /// <summary>
        /// 计算价格
        /// </summary>
        /// <returns></returns>
        public double Calculate(IOrderPriceRule rule)
        {
            return rule.CalculatePrice();
        }
    }

    /// <summary>
    /// 订单计算规则接口
    /// </summary>
    public interface IOrderPriceRule
    {
        double CalculatePrice();
    }

    /// <summary>
    /// Vip用户8折计算规则
    /// </summary>
    public class VipUserPrice : IOrderPriceRule
    {
        private User User { get; set; }

        private Order Order { get; set; }

        protected VipUserPrice() { }

        public VipUserPrice(User user, Order order)
        {
            User = user;
            Order = order;
        }

        /// <summary>
        /// 设置为虚方法,方便子类修改逻辑
        /// </summary>
        /// <returns></returns>
        public virtual double CalculatePrice()
        {
            if (User is VipUser)
            {
                return Order.TotalFee * 0.8;
            }
            return Order.TotalFee;
        }
    }

ok,这里我将Vip用户打8折的规则抽象成一种接口数据,转移到领域对象中,让领域对象能更好的应对变化.这样当产品提出修改VIP的优惠尺度时,我们就可以重新实现一个IOrderPriceRule的对象.或者给VipUserPrice实现一个新的计算规则类,或者跟直接将折扣写到配置文件中,方便随时修改,而不用向第一个版本那样,直接去修改领域模型中的核心逻辑,用扩展的方式去修改领域对象.这样对他的伤害最小.而且这种方式将规则作为一种领域模型中的对象,符合领域驱动的设计理念,也符合面向对象设计的初衷.这个时候的领域模型图如下:

ok,这样的模型更容易被程序员理解.

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 领域驱动系列五模型驱动设计的构造块

    为了保证软件实现的简洁性,并且与模型保持一致,不管实际情况有多复杂,必须使用建模和设计的最佳实践,即让通过我们的编程技术(设计模型、指责驱动、契约式设计)充分地...

    郑小超.
  • Facade外观模式(结构性模式)

    需求:开发一个坦克模拟系统用于模拟坦克车在各种作战环境中的行为,其中坦克系统由引擎、控制器、车轮等各子系统构成.然后由对应的子系统调用.

    郑小超.
  • C# Command命令(行为型模式)+队列 实现事务,带异步命令重试机制和生命周期

    耦合是软件不能抵御变变化的根本性原因,不仅实体对象与实体对象之间有耦合关系(如创建性设计模式存在的原因),对象和行为之间也存在耦合关系.

    郑小超.
  • .Net GDI+的图件绘制平台(四)-后台获取数据及窗体展示

    程序你好
  • 使用.net core ABP和Angular模板构建博客管理系统(创建后端服务)

    易兒善
  • Magicodes.WeiChat——使用OAuth 2.0获取微信用户信息

    使用Magicodes.WeiChat,可以很方便的获取到微信用户的信息。在使用OAuth 2.0之前,你先需要做以下操作:

    雪雁-心莱科技
  • 一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar](三)

    上一篇《一步一步创建ASP.NET MVC5程序[Repository+Autofac+Automapper+SqlSugar](二)》我们通过如下操作:

    Rector
  • Magicodes.IE之导入学生数据教程

    本教程主要说明如果使用Magicodes.IE.Excel完成学生数据的Excel导入。

    心莱科技雪雁
  • C#开发BIMFACE系列14 服务端API之批量获取转换状态详情

    上一篇《C#开发BIMFACE系列13 服务端API之获取转换状态》中介绍了根据文件ID查询单个文件的转换状态。

    张传宁老师
  • Magicodes.IE之导入学生数据教程

    本教程主要说明如果使用Magicodes.IE.Excel完成学生数据的Excel导入。

    雪雁-心莱科技

扫码关注云+社区

领取腾讯云代金券