很多人觉得自己写的是业务代码,按照逻辑写下去,再把公用的方法抽出来复用就可以了,设计模式根本就没必要用,更没必要学。

一开始的时候,我也是这么想,直到我遇到。。。

举个例子

我们先看一个普通的下单拦截接口。

基本逻辑,参数安全拦截,次数拦截,规则拦截,都通过,返回允许下单,任意一个失败,返回对应的失败原因。

多层嵌套if写法

我们正常多层嵌套if的写法

/**
 * @author saier
 * @date 2020/3/31 18:03
 */
public class Order {
    public Message interrupt1(){
        return null;
    }
    public Message interrupt2(){
        return null;
    }
    public Message interrupt3(){
        return null;
    }
    public Message interrupt4(){
        return null;
    }
    public Message interrupt5(){
        return null;
    }

    public static void main(String[] args) {
        Order order= new Order();
        if(order.interrupt1().getResult() == 1){
            if(order.interrupt2().getResult() == 1){
                if(order.interrupt3().getResult() == 1){
                    if(order.interrupt4().getResult() == 1){
                        if(order.interrupt5().getResult() == 1){
                            System.out.println("success");
                        }
                    }
                }
            }
        }

    }
}

@Data
class Message {
    private int result;
    private String msg;
}

异常处理逻辑

或者有些利用异常做逻辑,代码会简单一点

/**
 * @author saier
 * @date 2020/3/31 18:03
 */
public class Order2 {
    public void interrupt1(){

    }
    public void interrupt2(){

    }
    public void interrupt3(){
        //失败
        throw new RuntimeException();
    }
    public void interrupt4(){
        //失败
        throw new RuntimeException();
    }
    public void interrupt5(){
        //失败
        throw new RuntimeException();
    }

    public static void main(String[] args) {
        Order2 order2= new Order2();
        try{
            order2.interrupt1();
            order2.interrupt2();
            order2.interrupt3();
            order2.interrupt4();
            order2.interrupt5();
            System.out.println("success");
        }catch (RuntimeException e){
            System.out.println("fail");
        }

    }
}

一开始,我就直接使用异常来做逻辑。但后续逻辑越来越复杂之后,也会出现一些问题。例如异常只能返回异常信息,不能返回更多的字段信息。

后面也留意到,异常做逻辑,在阿里规范是禁止的。

阿里代码规范 :
【强制】异常不要用来做流程控制,条件控制。
说明:异常设计的初衷是解决程序运行中的各种意外情况,且异常的处理效率比条件判断方式要低很多。

更重要的是,代码可读性太差了,随时一个方法的异常抛出来,还要考虑代码本身的异常。

没更好的办法,只能考虑设计模式了

怎么改,会使代码的可读性高,扩展性好?

在同事的提醒下,突然想起了设计模式!

我们希望达到的目的

  1. 代码没有这么多if else嵌套,可读性高
  2. 如果新增新的拦截逻辑简单方便,不影响原本的逻辑,扩展性好
  3. 可以很方便地调换拦截逻辑顺序,低耦合

责任链模式

在这种场景下,非常适合责任链模式。(什么场景使用什么设计模式,这就需要平时有积累,知道各种设计模式的基本使用)

责任链,顾名思义,就是用来处理相关事务责任的一条执行链,执行链上有多个节点,每个节点都有机会(条件匹配)处理请求事务,如果某个节点处理完了就可以根据实际业务需求传递给下一个节点继续处理或者返回处理完毕。

首先,建立过滤器的抽象类

public abstract class AbstractFilter {

    private AbstractFilter nextFilter;

    /**
     * 责任链的下一个元素
     */
    public void setNextFilter(AbstractFilter nextFilter){
        this.nextFilter = nextFilter;
    }

    public AbstractFilter getLastFilter(){
        if(this.nextFilter != null){
            return this.nextFilter.getLastFilter();
        }else{
            return this;
        }
    }

    public void filter(FilterRequest filterRequest, Response response){
        doFilter(filterRequest,response);
        if(response.isFilterNext() && nextFilter != null){
            nextFilter.filter(filterRequest,response);
        }
    }

    /**
     * 具体拦截逻辑
     */
    public abstract void doFilter(FilterRequest filterRequest, Response response);

    /**
     * 根据拦截结果做处理
     */
    public void exec(FilterRequest filterRequest, Response response){
    }
}

过滤器的实现类

@Component
@Order(5)
public class CheckParamFilter1 extends AbstractFilter {
    @Override
    public void doFilter(FilterRequest filterRequest, Response response) {

    }
}

@Component
@Order(10)
public class CheckParamFilter2 extends AbstractFilter {
    @Override
    public void doFilter(FilterRequest filterRequest, Response response) {

    }
}

使用Order注解,确定过滤器的顺序,后续在spring注入的时候,会有奇效

//利用spring的自动注入机制
@Autowired
List<AbstractFilter> abstractFilterList;

private AbstractFilter firstFilter;

//spring注入后自动执行
@PostConstruct
public void initializeChainFilter(){
    //把所有调用的逻辑注入到责任链,按照Order排序,越小优先级越高
    for(int i = 0;i<abstractFilterList.size();i++){
        if(i == 0){
            firstFilter = abstractFilterList.get(i);
        }else{
            firstFilter.getLastFilter().setNextFilter(abstractFilterList.get(i));
        }
    }
}

//直接使用
public Response exec(){
    firstFilter.filter(filterRequest, response);
    return response;
}

使用设计模式的好处

看下使用责任链模式后,有什么好处!

  1. 新增拦截逻辑,只需要再实现一个AbstractFilter类即可
  2. 修改拦截顺序,只需要修改Order注解的大小,越小,优先级越高
  3. 代码清晰,所有处理逻辑下沉到实现类中

使用设计模式的缺点

做到了低耦合,高扩展。但也带来了一些不好的地方

  1. 逻辑更复杂,用了链式等数据结构,要注意单例的问题,不能重复使用
  2. 类数量激增,一个拦截器就一个类

最后小结一下

不是什么地方都适合使用设计模式,如果逻辑简单,你硬要使用设计模式,只会带来结构上的复杂,大家可以按照大家的业务场景来使用。

谁能干掉了if else的更多相关文章

  1. 干掉命令行窗口下MySql乱码

    晚上重温dos窗口操作mysql的时候,遇到了一个巨蛋疼的问题------>中文验证码  -->_-->,所以找了找资料弄懂了怎么解决乱码问题,,小记一下. 新建一个表 create ...

  2. 干掉Unity3D

    我为什么想干掉Unity3D? 这个问题容我不答,每个做技术的人总有一些完美主义. 你使用u3d的过程中是不是痛并快乐着呢. 就从两个国内具有相当普遍性的痛点说起. il2cpp,unity作出了这个 ...

  3. Markdown会干掉Html吗?

    Markdown会干掉Html吗? 很明显,MarkDown正在已一种比病毒还快的速度传播着,量子的机器人语言也是深受其启发,当然了,在这个东西没搞出来之前,MarkDown就能干很多事情,比如在线编 ...

  4. Excel应该这么玩——1、命名单元格:干掉常数

    命名单元格:通过名称来引用单元格中的值,常用于引用固定不变的值. 单元格是Excel中存储数据的最小单位,在公式中通过A1.B2之类的名称来引用其中的值.A1只是单元格的坐标,就好像人的身份证号.生活 ...

  5. 夺命雷公狗---DEDECMS----8dedecms干掉首页和-文档页-栏目页的页面的广告

    我们首先来将首页生成静态页面,如下图所示: 成功后,如下显示: 如果成功后则在文件夹下多了一个index.html的文件.. 我们的首页静态页面是通过模版文件生成,所以我们只需要把模版文件的广告标签删 ...

  6. 干掉cmd:windows下使用linux命令行

    对于喜欢用命令行的朋友们,在windows下面使用cmd窗口是不是很不爽?复制不方便?不能随意放大缩小?如果需要多个控制台要多个窗口?....各种不爽 一.基础工具 如果你也不爽,那就对了,所以给大家 ...

  7. Linux中 干掉原来的PHP方法

    干掉原来的PHP方法: 查看php版本命令:#php -v这个命令是删除不干净的#yum remove php因为使用这个命令以后再用#php -v还是会看到有版本信息的..... 必须强制删除#rp ...

  8. Xcode6为什么干掉pch(Precompile Prefix Header)&如何添加pch文件

    转载:  http://blog.csdn.net/iosdevtip/article/details/40918353 一直在用xcode6开发,但项目都是在xcode5上创建的,所以一直没注意到, ...

  9. Xcode6为什么干掉pch(Precompile Prefix Header)&amp;怎样加入pch文件

    一直在用xcode6开发,但项目都是在xcode5上创建的,所以一直没注意到,xcode6居然干掉pch文件了. 为什么xcode6没有自己主动创建pch文件呢? 简单地看:我们在写项目的时候,大部分 ...

  10. 使用gc、objgraph干掉python内存泄露与循环引用!

    Python使用引用计数和垃圾回收来做内存管理,前面也写过一遍文章<Python内存优化>,介绍了在python中,如何profile内存使用情况,并做出相应的优化.本文介绍两个更致命的问 ...

随机推荐

  1. 消息队列RabbitMQ(五):死信队列与延迟队列

    死信队列 引言 死信队列,英文缩写:DLX .Dead Letter Exchange(死信交换机),其实应该叫做死信交换机才更恰当. 当消息成为Dead message后,可以被重新发送到另一个交换 ...

  2. volatile 的使用

    ① 编译器很聪明,会帮我们做些优化,比如: int a; a = 0; // 这句话可以优化掉,不影响 a 的结果 a = 1; ② 有时候编译器会自作聪明,比如: int *p = ioremap( ...

  3. Markdown使用概述

    Markdown使用概述 序言 作为一名编程学习的爱好者和初学者,由于学习编程的过程中总是存在遗忘以及很难动手写起来的问题,所以在看了许多关于编程学习方法的文章之后,选择使用typora作为我的笔记工 ...

  4. golang:Channel协程间通信

    channel是Go语言中的一个核心数据类型,channel是一个数据类型,主要用来解决协程的同步问题以及协程之间数据共享(数据传递)的问题.在并发核心单元通过它就可以发送或者接收数据进行通讯,这在一 ...

  5. [OS] 操作系统课程(三)

    工具 源码阅读:understand 源码文档自动生成工具:Doxygen 编译环境:gcc 运行环境:x86机器或QEMU 调试工具:QEMU+(GDB or IDE) IDE:Eclipse-CD ...

  6. [Python] 命名空间&作用域

    Python的类语句不会创建实例 类会创建命名空间,通过对象访问类的属性和方法 类不会创建作用域,对方法和属性的引用必须加以限定(如在方法中必须通过self引用实例的属性) class My1(): ...

  7. django访问mysql数据库--模型(model)

    一.安装: sudo pip install mysql-connector-python sudo pip install MySQL-python 异常情况,如果提示pip超时 curl http ...

  8. 攻防世界(二)Training-WWW-Robots

    攻防世界系列:Training-WWW-Robots 1.查看robots.txt的要求  补充: 什么是robots.txt协议? Robots.txt是放在网站根目录下的一个文件,也是搜索引擎在网 ...

  9. Log4j 配置多个 Logger

    引言 Log4j 是 Java 的主流日志框架,通过灵活的配置可以提供各种类型的日志服务. 在使用 Log4j 进行实际项目开发的过程中,有时不想使用 rootLogger 记录器,把所有的日志都输出 ...

  10. Elasticsearch + Logstash + Kibana +Redis +Filebeat 单机版日志收集环境搭建

    1.前置工作 1.虚拟机环境简介 Linux版本:Linux localhost.localdomain 3.10.0-862.el7.x86_64 #1 SMP Fri Apr 20 16:44:2 ...