首页 > 其他分享 >【代码设计】链表结构解决多流程校验

【代码设计】链表结构解决多流程校验

时间:2023-06-22 14:06:06浏览次数:32  
标签:System 流程 校验 public 链表 println new next void


目的

使用合理的代码设计,解决业务场景的中的实际问题。

背景介绍

在实际的业务场景中,用户的一个操作行为,是否允许真正被执行,往往会涉及到多流程的校验,一旦有条件不满足将会被中止。以下面流程图为例:

【代码设计】链表结构解决多流程校验_数据结构

用户点击了打赏按钮,会进行是否有网络检查,没有网络,会有网络连接弹框,等待用户连接结果(涉及异步回调),如果没有连接,不继续下面流程直接结束;如果连接了,会进行是否登录判断,没有登录,则调用引导登录(涉及异步回调),等待登录结果,如果失败,则不继续下面的流程,成功则进行是否是成人账号判断(涉及异步回调),不是则直接结束,是成人账号则真正开始打赏。

根据流程写的代码

package com.example.quickdemo.node.before;

public class Test {
   public static void main(String[] args) {
      onClickTip();
   }

   /**
    * 点击打赏按钮
    */
   private static void onClickTip() {
      if (!NetworkHelper.getInstance().isConnect()) {
         System.out.println("网络未连接");
         NetworkHelper.getInstance().requestConnectNetwork(new NetworkHelper.NetworkCallback() {
            @Override
            public void onResult(boolean result) {
               if (result) {
                  System.out.println("网络连接成功");
                  if (!LoginHelper.getInstance().isLogin()) {
                     System.out.println("用户未登录");
                     LoginHelper.getInstance().requestLogin(new LoginHelper.LoginCallback() {
                        @Override
                        public void onResult(boolean success) {
                           if (success) {
                              System.out.println("用户登录成功");
                              AccountHelper.getInstance().checkAccountType(new AccountInfo(),
                                 new AccountHelper.AccountCallback() {
                                    @Override
                                    public void onResult(int type) {
                                       if (type == AccountHelper.ACCOUNT_TYPE_ADULT) {
                                          System.out.println("是成人帐号");
                                          doTip();
                                       } else {
                                          System.out.println("不是成人帐号");
                                       }
                                    }
                                 });
                           } else {
                              System.out.println("用户登录失败,结束流程");
                           }
                        }
                     });
                  }
               }
            }
         });
      }
   }

   /**
    * 进行打赏
    */
   public static void doTip() {
      System.out.println("通过了一系列合法性校验,可以进行打赏");
   }
}

会明显的发现,代码实现上陷入了回调地狱,即在回调中右进行了不断的回调,直接表现在会出现多层的代码缩进,回调地狱往往会使代码的易读性大大的降低,各个校验的耦合性大大增强。

下面是各个网络、登录,账号校验的实现代码,仅仅辅助示例说明,并不是真正的连接网络,进行登录,帐号校验的实现

package com.example.quickdemo.node.before;

/**
 * 网络辅助类
 */
public class NetworkHelper {
   private boolean connect;

   private static final NetworkHelper INSTANCE = new NetworkHelper();

   private NetworkHelper() {
   }

   public static NetworkHelper getInstance() {
      return INSTANCE;
   }

   public boolean isConnect() {
      return connect;
   }

   public void setConnect(boolean connect) {
      this.connect = connect;
   }

   public void requestConnectNetwork(final NetworkCallback callback) {
      new Thread(new Runnable() {
         @Override
         public void run() {
            try {
               Thread.sleep(500);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
            connect = true;
            if (callback != null) {
               callback.onResult(connect);
            }
         }
      }).start();
   }

   interface NetworkCallback {
      void onResult(boolean result);
   }
}
package com.example.quickdemo.node.before;

/**
 * 登录辅助类
 */
public class LoginHelper {
   private boolean isLogin;


   private static final LoginHelper INSTANCE = new LoginHelper();

   private LoginHelper() {
   }

   public static LoginHelper getInstance() {
      return INSTANCE;
   }

   public boolean isLogin() {
      return isLogin;
   }

   public void requestLogin(final LoginCallback callback) {
      new Thread(new Runnable() {
         @Override
         public void run() {
            try {
               Thread.sleep(500);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
            isLogin = true;
            if (callback != null) {
               callback.onResult(isLogin);
            }
         }
      }).start();
   }


   interface LoginCallback {
      void onResult(boolean success);
   }
}
package com.example.quickdemo.node.before;

/**
 * 帐号辅助类
 */
public class AccountHelper {
   public static final int ACCOUNT_TYPE_ADULT = 1;

   private static final AccountHelper INSTANCE = new AccountHelper();

   private AccountHelper() {
   }

   public static AccountHelper getInstance() {
      return INSTANCE;
   }

   public void checkAccountType(AccountInfo accountInfo, final AccountCallback callback) {
      new Thread(new Runnable() {
         @Override
         public void run() {
            try {
               Thread.sleep(500);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
            if (callback != null) {
               callback.onResult(ACCOUNT_TYPE_ADULT);
            }
         }
      }).start();
   }

   interface AccountCallback {
      void onResult(int type);
   }
}

使用链表结构解决回调地狱

每一种校验可以看做是链表的一个节点,进行当前节点的校验时,如果校验不成功则不继续进行,如果校验成功,则判断是否有下一个节点,如果有则执行下一个节点。

在开始执行链表的逻辑之前,首先需要构建出一个链表,而链表节点的先后顺序就是多流程校验各流程的顺序。

首先定义结点

package com.example.quickdemo.node.after;

public abstract class FlowNode {
    private FlowNode next;

    /**
     * 事务处理
     */
    public abstract void process();

    public FlowNode getNext() {
        return next;
    }

    public void setNext(FlowNode next) {
        this.next = next;
    }
}

创建网络、登录,账号校验各自的结点实现

package com.example.quickdemo.node.after;

import com.example.quickdemo.node.before.NetworkHelper;

/**
 * 网络结点
 */
public class NetworkFlowNode extends FlowNode{
    @Override
    public void process() {
        if (NetworkHelper.getInstance().isConnect()) {
            System.out.println("网络已连接");
            FlowNode next = getNext();
            if (next != null) {
                next.process();
            }
        } else {
            System.out.println("网络未连接");
            NetworkHelper.getInstance().requestConnectNetwork(new NetworkHelper.NetworkCallback() {
                @Override
                public void onResult(boolean result) {
                    if (result) {
                        System.out.println("网络连接成功");
                        FlowNode next = getNext();
                        if (next != null) {
                            next.process();
                        }
                    } else {
                        System.out.println("用户登录失败,结束流程");
                    }
                }
            });
        }
    }
}
package com.example.quickdemo.node.after;

import com.example.quickdemo.node.before.LoginHelper;

/**
 * 登录结点
 */
public class LoginFlowNode extends FlowNode{
    @Override
    public void process() {
        if (LoginHelper.getInstance().isLogin()) {
            System.out.println("用户已登录");
            FlowNode next = getNext();
            if (next != null) {
                next.process();
            }
        } else {
            System.out.println("用户未登录");
            LoginHelper.getInstance().requestLogin(new LoginHelper.LoginCallback() {
                @Override
                public void onResult(boolean success) {
                    if (success) {
                        System.out.println("用户登录成功");
                        FlowNode next = getNext();
                        if (next != null) {
                            next.process();
                        }
                    } else {
                        System.out.println("用户登录失败,结束流程");
                    }
                }
            });
        }
    }
}
package com.example.quickdemo.node.after;

import com.example.quickdemo.node.before.AccountHelper;
import com.example.quickdemo.node.before.AccountInfo;

/**
 * 账号校验结点
 */
public class AdultAccountFlowNode extends FlowNode {
    @Override
    public void process() {
        AccountHelper.getInstance().checkAccountType(new AccountInfo(), new AccountHelper.AccountCallback() {
            @Override
            public void onResult(int type) {
                if (type == AccountHelper.ACCOUNT_TYPE_ADULT) {
                    System.out.println("是成人帐号");
                    FlowNode next = getNext();
                    if (next != null) {
                        next.process();
                    }
                } else {
                    System.out.println("不是成人帐号,结束流程");
                }
            }
        });
    }
}

打赏服务,进行打赏业务时,进行了链表的构建,类似于搭积木,各个校验模块之间已经没有直接的耦合关系。

package com.example.quickdemo.node.after;

/**
 * 打赏服务
 */
public class TipService extends FlowNode{

    public void onClickTip() {
        // 构建校验链表
        // (1) 创建结点
        NetworkFlowNode networkFlowNode = new NetworkFlowNode();
        LoginFlowNode loginFlowNode = new LoginFlowNode();
        AdultAccountFlowNode adultAccountFlowNode = new AdultAccountFlowNode();
        // (2)根据校验顺序连接结点
        networkFlowNode.setNext(loginFlowNode);
        loginFlowNode.setNext(adultAccountFlowNode);
        adultAccountFlowNode.setNext(this);

        // 触发链表调用
        networkFlowNode.process();
    }

    @Override
    public void process() {
        doTip();
    }

    /**
     * 进行打赏
     */
    public void doTip() {
        System.out.println("通过了一系列合法性校验,可以进行打赏");
    }
}

调用测试

package com.example.quickdemo.node.after;

public class Test {
    public static void main(String[] args) {
        new TipService().onClickTip();
    }
}

输出

网络未连接
网络连接成功
用户未登录
用户登录成功
是成人帐号
通过了一系列合法性校验,可以进行打赏

如果进行登录引导时,用户不进行登录直接返回,即引导登录流程返回失败,输出将是:

网络未连接
网络连接成功
用户未登录
用户登录失败,结束流程

总结

针对具体的业务场景,设计出相适应的代码设计,才能够更好的演进。本文介绍的方案其实和设计模式中的责任链模式比较类似,责任链本质也是一个链表,只是其处理大致逻辑是,当前结点如果能够处理请求,则直接处理,然后结束,如果不能够处理,则交给下一个结点处理。

在一些框架中,如RxJava,Kotlin的协程,前端的Promise都是可以用来解决多流程校验,存在回调地狱的场景。提到的框架没有仔细深入的了解过,不敢讲太多~

标签:System,流程,校验,public,链表,println,new,next,void
From: https://blog.51cto.com/u_16163453/6534764

相关文章

  • Vue进阶(幺叁柒):动态表单校验
    在前期博文《Vue进阶(三十):vue中使用element-ui进行表单验证》、《Vue进阶(幺幺叁):关于vue.jselementui表单验证this.$refs[formName].validate()的问题》、《Vue进阶(幺贰幺):表单校验注意事项》中主要讲解了form表单校验应遵守的约定及常见问题解决方法。在实现动态表单,且表单项为后......
  • Android | Activity 启动流程分析
    前言Activity类是android应用的关键组件,在日常开发中,绝对少不了组件。既然用了这么久,你知道他的启动流程......
  • 链表程序(模板实现)
    本程序无法实现的功能就是想要头结点的数据域和其他结点的数据域类型不同,由于使用模板如果在构造函数的时候设置头结点的数据域类型,那么实例化的时候(LinkList<int>link;)其他结点数据域也要跟着变成整型,如果将头结点在构造函数中提前定义好(ListNode<int>*L=newListNode<int>;)......
  • 手写单双向链表
    手写双向链表双向链表类(BidirectionalLinkedList)publicclassBidirectionalLinkedList<E>{ //元素个数transientintsize=0;//0 //第一个节点transientNode<E>first;//null//最后一个节点transientNode<E>last;//null//添加类p......
  • Flink任务提交流程分析
    背景说明在早期的Flink1.9时,为了对Flink任务的进行部署管理,对Flink任务提交的流程进行分析。刚好以前的博客图片失效了,那就用Flink1.13来再读一遍相关源码。任务提交flink任务提交的起点是flink脚本,以提交至Yarn为例,我们运行wordcount的脚本如下:bin/flinkrun-tyarn-per-job......
  • 20230303 2.0. 数组和链表
    数组数组是最基本的构造类型,它是一组相同类型数据的有序集合。数组中的元素在内存中连续存放,用数组名和下标可以唯一地确定数组元素。链表链表是一种重要的基础数据结构,也是实现复杂数据结构的重要手段。它不按照线性的顺序存储数据,而是由若干个同一结构类型的“结点”依次......
  • 远程提交代码冲突合并流程
    步骤1:获取源代码,本地进行分支合并.gitfetchorigingitcheckout当前分支gitmergeorigin/xxx这里没太明白,反正就是远端的分支,origin/远端分支版本步骤2:查看冲突文件,本地解决冲突.步骤3:推送至源分支,修改直接展示在当前合并请求中.gitadd.gitcommit-m'fix......
  • springboot启动流程 (3) 自动装配
    在SpringBoot中,EnableAutoConfiguration注解用于开启自动装配功能。本文将详细分析该注解的工作流程。EnableAutoConfiguration注解启用SpringBoot自动装配功能,尝试猜测和配置可能需要的组件Bean。自动装配类通常是根据类路径和定义的Bean来应用的。例如,如果类路径上有tomcat-......
  • 14. SpringMVC执行流程
    14.1、SpringMVC常用组件DispatcherServlet:前端控制器,不需要工程师开发,由框架提供作用:统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求HandlerMapping:处理器映射器,不需要工程师开发,由框架提供作用:根据请求的url、method等信息查找Handler,即控制......
  • 【Java】使用 validation 完成自定义校验注解
    总括:validation让我们简化了开发过程,可以使用简单的一个注解就实现了很多常见的检验数据的功能,同时支持自定义注解。spring-boot-starter-validation是由SpringBoot整合的一套用于处理 validation的约定化自动配置启动器。Spring系列框架通过简单的安装依赖即可直接使用......