likes
comments
collection
share

万丈高楼平地起的建造者模式

作者站长头像
站长
· 阅读数 5

定义

建造者模式(Builder Pattern)是将一个复杂对象的构建过程与它的表示分离,使得同样的构建过程可以创建不同的表示,属于创建型模式。使用建造者模式对于用户而言只需指定需要建造的类型就可以获得对象,建造过程及细节不需要了解。

建造者模式适用于创建对象需要很多步骤,但是步骤的顺序不一定固定。如果一个对象有非常复杂的内部结构(很多属性),可以将复杂对象的创建和使用进行分离。

组装电脑

一般建造者模式是搭配着链式编程一起写的,所以这里不介绍传统的写法,直接介绍链式写法。这里以组装电脑为例,新建一个Computer类:

@Data
public class Computer {

    //CPU
    private String cpu;

    //RAM
    private String ram;

    //USB
    private int usbCount;

    //键盘
    private String keyboard;

    //显示器
    private String display;

}

然后新建一个ComputerBuilder类:

/**
 * @author jack xu
 */
public class ComputerBuilder {

    private Computer computer = new Computer();

    public ComputerBuilder setCPU(String cpu) {
        computer.setCpu(cpu);
        return this;
    }

    public ComputerBuilder setRAM(String ram) {
        computer.setRam(ram);
        return this;
    }

    public ComputerBuilder setUsbCount(int usbCount) {
        computer.setUsbCount(usbCount);
        return this;
    }

    public ComputerBuilder setKeyboard(String keyboard) {
        computer.setKeyboard(keyboard);
        return this;
    }

    public ComputerBuilder setDisplay(String display) {
        computer.setDisplay(display);
        return this;
    }

    public Computer build() {
        return this.computer;
    }

}

最后写一个测试类:

public class Test {

    public static void main(String[] args) {
        Computer computer = new ComputerBuilder()
                .setCPU("因特尔")
                .setRAM("三星")
                .setDisplay("三星24寸")
                .setKeyboard("罗技")
                .setUsbCount(2)
                .build();
        System.out.println(computer);
    }

}

运行一下,一个简单的建造者模式就完成了。

万丈高楼平地起的建造者模式

模仿JPA

下面看第二个例子,模仿 JPA 的 SQL 构造模式,我们在写 SQL 的时候,需要根据不同的条件来拼接 SQL,如果查询复杂的时候,我们SQL拼接的过程也会变得非常复杂,从而给我们的代码维护带来非常大的困难。因此,我们用建造者类QueryRuleSqlBuilder将复杂的构造SQL过程进行封装,用QueryRule对象专门保存SQL查询时的条件,最后根据查询条件,自动生成SQL语句。来看代码,先创建QueryRule类:

@Data
public final class QueryRule implements Serializable {
    private static final long serialVersionUID = 1L;
    public static final int ASC_ORDER = 101;
    public static final int DESC_ORDER = 102;
    public static final int LIKE = 1;
    public static final int IN = 2;
    public static final int NOTIN = 3;
    public static final int BETWEEN = 4;
    public static final int EQ = 5;
    public static final int NOTEQ = 6;
    public static final int GT = 7;
    public static final int GE = 8;
    public static final int LT = 9;
    public static final int LE = 10;
    public static final int ISNULL = 11;
    public static final int ISNOTNULL = 12;
    public static final int ISEMPTY = 13;
    public static final int ISNOTEMPTY = 14;
    public static final int AND = 201;
    public static final int OR = 202;

    private List<Rule> ruleList = new ArrayList<Rule>();


    public static QueryRule getInstance() {
        return new QueryRule();
    }

    /**
     * 添加升序规则
     *
     * @param propertyName
     * @return
     */
    public QueryRule addAscOrder(String propertyName) {
        this.ruleList.add(new Rule(ASC_ORDER, propertyName));
        return this;
    }

    /**
     * 添加降序规则
     *
     * @param propertyName
     * @return
     */
    public QueryRule addDescOrder(String propertyName) {
        this.ruleList.add(new Rule(DESC_ORDER, propertyName));
        return this;
    }

    public QueryRule andIsNull(String propertyName) {
        this.ruleList.add(new Rule(ISNULL, propertyName).setAndOr(AND));
        return this;
    }

    public QueryRule andIsNotNull(String propertyName) {
        this.ruleList.add(new Rule(ISNOTNULL, propertyName).setAndOr(AND));
        return this;
    }

    public QueryRule andIsEmpty(String propertyName) {
        this.ruleList.add(new Rule(ISEMPTY, propertyName).setAndOr(AND));
        return this;
    }

    public QueryRule andIsNotEmpty(String propertyName) {
        this.ruleList.add(new Rule(ISNOTEMPTY, propertyName).setAndOr(AND));
        return this;
    }

    public QueryRule andLike(String propertyName, Object value) {
        this.ruleList.add(new Rule(LIKE, propertyName, new Object[]{value}).setAndOr(AND));
        return this;
    }

    public QueryRule andEqual(String propertyName, Object value) {
        this.ruleList.add(new Rule(EQ, propertyName, new Object[]{value}).setAndOr(AND));
        return this;
    }

    public QueryRule andBetween(String propertyName, Object... values) {
        this.ruleList.add(new Rule(BETWEEN, propertyName, values).setAndOr(AND));
        return this;
    }

    public QueryRule andIn(String propertyName, List<Object> values) {
        this.ruleList.add(new Rule(IN, propertyName, new Object[]{values}).setAndOr(AND));
        return this;
    }

    public QueryRule andIn(String propertyName, Object... values) {
        this.ruleList.add(new Rule(IN, propertyName, values).setAndOr(AND));
        return this;
    }

    public QueryRule andNotIn(String propertyName, List<Object> values) {
        this.ruleList.add(new Rule(NOTIN, propertyName, new Object[]{values}).setAndOr(AND));
        return this;
    }

    public QueryRule orNotIn(String propertyName, Object... values) {
        this.ruleList.add(new Rule(NOTIN, propertyName, values).setAndOr(OR));
        return this;
    }


    public QueryRule andNotEqual(String propertyName, Object value) {
        this.ruleList.add(new Rule(NOTEQ, propertyName, new Object[]{value}).setAndOr(AND));
        return this;
    }

    public QueryRule andGreaterThan(String propertyName, Object value) {
        this.ruleList.add(new Rule(GT, propertyName, new Object[]{value}).setAndOr(AND));
        return this;
    }

    public QueryRule andGreaterEqual(String propertyName, Object value) {
        this.ruleList.add(new Rule(GE, propertyName, new Object[]{value}).setAndOr(AND));
        return this;
    }

    public QueryRule andLessThan(String propertyName, Object value) {
        this.ruleList.add(new Rule(LT, propertyName, new Object[]{value}).setAndOr(AND));
        return this;
    }

    public QueryRule andLessEqual(String propertyName, Object value) {
        this.ruleList.add(new Rule(LE, propertyName, new Object[]{value}).setAndOr(AND));
        return this;
    }


    public QueryRule orIsNull(String propertyName) {
        this.ruleList.add(new Rule(ISNULL, propertyName).setAndOr(OR));
        return this;
    }

    public QueryRule orIsNotNull(String propertyName) {
        this.ruleList.add(new Rule(ISNOTNULL, propertyName).setAndOr(OR));
        return this;
    }

    public QueryRule orIsEmpty(String propertyName) {
        this.ruleList.add(new Rule(ISEMPTY, propertyName).setAndOr(OR));
        return this;
    }

    public QueryRule orIsNotEmpty(String propertyName) {
        this.ruleList.add(new Rule(ISNOTEMPTY, propertyName).setAndOr(OR));
        return this;
    }

    public QueryRule orLike(String propertyName, Object value) {
        this.ruleList.add(new Rule(LIKE, propertyName, new Object[]{value}).setAndOr(OR));
        return this;
    }

    public QueryRule orEqual(String propertyName, Object value) {
        this.ruleList.add(new Rule(EQ, propertyName, new Object[]{value}).setAndOr(OR));
        return this;
    }

    public QueryRule orBetween(String propertyName, Object... values) {
        this.ruleList.add(new Rule(BETWEEN, propertyName, values).setAndOr(OR));
        return this;
    }

    public QueryRule orIn(String propertyName, List<Object> values) {
        this.ruleList.add(new Rule(IN, propertyName, new Object[]{values}).setAndOr(OR));
        return this;
    }

    public QueryRule orIn(String propertyName, Object... values) {
        this.ruleList.add(new Rule(IN, propertyName, values).setAndOr(OR));
        return this;
    }

    public QueryRule orNotEqual(String propertyName, Object value) {
        this.ruleList.add(new Rule(NOTEQ, propertyName, new Object[]{value}).setAndOr(OR));
        return this;
    }

    public QueryRule orGreaterThan(String propertyName, Object value) {
        this.ruleList.add(new Rule(GT, propertyName, new Object[]{value}).setAndOr(OR));
        return this;
    }

    public QueryRule orGreaterEqual(String propertyName, Object value) {
        this.ruleList.add(new Rule(GE, propertyName, new Object[]{value}).setAndOr(OR));
        return this;
    }

    public QueryRule orLessThan(String propertyName, Object value) {
        this.ruleList.add(new Rule(LT, propertyName, new Object[]{value}).setAndOr(OR));
        return this;
    }

    public QueryRule orLessEqual(String propertyName, Object value) {
        this.ruleList.add(new Rule(LE, propertyName, new Object[]{value}).setAndOr(OR));
        return this;
    }


    protected class Rule implements Serializable {
        private static final long serialVersionUID = 1L;
        private int type;    //规则的类型
        private String property_name;
        private Object[] values;
        private int andOr = AND;

        public Rule(int paramInt, String paramString) {
            this.property_name = paramString;
            this.type = paramInt;
        }

        public Rule(int paramInt, String paramString,
                    Object[] paramArrayOfObject) {
            this.property_name = paramString;
            this.values = paramArrayOfObject;
            this.type = paramInt;
        }

        public Rule setAndOr(int andOr) {
            this.andOr = andOr;
            return this;
        }

        public int getAndOr() {
            return this.andOr;
        }

        public Object[] getValues() {
            return this.values;
        }

        public int getType() {
            return this.type;
        }

        public String getPropertyName() {
            return this.property_name;
        }
    }
    
}

然后,创建QueryRuleSqlBuilder类:(这里类太长,粘贴不上来,可以点此查看QueryRuleSqlBuilder.java

万丈高楼平地起的建造者模式 创建Order类:

/**
 * sql排序组件
 */
public class Order {
    //升序还是降序
    private boolean ascending;
    //哪个字段升序,哪个字段降序
    private String propertyName;

    @Override
    public String toString() {
        return propertyName + ' ' + (ascending ? "asc" : "desc");
    }

    /**
     * Constructor for Order.
     */
    protected Order(String propertyName, boolean ascending) {
        this.propertyName = propertyName;
        this.ascending = ascending;
    }

    /**
     * Ascending order
     *
     * @param propertyName
     * @return Order
     */
    public static Order asc(String propertyName) {
        return new Order(propertyName, true);
    }

    /**
     * Descending order
     *
     * @param propertyName
     * @return Order
     */
    public static Order desc(String propertyName) {
        return new Order(propertyName, false);
    }

}

编写测试代码:

public static void main(String[] args) {
    QueryRule queryRule = QueryRule.getInstance();
    queryRule.addAscOrder("age");
    queryRule.andEqual("addr", "上海");
    queryRule.andLike("name", "jack xu");
    QueryRuleSqlBuilder builder = new QueryRuleSqlBuilder(queryRule);

    System.out.println(builder.builder("t_member"));

    System.out.println("Params: " + Arrays.toString(builder.getValues()));
}

这样我们的客户端代码看上去非常的干净简洁,看下运行结果:

万丈高楼平地起的建造者模式

源码中的建造者模式

我们看下JDK的StringBuilder,它提供的append()方法,给我们开放构造步骤,最后调用toString()方法就可以获得一个构造好的完整字符串。它有很多append方法,是不是似曾相识。

万丈高楼平地起的建造者模式

最后的toString方法相当于我们上面的build()方法,返回一个建造好的对象。 万丈高楼平地起的建造者模式

还有Mybatis中,比如CacheBuilder类,比如SqlSessionFactoryBuilder通过调用build()方法获得的是一个SqlSessionFactory类;还有Spring中BeanDefinitionBuilder通过调用getBeanDefinition()方法获得一个BeanDefinition对象等等。

建造者模式的优缺点

优点:

  1. 封装性好,创建和使用分离。
  2. 扩展性好,建造类之间独立,一定程度上解耦。 缺点:
  3. 产生多余的Builder对象。
  4. 产品内部发生变化,建造者都要修改,成本较大。

本文及其他设计模式源码:github.com/xuhaoj/patt…

转载自:https://juejin.cn/post/7006666910540496909
评论
请登录