据我所知,几乎全部的互联网公司都带有和电商有关的项目,并且在大多数公司里面仍是举足轻重的重头戏,好比京东,淘宝。既然有电商项目,必然会涉及到商品,一旦有商品就会有各类促销活动,好比 满100减20,三八妇女节9折等等相似活动。做为一个coder怎么才能在实现产品狗的需求下,最小改动代码,最优雅的实现呢。今天菜菜不才,就D妹子的问题献丑一番。如下以.netCore c#代码为例,其余语言相似。c#
首先D妹子有一个商品的对象,商品里有一个价格的属性,价格的单位是分ide
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妹子的代码就变成了这样this
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的缘由。spa
那具体要怎么优化呢?修改代码以前,我仍是想提醒一下,有几个要点须要注意一点:.net
基于以上几点,首先把商品的对象作一下抽象code
//商品抽象基类 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
如今咱们终于能优雅一点的同时进行商品的满减和打折活动了
以上代码已经能够比较优雅的能进行单品的促销活动了,可是现实每每很骨感,真实的电商场景中多以多个商品结算为主,那用一样的思路怎么实现呢?
//商品列表的基类,用于活动结算使用 class ActivityListProduct : List<BaseProduct> { //商品列表活动结算的方法,基类必须重写 public virtual int GetPrice() { int ret = 0; base.ForEach(s => { ret += s.GetPrice(); }); return ret; } }
//商品列表 活动的基类,继承自商品列表基类 internal abstract class BaseActivityList : ActivityListProduct { }
//打折活动基类,支持多个商品同时结算 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妹子吗?
添加关注,查看更精美版本,收获更多精彩