设计模式之职责链模式

在业务开发中,为了处理某个属性,可能需要复杂的处理逻辑,才能得到,一般的做法是按步骤处理,最终得到想要的结果。作者的业务场景为计算用户的年化收益率,这个年化收益率是根据用户的行为计算而来。目前项目中的代码是按常规步骤处理,代码非常混乱,今天重新看一下代码,觉得可以使用职责链模式来解决。

业务场景:

产品为一款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层的代码,估计有上千行代码,
接下来,作者使用职责链模式来重构一下这个业务逻辑代码。

类图

年化收益率类图.JPG
  • 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,接口的处理是完全不一样的。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容