我要打十個!詳解建造者模式(builder pattern)


前言

“我要打十個”,其實是我要打十個野怪!

這十個野怪呢,它們有不同的技能、裝備和武器,長得也不一樣。這里野怪是一個蠻復雜的對象,由各個不同的部分組成(技能、裝備、武器等),不同的野怪的它們各部分之間的構成方式就不同。因此,要創建這種復雜對象,就需要使用建造者模式。

什么是建造者模式

首先建造者模式Gof 23種設計模式之一。也叫Builder模式。

是將一個復雜對象的構建和其表示相分離,使得同樣的構建過程可以創建不同的表示。

我們來品一品這句話,首先是復雜對象,這個復雜對象中可能包含了多個不同的其他對象。其次是這個復雜對象的創建一定是用到了這些其他對象,通過一定的算法組合能才創建出這個對象。最后就是它能通過builder創建出一些特性不同但相似的對象。

好了,借用Linus 名言:

Talk is cheap. Show me the code!!!

表情包圖

代碼實現

開始創建我們的野怪類,就叫做Hero吧,它的組成部分有技能類Skill,裝備類Armor 和武器類Weapon 。

創建Skill、Armor和Weapon類

public class Skill {
    private String skillName;

    public Skill(String skillName) {
        this.skillName = skillName;
    }

    @Override
    public String toString() {
        return "Skill{" +
                "skillName='" + skillName + '\'' +
                '}';
    }
}

public class Armor {
    private String armorName;

    public Armor(String armorName) {
        this.armorName = armorName;
    }

    @Override
    public String toString() {
        return "Armor{" +
                "armorName='" + armorName + '\'' +
                '}';
    }
}

public class Weapon {
    private String weaponName;

    public Weapon(String weaponName) {
        this.weaponName = weaponName;
    }

    @Override
    public String toString() {
        return "Weapon{" +
                "weaponName='" + weaponName + '\'' +
                '}';
    }
}

創建Hero類,在該類中,我們使用靜態內部類的方式構建了Builder類,就是我們使用Builder類幫助我們創建對象。

忘了啥是內部類的,可以移駕下面這篇復習下。

Java內部類超詳細總結(含代碼示例)

public class Hero {
    private final String name;
    private final Skill skill;
    private final Armor armor;
    private final Weapon weapon;

    private Hero(Builder builder){
        this.name = builder.name;
        this.skill = builder.skill;
        this.armor = builder.armor;
        this.weapon = builder.weapon;
    }

    @Override
    public String toString() {
        return "Hero{" +
                "name='" + name + '\'' +
                ", skill=" + skill +
                ", armor=" + armor +
                ", weapon=" + weapon +
                '}';
    }

    public static class Builder{
        private final String name;
        private Skill skill;
        private Armor armor;
        private Weapon weapon;

        public Builder(String name){
            this.name = name;
        }

        public Builder withSkill(Skill skill){
            this.skill = skill;
            return this;
        }

        public Builder withArmor(Armor armor){
            this.armor = armor;
            return this;
        }

        public Builder withWeapon(Weapon weapon){
            this.weapon = weapon;
            return this;
        }

        public Hero build(){
            return new Hero(this);
        }
    }
}

好了,我們的builder模式的核心代碼已經晚了,其實關鍵的就是Builder類,我們創建復雜對象就是通過Builder類封裝了創建的細節,同時,Builder提供了一些公共方法,可以定制這些復雜對象的創建過程。

新建個測試類AppMain,測試一把。

public class AppMain {
    public static void main(String[] args) {
        Hero hero = new Hero.Builder("納什男爵")
                .withSkill(new Skill("飛龍在天"))
                .withArmor(new Armor("亢龍鎧甲"))
                .withWeapon(new Weapon("唾沫星子"))
                .build();

        System.out.println(hero);
    }
}

結果如下:

Hero{name='納什男爵', skill=Skill{skillName='飛龍在天'}, armor=Armor{armorName='亢龍鎧甲'}, weapon=Weapon{weaponName='唾沫星子'}}

當然了,這里也可以創建個“四鳥”,“河蟹”之類的。總之,你要打十個,么有問題啊,我們給你builder十個就好了,而且是不重樣的。

在這里插入圖片描述

Builder模式在源碼中的應用

StringBuilder

其實我們熟知的StringBuilder就是builder模式的典型實現。我們平時使用基本都是這樣:

        StringBuilder sb = new StringBuilder();
        sb.append(123).append('a')
                .append(1.23)
                .append(true)
                .append("hhhh");

看着就很平常,soeasy的感覺,其實可以看到它能添加不同的數據類型進去,對應建造者模式中的各個部分,通過append方法的不同組合構建出了不同的StringBuilder對象。

看下源碼:

    ......
    
    @Override
    public StringBuilder append(boolean b) {
        super.append(b);
        return this;
    }

    @Override
    public StringBuilder append(char c) {
        super.append(c);
        return this;
    }
    ......

上面列舉了兩個重載方法,可以看到其實是調用了父類的重載方法,父類是AbstractStringBuilder

	// 這里只列舉這一個父類的方法
    public AbstractStringBuilder append(boolean b) {
        if (b) {
            ensureCapacityInternal(count + 4);
            value[count++] = 't';
            value[count++] = 'r';
            value[count++] = 'u';
            value[count++] = 'e';
        } else {
            ensureCapacityInternal(count + 5);
            value[count++] = 'f';
            value[count++] = 'a';
            value[count++] = 'l';
            value[count++] = 's';
            value[count++] = 'e';
        }
        return this;
    }

Mybatis中的builder模式

Mybatis中的SqlSessionFactoryBuilder、XMLMapperBuilder、XMLStatementBuilder等都使用了builder模式。

這里簡單看下SqlSessionFactoryBuilder

在這里插入圖片描述

所有的build重載方法都在構建SqlSessionFactory 對象。只是可以根據需要調用不同的方法,傳入不同的參數,就可以構建出特性不同的SqlSessionFactory 。

看下其中一個build方法的源碼

    ......
    
    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;
    }
    ......

而且可以看到,這個方法中又使用了一個XMLConfigBuilder 。

Builder模式的使用場景

下面再總結一下builder模式的使用場景吧。

  • 創建復雜對象的算法應該獨立於組成對象的部件及其組裝方式。
  • 構造對象的過程允許所構造的對象的不同表示。

設計模式往期回顧

Java面試必備:手寫單例模式

工廠模式超詳解(代碼示例)

設計模式之原型模式

設計模式之代理模式

設計模式之委派模式,大名鼎鼎的Spring都在用

公眾號:二營長的筆記
免費領資料:公眾號內回復“二營長”


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM