zoukankan      html  css  js  c++  java
  • 领域驱动系列二策略模式的应用

    一、简介

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

    所以我们在建立领域模型的时候,不单单只关注实体和值对象,业务规则也被纳入到了领域模型中,如果业务规则变化不频繁,我们可以使用硬编码来解决,但是实际开发中业务规则的变化往往是变化的非常频繁的.当然你可以使用大量的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,这样的模型更容易被程序员理解.

  • 相关阅读:
    mysql主从同步图
    VS2010智能提示失效,关键字不智能提示!
    mvc3的SaveChanges()方法无效,数据并没有更新!
    使用uploadify上传插件时遇到 NetworkError: 403 Forbidden http://xxxx/xxxx/ 错误
    Asp.net 调用mysql存储过程参数传中文乱码!
    The entity type XXXInfo is not part of the model for the current context.
    爬取汽车网站汽车数据
    《牛顿和莱布尼兹对最速降落线问题的解法,少为人知》 回复
    《谁能证明:标准波面的光若能汇集于一点,则它们的光程长度必然相等》 回复
    200^199 和 199^200 哪个大 ?
  • 原文地址:https://www.cnblogs.com/GreenLeaves/p/10198295.html
Copyright © 2011-2022 走看看