多场景抢红包业务引发.NETCore下使用适配器模式实现业务接口分离
事情的起因
我們公司現(xiàn)有一塊業(yè)務(wù)叫做搶紅包,最初的想法只是實(shí)現(xiàn)了一個初代版本,就是給指定的好友單發(fā)紅包,隨著業(yè)務(wù)的發(fā)展,發(fā)紅包和搶紅包的場景也越來越多,目前主要應(yīng)用的場景有:單聊發(fā)紅包、群聊發(fā)紅包、名片發(fā)紅包、直播場景中的主播發(fā)紅包/觀眾給主播發(fā)紅包/定時搶紅包,接下來,如果出現(xiàn)其它產(chǎn)品的業(yè)務(wù),也將大概率的出現(xiàn)搶紅包的需求。
大同小異的搶紅包業(yè)務(wù)
紅包的場景無論怎么變化,其核心算法不變,這部分是可以抽象的內(nèi)容,隨著迭代發(fā)展,我們之前通常都是通過增加紅包的類型(業(yè)務(wù))來擴(kuò)展,但是隨著肉眼可見的發(fā)展,部分業(yè)務(wù)的改動如果需要對紅包業(yè)務(wù)進(jìn)行調(diào)整和優(yōu)化對話,將有可能產(chǎn)生牽一發(fā)而動全身的 DeBuff 效果。
新的改變
其實(shí)這些業(yè)務(wù)代碼早該優(yōu)化一下,我就是懶+忙(借口),正好有位新同事入職,這塊的優(yōu)化任務(wù)就交給他來做了,從頭到尾我都沒有參與(不知道有沒有吐槽我的代碼,捂臉~),我初步看了一下,代碼的實(shí)現(xiàn)質(zhì)量還是挺高的,正好也是一個比較好的應(yīng)用場景,我就簡單實(shí)現(xiàn)一下他做的適配器模式,徹底的將各個紅包業(yè)務(wù)類型分離,很好的實(shí)現(xiàn)了設(shè)計(jì)模式的開閉原則,加入某天某個場景的搶紅包業(yè)務(wù)下線了,這種做法是非常有利于業(yè)務(wù)的擴(kuò)展和維護(hù)。
定義搶紅包接口
public interface IRedPacket { string Name { get; } string Put(int org_id, int money, int count, string reason); string Get(int id); }以上接口包含一個屬性和2個方法,用于設(shè)置業(yè)務(wù)名稱和收發(fā)紅包。初次之外,我們還需要定義一個實(shí)現(xiàn)業(yè)務(wù)的基類,用于處理公共業(yè)務(wù)。
紅包基類業(yè)務(wù)實(shí)現(xiàn)
public abstract class RedPacket : IRedPacket { public abstract string Name { get; } public abstract string Put(int org_id, int money, int count, string reason); public abstract string Get(int id); protected string Create(string reason, int money, int count) { Console.WriteLine("創(chuàng)建了紅包:{0},金額:Money:{1},數(shù)量:{2}", reason, money, count); return "成功"; } protected string Fighting() { Console.WriteLine("調(diào)用了搶紅包方法:{0}", nameof(Fighting)); return "成功"; } }在基類中,我們選擇不實(shí)現(xiàn)接口,將接口方法定義為抽象類型。同時,定義并實(shí)現(xiàn)兩個受保護(hù)的方法 Create(創(chuàng)建紅包)/Fighting(搶紅包),接口方法由子類實(shí)現(xiàn)具體的業(yè)務(wù)細(xì)節(jié),當(dāng)子類針對具體的業(yè)務(wù)細(xì)節(jié)實(shí)現(xiàn)完成后,他們應(yīng)該會調(diào)用Create(創(chuàng)建紅包)/Fighting(搶紅包)的方法,直至最終完成整個紅包的流程。
實(shí)現(xiàn)單聊紅包
public class ChatOneRedPacket : RedPacket { public override string Name { get; } = "ChatOne"; public override string Put(int org_id, int money, int count, string reason) { Console.WriteLine("檢查接收人ID:{0}是否存在", org_id); return base.Create(reason, money, count); } public override string Get(int id) { Console.WriteLine("檢查紅包ID:{0},是否具有領(lǐng)取資格", id); return base.Fighting(); } }群聊紅包
public class ChatGroupRedPacket : RedPacket { public override string Name { get; } = "ChatGroup"; public override string Put(int org_id, int money, int count, string reason) { Console.WriteLine("檢查群ID:{0},是否存在", org_id); return base.Create(reason, money, count); } public override string Get(int id) { Console.WriteLine("檢查是否群ID:{0},當(dāng)前用戶是否群成員", id); return base.Fighting(); } }直播紅包
public class LiveRedPacket : RedPacket { public override string Name { get; } = "Live"; public override string Put(int org_id, int money, int count, string reason) { Console.WriteLine("檢查直播ID:{0}是否存在", org_id); return base.Create(reason, money, count); } public override string Get(int id) { Console.WriteLine("檢查紅包ID:{0} 是否當(dāng)前主播紅包", id); return base.Fighting(); } }為了方便演示,上面的三種紅包子類僅簡單的實(shí)現(xiàn)類屬性 Name="ChatOne",除此之外,還實(shí)現(xiàn)類接口的收發(fā)紅包接口,子類實(shí)現(xiàn) Name 屬性主要是便于我們在DI中去靈活的區(qū)分調(diào)用的主體,實(shí)現(xiàn)業(yè)務(wù)的分離。除了單聊紅包外,我們還有群聊和直播紅包,都采用上面的處理方式,只是各自實(shí)現(xiàn)的 Name 屬性時,指定不同的名字即可。在接口實(shí)現(xiàn)的方法中,各自的業(yè)務(wù)還需要執(zhí)行不同的業(yè)務(wù)檢查,比如單聊紅包就需要檢查接收人是否存在,群聊紅包還需要檢查群是否存在,該群是否被凍結(jié)等等,直播紅包需要檢查主播是否在直播中,觀眾是否在直播房間內(nèi),這些都是不同業(yè)務(wù)場景產(chǎn)生的特殊的業(yè)務(wù)處理需求。
創(chuàng)建容器實(shí)例
public void ConfigureServices(IServiceCollection services) { services.AddScoped(typeof(IRedPacket), typeof(ChatOneRedPacket)) .AddScoped(typeof(IRedPacket), typeof(ChatGroupRedPacket)) .AddScoped(typeof(IRedPacket), typeof(LiveRedPacket)); ... }容器實(shí)例的創(chuàng)建非常簡單,只需要將已實(shí)現(xiàn) IRedPacket 接口的子類注冊到服務(wù)管道即可。
依賴注入,以實(shí)例集的方式
[Route("api/[controller]")] [ApiController] public class HomeController : ControllerBase { private readonly IEnumerable<IRedPacket> redpackets; public HomeController(IEnumerable<IRedPacket> redpackets) { this.redpackets = redpackets; } }通過建立一個控制臺 HomeController 用于演示,在 HomeController 的構(gòu)造方法中,使用 IEnumerable獲得在服務(wù)中創(chuàng)建的所有實(shí)現(xiàn)接口 IRedPacket 的實(shí)例。下面將在 HomeController 中 創(chuàng)建兩個接口進(jìn)行演示發(fā)紅包/搶紅包。
發(fā)紅包
[HttpPost] public ActionResult<string> Post([FromBody] RedPacketViewModel model) { var rp = this.redpackets.Where(f => f.Name == model.Type).FirstOrDefault(); if (rp == null) { var msg = $"紅包業(yè)務(wù)類型:{model.Type}不存在"; Console.WriteLine(msg); return msg; } var result = rp.Put(model.Org_Id, model.Money, model.Count, model.Reason); return result; }為了演示方便,我們構(gòu)造4中不同的業(yè)務(wù)實(shí)體去調(diào)用發(fā)紅包的接口,分別將結(jié)果輸出到客戶端
// 單聊紅包 { "type":"ChatOne", "org_id":1, "money":8, "count":1, "reason":"恭喜發(fā)財(cái),大吉大利!" } // 群聊紅包 { "type":"ChatGroup", "org_id":2, "money":9, "count":3, "reason":"恭喜發(fā)財(cái),大吉大利!" } // 直播紅包 { "type":"Live", "org_id":3, "money":8, "count":1, "reason":"恭喜發(fā)財(cái),大吉大利!" } //圈子紅包 { "type":"Quanzi", "org_id":4, "money":8, "count":1, "reason":"恭喜發(fā)財(cái),大吉大利!" }輸出結(jié)果為:
// 單聊紅包 檢查接收人ID:1是否存在 紅包類型:ChatOne,創(chuàng)建了紅包:恭喜發(fā)財(cái),大吉大利!,金額:Money:8,數(shù)量:1 // 群聊紅包 檢查群ID:2,是否存在 紅包類型:ChatGroup,創(chuàng)建了紅包:恭喜發(fā)財(cái),大吉大利!,金額:Money:9,數(shù)量:3 // 直播紅包 檢查直播ID:3是否存在 紅包類型:Live,創(chuàng)建了紅包:恭喜發(fā)財(cái),大吉大利!,金額:Money:8,數(shù)量:1 //圈子紅包紅包業(yè)務(wù)類型:Quanzi不存在
搶紅包
搶紅包的過程,傳入一個紅包ID,然后跟進(jìn)該ID到數(shù)據(jù)庫進(jìn)行查找,得到紅包后,根據(jù)紅包類型找出 IRedPacket 的實(shí)現(xiàn)類,并進(jìn)行調(diào)用,完成搶紅包的操作。可能有的同學(xué)會覺得比較奇怪,為什么不直接拆紅包呢?這是因?yàn)槲覀円鶕?jù)紅包設(shè)計(jì)的初衷,不同的紅包,其所執(zhí)行的業(yè)務(wù)規(guī)范性檢查是不同的,不能直接進(jìn)行暴力拆包。
結(jié)束語
上面我們創(chuàng)建了3個IRedPacket的實(shí)現(xiàn)類,并將他們注冊到服務(wù)管道中,然后在HomeController中獲得服務(wù)依賴注入的實(shí)例對象,通過在不同的參數(shù)傳入,實(shí)現(xiàn)了不同的紅包業(yè)務(wù)場景的拆分,很好的實(shí)現(xiàn)了設(shè)計(jì)模式中所說的開閉原則。
演示代碼下載
https://github.com/lianggx/Examples/tree/master/Ron.RedPacketTest
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的多场景抢红包业务引发.NETCore下使用适配器模式实现业务接口分离的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: asp.net core 中使用 sig
- 下一篇: 【C】KoobooJson在asp.ne