如何用最小代价重构你的"重复查询条件"

本文将介绍如何重构项目中的重复查询条件。提升代码的可读性和可维护性。先来看一段代码,这段代码主要就是用于查询电商系统中跟订单有关的信息:设计模式

public class Order {
    public static final int PAY_TYPE_WECHAT = 1;
    public static final int PAY_TYPE_ALIPAY = 2;
    public static final int PAY_TYPE_UNIONPAY = 3;

    //实际支付金额
    private int payValue;
    //实际支付时间
    private long payTime;

    //实际支付类型 1 微信支付  2支付宝支付 3银联支付
    private int type;

    //支付过程是否使用了贷款
    private boolean useLoan;
    //下面省略一堆get set方法
}

复制代码

而后有一个订单结果处理系统:bash

public class OrderFinder {

    private List<Order> orders = new ArrayList<>();

    //新增一笔订单
    public void addOrder(Order order) {
        orders.add(order);
    }

    //返回没有使用过贷款的订单
    public List<Order> getNoUseLoan() {
        List<Order> orders = new ArrayList<>();
        Iterator iterator = orders.iterator();
        while (iterator.hasNext()) {
            Order order = (Order) iterator.next();
            if (!order.isUseLoan()) {
                orders.add(order);
            }
        }
        return orders;
    }

    //返回在这个日期以前的订单
    public List<Order> beforDate(long date) {
        List<Order> orders = new ArrayList<>();
        Iterator iterator = orders.iterator();
        while (iterator.hasNext()) {
            Order order = (Order) iterator.next();
            if (order.getPayTime() < date) {
                orders.add(order);
            }
        }
        return orders;
    }

    //返回在这个日期以前 且金额大于固定值的  的订单
    public List<Order> beforDateAndOverValue(long date, int payValue) {
        List<Order> orders = new ArrayList<>();
        Iterator iterator = orders.iterator();
        while (iterator.hasNext()) {
            Order order = (Order) iterator.next();
            if (order.getPayTime() < date && order.getPayValue() > payValue) {
                orders.add(order);
            }
        }
        return orders;
    }
    //返回大于这个支付金额 而且是使用微信支付的订单
    public List<Order> overPayAndUseWechat(int payValue) {
        List<Order> orders = new ArrayList<>();
        Iterator iterator = orders.iterator();
        while (iterator.hasNext()) {
            Order order = (Order) iterator.next();
            if (order.getPayValue() > payValue && order.getType() == PAY_TYPE_WECHAT) {
                orders.add(order);
            }
        }
        return orders;
    }


}

复制代码

上面这段代码我想不少人在一些老项目的老类里面应该都会遇到,咱们来概括总结一下上述代码的缺点:微信

  • 重复代码过多,能够看一下暴露出来的public 的查询方法 里面重复的代码太多了,之后维护起来很费劲,不少地方都要改。
  • 若是来一个新的需求,须要新增一个新的条件,咱们又要重写一堆重复的代码,好比对于上述的系统,我要求提供一个返回在某个日期以前而且是使用微信支付的订单。
  • 函数体内部的语句过于僵硬,当系统稍微复杂一点的时候很难一眼看出具体的查询条件是如何,咱们这个是demo因此函数名称写的还算清晰,可是当查询条件复杂或者老系统代码写的很差的时候,每每经过函数名是没法得知具体查询条件的,这个时候要常常读函数源码才知道要干什么。

为了解决上述的痛点,咱们来作一番重构,这个重构咱们要解决的痛点以下:ide

  • 消除上述代码中的重复代码
  • 新增需求大部分能够经过组合的形式来写,无需新增查询语句
  • 代码的可读性要好,易于维护

咱们首先针对上述的需求,来对这些查询条件进行一次归拢:函数

//查询的基类
public abstract class Spec {
    public abstract boolean isFitByCondition(Order order);
}
//早于这个时间的
public class BeforeDateSpec extends Spec {
    private long date;

    public BeforeDateSpec(long date) {
        this.date = date;
    }

    @Override
    public boolean isFitByCondition(Order order) {
        return order.getPayTime() < date;
    }
}
//不使用贷款的
public class NoLoanSpec extends Spec {
    @Override
    public boolean isFitByCondition(Order order) {
        return !order.isUseLoan();
    }
}
//超过必定金额的
public class OverPaySpec extends Spec {

    public OverPaySpec(int value) {
        this.value = value;
    }

    private int value;

    @Override
    public boolean isFitByCondition(Order order) {
        return order.getPayValue() > value;
    }
}
//使用微信支付的
public class UseWechatSpec extends Spec {

    @Override
    public boolean isFitByCondition(Order order) {
        return order.getType() == Order.PAY_TYPE_WECHAT;
    }
}

//组合查询
public class AndSpec extends Spec {

    public AndSpec(Spec augEndSpec, Spec addEndSpec) {
        this.augEndSpec = augEndSpec;
        this.addEndSpec = addEndSpec;
    }

    private Spec augEndSpec;
    private Spec addEndSpec;


    @Override
    public boolean isFitByCondition(Order order) {
        return augEndSpec.isFitByCondition(order) && addEndSpec.isFitByCondition(order);
    }
}
复制代码

有了这些查询条件,咱们来看看,主类就能够改为:测试

public class OrderFinder2 {
    private List<Order> orders = new ArrayList<>();

    //新增一笔订单
    public void addOrder(Order order) {
        orders.add(order);
    }

    //返回没有使用过贷款的订单
    public List<Order> getNoUseLoan() {
        Spec spec = new NoLoanSpec();
        List<Order> orders = new ArrayList<>();
        Iterator iterator = orders.iterator();
        while (iterator.hasNext()) {
            Order order = (Order) iterator.next();
            if (spec.isFitByCondition(order)) {
                orders.add(order);
            }
        }
        return orders;
    }

    //返回在这个日期以前的订单
    public List<Order> beforDate(long date) {
        Spec spec = new BeforeDateSpec(date);
        List<Order> orders = new ArrayList<>();
        Iterator iterator = orders.iterator();
        while (iterator.hasNext()) {
            Order order = (Order) iterator.next();
            if (spec.isFitByCondition(order)) {
                orders.add(order);
            }
        }
        return orders;
    }

    //返回在这个日期以前 且金额大于固定值的  的订单
    public List<Order> beforDateAndOverValue(long date, int payValue) {

        Spec spec1 = new BeforeDateSpec(date);
        Spec spec2 = new OverPaySpec(payValue);
        AndSpec andSpec = new AndSpec(spec1, spec2);

        List<Order> orders = new ArrayList<>();
        Iterator iterator = orders.iterator();
        while (iterator.hasNext()) {
            Order order = (Order) iterator.next();
            if (andSpec.isFitByCondition(order)) {
                orders.add(order);
            }
        }
        return orders;
    }

    public List<Order> overPayAndUseWechat(int payValue) {

        Spec spec1 = new UseWechatSpec();
        Spec spec2 = new OverPaySpec(payValue);
        AndSpec andSpec = new AndSpec(spec1, spec2);

        List<Order> orders = new ArrayList<>();
        Iterator iterator = orders.iterator();
        while (iterator.hasNext()) {
            Order order = (Order) iterator.next();
            if (andSpec.isFitByCondition(order)) {
                orders.add(order);
            }
        }
        return orders;
    }
}

复制代码

看看上述的第二版改动里面,咱们基本完成了可读性和热插拔的特色,新增的查询之后,只要用spec 组合起来就能够,无需再重复写查询条件,可是这样作显然还不够完美,咱们还有那么多的迭代器之类的重复代码, 继续重构:微信支付

public class OrderFinder3 {
    private List<Order> orders = new ArrayList<>();

    //新增一笔订单
    public void addOrder(Order order) {
        orders.add(order);
    }

    //注意这个函数是private的
    private List<Order> selectBy(Spec spec) {
        List<Order> orders = new ArrayList<>();
        Iterator iterator = orders.iterator();
        while (iterator.hasNext()) {
            Order order = (Order) iterator.next();
            if (spec.isFitByCondition(order)) {
                orders.add(order);
            }
        }
        return orders;
    }


    //返回没有使用过贷款的订单
    public List<Order> getNoUseLoan() {
        return selectBy(new NoLoanSpec());
    }

    //返回在这个日期以前的订单
    public List<Order> beforDate(long date) {
        return selectBy(new BeforeDateSpec(date));
    }

    //返回在这个日期以前 且金额大于固定值的  的订单
    public List<Order> beforDateAndOverValue(long date, int payValue) {
        return selectBy(new AndSpec(new BeforeDateSpec(date), new OverPaySpec(payValue)));
    }

    public List<Order> overPayAndUseWechat(int payValue) {
        return selectBy(new AndSpec(new UseWechatSpec(), new OverPaySpec(payValue)));
    }
}

复制代码

到这里,咱们就基本完成了重构。 且这样的改动本质上来讲也不算很大(可是效果仍是不错的),不用担忧改动大了之后,测试范围影响广, 在一个成熟的系统里面coding,不要总想着用各类设计模式去优化一些很大的模块,而是应该从小范围的类或者函数入口,持续重构一个个小模块,达到最终优化整个系统的目的。优化

对于本文来讲,实际应用的场景大可没必要拘泥在“查询” 这种业务场景上,只要你发现你的类对外提供的众多功能函数里面有大多数重复的代码,和组合起来的条件,那么均可以利用这种写法进行重构。ui

相关文章
相关标签/搜索