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

一、简介

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

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

原文地址:https://www.cnblogs.com/GreenLeaves/p/10198295.html

时间: 2024-11-05 21:55:20

领域驱动系列二策略模式的应用的相关文章

设计模式总结篇系列:策略模式(Strategy)

前面的博文中分别介绍了Java设计模式中的创建型模式和结构型模式.从本文开始,将分别介绍设计模式中的第三大类,行为型模式.首先我们了解下分为此三大类的依据. 创建型模式:主要侧重于对象的创建过程: 结构型模式:主要侧重于处理类或对象的组合: 行为型模式:主要侧重于类或对象之间的交互以及职责分配. 首先了解下策略模式的概念:定义了多个算法,并将它们封装起来(一般的是每个算法封装成一个单独的类),让算法独立于客户端而可以单独变化. 具体可以看一下下面的例子(以计算加.减.乘为例): 1. 对加.减.

Java设计模式菜鸟系列(一)策略模式建模与实现

转载请注明出处:http://blog.csdn.net/lhy_ycu/article/details/39721563 今天开始咱们来谈谈Java设计模式.这里会结合uml图形来讲解,有对uml建模不熟的可以参考我的另一篇博文uml建模. 首先,个人觉得模式的设计就是一个将变化的东西和不变(稳定)的东西分离的过程.咱们的应用中可能有很多需要改变的地方,而模式要做的就是把它们"抽取"出来并进行"封装"和"实现",因此更多的时候咱们是面向接口编程

小酌重构系列[15]&mdash;&mdash;策略模式代替分支

前言 在一些较为复杂的业务中,客户端需要依据条件,执行相应的行为或算法.在实现这些业务时,我们可能会使用较多的分支语句(switch case或if else语句).使用分支语句,意味着"变化"和"重复",每个分支条件都代表一个变化,每个分支逻辑都是相似行为或算法的重复.当追加新的条件时,我们需要追加分支语句,并追加相应的行为或算法. 上一篇文章"使用多态代替条件判断"中,我们讲到它可以处理这些"变化"和"重复&qu

设计模型之二策略模式

//strategy model //策略模式是一种定义一系列算法的方法 #include<iostream> using namespace std; //一个抽象的策略类 class Strategy{ public: virtual void algorithmInterface()=0; }; //三种不同策略 class StrategyA:public Strategy{ void algorithmInterface() { cout<<"A strateg

二 策略模式

策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合 策略模式封装了变化 在实践中,我们发现可以用它来封装几乎任类型的规则,只要在分析过程中听到需要在不同时间应用到不同的业务规则,就考虑使用策略模式来处理这种变化的可能 商场促销例子 现金收费抽象类 using System; using System.Collections.Generic; using System.Linq;

Java设计模式系列之策略模式

策略模式的定义: 策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换,策略模式让算法独立于使用它的客户而独立变化. 策略模式使这些算法在客户端调用它们的时候能够互不影响地变化. 策略模式的意义:   策略模式使开发人员能够开发出由许多可替换的部分组成的软件,并且各个部分之间是低耦合的关系. 低耦合的特性使软件具有更强的可扩展性,易于维护:更重要的是,它大大提高了软件的可重用性.    策略模式中有三个对象:      环境对象(Context):该类中实现了对抽象策略中

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

一.简介 为了保证软件实现的简洁性,并且与模型保持一致,不管实际情况有多复杂,必须使用建模和设计的最佳实践,即让通过我们的编程技术(设计模型.指责驱动.契约式设计)充分地体现领域模型,并保持模型地健壮性和可扩展性,而不是单单地实现模型.某些决策设计能和模型紧紧地结合,这种结合要求我们注意每个元素地细节. 开发一个好的领域模型是一门艺术,而模型中的各个元素的实际设计和实现则相对系统化,将领域设计(也可以是软件系统中的其他关注点)与软件系统中的其他关注点(也可以是领域设计)分离使整个领域模型非常的清

初探领域驱动设计(2)Repository在DDD中的应用

概述 上一篇我们算是粗略的介绍了一下DDD,我们提到了实体.值类型和领域服务,也稍微讲到了DDD中的分层结构.但这只能算是一个很简单的介绍,并且我们在上篇的末尾还留下了一些问题,其中大家讨论比较多的,也是我本人之前有一些疑问的地方就是Repository.我之前觉得IRepository和三层里面的IDAL很像,为什么要整出这么个东西来:有人说用EF的话就不需要Repository了:IRepository是鸡肋等等. 我觉得这些问题都很好,我自己也觉得有问题,带着这些问题我们就来看一看Repo

初探领域驱动设计(1)为复杂业务而生

概述 领域驱动设计也就是3D(Domain-Driven Design)已经有了10年的历史,我相信很多人或多或都都听说过这个名词,但是有多少人真正懂得如何去运用它,或者把它运用好呢?于是有人说,DDD和TDD这些玩意是一些形而上的东西,只是一茶余饭后的谈资,又或是放到简历上提升逼格而已.前面这句话我写完之后犹豫了,犹豫要不要把它删掉,因为它让我看起来像个喷子,我确实感到不解,为什么别人10年前创造总结出来的东西,我们在10年之后对它的理解还处于这么低的一个层次.开篇就说远了,我也是最近才开始认