人人都會設計模式---建造者模式--Builder

本節課程大綱

PS:轉載請註明出處 做者: TigerChain
地址: www.jianshu.com/p/300cbb9ee…
本文出自 TigerChain 簡書 人人都會設計模式java

教程簡介android

  • 一、閱讀對象 本篇教程適合新手閱讀,老手直接略過
  • 二、教程難度 初級,本人水平有限,文章內容不免會出現問題,若是有問題歡迎指出,謝謝
  • 三、Demo 地址:github.com/githubchen0…

正文git

1、什麼是建造者模式

一、生活中的建造者模式

一、蓋房子程序員

咱們在生活中蓋房子,通常就是打地基,蓋框架「用磚頭或鋼筋混凝土」,而後是粉刷。基本上就是這個路子。固然咱們這些工做所有能夠本身作,可也以找幾個工人去幹,固然還能夠能夠直接找一個設計師,直接說我就要這樣的房子,而後就無論了,最後問設計師「設計師給一張紙給工人,工人就啪啪的幹了」驗收房子便可「至於你是如何建的過程我不關心,我只要結果」---這就是建造者模式github

二、組裝電腦設計模式

咱們買的電腦都是由主板、內存、cpu、顯卡等組成,如何把這些東西組裝起來給用戶這就是建造者模式的做用,不一樣的人對電腦的配置需求不同的「打遊戲的對顯卡要求高」,可是電腦構成部件是固定的,咱們找電腦城的裝機人員把電腦裝起來這一過程就是建造模式app

三、軟件開發框架

咱們開發一款產品,須要技術主管、產品經理、苦逼的程序員。在這裏,產品經理就是指揮者「Director」和客戶溝通,瞭解產品需求,技術主管是抽象的建造者[Builder],讓猿們雜作就雜作,而程序員就是體力勞動者「即具體的建造者,按照技術主管下發的任務去作」--- 這就是一個接近完美的建造者模式「爲何說接近呢?由於沒有百分之百,靠:又忘記吃藥了」ide

二、程序中的建造者模式

建造者模式的定義函數

將一個複雜對象的構建與它的表示分離,使得一樣的構建過程能夠建立不一樣的表示,這是官方定義,通俗的說就是:建造者模式就是如何一步步構建一個包含多個組成部件的對象,相同的構建過程能夠建立不一樣的產品

建造者模式的特色

建造者模式是一種建立型模式,適用於那些流程固定「順序不必定固定」,建造的目標對象會有所改變這種場景「好比畫一條狗,這個目標不變,可是不一樣的是有黃狗,胖狗,瘦狗等」,還有一種場景是代替多參數構造器

建造者模式的做用

  • 一、用戶不知道對象的建造過程和細節就能夠建立出複雜的對象「屏蔽了建造的具體細節」
  • 二、用戶只需給出複雜對象的內容和類型能夠建立出對象
  • 三、建造者模工按流程一步步的建立出複雜對象

建造者模式的結構

角色 類別 說明
Builder 接口或抽象類 抽象的建造者,不是必須的
ConcreateBuilder 具體的建造者 能夠有多個「由於每一個建造風格可能不同」
Product 普通的類 具體的產品「即被建造的對象」
Director 導演也叫指揮者 統一指揮建造者去建造目標,導演不是必須的

建造者模式簡單的 UML

建造者模式簡單的 UML

2、建造者模式的舉例

一、組裝電腦

小明想組裝一個臺式電腦,小明對電腦配置一竅不通,就直接跑到電腦城給裝機老闆說我要一臺打遊戲很是爽的電腦,麻煩你給裝一下「配置什麼的你給我推薦一下吧」,因而老闆就讓它的員工「小美」按小明的要求裝了一個性能灰常牛 B 的電腦,1 個小時後電腦裝好了,小明交錢拿電腦走人。不一下子小張又來了,要一個知足平時寫文章就能夠的電腦,老闆針對小張的要求給不一樣的裝機配置。不一樣的人有不一樣的配置方案「可是裝機流程是同樣的」,這就是一個典型的建造者模式

組裝電腦簡單的 UML

組裝電腦簡單的 UML

根據 UML 擼碼

  • 一、建立被建造的對象電腦 --- Computer.java
/** * Created by TigerChain * 產品類--被建造的對象 */
public class Computer {
    private String cpu ; // cpu
    private String hardDisk ; //硬盤
    private String mainBoard ; // 主板
    private String memory ; // 內存
    ... 省略 getter 和 setter
}
複製代碼
  • 二、抽象的建造者 --- Builder.java
/** * Created by TigerChain * 抽象的建造者,即裝電腦的步驟 * 至於安裝什麼型號的主板,不是我關心,而是具體的建造者關心的 */
public interface Builder {
    // 安裝主板
    void createMainBoard(String mainBoard) ;
    // 安裝 cpu
    void createCpu(String cpu) ;
    // 安裝硬盤
    void createhardDisk(String hardDisk) ;
    // 安裝內存
    void createMemory(String memory) ;
    // 組成電腦
    Computer createComputer() ;
}

複製代碼
  • 三、具體建造者,也就是裝機工人小美 --- AssemblerBuilder.java
/** * Created by TigerChain * 具體的建造者,這裏是商場的一個裝機人員 */
public class AssemblerBuilder implements Builder {

    private Computer computer = new Computer() ;
    @Override
    public void createCpu(String cpu) {
        computer.setCpu(cpu);
    }

    @Override
    public void createhardDisk(String hardDisk) {
        computer.setHardDisk(hardDisk);
    }

    @Override
    public void createMainBoard(String mainBoard) {
        computer.setMainBoard(mainBoard);
    }

    @Override
    public void createMemory(String memory) {
        computer.setMemory(memory);
    }

    @Override
    public Computer createComputer() {
        return computer;
    }
}
複製代碼
  • 四、還有老闆「"指手畫腳的人"」安排裝機工工做 --- Direcror.java
/** * Created by TigerChain * 聲明一個導演類「指揮者,這裏能夠裝電腦的老闆」,用來指揮組裝過程,也就是組裝電腦的流程 */
public class Director {
    private Builder builder ;
	// 使用多態,裝機工很是多,我管你小美,小蘭,小豬,我通通收了
    public Direcror(Builder builder){
        this.builder = builder ;
    }
	// 老闆最後只想看到裝成的成品---要交到客戶手中
    public Computer createComputer(String cpu,String hardDisk,String mainBoard,String memory){
        // 具體的工做是裝機工去作
        this.builder.createMainBoard(mainBoard);
        this.builder.createCpu(cpu) ;
        this.builder.createMemory(memory);
        this.builder.createhardDisk(hardDisk);
        return this.builder.createComputer() ;
    }
}
複製代碼
  • 五、測試類
/** * Created by TigerChain * 測試類 */
public class Test {
  public static void main(String args[]){
	  // 裝機員小美
      Builder builder = new AssemblerBuilder() ;
      // 老闆把小明的需求轉給小美
      Direcror direcror = new Direcror(builder) ;
      // 老闆最後拿到成品機子,工做全由小美去作
      Computer computer = direcror.createComputer("Intel 酷睿i9 7900X","三星M9T 2TB (HN-M201RAD)","技嘉AORUS Z270X-Gaming 7","科賦Cras II 紅燈 16GB DDR4 3000") ;
      System.out.println("小明這臺電腦使用的是:\n"+computer.getMainBoard()+" 主板\n"+computer.getCpu()+" CPU\n"+computer.getHardDisk()+"硬盤\n"+computer.getMainBoard()+" 內存\n");

  }
}
複製代碼
  • 六、運行查看結果

裝機結果

怎麼樣,至於小張,小豬要裝機把本身要的配置給老闆便可,而後老闆如何裝機不用你管,你就等着收裝好的機子吧

二、蓋房子

蓋房子的基本步驟和流程是固定的無非就是打地基、蓋框架、而後澆築「至於蓋平房、仍是樓房那是每一個客戶的具體需求」。整體來講蓋房子以有以三種方式:

  • 一、本身蓋房子「沒有辦法有的人就是牛 B ,本身設計,本身動手,固然這屬於小房子,你讓一我的蓋個32 層讓我看看」
  • 二、想蓋房子的人是一個包工頭,本身找一幫工人本身就把房子搞定了
  • 三、想蓋房子的人就是一個普通人,啥也不會,找一個設計師說「我就要蓋個房子,南北通透,四秀常春」,設計師說沒有問題,設計師把設計出來的圖紙扔給包工頭說:「就照這個樣子蓋」,包工頭拿着圖紙給工人們分工派活,最後完工

蓋房子建造者模式簡單的 UML

蓋房子建造者模式簡單的 UML

根據 UML 擼碼

  • 一、房子對象 House.java
/** * Created by TigerChain * 最終的產品---房子 */
public class House {
    // 打地基
    private String foundation ;
    // 蓋框架
    private String frame ;
    // 澆築
    private String pouring ;
    ... 省略 setter 和 getter 
}
複製代碼
  • 二、抽象建造者「包工頭」 HouseBuilder.java
public interface HouseBuilder {
    // 打地基
    void doFoundation() ;
    // 蓋框架
    void doFrame() ;
    // 澆灌
    void dpPouring() ;
    // 房子建成 
    House getHouse() ;
}
複製代碼
  • 三、具體建造者「工人」--蓋平房 PingFangBuilder.java
/** * Created by TigerChain * 蓋平房 */
public class PingFangBuilder implements HouseBuilder {

    private House house = new House() ;

    @Override
    public void doFoundation() {
        house.setFoundation("蓋平房的地基");
    }

    @Override
    public void doFrame() {
        house.setFrame("蓋平房的框架");
    }

    @Override
    public void dpPouring() {
        house.setPouring("蓋平房不用澆灌,直接人工手刷就能夠");
    }

    @Override
    public House getHouse() {
        return house;
    }
}
複製代碼
  • 四、具體建造者「工人」--蓋樓房 LouFangBuilder.java
/** * Created by TigerChain * 蓋樓房 */
public class LouFangBuilder implements HouseBuilder {

    private House house = new House() ;
    @Override
    public void doFoundation() {
        house.setFoundation("蓋樓房的地基就打十米深");
    }

    @Override
    public void doFrame() {
        house.setFrame("樓房的框架要使用很是堅固鋼筋混凝土");
    }

    @Override
    public void dpPouring() {
        house.setPouring("樓房拿個罐車把框架拿混凝土灌滿便可");
    }

    @Override
    public House getHouse() {
        return house;
    }
}
複製代碼
  • 五、指揮者「設計師」 HouseDirector.java
/** * Created by TigerChain * 設計師 */
public class HouseDirector {
    // 指揮包工頭
    public void buildHouse(HouseBuilder houseBuilder){
        houseBuilder.doFoundation();
        houseBuilder.doFrame();
        houseBuilder.dpPouring();
    }
}

複製代碼
  • 六、測試一下 Test.java
/** * Created by TigerChain * 測試 */
public class Test {
    public static void main(String args[]){

        // 方式1、客戶本身蓋房子,親力親爲
        System.out.println("========客戶本身建房子,必須知道蓋房的細節========");
        House house = new House() ;
        house.setFoundation("用戶本身建造房子:打地基");
        house.setFrame("用戶本身建造房子:蓋框架");
        house.setPouring("用戶本身建造房子:澆築");

        System.out.println(house.getFoundation());
        System.out.println(house.getFrame());
        System.out.println(house.getPouring());


        // 方式2、客戶找一個建造者蓋房子「充當包工頭角色」,可是要知道如何蓋房子「調用建造者蓋房子的順序」
        System.out.println("========客戶直接找蓋房子的工人「建造者」,客戶要調用建造者方法去蓋房子,客戶必須得知道房子如何造========");

        HouseBuilder houseBuilder = new PingFangBuilder() ;
        houseBuilder.doFoundation();
        houseBuilder.doFrame();
        houseBuilder.dpPouring();
        House house1 = houseBuilder.getHouse() ;
        System.out.println(house1.getFoundation());
        System.out.println(house1.getFrame());
        System.out.println(house1.getPouring());

        // 方式3、使用建造者模式,找一個設計師「設計師拉一幫建造者去幹活」,告訴他我想要什麼樣的房子,最後客戶只問設計師要房子便可
        System.out.println("========客戶直接找一個設計師,設計師統一指揮建造者蓋房子,房子雜蓋,客戶不關心,最後只是找設計師要房子便可========");

        HouseBuilder pingFangBuilder = new PingFangBuilder() ;
        HouseDirector houseDirector = new HouseDirector() ;
        houseDirector.buildHouse(pingFangBuilder);
        House houseCreateByBuilder = pingFangBuilder.getHouse() ;

        System.out.println(houseCreateByBuilder.getFoundation());
        System.out.println(houseCreateByBuilder.getFrame());
        System.out.println(houseCreateByBuilder.getPouring());
    }
}

複製代碼

咱們對比了三種方式,本身蓋房子,找工人蓋房子,找設計師蓋房子來逐步感覺一下建造者模式的優勢

  • 六、運行查看結果

蓋房子結果

能夠看到最後一種最舒服,蓋房子的時候直接外包給設計師本身就不用管了,到時候問設計師要建好的成品房子便可,這樣對客戶來講具體如何蓋房子我不須要知道,屏蔽細節「只能說有錢就是任性」

三、替代多參數構造函數的建造者模式,以組裝電腦爲例子

前面咱們說了在建造者模式中 Director 不是必須的,Director 的做用不是構造產品「建造產品是建造者的事情」而是指揮協調建造的步驟「當有一個新的建造者的時候直接實現抽象建造者,而不用關心具體的執行步驟,這就是 Director 乾的事情」,咱們直接看代碼吧

  • 一、原始的 Computer.java
public class Computer {
    private String mainBoard ;     // 主板
    private String cpu ;           // cpu
    private String hd ;            // 硬盤
    private String powerSupplier ; // 電源
    private String graphicsCard;   // 顯卡

    // 其它一些可選配置
    private String mouse ; // 鼠標
    private String computerCase ; //機箱
    private String mousePad ;   //鼠標墊
    private String other ;  //其它配件


    public Computer(String mainBoard,String cpu,String hd,String powerSupplier, String graphicsCard,String mouse,String computerCase,String mousePad,String other){
        this.mainBoard = mainBoard ;
        this.cpu = cpu ;
        this.hd = hd ;
        this.powerSupplier = powerSupplier ;
        this.graphicsCard = graphicsCard ;
        this.mouse = mouse ;
        this.computerCase = computerCase ;
        this.mousePad = mousePad ;
        this.other = other ;
    }

    public Computer(String mainBoard,String cpu,String hd,String powerSupplier, String graphicsCard,String mouse,String computerCase,String mousePad){
        this.mainBoard = mainBoard ;
        this.cpu = cpu ;
        this.hd = hd ;
        this.powerSupplier = powerSupplier ;
        this.graphicsCard = graphicsCard ;
        this.mouse = mouse ;
        this.computerCase = computerCase ;
        this.mousePad = mousePad ;
    }
    ... 省略其它的構造方法和 setter 和 getter 方法
}
複製代碼

若是咱們想要調用這個類就得在構參數方法中傳遞「無數個參數」「若是有的參是一些可選項,咱們還得重寫構造方法」,要麼就要調用多個 setter 方法,才能給一個對象賦值,方法雖然可行,可是也太扯淡了「誰能記住那些參數呀」,那麼建造者模式能夠解決多參數構造方法來建造對象

  • 二、使用建造者創建 ComputerB.java
/** * Created by TigerChain * 替代多參構造方法--建造者模式 */
public class ComputerB {

    private String mainBoard ;     // 主板
    private String cpu ;           // cpu
    private String hd ;            // 硬盤
    private String powerSupplier ; // 電源
    private String graphicsCard;   // 顯卡

    // 其它一些可選配置
    private String mouse ; // 鼠標
    private String computerCase ; //機箱
    private String mousePad ;   //鼠標墊
    private String other ;  //其它配件

	// ComputerB 本身充當 Director 
    private ComputerB(ComputerBuilder builder) {
        this.mainBoard = builder.mainBoard ;
        this.cpu = builder.cpu ;
        this.hd = builder.hd ;
        this.powerSupplier = builder.powerSupplier ;
        this.graphicsCard = builder.graphicsCard ;

        this.mouse = builder.mouse ;
        this.computerCase = builder.computerCase ;
        this.mousePad = builder.mousePad ;
        this.other = builder.other ;
    }
	// 聲明一個靜態內存類 Builder
    public static class ComputerBuilder{
        // 一個電腦的必須配置
        private String mainBoard ;     // 主板
        private String cpu ;           // cpu
        private String hd ;            // 硬盤
        private String powerSupplier ; // 電源
        private String graphicsCard;   // 顯卡

        // 其它一些可選配置
        private String mouse ; // 鼠標
        private String computerCase ; //機箱
        private String mousePad ;   //鼠標墊
        private String other ;  //其它配件

		// 這裏聲明一些必需要傳的參數「規定這些參數是必須傳的,這裏只是舉例,再實中可能參數都是可選的」
        public ComputerBuilder(String mainBoard,String cpu,String hd,String powerSupplier,String graphicsCard){
            this.mainBoard = mainBoard ;
            this.cpu = cpu ;
            this.hd = hd ;
            this.powerSupplier = powerSupplier ;
            this.graphicsCard = graphicsCard ;
        }

        public ComputerBuilder setMainBoard(String mainBoard) {
            this.mainBoard = mainBoard;
            return this ;
        }

        public ComputerBuilder setCpu(String cpu) {
            this.cpu = cpu;
            return this ;
        }

       	... 其它的一些 setXXX() 方法
		
		// 生成最終的產品
        public ComputerB build(){
            return new ComputerB(this) ;
        }
    }
}
複製代碼

代碼註釋很是詳細,乍一看好像和建造者模式沒有毛關係,可是咱們細細一分析這個確實是一個建造者模式,咱們看一看:產品是-->ComputerB,具體的建造者是一個靜態內存類-->ComputerBuilder,可是沒有抽象的建造者和指揮者「其實 ComputerB 充當的就是指揮者的角色」,咱們說過建造者模式中指揮者和抽象建造者都不是必須的,因此這是一個典型的建造者模式

  • 三、如何調用來個測試類 Test.java
public class Test {
    public static void main(String args[]){

        // 不使用建造者模式
        Computer computer = new Computer("主板","cpu","hd","電源","顯卡"
        ,"鼠標","機箱","鼠標墊") ;
        System.out.println("使用普通的構造方法組裝電腦:"+computer.toString());

        // 使用建造者模式
        ComputerB computerB = new ComputerB.ComputerBuilder("主板","cpu","hd","電源","顯卡")
                .setMouse("鼠標").setMousePad("墊子").build() ;
        System.out.println("使用建造者模式組裝電腦:"+computerB.toString());
    }
}
複製代碼

咱們分別使用普通構造方法「調用者能吐血」和建造者模式組裝電腦,能夠看到建造者模式調用 new ComputerB.ComputerBuilder(xxx).setxxx().setxxx().build() 調用方法直接打點調用「也叫流式調用,這樣調用方便多了,想點那個就點那個」,若是使用過 rx 的話會很是有感受

  • 四、運行查看一下結果

替代多參數構造方法的的建造者模式結果

若是在之後若是遇到多參數構造對象的時候不仿考慮使用建造者模式

3、Android 源碼中的建造者模式

一、AlertDialog

作 Android 的朋友必定不會對 AlertDialog 陌生,它是一個能夠添加列表、單選列表、文本輸入框,多選列表等彈窗組件,內部使用的是典型的建造者模式,咱們看看 AlertDialog 的基本使用方法

// 建立構建器
   AlertDialog.Builder builder = new AlertDialog.Builder(this);
// 使用建造者模式代替多參構造函數
   Dialog dialog= builder.setTitle(XXX).setIcon(XXX).setXXX(xxx).create() ;
   dialog.show() ;
複製代碼

AlertDialog 簡單的 UML

AlertDialog 簡單的 UML

AlertDialog 核心代碼剝離

AlertDialog 核心代碼剝離

上圖明顯的顯示出了 AlertDialog 的建造者模式「AlertDialog.Builder 同時扮演了 Builder、ConcreateBuilder、Director 等角色」

二、Notification 的 setLatestEventInfo 方法「過期了,可是思想能夠學習」

咱們看看 Notification 的 setLatestEventInfo 一看便知道使用的是建造者模式,咱們看下圖

setLatestEventInfo

以上的方法被 Notification.Builder 替代了「setLatestEventInfo 從終也是調用 Notification.Builder」,真正的建造者模式是 Notification.Builder

若是要支持到低版本可使用 android.support.v4.app.NotificationCompat.Builder 來建立 Notification 名字一看又是一個建造者模式,感興趣的能夠看看 NotificationCompat.Builder 的源碼

三、AnimatorSet.Builder

AnimatorSet 用做將一個動畫集合按選定的順序執行,咱們可使用 AnimatorSet.Builder 添加播放動畫順序「這只是其中一個方法」

使用方法舉例

AnimatorSet animSet = new AnimatorSet();  
//AnimatorSet.Builder 不能直接創建 ,只能經過 play(Animation)
AnimatorSet.Builder builder = animSet.play(anim2);  
builder.with(anim3).after(anim1).before(anim4);// anim1先執行,而後再同步執行anim二、anim3,最後執行anim4 
animSet.setDuration(200);  
animSet.start();  
複製代碼

核心代碼

AnimatorSet.Builder 部分代碼

4、建造者模式的優缺點

優勢

  • 一、使建立產品的步驟「把建立產品步驟放在不一樣的方法中,更加清晰直觀」和產品自己分離,即便用相同的建立過程要吧建立出不一樣的產品
  • 二、每一個建造者都是獨立的互不影響,這樣就達到解耦的目的,因此若是想要替換現有的建造者那很是方便,添加一個實現便可。

缺點

  • 一、只適用於產品具備相同的特色「過程和步驟」,若是產品之間差別很是大,則不適用「使用範圍受限」
  • 二、萬一那天產品內部發生改變,那多個建造者都要修改,成本太大

5、建造者模式的使用場景

  • 一、若是一個對象有很是複雜的內部結構「這些產品一般有不少屬性」,那麼使用建造者模式
  • 二、若是想把複雜對象的建立和使用分離開來,那麼使用建造者模式「使用相同的建立步驟能夠建立不一樣的產品」

6、建造者模式 VS 簡單工廠模式

類似點

它們都屬於建立型模式「都是建立產品的」

區別

  • 一、建立對象的粒度不一樣

工廠模式建立的對象都是一個鳥樣子,而建造者模式建立的是一個複合產品,由各個複雜的部件組成,部件不一樣所構成的產品也不一樣

  • 二、關注點不一樣:

工廠模式注重只要把這個對象建立出來就 o 了「不關心這個產品的組成部分」,而建造者模式不似要創造出這個產品,還有知道這個產品的組成部分

到此爲止,咱們就介紹完了建造者模式,必定要動手試一下,關注博主有更多精彩的文章等着你,順便伸出你的小手點一個贊吧

公衆號:TigerChain 歡迎你們關注

TigerChain
相關文章
相關標籤/搜索