[01][01][06] 建造者模式詳解

[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)圖:


image

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)用大家就會明白.接下來,我們再來看一下類圖的變化∶


image

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ù)建造過程中的順序不一樣,最終的對象部件組成也不一樣
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市俄烁,隨后出現(xiàn)的幾起案子西设,更是在濱河造成了極大的恐慌柳譬,老刑警劉巖群扶,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異笨使,居然都是意外死亡卿樱,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門硫椰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來繁调,“玉大人萨蚕,你說我怎么就攤上這事∩婺伲” “怎么了门岔?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長烤送。 經(jīng)常有香客問我寒随,道長,這世上最難降的妖魔是什么帮坚? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任妻往,我火速辦了婚禮,結(jié)果婚禮上试和,老公的妹妹穿的比我還像新娘讯泣。我一直安慰自己,他們只是感情好阅悍,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布好渠。 她就那樣靜靜地躺著,像睡著了一般节视。 火紅的嫁衣襯著肌膚如雪拳锚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天寻行,我揣著相機(jī)與錄音霍掺,去河邊找鬼。 笑死拌蜘,一個胖子當(dāng)著我的面吹牛杆烁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播简卧,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼兔魂,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了举娩?” 一聲冷哼從身側(cè)響起入热,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎晓铆,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绰播,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡骄噪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蠢箩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片链蕊。...
    茶點(diǎn)故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡事甜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出滔韵,到底是詐尸還是另有隱情逻谦,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布陪蜻,位于F島的核電站邦马,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏宴卖。R本人自食惡果不足惜滋将,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦决采、人聲如沸惕鼓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春魏滚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背医寿。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工栏赴, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人靖秩。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓须眷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親沟突。 傳聞我的和親對象是個殘疾皇子花颗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評論 2 355