據我所知,幾乎全部的互聯網公司都帶有和電商有關的項目,並且在大多數公司裏面仍是舉足輕重的重頭戲,好比京東,淘寶。既然有電商項目,必然會涉及到商品,一旦有商品就會有各類促銷活動,好比 滿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妹子嗎?
添加關注,查看更精美版本,收穫更多精彩