本文节选自《设计模式就该这样学》
1 建造者模式的链式写法
以构建一门课程为例,一个完整的课程由PPT课件、回放视频、课堂笔记、课后作业组成,但是这些内容的设置顺序可以随意调整,我们用建造者模式来代入理解一下。首先创建一个产品类Course。
@Data public 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,这样,产品表示与创建之间的联系更加紧密,结构更加紧凑,同时使得建造者模式的形式更加简洁。
如果采用静态内部类形式实现建造者模式,则前面的案例可以改写如下。
@Data public 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弹架构:30个设计模式真实案例(附源码),挑战年薪60W不是梦
本文为“Tom弹架构”原创,转载请注明出处。技术在于分享,我分享我快乐!
如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力。