马上加入IBC程序猿 各种源码随意下,各种教程随便看! 注册 每日签到 加入编程讨论群

C#教程 ASP.NET教程 C#视频教程程序源码享受不尽 C#技术求助 ASP.NET技术求助

【源码下载】 社群合作 申请版主 程序开发 【远程协助】 每天乐一乐 每日签到 【承接外包项目】 面试-葵花宝典下载

官方一群:

官方二群:

多场景抢红包业务引发.NETCore下使用适配器模式实现业务接口分离

[复制链接]
查看2687 | 回复1 | 2019-10-12 10:22:17 | 显示全部楼层 |阅读模式

事故的因由

我们公司现有一块业务叫做抢红包,最初的想法只是实现了一个初代版本,就是给指定的好友单发红包,随着业务的发展,发红包和抢红包的场景也越来越多,目前主要应用的场景有:单聊发红包、群聊发红包、名片发红包、直播场景中的主播发红包/观众给主播发红包/定时抢红包,接下来,如果出现其它产品的业务,也将大概率的出现抢红包的需求。

大同小异的抢红包业务

红包的场景无论怎么厘革,其核默算法稳定,这部分是可以抽象的内容,随着迭代发展,我们之前通常都是通过增长红包的类型(业务)来扩展,但是随着肉眼可见的发展,部分业务的改动如果需要对红包业务进行调解和优化对话,将有可能产生牵一发而动满身的debuff效果。

新的改变

其实这些业务代码早该优化一下,我就是懒+忙(借口),恰好有位新同事入职,这块的优化任务就交给他来做了,从头至尾我都没有到场(不知道有没有吐槽我的代码,捂脸~),我开端看了一下,代码的实现质量还是挺高的,恰好也是一个比力好的应用场景,我就简朴实现一下他做的适配器模式,彻底的将各个红包业务类型分离,很好的实现了计划模式的开闭原则,加入某天某个场景的抢红包业务下线了,这种做法是非常有利于业务的扩展和维护。

界说抢红包接口

  1. <code>public interface IRedPacket
  2. {
  3. string Name { get; }
  4. string Put(int org_id, int money, int count, string reason);
  5. string Get(int id);
  6. }</code>
复制代码

以上接口包含一个属性和2个方法,用于设置业务名称和收发红包。初次之外,我们还需要界说一个实现业务的基类,用于处理惩罚公共业务。

红包基类业务实现

  1. <code>public abstract class RedPacket : IRedPacket
  2. {
  3. public abstract string Name { get; }
  4. public abstract string Put(int org_id, int money, int count, string reason);
  5. public abstract string Get(int id);
  6. protected string Create(string reason, int money, int count)
  7. {
  8. Console.WriteLine("创建了红包:{0},金额:Money:{1},数量:{2}", reason, money, count);
  9. return "乐成";
  10. }
  11. protected string Fighting()
  12. {
  13. Console.WriteLine("调用了抢红包方法:{0}", nameof(Fighting));
  14. return "乐成";
  15. }
  16. }</code>
复制代码

在基类中,我们选择不实现接口,将接口方法界说为抽象类型。同时,界说并实现两个受保护的方法 Create(创建红包)/Fighting(抢红包),接口方法由子类实现具体的业务细节,当子类针对具体的业务细节实现完成后,他们应该会调用Create(创建红包)/Fighting(抢红包)的方法,直至最终完成整个红包的流程。

实现单聊红包

  1. <code> public class ChatOneRedPacket : RedPacket
  2. {
  3. public override string Name { get; } = "ChatOne";
  4. public override string Put(int org_id, int money, int count, string reason)
  5. {
  6. Console.WriteLine("检查接收人ID:{0}是否存在", org_id);
  7. return base.Create(reason, money, count);
  8. }
  9. public override string Get(int id)
  10. {
  11. Console.WriteLine("检查红包ID:{0},是否具有领取资格", id);
  12. return base.Fighting();
  13. }
  14. }</code>
复制代码

群聊红包

  1. <code>public class ChatGroupRedPacket : RedPacket
  2. {
  3. public override string Name { get; } = "ChatGroup";
  4. public override string Put(int org_id, int money, int count, string reason)
  5. {
  6. Console.WriteLine("检查群ID:{0},是否存在", org_id);
  7. return base.Create(reason, money, count);
  8. }
  9. public override string Get(int id)
  10. {
  11. Console.WriteLine("检查是否群ID:{0},当前用户是否群成员", id);
  12. return base.Fighting();
  13. }
  14. }</code>
复制代码

直播红包

  1. <code>public class LiveRedPacket : RedPacket
  2. {
  3. public override string Name { get; } = "Live";
  4. public override string Put(int org_id, int money, int count, string reason)
  5. {
  6. Console.WriteLine("检查直播ID:{0}是否存在", org_id);
  7. return base.Create(reason, money, count);
  8. }
  9. public override string Get(int id)
  10. {
  11. Console.WriteLine("检查红包ID:{0} 是否当前主播红包", id);
  12. return base.Fighting();
  13. }
  14. }</code>
复制代码

为了方便演示,上面的三种红包子类仅简朴的实现类属性 Name="ChatOne",除此之外,还实现类接口的收发红包接口,子类实现 Name 属性主要是便于我们在DI中去机动的区分调用的主体,实现业务的分离。除了单聊红包外,我们另有群聊和直播红包,都接纳上面的处理惩罚方式,只是各自实现的 Name 属性时,指定差异的名字即可。在接口实现的方法中,各自的业务还需要执行差异的业务检查,好比单聊红包就需要检查接收人是否存在,群聊红包还需要检查群是否存在,该群是否被冻结等等,直播红包需要检查主播是否在直播中,观众是否在直播房间内,这些都是差异业务场景产生的特殊的业务处理惩罚需求。

创建容器实例

  1. <code>public void ConfigureServices(IServiceCollection services)
  2. {
  3. services.AddScoped(typeof(IRedPacket), typeof(ChatOneRedPacket))
  4. .AddScoped(typeof(IRedPacket), typeof(ChatGroupRedPacket))
  5. .AddScoped(typeof(IRedPacket), typeof(LiveRedPacket));
  6. ...
  7. }</code>
复制代码

容器实例的创建非常简朴,只需要将已实现 IRedPacket 接口的子类注册到服务管道即可。

依赖注入,以实例集的方式

  1. <code>[Route("api/[controller]")]
  2. [ApiController]
  3. public class HomeController : ControllerBase
  4. {
  5. private readonly IEnumerable<IRedPacket> redpackets;
  6. public HomeController(IEnumerable<IRedPacket> redpackets)
  7. {
  8. this.redpackets = redpackets;
  9. }
  10. }</code>
复制代码

通过创建一个控制台 HomeController 用于演示,在 HomeController 的构造方法中,使用 IEnumerable 得到在服务中创建的全部实现接口 IRedPacket 的实例。下面将在 HomeController 中 创建两个接口进行演示发红包/抢红包。

发红包

  1. <code>[HttpPost]
  2. public ActionResult<string> Post([FromBody] RedPacketViewModel model)
  3. {
  4. var rp = this.redpackets.Where(f => f.Name == model.Type).FirstOrDefault();
  5. if (rp == null)
  6. {
  7. var msg = $"红包业务类型:{model.Type}不存在";
  8. Console.WriteLine(msg);
  9. return msg;
  10. }
  11. var result = rp.Put(model.Org_Id, model.Money, model.Count, model.Reason);
  12. return result;
  13. }</code>
复制代码

为了演示方便,我们构造4中差异的业务实体去调用发红包的接口,分别将效果输出到客户端

  1. <code>// 单聊红包
  2. {
  3. "type":"ChatOne",
  4. "org_id":1,
  5. "money":8,
  6. "count":1,
  7. "reason":"恭喜发财,大吉大利!"
  8. }
  9. // 群聊红包
  10. {
  11. "type":"ChatGroup",
  12. "org_id":2,
  13. "money":9,
  14. "count":3,
  15. "reason":"恭喜发财,大吉大利!"
  16. }
  17. // 直播红包
  18. {
  19. "type":"Live",
  20. "org_id":3,
  21. "money":8,
  22. "count":1,
  23. "reason":"恭喜发财,大吉大利!"
  24. }
  25. //圈子红包
  26. {
  27. "type":"Quanzi",
  28. "org_id":4,
  29. "money":8,
  30. "count":1,
  31. "reason":"恭喜发财,大吉大利!"
  32. }</code>
复制代码

输出效果为:

  1. <code>// 单聊红包
  2. 检查接收人ID:1是否存在
  3. 红包类型:ChatOne,创建了红包:恭喜发财,大吉大利!,金额:Money:8,数量:1
  4. // 群聊红包
  5. 检查群ID:2,是否存在
  6. 红包类型:ChatGroup,创建了红包:恭喜发财,大吉大利!,金额:Money:9,数量:3
  7. // 直播红包
  8. 检查直播ID:3是否存在
  9. 红包类型:Live,创建了红包:恭喜发财,大吉大利!,金额:Money:8,数量:1
  10. //圈子红包</code>
复制代码

红包业务类型:Quanzi不存在

抢红包

  1. <code>[HttpGet("{id}")]
  2. public ActionResult<string> Get(int id)
  3. {
  4. // 生产环境下,该红包消息应该是从数据库中读取
  5. var model = GetRedPacket(id);
  6. var rp = this.redpackets.Where(f => f.Name == model.Type).FirstOrDefault();
  7. var result = rp.Get(id);
  8. return result;
  9. }
  10. private RedPacketViewModel GetRedPacket(int id)
  11. {
  12. int type = --id;
  13. string[] redPackets = { "ChatOne", "ChatGroup", "Live" };
  14. var model = new RedPacketViewModel
  15. {
  16. Count = 3,
  17. Money = 8,
  18. Org_Id = 115,
  19. Reason = "恭喜发财,大吉大利!",
  20. Type = redPackets[type]
  21. };
  22. return model;
  23. }</code>
复制代码

抢红包的过程,传入一个红包ID,然后跟进该ID到数据库进行查找,得到红包后,根据红包类型找出 IRedPacket 的实现类,并进行调用,完成抢红包的操作。可能有的同砚会以为比力奇怪,为什么不直接拆红包呢?这是由于我们要根据红包计划的初志,差异的红包,其所执行的业务规范性检查是差异的,不能直接进行暴力拆包。

竣事语

上面我们创建了3个IRedPacket的实现类,并将他们注册到服务管道中,然后在HomeController中得到服务依赖注入的实例对象,通过在差异的参数传入,实现了差异的红包业务场景的拆分,很好的实现了计划模式中所说的开闭原则。

演示代码下载

https://github.com/lianggx/Examples/tree/master/Ron.RedPacketTest







来源:https://www.cnblogs.com/viter/p/11532822.html
C#论坛 www.ibcibc.com IBC编程社区
C#
C#论坛
IBC编程社区
*滑块验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则