在业务开发中,为了处理某个属性,可能需要复杂的处理逻辑,才能得到,一般的做法是按步骤处理,最终得到想要的结果。作者的业务场景为计算用户的年化收益率,这个年化收益率是根据用户的行为计算而来。目前项目中的代码是按常规步骤处理,代码非常混乱,今天重新看一下代码,觉得可以使用职责链模式来解决。
业务场景:
产品为一款WIFI连接APP,类似万能钥匙之类的,并有理财功能,理财的年化收益率是根据用户的行为计算而来,用户打开理财页面的时候,根据用户前一天的行为计算年化收益率
用户行为:
这里只列出以下几种,实际情况更加复杂,种类跟多
1.是否为vip
2.是否分享过wifi
3.是否使用过连接wifi
4.通过连接wifi使用的流量,流量使用越多,加息越多
5.是否支付1元,购买加息
当前的处理逻辑
double totalYield = 0.04;//某基金基础年化收益率
int vipCode = getUserVipCode();
if(vipCode.equlas("100")){
totalYield = totalYiedl+0.02;//如果是vip,就增加0.02
}
boolean wifiConnect = getWifiConnect();
if(wifiConnect){
totalYield = totalYiedl+0.01;//如果连接过wifi,就增加0.01
}
//如果分享过wifi,就增加0.01
//如果...,就增加...
...
return totalYield;
这里只是伪代码,实际项目中有好几百行,如果算上调用其他service层的代码,估计有上千行代码,
接下来,作者使用职责链模式来重构一下这个业务逻辑代码。
类图
- UserActive 用户行为
- YeildCaculatorHandler 抽象类年化收益率计算器
- YeildCaculatorOfVip vip年化收益率计算器
- YeildCaculatorOfFlow 流量使用年化收益率计算器
- YeildCaculatorOfOneYuan 购买一元加息年化收益率计算器
- YeildCaculatorOfShare 分享WIFI年化收益率计算器
- YeildCaculatorOfWifiConnect WIFI连接年化收益率计算器
UserActivie
package com.charlie.wifi.yeild;
public class UserActive {
private String userName;
private int vipCode;
private boolean oneYuan;
private boolean wifiConnect;
private int flow;
private boolean share;
private double totalYeild = 0.04;
public UserActive(String userName, int vipCode, boolean oneYuan, boolean wifiConnect, int flow, boolean share) {
super();
this.userName = userName;
this.vipCode = vipCode;
this.oneYuan = oneYuan;
this.wifiConnect = wifiConnect;
this.flow = flow;
this.share = share;
}
//省略get set
YeildCaculatorHandler
package com.charlie.wifi.yeild;
public abstract class YeildCaculatorHandler {
protected String name;
protected YeildCaculatorHandler successor;
public YeildCaculatorHandler(String name) {
super();
this.name = name;
}
public void setSuccessor(YeildCaculatorHandler successor) {
this.successor = successor;
}
public abstract double caculator(UserActive userActive);
}
YeildCaculatorOfFlow
package com.charlie.wifi.yeild;
import java.math.BigDecimal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class YeildCaculatorOfFlow extends YeildCaculatorHandler {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public YeildCaculatorOfFlow(String name) {
super(name);
}
@Override
public double caculator(UserActive userActive) {
if (userActive.getFlow() > 0) {
BigDecimal totalYeild = new BigDecimal(String.valueOf(userActive.getTotalYeild()));
BigDecimal flowYeild = new BigDecimal(String.valueOf(userActive.getFlow() * 0.0001));
userActive.setTotalYeild(totalYeild.add(flowYeild).doubleValue());
logger.info("【" + userActive.getUserName() + "】" + "使用了流量" + userActive.getFlow() + "M,年华收益率为"
+ userActive.getTotalYeild());
} else {
logger.info("【" + userActive.getUserName() + "】" + "没有使用流量,年华收益率为" + userActive.getTotalYeild());
}
if (successor != null) {
successor.caculator(userActive);
}
return userActive.getTotalYeild();
}
}
YeildCaculatorOfOneYuan
package com.charlie.wifi.yeild;
import java.math.BigDecimal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class YeildCaculatorOfOneYuan extends YeildCaculatorHandler {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public YeildCaculatorOfOneYuan(String name) {
super(name);
}
@Override
public double caculator(UserActive userActive) {
if (userActive.isOneYuan()) {
BigDecimal totalYeild = new BigDecimal(String.valueOf(userActive.getTotalYeild()));
BigDecimal oneYuanYeild = new BigDecimal("0.01");
userActive.setTotalYeild(totalYeild.add(oneYuanYeild).doubleValue());
logger.info("【" + userActive.getUserName() + "】" + "参与了1元加息,年华收益率为" + userActive.getTotalYeild());
} else {
logger.info("【" + userActive.getUserName() + "】" + "没有参与了1元加息,年华收益率为" + userActive.getTotalYeild());
}
if (successor != null) {
successor.caculator(userActive);
}
return userActive.getTotalYeild();
}
}
YeildCaculatorOfShare
package com.charlie.wifi.yeild;
import java.math.BigDecimal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class YeildCaculatorOfShare extends YeildCaculatorHandler {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public YeildCaculatorOfShare(String name) {
super(name);
}
@Override
public double caculator(UserActive userActive) {
if (userActive.isShare()) {
BigDecimal totalYeild = new BigDecimal(String.valueOf(userActive.getTotalYeild()));
BigDecimal shareYeild = new BigDecimal("0.01");
userActive.setTotalYeild(totalYeild.add(shareYeild).doubleValue());
logger.info("【" + userActive.getUserName() + "】" + "分享了WIFI,年华收益率为" + userActive.getTotalYeild());
} else {
logger.info("【" + userActive.getUserName() + "】" + "没有分享WIFI,年华收益率为" + userActive.getTotalYeild());
}
if (successor != null) {
successor.caculator(userActive);
}
return userActive.getTotalYeild();
}
}
YeildCaculatorOfVip
package com.charlie.wifi.yeild;
import java.math.BigDecimal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class YeildCaculatorOfVip extends YeildCaculatorHandler {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public YeildCaculatorOfVip(String name) {
super(name);
}
@Override
public double caculator(UserActive userActive) {
if (userActive.getVipCode() == 100) {
BigDecimal totalYeild = new BigDecimal(String.valueOf(userActive.getTotalYeild()));
BigDecimal vipYeild = new BigDecimal("0.02");
userActive.setTotalYeild(totalYeild.add(vipYeild).doubleValue());
logger.info("【" + userActive.getUserName() + "】" + "为VIP,年华收益率为" + userActive.getTotalYeild());
} else {
logger.info("【" + userActive.getUserName() + "】" + "为非VIP,年华收益率为" + userActive.getTotalYeild());
}
if (successor != null) {
successor.caculator(userActive);
}
return userActive.getTotalYeild();
}
}
YeildCaculatorOfWifiConnect
package com.charlie.wifi.yeild;
import java.math.BigDecimal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class YeildCaculatorOfWifiConnect extends YeildCaculatorHandler {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public YeildCaculatorOfWifiConnect(String name) {
super(name);
}
@Override
public double caculator(UserActive userActive) {
if (userActive.isWifiConnect()) {
BigDecimal totalYeild = new BigDecimal(String.valueOf(userActive.getTotalYeild()));
BigDecimal vipYeild = new BigDecimal("0.01");
userActive.setTotalYeild(totalYeild.add(vipYeild).doubleValue());
logger.info("【" + userActive.getUserName() + "】" + "连接了WIFI,年华收益率为" + userActive.getTotalYeild());
} else {
logger.info("【" + userActive.getUserName() + "】" + "没有连接WIFI,年华收益率为" + userActive.getTotalYeild());
}
if (successor != null) {
successor.caculator(userActive);
}
return userActive.getTotalYeild();
}
}
Main
package com.charlie.wifi.yeild;
import java.math.BigDecimal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Main {
private static final Logger logger = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) {
UserActive xiaoming = new UserActive("小明", 0, false, false, 0, false);
UserActive xiaowang = new UserActive("小王", 100, false, true, 0, false);
UserActive xiaoli = new UserActive("小李", 0, true, false, 0, true);
UserActive xiaojiang = new UserActive("小姜", 100, true, true, 100, true);
YeildCaculatorHandler vip = new YeildCaculatorOfVip("VIP加息计算器");
YeildCaculatorHandler oneYuan = new YeildCaculatorOfOneYuan("一元加息计算器");
YeildCaculatorHandler wifiConnect = new YeildCaculatorOfWifiConnect("WIFI连接计算器");
YeildCaculatorHandler share = new YeildCaculatorOfShare("分享WIFI计算器");
YeildCaculatorHandler flow = new YeildCaculatorOfFlow("流量使用计算器");
vip.setSuccessor(oneYuan);
oneYuan.setSuccessor(wifiConnect);
wifiConnect.setSuccessor(share);
share.setSuccessor(flow);
vip.caculator(xiaoming);
logger.info("\n");
vip.caculator(xiaowang);
logger.info("\n");
vip.caculator(xiaoli);
logger.info("\n");
vip.caculator(xiaojiang);
logger.info("【小姜】最终年化收益率为" + xiaojiang.getTotalYeild());
}
}
运行结果
INFO com.charlie.wifi.yeild.YeildCaculatorOfVip - 【小明】为非VIP,年华收益率为0.04
INFO com.charlie.wifi.yeild.YeildCaculatorOfOneYuan - 【小明】没有参与了1元加息,年华收益率为0.04
INFO com.charlie.wifi.yeild.YeildCaculatorOfWifiConnect - 【小明】没有连接WIFI,年华收益率为0.04
INFO com.charlie.wifi.yeild.YeildCaculatorOfShare - 【小明】没有分享WIFI,年华收益率为0.04
INFO com.charlie.wifi.yeild.YeildCaculatorOfFlow - 【小明】没有使用流量,年华收益率为0.04
INFO com.charlie.wifi.yeild.Main - 【小明】最终年化收益率为0.04
INFO com.charlie.wifi.yeild.YeildCaculatorOfVip - 【小王】为VIP,年华收益率为0.06
INFO com.charlie.wifi.yeild.YeildCaculatorOfOneYuan - 【小王】没有参与了1元加息,年华收益率为0.06
INFO com.charlie.wifi.yeild.YeildCaculatorOfWifiConnect - 【小王】连接了WIFI,年华收益率为0.07
INFO com.charlie.wifi.yeild.YeildCaculatorOfShare - 【小王】没有分享WIFI,年华收益率为0.07
INFO com.charlie.wifi.yeild.YeildCaculatorOfFlow - 【小王】没有使用流量,年华收益率为0.07
INFO com.charlie.wifi.yeild.Main - 【小王】最终年化收益率为0.07
INFO com.charlie.wifi.yeild.YeildCaculatorOfVip - 【小李】为非VIP,年华收益率为0.04
INFO com.charlie.wifi.yeild.YeildCaculatorOfOneYuan - 【小李】参与了1元加息,年华收益率为0.05
INFO com.charlie.wifi.yeild.YeildCaculatorOfWifiConnect - 【小李】没有连接WIFI,年华收益率为0.05
INFO com.charlie.wifi.yeild.YeildCaculatorOfShare - 【小李】分享了WIFI,年华收益率为0.06
INFO com.charlie.wifi.yeild.YeildCaculatorOfFlow - 【小李】没有使用流量,年华收益率为0.06
INFO com.charlie.wifi.yeild.Main - 【小李】最终年化收益率为0.06
INFO com.charlie.wifi.yeild.YeildCaculatorOfVip - 【小姜】为VIP,年华收益率为0.06
INFO com.charlie.wifi.yeild.YeildCaculatorOfOneYuan - 【小姜】参与了1元加息,年华收益率为0.07
INFO com.charlie.wifi.yeild.YeildCaculatorOfWifiConnect - 【小姜】连接了WIFI,年华收益率为0.08
INFO com.charlie.wifi.yeild.YeildCaculatorOfShare - 【小姜】分享了WIFI,年华收益率为0.09
INFO com.charlie.wifi.yeild.YeildCaculatorOfFlow - 【小姜】使用了流量100M,年华收益率为0.1
INFO com.charlie.wifi.yeild.Main - 【小姜】最终年化收益率为0.1
总结
通过重构,将用户行为的查询,不同行为的加息计算处理分别解耦,之前是全部耦合在一起。这样维护起来非常方便,比如流量使用年化收益率的业务规则改变了,那么我就修改这个计算器就行,如果增加新的用户加息行为,就再增加一个计算器,如果取消了某个用户加息行为,就去掉就行,如果在之前几百行的代码里面去修改,就会显得非常的头疼。其实很多时候,当业务规则简单的时候,不使用设计模式,也没有什么问题,维护起来也不难。只有当业务规则非常之多,越来越复杂的时候,问题就会暴露出来。就像访问量一样,一个接口的访问量是100w和100,接口的处理是完全不一样的。