首先D妹子有一个商品的工具,商品里有一个价格的属性,价格的单位是分
class Product { //其他属性省略 public int Price { get; set; } }
下面有一个满100减20的活动,在结算价格的时候代码是这样的
public int GetPrice() { Product p = new Product(); int ret = p.Price; if (p.Price >= 100100) { 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的缘故原由。
优化版本那详细要怎么优化呢?修正代码之前,我还是想提醒一下,有几个要点须要把稳一点:
商品菜菜认为有一个共同的基类比较好,这样就有了一个所有商品的掌握点,为往后统一添加属性留一个入口。好比一个网关系统,为什么会出身网关这个组件呢,由于有了它我们能方便的统一添加认证,授权,统计等一些列行为。任何匆匆销的活动最好有一个基类,浸染类似商品基类。对付商品而言,任何匆匆销活动是商品的行为变革点,影响到的是终极的商品价格,以是获取价格这个行为要做分外的处理。不同种类的匆匆销活动该当能自行扩展,不会影响别的类型匆匆销活动。不同种类的匆匆销活动能叠加利用(实在这里涉及到每个活动打算的标准是商品原价还是匆匆销之后价格的问题)。基于以上几点,首先把商品的工具做一下抽象
//商品抽象基类 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; } }把多商品匆匆销活动的基类抽象出来,供不同的匆匆销活动继续利用,这里须要继续ActivityListProduct,为什么呢?和单品的类似,为了多个子类能够嵌套调用
//商品列表 活动的基类,继续自商品列表基类 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