[TOC]
1. 定義
建造者模式(Builder Pattern)是將一個復(fù)雜對象的構(gòu)建過程與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示,屬于創(chuàng)建型模式.使用建造者模式對于用戶而言只需指定需要建造的類型就可以獲得對象,建造過程及細(xì)節(jié)不需要了解
官方原文:Separate the construction of acomplex object from its representation so that the same construction process can create different representations
建造者模式適用于創(chuàng)建對象需要很多步驟,但是步驟的順序不一定固定.如果一個對象有非常復(fù)雜的內(nèi)部結(jié)構(gòu)(很多屬性),可以將復(fù)雜對象的創(chuàng)建和使用進(jìn)行分離
建造者模式的設(shè)計(jì)中主要有四個角色:
- 產(chǎn)品(Product):要創(chuàng)建的產(chǎn)品類對象
- 建造者抽象(Builder):建造者的抽象類,規(guī)范產(chǎn)品對象的各個組成部分的建造,一般由子類實(shí)現(xiàn)具體的建造過程
- 建造者(ConcreteBuilder):具體的 Builder 類,根據(jù)不同的業(yè)務(wù)邏輯,具體化對象的各個組成部分的創(chuàng)建
- 調(diào)用者(Director):調(diào)用具體的建造者,來創(chuàng)建對象的各個部分,在指導(dǎo)者中不涉及具體產(chǎn)品的信息,只負(fù)責(zé)保證對象各部分完整創(chuàng)建或按某種順序創(chuàng)建
2. 建造者模式的應(yīng)用場景
建造者模式適用于一個具有較多的零件的復(fù)雜產(chǎn)品的創(chuàng)建過程,由于需求的變化,組成這個復(fù)雜產(chǎn)品的各個零件經(jīng)常猛烈變化,但是它們的組合方式卻相對穩(wěn)定
建造者模式適用于以下幾種場景:
- 相同的方法,不同的執(zhí)行順序,產(chǎn)生不同的結(jié)果時(shí)
- 多個部件或零件,都可以裝配到一個對象中,但是產(chǎn)生的結(jié)果又不相同
- 產(chǎn)品類非常復(fù)雜,或者產(chǎn)品類中的調(diào)用順序不同產(chǎn)生不同的作用
- 當(dāng)初始化一個對象特別復(fù)雜,參數(shù)多,而且很多參數(shù)都具有默認(rèn)值時(shí)
3. 建造者模式的基本寫法
我們還是以課程為例,一個完整的課程需要由 PPT 課件,回放視頻,課堂筆記,課后作業(yè)組成,但是這些內(nèi)容的設(shè)置順序可以隨意調(diào)整,我們用建造者模式來代入理解一下.首先我們創(chuàng)建一個需要構(gòu)造
的產(chǎn)品類 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 + '\'' +
'}';
}
}
然后創(chuàng)建建造者類 CourseBuilder,將復(fù)雜的構(gòu)造過程封裝起來,構(gòu)造步驟由用戶決定:
public class CourseBuilder{
private Course course = new Course();
public void addName(String name) {
course.setName(name);
}
public void addPPT(String ppt) {
course.setPpt(ppt);
}
public void addVideo(String video) {
course.setVideo(video);
}
public void addNote(String note) {
course.setNote(note);
}
public void addHomework(String homework) {
course.setHomework(homework);
}
public Course build() {
return course;
}
}
編寫測試類:
public class Test {
public static void main(String[] args) {
CourseBuilder builder = new CourseBuilder();
builder.addName("設(shè)計(jì)模式");
builder.addPPT("【PPT 課件】");
builder.addVideo("【回放視頻】");
builder.addNote("【課堂筆記】");
builder.addHomework("【課后作業(yè)】");
System.out.println(builder.build());
}
}
來看一下類結(jié)構(gòu)圖:
3. 建造者模式的鏈?zhǔn)綄懛?/h1>
在平時(shí)的應(yīng)用中,建造者模式通常是采用鏈?zhǔn)骄幊痰姆绞綐?gòu)造對象,下面我們來一下演示代碼,修改 CourseBuilder 類,將 Course 變?yōu)?CourseBuilder 的內(nèi)部類,將構(gòu)建步驟添加進(jìn)去,每完成一個步驟都返回 this
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 build() {
return this.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 + '\'' +
'}';
}
}
}
客戶端代碼
public class Test {
public static void main(String[] args) {
CourseBuilder builder = new CourseBuilder()
.addName("設(shè)計(jì)模式")
.addPPT("【PPT 課件】")
.addVideo("【回放視頻】")
.addNote("【課堂筆記】")
.addHomework("【課后作業(yè)】");
System.out.println(builder.build());
}
}
這樣寫法是不是很眼熟,好像在哪見過呢?后面我們分析建造者模式在源碼中的應(yīng)用大家就會明白.接下來,我們再來看一下類圖的變化∶
4. 建造者模式應(yīng)用案例
下面我們再來看一個實(shí)戰(zhàn)案例,這個案例參考了開源框架 JPA 的 SQL 構(gòu)造模式.是否記得我們在構(gòu)造 SQL 查詢條件的時(shí)候,需要根據(jù)不同的條件來拼接 SQL 字符串.如果查詢條件復(fù)雜的時(shí)候,我們 SQL 拼接的過程也會變得非常復(fù)雜,從而給我們的代碼維護(hù)帶來非常大的困難.因此,我們用建造者類 QueryRuleSqlBuilder 將復(fù)雜的構(gòu)造 SQL 過程進(jìn)行封裝,用 QueryRule 對象專門保存 SQL 查詢時(shí)的條件,最后根據(jù)查詢條件,自動生成 SQL 語句.來看代碼,先創(chuàng)建 QueryRule 類
/**
* QueryRule,主要功能用于構(gòu)造查詢條件
*/
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();
}
/**
* 添加升序規(guī)則
* @param propertyName
* @return
*/
public QueryRule addAscOrder(String propertyName) {
this.ruleList.add(new Rule(ASC_ORDER, propertyName));
return this;
}
/**
* 添加降序規(guī)則
* @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;
}
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; //規(guī)則的類型
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;
}
}
}
然后創(chuàng)建 QueryRuleSqlBuilder 類
/**
* 根據(jù) QueryRule 自動構(gòu)建 sql 語句
* @author Tom
*
*/
public class QueryRuleSqlBuilder {
private int CURR_INDEX = 0; //記錄參數(shù)所在的位置
private List<String> properties; //保存列名列表
private List<Object> values; //保存參數(shù)值列表
private List<Order> orders; //保存排序規(guī)則列表
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;
}
/**
* 獲得參數(shù)值列表
* @return
*/
public Object [] getValues(){
return this.valueArr;
}
/**
* 獲取參數(shù)列表
* @return
*/
private Map<Object,Object> getValueMap(){
return this.valueMap;
}
/**
* 創(chuàng)建 SQL 構(gòu)造器
* @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();
//拼裝參數(shù)值
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],"");
}
/**
* 處理 =
* @param rule
*/
private void processEqual(QueryRule.Rule rule) {
if (ArrayUtils.isEmpty(rule.getValues())) {
return;
}
add(rule.getAndOr(),rule.getPropertyName(),"=",rule.getValues()[0]);
}
/**
* 處理 <>
* @param rule
*/
private void processNotEqual(QueryRule.Rule rule) {
if (ArrayUtils.isEmpty(rule.getValues())) {
return;
}
add(rule.getAndOr(),rule.getPropertyName(),"<>",rule.getValues()[0]);
}
/**
* 處理 >
* @param rule
*/
private void processGreaterThen(
QueryRule.Rule rule) {
if (ArrayUtils.isEmpty(rule.getValues())) {
return;
}
add(rule.getAndOr(),rule.getPropertyName(),">",rule.getValues()[0]);
}
/**
* 處理>=
* @param rule
*/
private void processGreaterEqual(
QueryRule.Rule rule) {
if (ArrayUtils.isEmpty(rule.getValues())) {
return;
}
add(rule.getAndOr(),rule.getPropertyName(),">=",rule.getValues()[0]);
}
/**
* 處理<
* @param rule
*/
private void processLessThen(QueryRule.Rule rule) {
if (ArrayUtils.isEmpty(rule.getValues())) {
return;
}
add(rule.getAndOr(),rule.getPropertyName(),"<",rule.getValues()[0]);
}
/**
* 處理<=
* @param rule
*/
private void processLessEqual(
QueryRule.Rule rule) {
if (ArrayUtils.isEmpty(rule.getValues())) {
return;
}
add(rule.getAndOr(),rule.getPropertyName(),"<=",rule.getValues()[0]);
}
/**
* 處理 is null
* @param rule
*/
private void processIsNull(QueryRule.Rule rule) {
add(rule.getAndOr(),rule.getPropertyName(),"is null",null);
}
/**
* 處理 is not null
* @param rule
*/
private void processIsNotNull(QueryRule.Rule rule) {
add(rule.getAndOr(),rule.getPropertyName(),"is not null",null);
}
/**
* 處理 <>''
* @param rule
*/
private void processIsNotEmpty(QueryRule.Rule rule) {
add(rule.getAndOr(),rule.getPropertyName(),"<>","''");
}
/**
* 處理 =''
* @param rule
*/
private void processIsEmpty(QueryRule.Rule rule) {
add(rule.getAndOr(),rule.getPropertyName(),"=","''");
}
/**
* 處理 in 和 not in
* @param rule
* @param name
*/
private void inAndNotIn(QueryRule.Rule rule,String name){
if (ArrayUtils.isEmpty(rule.getValues())) {
return;
}
if ((rule.getValues().length == 1) && (rule.getValues()[0] != null)
&& (rule.getValues()[0] instanceof List)) {
List<Object> list = (List) rule.getValues()[0];
if ((list != null) && (list.size() > 0)){
for (int i = 0; i < list.size(); i++) {
if(i == 0 && i == list.size() - 1){
add(rule.getAndOr(),rule.getPropertyName(),"",name + " (",list.get(i),")");
}else if(i == 0 && i < list.size() - 1){
add(rule.getAndOr(),rule.getPropertyName(),"",name + " (",list.get(i),"");
}
if(i > 0 && i < list.size() - 1){
add(0,"",",","",list.get(i),"");
}
if(i == list.size() - 1 && i != 0){
add(0,"",",","",list.get(i),")");
}
}
}
} else {
Object[] list = rule.getValues();
for (int i = 0; i < list.length; i++) {
if(i == 0 && i == list.length - 1){
add(rule.getAndOr(),rule.getPropertyName(),"",name + " (",list[i],")");
}else if(i == 0 && i < list.length - 1){
add(rule.getAndOr(),rule.getPropertyName(),"",name + " (",list[i],"");
}
if(i > 0 && i < list.length - 1){
add(0,"",",","",list[i],"");
}
if(i == list.length - 1 && i != 0){
add(0,"",",","",list[i],")");
}
}
}
}
/**
* 處理 not in
* @param rule
*/
private void processNotIN(QueryRule.Rule rule){
inAndNotIn(rule,"not in");
}
/**
* 處理 in
* @param rule
*/
private void processIN(QueryRule.Rule rule) {
inAndNotIn(rule,"in");
}
/**
* 處理 order by
* @param rule 查詢規(guī)則
*/
private void processOrder(Rule rule) {
switch (rule.getType()) {
case QueryRule.ASC_ORDER:
// propertyName 非空
if (!StringUtils.isEmpty(rule.getPropertyName())) {
orders.add(Order.asc(rule.getPropertyName()));
}
break;
case QueryRule.DESC_ORDER:
// propertyName 非空
if (!StringUtils.isEmpty(rule.getPropertyName())) {
orders.add(Order.desc(rule.getPropertyName()));
}
break;
default:
break;
}
}
/**
* 加入到 sql 查詢規(guī)則隊(duì)列
* @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 查詢規(guī)則隊(duì)列
* @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()));
}
/**
* 拼裝參數(shù)值
*/
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", "") + " ";
}
}
創(chuàng)建 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 class Test {
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()));
}
}
這樣一來,我們的客戶端代碼就非常清朗,來看運(yùn)行結(jié)果
select * from t_member where addr = ? and name like ? order by age asc
5. 建造者模式在源碼中的體現(xiàn)
下面來看建造者模式在哪些源碼中有應(yīng)用呢?首先來看 JDK 的 StringBuider,它提供 append() 方法,給我們開放構(gòu)造步驟,最后調(diào)用 toString() 方法就可以獲得一個構(gòu)造好的完整字符串,源碼如下∶
public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence
{
...
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
...
}
在 MyBatis 中也有體現(xiàn),比如 CacheBuilder 類
public class CacheBuilder {
...
public Cache build() {
this.setDefaultImplementations();
Cache cache = this.newBaseCacheInstance(this.implementation, this.id);
this.setCacheProperties((Cache)cache);
if (PerpetualCache.class.equals(cache.getClass())) {
Iterator var2 = this.decorators.iterator();
while(var2.hasNext()) {
Class<? extends Cache> decorator = (Class)var2.next();
cache = this.newCacheDecoratorInstance(decorator, (Cache)cache);
this.setCacheProperties((Cache)cache);
}
cache = this.setStandardDecorators((Cache)cache);
} else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
cache = new LoggingCache((Cache)cache);
}
return (Cache)cache;
}
...
}
同樣在 MyBatis 中,比如 SqlSessionfactoryBuilder 通過調(diào)用 build()方法獲得的是一個 SqlSessionFactory 類
public class SqlSessionFactoryBuilder {
...
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
SqlSessionFactory var5;
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
var5 = this.build(parser.parse());
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException var13) {
}
}
return var5;
}
...
}
當(dāng)然,在 Spring 中自然也少不了,比如 BeanDefinitionBuilder 通過調(diào)用 getBeanDefinition)方法獲得一個 BeanDefinition 對象
public class BeanDefinitionBuilder {
...
public AbstractBeanDefinition getBeanDefinition() {
this.beanDefinition.validate();
return this.beanDefinition;
}
...
}
6. 建造者模式的優(yōu)缺點(diǎn)
6.1 建造者模式的優(yōu)點(diǎn)
- 封裝性好,創(chuàng)建和使用分離
- 擴(kuò)展性好,建造類之間獨(dú)立,一定程度上解耦
6.2 建造者模式的缺點(diǎn)
- 產(chǎn)生多余的 Builder 對象
- 產(chǎn)品內(nèi)部發(fā)生變化,建造者都要修改,成本較大
7. 建造者模式和工廠模式的區(qū)別
同過前面的學(xué)習(xí),我們已經(jīng)了解建造者模式,那么它和工廠模式有什么區(qū)別?
- 建造者模式更加注重方法的調(diào)用順序,工廠模式注重于創(chuàng)建對象
- 創(chuàng)建對象的力度不同,建造者模式創(chuàng)建復(fù)雜的對象,由各種復(fù)雜的部件組成,工廠模式創(chuàng)建出來的都一樣
- 關(guān)注重點(diǎn)不一樣,工廠模式模式只需要把對象創(chuàng)建出來就可以了,而建造者模式中不僅要創(chuàng)建出這個對象,還要知道這個對象由哪些部件組成
- 建造者模式根據(jù)建造過程中的順序不一樣,最終的對象部件組成也不一樣