阿里云开发者社区,千万开发者的选择
阿里云开发者社区,百万精品技术内容、千节免费系统课程、丰富的体验场景、活跃的社群活动、行业专家分享交流,欢迎点击【阅读原文】加入我们。
阿里妹导读
一、背景
二、业务承接
2.1 业务流程抽象
从整个系统出发,挖掘出面向不同的用户提供的能力有哪些,在这些用例背后需要进行的流程和节点又是什么。通过这些流程和节点才能进行后续的系统时序和流程抽象,举例如下
在互动领域内的流程和节点如下:
风控检查
评论持久化
评论上屏
基于用户用例进行分析,这些用例都需要经过什么的流程节点进行处理,然后将这些流程按照系统时序进行呈现。
到此基于上述的用例和时序是不是可以抽象出具体互动流程了,显而易见。
有了系统用例和系统时序这一步就比较简单,从系统时序里很容易可以抽象出具体的流程和流程中的处理节点,具体如下:
到此,大家可以按照上述步骤在大脑里对自己的业务域进行抽象出来了。
按照业务主流程可以通过模板模式将处理流程固定下来,如下所示:
public InteractionResult interactionSubmit(MobileInteractionRequest request, InteractionLiveRoom liveRoom) {Boolean needSave = MapUtils.getBoolean(request.getExtInfo(), LiveInteractionConstant.NEED_SAVE);// 默认保存InteractionResult saveResult = null;if (Objects.isNull(request.getExtInfo()) || Objects.isNull(needSave) || needSave) {saveResult = save(request, liveRoom);if(Objects.nonNull(saveResult) && !saveResult.isSuccess()) {return saveResult;}}// 默认进沟通InteractionResult chatResult;if (Objects.isNull(request.getSendToChat()) || Boolean.parseBoolean(request.getSendToChat())) {chatResult = sendToChat(request);if(Objects.nonNull(chatResult) && !chatResult.isSuccess()) {return chatResult;}}if(Objects.nonNull(saveResult) && saveResult.isSuccess()) {return saveResult;}return null;}/*** 互动行为保存到数据库或者缓存中** @param request* @return*/protected abstract InteractionResult save(MobileInteractionRequest request, InteractionLiveRoom liveRoom);/*** 进沟通** @param request* @return*/protected abstract InteractionResult sendToChat(MobileInteractionRequest request);
2.2 业务流程扩展
因在上述模版模式中预留了两个扩展点,所以在子类中可以通过扩展点进行扩展,举例如下:
如果有更多的场景就需要扩展实现上述两个扩展点进行扩展即可,这样保证了业务的高效承接。这里会有两个问题:
在程序运行时如何根据具体的场景选择哪个子类进行逻辑处理
如何进行适配端和场景返回的数据模型
枚举法
表驱动法
其中枚举法和表驱动法比较简单易用,原理就是将映射关系封装在枚举类或本地缓存中,这里简单介绍下如何通过策略模式消除if else。
// 策略接口public interface Opt {int apply(int a, int b);}// 策略实现类(value = "addOpt")public class AddOpt implements Opt {xxxAddResource resource; // 这里通过Spring框架注入了资源public int apply(int a, int b) {return resource.process(a, b);}}// 策略实现类(value = "devideOpt")public class devideOpt implements Opt {xxxDivResource resource; // 这里通过Spring框架注入了资源public int apply(int a, int b) {return resource.process(a, b);}}// 策略处理public class OptStrategyContext{private Map<String, Opt> strategyMap = new ConcurrentHashMap<>();public OptStrategyContext(Map<String, TalkService> strategyMap) {this.strategyMap.clear();this.strategyMap.putAll(strategyMap);}public int apply(Sting opt, int a, int b) {return strategyMap.get(opt).apply(a, b);}}
// 抽象类固定业务流程 预留扩展点public abstract class AbstractXxxx {doXxx(Object context) {// 节点1doNode1(context);// 节点2doNode2(context);// 节点3doNode3(context);// 节点n...}// 扩展点1protected abstract Result doNode1(Object context);// 扩展点2protected abstract Result doNode2(Object context);// 扩展点3protected abstract Result doNode3(Object context);}// 策略处理public class OptStrategyContext{private Map<String, Opt> strategyMap = new ConcurrentHashMap<>();static {// 上述模版模式的实现类strategyMap.put("business1", Xxxx1);strategyMap.put("business2", Xxxx2);strategyMap.put("business3", Xxxx3);}// 初始化public OptStrategyContext(Map<String, Opt> strategyMap) {this.strategyMap.clear();this.strategyMap.putAll(strategyMap);}public int doXxxx(Object context) {return strategyMap.get(business).doXxxx(context);}}
2.3 多场景多端型适配
既然是模版模式,这里的主干流程又是什么呢?主要跟我们解决的问题有关系,按照2.1中的流程步骤,可以抽象出固定的流程为:请求入参处理-》业务逻辑处理-》结果返回处理。
其中业务逻辑处理可以选定为2.2中介绍的通过策略模式选择业务扩展的子类,来处里业务部分;请求入参和结果返回处理部分可以设置为扩展点,供子类扩展。具体伪代码如下:
// 抽象类固定业务流程 预留扩展点 适配多端型多场景public abstract class AbstractSceneAdapter {<T> T doXxx(Object context) {// 节点1doRequestFilter(context);// 节点2getBusinessService(context).doBusiness(context);// 节点3return doResultWrap(context);}// 扩展点1protected abstract Result doRequestFilter(Object context);// 扩展点2protected abstract Result doBusiness(Object context);// 扩展点3protected abstract Result doResultWrap(Object context);// 业务逻辑处理子类protected abstract BusinessService getBusinessService(Object context);}// 策略处理 根据不同端型场景选择合适的子类public class SceneAdapterViewService {private Map<String, SceneAdapter> strategyMap = new ConcurrentHashMap<>();static {// 上述模版模式的实现类strategyMap.put("scene1", Xxxx1);strategyMap.put("scene2", Xxxx2);strategyMap.put("scene3", Xxxx3);}// 初始化public SceneAdapterViewService(Map<String, SceneAdapter> strategyMap) {this.strategyMap.clear();this.strategyMap.putAll(strategyMap);}public Result doXxxx(Object context) {return strategyMap.get(scene).doXxxx(context);}}
通过模版模式来适配时会有一个小问题,当需要有多个请求入参处理器或者多个结果包装器的时候需要在模版里增加处理节点,但其实这些节点是有共性的可抽象出来的。因此可以针对入参处理器和结果包装器定义单独的接口,需要多个处理器时同时实现接口进行处理。然后这些实现类打包放在单独的类中依次执行即可。当然其中的业务处理部分也可以定义接口动态实现。伪代码如下:
// 入参处理器public interface IRequestFilter<> {void doFilter(T t);}// 结果包装器public interface IResultWrapper<R, T> {Result<R> doWrap(Result<T> res);}public class SceneAdapterViewService implements InitializingBean {private List<IRequestFilter> filters;private List<IResultWrapper> wrappers;private Map<String, SceneAdapter> strategyMap = new ConcurrentHashMap<>();// 请求过滤器实现类("filter1")private IRequestFilter filter1;("filter2")private IRequestFilter filter2;// 结果处理器实现类("wrapper1")private IResultWrapper wrapper1;// 业务处理实现类("scene1")private SceneAdapter scene1;("scene2")private SceneAdapter scene2;("scene3")private SceneAdapter scene3;// 主方法publice Result sceneAdapte(Object context) {// 请求入参过滤 异常时返回for(int i = 0; i<filters.size(); i++) {try {filters.get(i).doFilter(context)} catch {return null;}}// 策略模式执行业务逻辑,执行是按照模版模式Result res = strategyMap.get(scene).doXxxx(context);Result result = res;// 过滤处理,包括树结构改变,数据字段裁剪等for(int i = 0; i<wrappers.size(); i++) {try {result = wrappers.get(i).doWrap(result)} catch {return res;}}return result;}// 初始化各个节点值public void afterPropertiesSet() throws Exception {// 入参过滤器 可多个filters.add(filter1);filters.add(filter2);// 结果处理器 可多个wrappers.add(wrapper1);// 业务处理部分strategyMap.put("scene1", Xxxx1);strategyMap.put("scene2", Xxxx2);strategyMap.put("scene3", Xxxx3);}}
三、接口设计
基于上述两种设计模式来适配时我们的接口又该如何设计,是设计面向通用的业务层接口还是面向定制化的业务接口,两种方式各有优缺点:
对于接口提供者来说肯定不希望频繁改动代码发布代码,但是又希望能够在业务承接过程中能够高效适配多端型多场景,因此这里总结了下接口设计原则:
1、对于越底层的接口应该越通用,例如HSF接口、领域服务、中间件提供的接口;
2、对于越上层的接口应该越定制化,例如对于不同的UI适配、不同的场景适配等;
3、对于业务领域内的接口应该通用化,例如直播业务域的分发领域、互动领域内的接口尽可能的通用化;
四、总结
在承接业务过程中会面临频繁包接口、一个view层的数据模型充满了小100个属性,系统的扩展性遇到瓶颈,这些问题除了通过平台化配置化的能力来解决,但是回归到代码本身我们任然可以通过抽象的设计模式来解决。
阿里云开发者社区,千万开发者的选择
阿里云开发者社区,百万精品技术内容、千节免费系统课程、丰富的体验场景、活跃的社群活动、行业专家分享交流,欢迎点击【阅读原文】加入我们。
文章引用微信公众号"阿里开发者",如有侵权,请联系管理员删除!