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

目的

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

背景介绍

在实际的业务场景中,用户的一个操作行为,是否允许真正被执行,往往会涉及到多流程的校验,一旦有条件不满足将会被中止。以下面流程图为例: 条件结构流程图.png 用户点击了打赏按钮,会进行是否有网络检查,没有网络,会有网络连接弹框,等待用户连接结果(涉及异步回调),如果没有连接,不继续下面流程直接结束;如果连接了,会进行是否登录判断,没有登录,则调用引导登录(涉及异步回调),等待登录结果,如果失败,则不继续下面的流程,成功则进行是否是成人账号判断(涉及异步回调),不是则直接结束,是成人账号则真正开始打赏。

根据流程写的代码

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都是可以用来解决多流程校验,存在回调地狱的场景。提到的框架没有仔细深入的了解过,不敢讲太多~

文末

我总结了一些Android核心知识点,以及一些最新的大厂面试题、知识脑图和视频资料解析。

需要的小伙伴直接点击文末小卡片免费领取哦,以后的路也希望我们能一起走下去。(谢谢大家一直以来的支持,需要的自己领取)

Android学习PDF+架构视频+面试文档+源码笔记

部分资料一览:

  • 330页PDF Android学习核心笔记(内含8大板块)

  • Android学习的系统对应视频

  • Android进阶的系统对应学习资料

  • Android BAT大厂面试题(有解析)

领取地址:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值