设计模式 -- 责任链模式

90 阅读3分钟

概念说明: 在责任链模式中,通常会有多个处理者对象(也称为处理器或者节点),它们按照某个顺序组成一个链。客户端向该链的开头发送请求,并沿着链传递直到有一个处理者能够处理该请求。每个处理者都有机会处理请求,如果一次请求不能被任何处理者处理,则请求将最终无法处理。

说明: 如果我们要实现一串复杂的规则校验,本来可能由一长串的 if-else 的判断来实现,但是如果校验规则非常复杂,使用 if-else 非常的不优雅,于是我们可以使用责任链模式来实现!

案例说明: 我们创建一个作品的实体类,我们使用责任链模式对其中的标题和内容进行校验

代码实现

1 文章实体类ArticleInfo

import lombok.Data;

@Data
public class ArticleInfo {

    private String title; //文章标题

    private String content; //文章内容

    private String author; //文章作者
}

2 失败信息结果类RuleCheckResult

import lombok.Data;

import java.util.List;
@Data
public class RuleCheckResult {

    /**
     * 失败信息列表(存失败信息情况)
     */
    private List<String> failedMsgList;
}

3 规则校验结果类RuleCheckContext

import org.apache.commons.collections4.CollectionUtils;

public class RuleCheckContext {

    /**
     * 规则检查结果(下一个链执行时,要检查上一个链的结果,看是否有错误,有错误就直接返回,不会向下传递、执行)
     */

    private RuleCheckResult ruleCheckResult = new RuleCheckResult();

    public RuleCheckResult getRuleCheckResult() {
        return ruleCheckResult;
    }

    public boolean hasError() {
        return CollectionUtils.isNotEmpty(ruleCheckResult.getFailedMsgList());
    }
}

4 责任链模式: 文章规则校验实现

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;

@Component
@Slf4j
public class RuleCheckCompont {

    public RuleCheckResult checkResult(ArticleInfo articleInfo) {
        RuleCheckContext ruleCheckContext = new RuleCheckContext();
        //this代表当前类,加不加都一样
        return this.checkTitle(articleInfo)
                .andThen(this.checkContentLength(articleInfo)) //andThen,当前结果接着传递给下一个方法使用,可使RuleCheckContext对象共享,调用hasError方法时,会按照该类的成员属性创建一个Result对象
                .apply(ruleCheckContext) //传入T,返回R,此时上一个链返回的对象为Function<T,T>,所以传入RuleCheckContext,返回类型也一样
                .getRuleCheckResult();
    }

    /**
     * 检验文章标题不能包含中国
     *
     */
    private Function<RuleCheckContext, RuleCheckContext> checkTitle(ArticleInfo articleInfo) {
//        return buildCheck(new Consumer<RuleCheckContext>() { //消费模型的T和Function的T,R都一样
//            @Override
//            public void accept(RuleCheckContext ruleCheckContext) {
//                if(ruleCheckContext.hasError()) { //如果当前失败列表有值,则直接返回,不往下传递
//                    return; //该return返回的是accept消费模型的无返回值的方法
//                }
//                String title = articleInfo.getTitle();
//                if(title.contains("中国")) {
//                    RuleCheckCompont.this.addFailedMsg(ruleCheckContext.getRuleCheckResult(), "标题不能包含中国");
//                }
//            }
//        });
        return buildCheck(titleArticle -> {
            if(titleArticle.hasError()) {
                return;
            }
            String title = articleInfo.getTitle();
            if(title.contains("中国")) {
                RuleCheckCompont.this.addFailedMsg(titleArticle.getRuleCheckResult(), "标题不能包含中国");
            }
        });
    }

    /**
     * 检查内容长度
     */
    private Function<RuleCheckContext, RuleCheckContext> checkContentLength(ArticleInfo articleInfo) {
        return buildCheck(new Consumer<RuleCheckContext>() {
            @Override
            public void accept(RuleCheckContext ruleCheckContext) {
                if(ruleCheckContext.hasError()) {
                    return;
                }
                String content = articleInfo.getContent();
                if(content.length() > 10) {
                    addFailedMsg(ruleCheckContext.getRuleCheckResult(), "内容长度不能大于10");
                }
            }
        });
    }


    /**
     * 添加失败的信息
     */
    private void addFailedMsg(RuleCheckResult ruleCheckResult, String message) {
        List<String> failedMsgList = ruleCheckResult.getFailedMsgList();
        if(failedMsgList == null) {
            failedMsgList = new ArrayList<>();
            ruleCheckResult.setFailedMsgList(failedMsgList);
        }
        failedMsgList.add(message);
    }

    /**
     * buildCheck可直接返回Function接口,返回该接口需要创建匿名内部类,重写apply方法(该方法可进行类型转换,把T转为R),但这里TR都一样,
     * 函数内部,根据消费模型consumer接口进行消费,该接口内部的accept方法无返回值。
     */
    private <T> Function<T, T> buildCheck(Consumer<T> consumer) {
//        return new Function<T, T>() {
//            @Override
//            public T apply(T t) { //apply有返回值,所以最后返回R,也就是t
//                consumer.accept(t); //Consumer的accept方法无返回值,后面调用该方法的函数重写accept方法进行执行。
//                return t;
//            }
//        };
        return t -> {
            consumer.accept(t);
            return t;
        };
    }
}

5 测试类

import com.ssm.user.designPattern.responseibilityChainPattern.ArticleInfo;
import com.ssm.user.designPattern.responseibilityChainPattern.RuleCheckCompont;
import com.ssm.user.designPattern.responseibilityChainPattern.RuleCheckResult;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;

@SpringBootTest(classes = UserApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Slf4j
@RunWith(SpringRunner.class)
public class ArticleCheckTest {

    @Resource
    private RuleCheckCompont ruleCheckCompont;

    @Test
    public void test() {
        ArticleInfo articleInfo = new ArticleInfo();
        articleInfo.setAuthor("石朔铭");
        articleInfo.setTitle("标题中");
        articleInfo.setContent("内容1111111111");
        RuleCheckResult ruleCheckResult = ruleCheckCompont.checkResult(articleInfo);
        System.out.println(ruleCheckResult.getFailedMsgList());
    }

}

image.png