本文节选自《设计模式就该这样学》

1 建造者模式的链式写法

以构建一门课程为例,一个残缺的课程由PPT课件、回放视频、课堂笔记、课后作业组成,然而这些内容的设置程序能够随便调整,咱们用建造者模式来代入了解一下。首先创立一个产品类Course。

@Datapublic class Course {    private String name;    private String ppt;    private String video;    private String note;    private String homework;    @Override    public String toString() {        return "CourseBuilder{" +                "name='" + name + '\'' +                ", ppt='" + ppt + '\'' +                ", video='" + video + '\'' +                ", note='" + note + '\'' +                ", homework='" + homework + '\'' +                '}';    }}

而后创立建造者类CourseBuilder,将简单的创立过程封装起来,创立步骤由用户决定。

public class CourseBuilder {    private Course course = new Course();    public CourseBuilder addName(String name){        course.setName(name);        return this;    }    public CourseBuilder addPpt(String ppt){        course.setPpt(ppt);        return this;    }    public CourseBuilder addVideo(String video){        course.setVideo(video);        return this;    }    public CourseBuilder addNote(String note){        course.setNote(note);        return this;    }    public CourseBuilder addHomework(String homework){        course.setHomework(homework);        return this;    }    public Course builder(){        return course;    }}

最初编写客户端测试代码。

    public static void main(String[] args) {        CourseBuilder builder = new CourseBuilder()                    .addName("设计模式")                    .addPPT("【PPT课件】")                    .addVideo("【回放视频】")                    .addNote("【课堂笔记】")                    .addHomework("【课后作业】");        System.out.println(builder.build());}

这样的写法是不是很眼生?前面剖析建造者模式在框架源码中的利用时大家就会明确。再来看一下类图的变动,如下图所示。

2 应用动态外部类实现建造者模式

事实上,在平时的编码中,咱们通常都会疏忽对象的复杂性,优先思考应用工厂模式创建对象,而不是建造者模式。因为工厂模式和建造者模式的作用都是创立一个产品对象,而工厂模式的构造更加简洁间接(没有Builder和 Director),因而更常应用。
个别状况下,咱们更习惯应用动态外部类的形式实现建造者模式,即一个产品类外部主动带有一个具体建造者,由它负责该产品的组装创立,不再须要Builder和Director,这样,产品示意与创立之间的分割更加严密,构造更加紧凑,同时使得建造者模式的模式更加简洁。
如果采纳动态外部类模式实现建造者模式,则后面的案例能够改写如下。

@Datapublic class Course {    private String name;    private String ppt;    private String video;    private String note;    private String homework;    @Override    public String toString() {        return "Course{" +                "name='" + name + '\'' +                ", ppt='" + ppt + '\'' +                ", video='" + video + '\'' +                ", note='" + note + '\'' +                ", homework='" + homework + '\'' +                '}';    }    public static class Builder {        private Course course = new Course();        public Builder addName(String name){            course.setName(name);            return this;        }        public Builder addPpt(String ppt){            course.setPpt(ppt);            return this;        }        public Builder addVideo(String video){            course.setVideo(video);            return this;        }        public Builder addNote(String note){            course.setNote(note);            return this;        }        public Builder addHomework(String homework){            course.setHomework(homework);            return this;        }        public Course builder(){            return course;        }    }}

客户端测试代码如下。

    public static void main(String[] args) {         Course course = new Course.Builder()                .addName("设计模式")                .addPpt("【PPT课件】")                .addVideo("【录播视频】")                 .builder();        System.out.println(course);    }        

这样,代码也会看上去更加简洁,不会让人感觉到多了一个类。

3 应用建造者模式动静构建SQL语句

上面来看一个实战案例,这个案例参考了开源框架JPA的SQL结构模式。咱们在结构SQL查问条件的时候,须要依据不同的条件来拼接SQL字符串。如果查问条件简单,则SQL拼接的过程也会变得非常复杂,从而给代码保护带来十分大的艰难。因而,咱们用建造者类QueryRuleSqlBuilder将简单的SQL结构过程进行封装,用QueryRule对象专门保留SQL查问时的条件,最初依据查问条件,主动生成SQL语句。首先创立QueryRule类,代码如下。

import java.io.Serializable;import java.util.ArrayList;import java.util.List;/** * QueryRule,次要性能用于结构查问条件 *  * @author Tom */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>();    private List<QueryRule> queryRuleList = new ArrayList<QueryRule>();    private String propertyName;    private QueryRule() {}    private QueryRule(String propertyName) {        this.propertyName = propertyName;    }    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 List<Rule> getRuleList() {        return this.ruleList;    }    public List<QueryRule> getQueryRuleList() {        return this.queryRuleList;    }    public String getPropertyName() {        return this.propertyName;    }    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类。

package com.tom.vip.pattern.builder.sql;/** * 依据QueryRule主动构建SQL语句 * @author Tom * */public class QueryRuleSqlBuilder {    private int CURR_INDEX = 0; //记录参数所在的地位    private List<String> properties; //保留列名列表    private List<Object> values; //保留参数值列表    private List<Order> orders; //保留排序规定列表        private String whereSql = "";     private String orderSql = "";    private Object [] valueArr = new Object[]{};    private Map<Object,Object> valueMap = new HashMap<Object,Object>();        /**     * 取得查问条件     * @return     */    private String getWhereSql(){        return this.whereSql;    }        /**     * 取得排序条件     * @return     */    private String getOrderSql(){        return this.orderSql;    }        /**     * 取得参数值列表     * @return     */    public Object [] getValues(){        return this.valueArr;    }        /**     * 取得参数列表     * @return     */    private Map<Object,Object> getValueMap(){        return this.valueMap;    }        /**     * 创立SQL结构器     * @param queryRule     */    public QueryRuleSqlBuilder(QueryRule queryRule) {        CURR_INDEX = 0;        properties = new ArrayList<String>();        values = new ArrayList<Object>();        orders = new ArrayList<Order>();        for (QueryRule.Rule rule : queryRule.getRuleList()) {            switch (rule.getType()) {            case QueryRule.BETWEEN:                processBetween(rule);                break;            case QueryRule.EQ:                processEqual(rule);                break;            case QueryRule.LIKE:                processLike(rule);                break;            case QueryRule.NOTEQ:                processNotEqual(rule);                break;            case QueryRule.GT:                processGreaterThen(rule);                break;            case QueryRule.GE:                processGreaterEqual(rule);                break;            case QueryRule.LT:                processLessThen(rule);                break;            case QueryRule.LE:                processLessEqual(rule);                break;            case QueryRule.IN:                processIN(rule);                break;            case QueryRule.NOTIN:                processNotIN(rule);                break;            case QueryRule.ISNULL:                processIsNull(rule);                break;            case QueryRule.ISNOTNULL:                processIsNotNull(rule);                break;            case QueryRule.ISEMPTY:                processIsEmpty(rule);                break;            case QueryRule.ISNOTEMPTY:                processIsNotEmpty(rule);                break;            case QueryRule.ASC_ORDER:                processOrder(rule);                break;            case QueryRule.DESC_ORDER:                processOrder(rule);                break;            default:                throw new IllegalArgumentException("type"+rule.getType()+"not supported.");            }        }        //拼装where语句        appendWhereSql();        //拼装排序语句        appendOrderSql();        //拼装参数值        appendValues();    }        /**     * 去掉order     *      * @param sql     * @return     */    private String removeOrders(String sql) {        Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*", Pattern.CASE_INSENSITIVE);        Matcher m = p.matcher(sql);        StringBuffer sb = new StringBuffer();        while (m.find()) {            m.appendReplacement(sb, "");        }        m.appendTail(sb);        return sb.toString();    }        /**     * 去掉select     *      * @param sql     * @return     */    private String removeSelect(String sql) {        if(sql.toLowerCase().matches("from\\s+")){            int beginPos = sql.toLowerCase().indexOf("from");            return sql.substring(beginPos);        }else{            return sql;        }    }        /**     * 解决like     * @param rule     */    private  void processLike(QueryRule.Rule rule) {        if (ArrayUtils.isEmpty(rule.getValues())) {            return;        }        Object obj = rule.getValues()[0];        if (obj != null) {            String value = obj.toString();            if (!StringUtils.isEmpty(value)) {                value = value.replace('*', '%');                obj = value;            }        }        add(rule.getAndOr(),rule.getPropertyName(),"like","%"+rule.getValues()[0]+"%");    }    /**     * 解决between     * @param rule     */    private  void processBetween(QueryRule.Rule rule) {        if ((ArrayUtils.isEmpty(rule.getValues()))                || (rule.getValues().length < 2)) {            return;        }        add(rule.getAndOr(),rule.getPropertyName(),"","between",rule.getValues()[0],"and");        add(0,"","","",rule.getValues()[1],"");    }    //此处省略局部代码            /**     * 退出SQL查问规定队列     * @param andOr and或者or     * @param key 列名     * @param split 列名与值之间的距离     * @param value 值     */    private  void add(int andOr,String key,String split ,Object value){        add(andOr,key,split,"",value,"");    }        /**     * 退出SQL查问规定队列     * @param andOr and或者or     * @param key 列名     * @param split 列名与值之间的距离     * @param prefix 值前缀     * @param value 值     * @param suffix 值后缀     */    private  void add(int andOr,String key,String split ,String prefix,Object value,String      suffix){        String andOrStr = (0 == andOr ? "" :(QueryRule.AND == andOr ? " and " : " or "));          properties.add(CURR_INDEX,          andOrStr + key + " " + split + prefix + (null != value ? " ? " : " ") + suffix);        if(null != value){            values.add(CURR_INDEX,value);            CURR_INDEX ++;        }    }            /**     * 拼装where语句     */    private void appendWhereSql(){        StringBuffer whereSql = new StringBuffer();        for (String p : properties) {            whereSql.append(p);        }        this.whereSql = removeSelect(removeOrders(whereSql.toString()));    }        /**     * 拼装排序语句     */    private void appendOrderSql(){        StringBuffer orderSql = new StringBuffer();        for (int i = 0 ; i < orders.size(); i ++) {            if(i > 0 && i < orders.size()){                orderSql.append(",");            }            orderSql.append(orders.get(i).toString());        }        this.orderSql = removeSelect(removeOrders(orderSql.toString()));    }        /**     * 拼装参数值     */    private void appendValues(){        Object [] val = new Object[values.size()];        for (int i = 0; i < values.size(); i ++) {            val[i] = values.get(i);            valueMap.put(i, values.get(i));        }        this.valueArr = val;    }    public String builder(String tableName){        String ws = removeFirstAnd(this.getWhereSql());        String whereSql = ("".equals(ws) ? ws : (" where " + ws));        String sql = "select * from " + tableName + whereSql;        Object [] values = this.getValues();        String orderSql = this.getOrderSql();        orderSql = (StringUtils.isEmpty(orderSql) ? " " : (" order by " + orderSql));        sql += orderSql;        return sql;    }    private String removeFirstAnd(String sql){        if(StringUtils.isEmpty(sql)){return sql;}        return sql.trim().toLowerCase().replaceAll("^\\s*and", "") + " ";    }}

接着创立Order类。

/** * SQL排序组件 * @author Tom */public class Order {    private boolean ascending; //升序还是降序    private String propertyName; //哪个字段升序,哪个字段降序        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","Changsha");        queryRule.andLike("name","Tom");        QueryRuleSqlBuilder builder = new QueryRuleSqlBuilder(queryRule);        System.out.println(builder.builder("t_member"));        System.out.println("Params: " + Arrays.toString(builder.getValues()));}

这样一来,客户端代码就十分分明,运行后果如下图所示。

本文为“Tom弹架构”原创,转载请注明出处。技术在于分享,我分享我高兴!
如果本文对您有帮忙,欢送关注和点赞;如果您有任何倡议也可留言评论或私信,您的反对是我保持创作的能源。关注微信公众号『 Tom弹架构 』可获取更多技术干货!