How to reconstruct your "repeat the query" with minimum cost

This article describes how to reconstruct the project repeated the query. Improve the readability and maintainability of the code. Look at a code, which is mainly used to query the information system with the electricity supplier orders related to:

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方法
}

复制代码

Then there is the result of an order processing system:

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;
    }


}

复制代码

The code above I think many people will be encountered in some old project inside the old class, let's summarize what disadvantage of the above code:

  • Too much duplicate code , can look too much public query methods exposed inside the duplicate code, after maintenance was difficult, a lot of places must be changed.
  • If you come to a new demand, the need to add a new condition, we have to rewrite a bunch of duplicate code , such as the above-mentioned system, I requested a return before a certain date and the order is to use micro-channel pay.
  • The statements inside the function body is too stiff , a little bit more complicated when the system time is difficult to see at a glance how a particular query, this is a demo so we write the name of the function fairly clear, but when the query is complex or write the old system code the bad times, often by the function name is not known specific query criteria , this time to regularly read function source did not know what to do.

To solve the pain points, we do some reconstruction, reconstruction of pain this point we have to solve is as follows:

  • Elimination of duplicate code in the code above
  • Most of the new demand can be written in the form of a combination, without adding a single query
  • Better readability, ease of maintenance

Our first response to these needs, to conduct a trace - these query:

//查询的基类
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);
    }
}
复制代码

With this query, we look at the main class can be changed to:

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;
    }
}

复制代码

Take a look at the above-mentioned changes in the second edition of which, we basically completed the readability and features hot-swappable, after the new query, combined with the spec as long as you can, do not need to repeat the write query, but this is clearly not perfect, repeat the code we have so many iterator like to continue reconstruction:

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)));
    }
}

复制代码

Here, we have basically completed the reconstruction. And the very nature of such changes is not too big ( but the effect is good ), do not worry about big changes in the future, affecting a wide range of test, in a mature system which coding, do not always think with a variety of design patterns to optimize a large number of modules, but should range from small class or function entry, sustained a small reconstruction module , to achieve the ultimate purpose of optimizing the overall system.

For this article, the scene is no need to rigidly stick to practical application in the "Query" This business scenario , if you find that your class provides many features of external function which has most of the duplicate code, and conditions are combined, then can take advantage of such an approach is reconstructed.

Guess you like

Origin juejin.im/post/5e12a59d5188253abf5cdd43