菜菜哥,YY說你幫她解決了幾個問題,也幫我解決一個唄web
原來是D妹子,來坐我身邊,說下狀況小程序
個人項目是個電商項目,如今產品狗要給商品作活動c#
正常呀設計模式
我一個新手初來咋到頂不住壓力了,上次來一個折扣活動,如今又來一個滿減微信
正常呀多線程
最要命的兩個活動還能疊加使用app
正常呀less
我寫的代碼讓老大罵了一頓,讓我作優化編輯器
代碼有多爛?ide
離近一點,我給你看看
好嘞
據我所知,幾乎全部的互聯網公司都帶有和電商有關的項目,並且在大多數公司裏面仍是舉足輕重的重頭戲,好比京東,淘寶。既然有電商項目,必然會涉及到商品,一旦有商品就會有各類促銷活動,好比 滿100減20,三八婦女節9折等等相似活動。做爲一個coder怎麼才能在實現產品狗的需求下,最小改動代碼,最優雅的實現呢。今天菜菜不才,就D妹子的問題獻醜一番。如下以.netCore c#代碼爲例,其餘語言相似。
首先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;
}
}
2. 把多商品促銷活動的基類抽象出來,供不一樣的促銷活動繼承使用,這裏須要繼承ActivityListProduct,爲何呢?和單品的相似,爲了多個子類可以嵌套調用
//商品列表 活動的基類,繼承自商品列表基類
internal abstract class BaseActivityList : ActivityListProduct
{
}
3. 建立一個打折和滿減活動
//打折活動基類,支持多個商品同時結算
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妹子嗎?
爲了答謝粉絲的支持,菜菜二期福利來臨,關注公衆號查收,請奔走相告送書活動規則
規則:經過如下抽獎小程序碼進入小程序,點擊參加抽獎便可,也能夠經過點擊公衆號菜單直接進入小程序。具體的抽獎事宜請見小程序介紹。
兌獎:中獎者如下書籍任選一本(圖解Java多線程設計模式,Kafka權威指南,圖解深度學習)。活動結束,請注意查收抽獎小程序發送的消息,在小程序添加收貨地址等信息;也能夠添加菜菜我的微信號(qianbaojiajia)爲好友進行領獎。
互聯網之路,菜菜與君一同成長
長按識別二維碼關注