乐趣区

关于c#:论商品促销代码的优雅性

背景介绍

据我所知,简直所有的互联网公司都带有和电商无关的我的项目,而且在大多数公司外面还是无足轻重的重头戏,比方京东,淘宝。既然有电商我的项目,必然会波及到商品,一旦有商品就会有各种促销流动,比方 满 100 减 20, 三八妇女节 9 折等等相似流动。作为一个 coder 怎么能力在实现产品狗的需要下,最小改变代码,最优雅的实现呢。明天菜菜不才,就 D 妹子的问题献丑一番。以下以.netCore c# 代码为例,其余语言相似。

D 妹子版本

首先 D 妹子有一个商品的对象,商品里有一个价格的属性,价格的单位是分

 class Product
    {
        // 其余属性省略
        public int Price {get; set;}
    }

上面有一个满 100 减 20 的流动,在结算价格的时候代码是这样的

 public int GetPrice()
        {Product p = new Product();
            int ret = p.Price;
            if (p.Price >= 100*100)
            {ret = ret - 20 * 100;}
            return ret;
        }

有问题吗?依照需要来说没有问题,而且计算的后果也正确。然而从程序艺术来说,其实很俊俏。当初又有一个全场 9 折的流动,凑巧有一个商品参加了以上两个流动,而且还能够叠加应用 (假如流动参加的程序是先折扣后满减)。这时候 D 妹子的代码就变成了这样

 public int GetPrice()
        {Product p = new Product();
            // 9 折流动
            int ret = p.Price * 90 / 100;
            // 满减流动
            if (ret >= 100 * 100)
            {ret = ret - 20 * 100;}
            return ret;
        }

如果当初又来一个相似流动,那这块代码还须要批改,重大违反了凋谢敞开准则,而且频繁批改曾经上线的代码,bug 的几率会大大增高。这也是 D 妹子领导骂她并且让她 codereview 的起因。

优化版本

那具体要怎么优化呢?批改代码之前,我还是想揭示一下,有几个要点须要留神一点:

  1. 商品菜菜认为有一个独特的基类比拟好,这样就有了一个所有商品的控制点,为当前对立增加属性留一个入口。好比一个网关零碎,为什么会诞生网关这个组件呢,因为有了它咱们能不便的对立增加认证,受权,统计等一些列行为。
  2. 任何促销的流动最好有一个基类,作用相似商品基类。
  3. 对于商品而言,任何促销流动是商品的行为变动点,影响到的是最终的商品价格,所以获取价格这个行为要做非凡的解决。
  4. 不同品种的促销流动应该能自行扩大,不会影响别的类型促销流动。
  5. 不同品种的促销流动能叠加应用 (其实这里波及到每个流动计算的规范是商品原价还是促销之后价格的问题)。

基于以上几点,首先把商品的对象做一下形象

 // 商品形象基类
    abstract class BaseProduct
    {
        // 商品价格,单位:分
        public int Price {get; set;}
        // 获取商品价格形象办法
        public abstract int GetPrice();}
    // 形象商品(比方话费商品),继承商品基类
    class VirtualProduct : BaseProduct
    {public override int GetPrice()
        {return this.Price;}
    }

接下来流动的基类也须要形象进去

// 各种流动的形象基类,继承要包装的类型基类
    abstract class BaseActivity : BaseProduct
    {}

有的同学会问,这里为什么要继承商品的基类呢?次要是为了流动的基类能嵌套应用,这样我就能够实现多个流动同时应用,如果不明确没关系,带着这个问题接着往下看

实现一个打折的流动

// 打折流动基类, 反对多个商品同时结算
    class DiscountActivity : BaseActivity
    {
        BaseProduct product = null;

        public DiscountActivity(int discount, BaseProduct _product)
        {
            Discount = discount;
            product = _product;
        }

        // 折扣,比方 90 折 即为 90
        public int Discount {get; set;}
        // 获取折扣之后的价格
        public override int GetPrice()
        {return product.GetPrice() * Discount / 100;
        }
    }

实现一个满减的流动,而且反对自定义满减条件

  class ReductionActivity : BaseActivity
    {
        BaseProduct product = null;
        // 满减的对应表
        Dictionary<int, int> reductMap = null;

        public ReductionActivity(Dictionary<int, int> _redutMap, BaseProduct _product)
        {
            reductMap = _redutMap;
            product = _product;
        }
        // 获取折扣之后的价格
        public override int GetPrice()
        {var productAmount = product.GetPrice();
            // 依据商品的总价获取到要减的价格
            var reductValue = reductMap.OrderByDescending(s => s.Key).FirstOrDefault(s => productAmount >= s.Key).Value;
            return productAmount - reductValue;
        }
    }

当初咱们来给商品做个促销流动吧

            VirtualProduct p = new VirtualProduct() {  Price=1000};           
            // 打折流动
            DiscountActivity da = new DiscountActivity(90, p);
            var retPrice= da.GetPrice();
            Console.WriteLine($"打折后的价格 {retPrice}");
            // 还能叠加加入满减流动
            Dictionary<int, int> m = new Dictionary<int, int>() ;
            m.Add(200, 5); // 满 200 减 5
            m.Add(300, 10); 
            m.Add(500, 20);
            m.Add(1000, 50);
            // 这里流动能叠加应用了
            ReductionActivity ra = new ReductionActivity(m, da);
            retPrice = ra.GetPrice();
            Console.WriteLine($"打折满减后的价格 {retPrice}");

            ReductionActivity ra2 = new ReductionActivity(m, ra);
            retPrice = ra2.GetPrice();
            Console.WriteLine($"再打折后的价格 {retPrice}");

输入后果:

 打折后的价格 900
打折满减后的价格 880
再打折后的价格 860

当初咱们终于能优雅一点的同时进行商品的满减和打折流动了

进化到多个商品同时促销

以上代码曾经能够比拟优雅的能进行单品的促销流动了,然而事实往往很骨感,实在的电商场景中多以多个商品结算为主,那用同样的思路怎么实现呢?

  1. 因为这次须要实现的是多商品促销结算,所以须要一个自定义的商品列表来作为要进行结算的对象。此对象行为级别上与单品相似,有一个需要变动点的形象:获取价格
// 商品列表的基类, 用于流动结算应用
    class ActivityListProduct : List<BaseProduct>
    {
        // 商品列表流动结算的办法,基类必须重写
        public virtual int GetPrice()
        {
            int ret = 0;
            base.ForEach(s =>
            {ret += s.GetPrice();
            });
            return ret;
        }
    }
  1. 把多商品促销流动的基类形象进去,供不同的促销流动继承应用, 这里须要继承 ActivityListProduct,为什么呢?和单品的相似,为了多个子类可能嵌套调用
// 商品列表 流动的基类,继承自商品列表基类
    internal abstract class BaseActivityList : ActivityListProduct
    {}
  1. 创立一个打折和满减流动
// 打折流动基类, 反对多个商品同时结算
    class DiscountActivityList : BaseActivityList
    {
        ActivityListProduct product = null;
        public DiscountActivityList(int discount, ActivityListProduct _product)
        {
            Discount = discount;
            product = _product;
        }
        // 折扣,比方 90 折 即为 90
        public int Discount {get; set;}
        public override int GetPrice()
        {var productPrice = product.GetPrice();
            return productPrice * Discount / 100;
        }
    }
    // 满减的流动
    class ReductionActivityList : BaseActivityList
    {
        ActivityListProduct product = null;
        // 满减的对应表
        Dictionary<int, int> reductMap = null;

        public ReductionActivityList(Dictionary<int, int> _redutMap, ActivityListProduct _product)
        {
            reductMap = _redutMap;
            product = _product;
        }
        // 获取折扣之后的价格
        public override int GetPrice()
        {var productAmount = product.GetPrice();
            // 依据商品的总价获取到要减的价格
            var reductValue = reductMap.OrderByDescending(s => s.Key).FirstOrDefault(s => productAmount >= s.Key).Value;
            return productAmount - reductValue;
        }
    }

先来一波多商品促销流动

VirtualProduct p = new VirtualProduct() { Price = 1000};
            VirtualProduct p2 = new VirtualProduct() { Price = 1000};
            ActivityListProduct lst = new ActivityListProduct();
            lst.Add(p);
            lst.Add(p2);
            DiscountActivityList dalist = new DiscountActivityList(80, lst);
            Console.WriteLine($"打折后的价格 {dalist.GetPrice()}");
            DiscountActivityList dalist2 = new DiscountActivityList(90, dalist);
            Console.WriteLine($"打折后的价格 {dalist2.GetPrice()}");

            DiscountActivityList dalist3 = new DiscountActivityList(90, dalist2);
            Console.WriteLine($"打折后的价格 {dalist3.GetPrice()}");

            // 还能叠加加入满减流动
            Dictionary<int, int> m = new Dictionary<int, int>();
            m.Add(200, 5); // 满 200 减 5
            m.Add(300, 10);
            m.Add(500, 20);
            m.Add(1000, 50);

            ReductionActivityList ral = new ReductionActivityList(m, dalist3);
            Console.WriteLine($"再满减打折后的价格 {ral.GetPrice()}");

结算后果:

 打折后的价格 1600
打折后的价格 1440
打折后的价格 1296
再满减打折后的价格 1246

当初基本上能够让 D 妹子不被挨骂了

神化版本见留言区

晓得 D 妹子为什么取名 D 妹子吗?

支付架构师进阶材料大礼包

退出移动版