目的
使用合理的代码设计,解决业务场景的中的实际问题。
背景介绍
在实际的业务场景中,用户的一个操作行为,是否允许真正被执行,往往会涉及到多流程的校验,一旦有条件不满足将会被中止。以下面流程图为例:
用户点击了打赏按钮,会进行是否有网络检查,没有网络,会有网络连接弹框,等待用户连接结果(涉及异步回调),如果没有连接,不继续下面流程直接结束;如果连接了,会进行是否登录判断,没有登录,则调用引导登录(涉及异步回调),等待登录结果,如果失败,则不继续下面的流程,成功则进行是否是成人账号判断(涉及异步回调),不是则直接结束,是成人账号则真正开始打赏。
根据流程写的代码
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