探讨跨客户业务处理模式
背景
目前系统中,有一个服务,此服务用于客户供应商的绩效。服务设计之初,只支持一家客户。
缺点分析
随着客户的增加以及每个客户的处理越来越个性化,原来服务设计已经补满足。此服务一共分为5个步骤。步骤一,步骤二……..,每个步骤出错,可以从上一个步骤继续。每次从当前步骤升级到下一个步骤,需要判断是否能满足升级条件。
现在由于客户的增加,每个客户的每个步骤里的处理逻辑不一致。
解决办法
- 设计模式之:模板方法模式
采购模板方法模式,不同客户继承相同的模板,各自实现自己的业务
2.设计模式之:策略模式
最近又接到一个新的需要,要求每个客户本身根据厂区的不同产生不同的处理逻辑,由于前期的设计局限,所以只能在现有设计上继续补充
结合项目:策略模式在什么时候使用呢?策略模式在每个客户的实现哪里使用,通过抽象出接口,然后达到不同的厂区,调用的逻辑也不一样
代码演示:
其中的一个模板
using ESIC.RFQ.BizInterface.PerformanceCalculation; using ESIC.RFQ.Common.CustomAttribute; using ESIC.RFQ.Common.CustomExtend; using ESIC.RFQ.Common.IOC; using ESIC.SRM.Service.SupplierPerformance.Base; using ESIC.SRM.Service.SupplierPerformance.Decorator; using SRM.Dto.CustomEnum.SupplierCustomEnum; using System; namespace ESIC.SRM.Service.SupplierPerformance.Template { /// <summary> /// Functional Description:获取需要的基础数据 /// Creater:ZhangQC /// Date:2018/4/11 15:12:03 /// </summary> public abstract class GetBaseDataTemplate : BaseDecorator { /// <summary> /// 基础类 /// </summary> BaseRoots _baseRoots; /// <summary> /// Functional Description:构造函数 /// Creater:ZhangQC /// Date: 2018/4/12 14:39 /// </summary> protected GetBaseDataTemplate(BaseRoots baseRoots):base(baseRoots) { this._baseRoots = baseRoots; } /// <summary> /// Functional Description:记录当前步骤 /// Creater:ZhangQC /// Date: 2018/4/12 14:40 /// </summary> public override void WriteLog() { Log.Debug("当前步骤:进入获取基础数据"); } /// <summary> /// Functional Description:从用户上传文件获取数据 /// Creater:ZhangQC /// Date: 2018/4/11 16:48 /// </summary> protected abstract bool FromUpload(); /// <summary> /// Functional Description:从外部接口获取数据 /// Creater:ZhangQC /// Date: 2018/4/11 16:49 /// </summary> protected abstract bool FromInterface(); /// <summary> /// Functional Description:从系统获取数据 /// Creater:ZhangQC /// Date: 2018/4/11 16:49 /// </summary> protected abstract bool FromSystemData(); /// <summary> /// Functional Description:获取基础数据 /// Creater:ZhangQC /// Date: 2018/4/11 16:49 /// </summary> private bool GetBaseData() { return FromUpload() & FromSystemData(); } /// <summary> /// Functional Description:开始获取基础数据 /// Creater:ZhangQC /// Date: 2018/4/12 9:00 /// </summary> public void Start() { try { _baseRoots.WriteLog(); WriteLog(); //1.0不是当前步骤直接返回 if (GetExecutionStep() != (int) ExecutionStepEnum.ProcessFiles) return; //1.1是当前步骤,执行状态是已完成,判断是否满足升级条件。满足=》升级,修改执行状态,不满足,继续在当前步骤等待,直到满足 if (GetExecutionStatus() == (int) ExecutionStatusEnum.End) { if (IocFactory.Resolve<IBaseRoots>().IsMeetTheUpgrade(OrgId, CalcVersion, ExecutionStepEnum.ProcessFiles, IpCount)) { //重写下一个执行步骤 ExecutionStep(ExecutionStepEnum.EarlyPeriod.Value().ToString()); var nextExecutionStatus = ExecutionStatusEnum.Start.Value().CToString(); ExecutionStatus(nextExecutionStatus); } else { Log.Debug("当前步骤:获取数据未满足升级要求"); } return; } //1.2是当前步骤,执行状态初始化,退出 if (GetExecutionStatus() == (int) ExecutionStatusEnum.Init) return; //1.3是当前步骤,执行状态是开始状态,执行 var getBaseDataFlag = GetBaseData(); if (getBaseDataFlag) { //执行完成,修改状态为完成状态 var executionStatus = ExecutionStatusEnum.End.Value().CToString(); ExecutionStatus(executionStatus); //判断是否满足升级 。满足=》升级,修改执行状态;不满足=》继续在当前步骤等待。直到满足 if (IocFactory.Resolve<IBaseRoots>().IsMeetTheUpgrade(OrgId, CalcVersion, ExecutionStepEnum.ProcessFiles, IpCount)) { //重写下一个执行步骤 ExecutionStep(ExecutionStepEnum.EarlyPeriod.Value().ToString()); var nextExecutionStatus = ExecutionStatusEnum.Start.Value().CToString(); ExecutionStatus(nextExecutionStatus); } else { Log.Debug("当前步骤:获取数据未满足升级要求"); } } } catch (Exception ex) { Log.ErrorFormat("获取基础数据就报错了:{0}",ex); } } } }
模板的实现
/// <summary> /// Functional Description:通用上传文件模板 /// Creater:ZhangQC /// Date:2018/4/12 16:27:36 /// </summary> public class SccGetBaseDataTemplate : GetBaseDataTemplate { /// <summary> /// 基础类 /// </summary> readonly BaseRoots _baseRoots; /// <summary> /// 预热数据适用 /// </summary> private static bool _isRunForSystem = false; /// <summary> /// 预热数据适用 /// </summary> private static bool _isRunForInterface = false; private readonly List<ConfiguredSupplierList> _configuredSupplierList; public SccGetBaseDataTemplate(BaseRoots baseRoots) : base(baseRoots) { //base.WriteLog(); this._baseRoots = baseRoots; _configuredSupplierList = ConfiguredSupplier; } /// <summary> /// 处理接口数据 /// </summary> private readonly IProcessExternalInterface _processInterfaceData = IocFactory.Resolve<IProcessExternalInterface>(); /// <summary> /// function description:处理来自文件上传的数据 /// creator:ZhangQC /// date:2019-3-29 /// </summary> /// <returns></returns> protected override bool FromUpload() { try { var needProcessFiles = _baseRoots.GetContext().GetNeedProcessFile(j => j.OrgID == OrgId); if (needProcessFiles.Any()) { //循环组织结构 foreach (var needProcessFile in needProcessFiles) { IocFactory.Resolve<IProcessFromUpload>().ProcessExeclSaveToData(needProcessFile); } } return true; } catch (Exception ex) { Log.ErrorFormat("从用户上传获取数据出错:{0}", ex); return false; } } /// <summary> /// function description:处理来自接口的数据 /// creator:ZhangQC /// date:2019-3-29 /// </summary> /// <returns></returns> protected override bool FromInterface() { try { return true; } catch (Exception ex) { Log.ErrorFormat("从系统获取数据初始化出错:{0}", ex); return false; } } /// <summary> /// 指标绩效计算需要的系统数据 /// </summary> /// <returns></returns> protected override bool FromSystemData() { try { return true; } catch (Exception ex) { Log.ErrorFormat("从系统获取数据初始化出错:{0}", ex); return false; } } }
只要继承模板就可以实现每个客户每个步骤的不同实现了
下面我们来看加入策略模式后的代码
跟上面的是不同的步骤,但是意思是样的,就是把实现换成下面的差不多格式
public class SccGetBaseDataTemplate : GetBaseDataTemplate { private readonly IRunStartegy _startegy; public CommonStep(IRunStartegy startegy) { this._startegy = startegy; } public override void WriteLog() { _startegy.LogStep(); } protected override bool Run() { try { return this._startegy.Run(); } catch (Exception ex) { Log.ErrorFormat("逻辑运算出错咯:{0}", ex); return false; } } }
下面就可以对相同客户实现不同的实现,只要实现的类继承IRunStartegy 此接口可以了
public class SccFrequency: StartegyBase, IRunStartegy { public bool Run() { using (var dbContext = new SMS_V40Context()) { dosomething } } public void LogStep() { Log.Debug("进入频率计算步骤"); } }