Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> java/android 設計模式學習筆記(10)---建造者模式

java/android 設計模式學習筆記(10)---建造者模式

編輯:關於Android編程

這篇博客我們來介紹一下建造者模式(Builder Pattern),建造者模式又被稱為生成器模式,是創造性模式之一,與工廠方法模式和抽象工廠模式不同,後兩者的目的是為了實現多態性,而 Builder 模式的目的則是為了將對象的構建與展示分離。Builder 模式是一步一步創建一個復雜對象的創建型模式,它允許用戶在不知道內部構建細節的情況下,可以更精細地控制對象的構造流程。一個復雜的對象有大量的組成部分,比如汽車它有車輪、方向盤、發動機、以及各種各樣的小零件,要將這些部件裝配成一輛汽車,這個裝配過程無疑會復雜,對於這種情況,為了實現在構建過程中對外部隱藏具體細節,就可以使用 Builder 模式將部件和組裝過程分離,使得構建過程和部件都可以自由擴展,同時也能夠將兩者之間的耦合降到最低。

特點

將一個復雜對象的構建和它的表示分離,使得同樣的構建過程可以創建不同的表示。Builder 模式適用的使用場景:

相同的方法,不同的執行順序,產生不同的事件結果;多個部件或零件,都可以裝配到一個對象中,但是產生的運行結果又不相同時;產品類非常復雜,或者產品類中的調用順序不同產生不同的作用,這個時候使用建造者模式非常適合;當初始化一個對象特別復雜,如參數多,且很多參數都具有默認值時。在實際開發過程中,Builder 模式可以使用工廠模式或者任一其他創建型模式去生成自己的指定組件,Builder 模式也是實現 fluent interface 一種非常好的方法。

 

UML類圖

Builder 模式的 uml 類圖如下所示:
這裡寫圖片描述
有四個角色:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NClByb2R1Y3QgsvrGt8Sjv+my+sa3tcTP4LnYwOCju0J1aWxkZXIgvdO/2rvys+nP88Dguea3trL6xre1xNfpvP6jrNK7sOPKx9PJ19PA4Mq1z9a+38zltcTX6bz+uf2zzKOs0OjSqtei0uK1xMrH1eK49r3HyavU2sq1vMrKudPDuf2zzNbQv8nS1MqhwtSjrNfuteTQzbXEvs3Kx8/xIEFsZXJ0RGlhbG9nLkJ1aWxkZXIg0rvR+aOsyqHC1CBCdWlsZGVyINDpxOLA4KOsvasgQ29uY3JldGVCdWlsZGVyINC0s8nSu7j2vrLMrMTasr/A4KO7Q29uY3JlYXRlQnVpbGRlciDA4L7fzOW1xCBCdWlsZGVyIMDgo7tEaXJlY3RvciDA4M2z0rvX6dewuf2zzKOszazR+da1tcPXotLitcTKx6Os1NrP1sq1v6q3orn9s8zW0KOsRGlyZWN0b3IgvcfJq9Kyvq2zo7vhsbvKocLUo6y2+NaxvdPKudPD0ru49iBCdWlsZGVyIMC0vfjQ0LbUz/O1xNfp17CjrNXiuPYgQnVpbGRlciDNqLOjzqrBtMq9tffTw6Os0rK+zcrHyc/D5szhtb21xCA8YSBocmVmPQ=="https://en.wikipedia.org/wiki/Fluent_interface">fluent interface ,它的關鍵點是每個 setter 方法都返回自身,也就是 return this,這樣就使得 setter 方法可以鏈式調用,最典型的仍然是 AlertDialog.Builder 類,使用這種方式不僅去除了 Director 角色,使得整個結構更加簡單,也能對 Product 對象的組件過程有著更精細的控制。
據此我們可以寫出 Builder 模式的通用代碼:
Product.class

 

public class Product {

    public int partB;
    public int partA;

    public int getPartA() {
        return partA;
    }
    public void setPartA(int partA) {
        this.partA = partA;
    }

    public int getPartB() {
        return partB;
    }
    public void setPartB(int partB) {
        this.partB = partB;
    }

    @Override
    public String toString() {
        return "partA : " + partA + "   partB : " + partB;
    }
}

產品類在此聲明了兩個 setter 方法,然後是 Builder 相關類:
Builder.class

public abstract class Builder {
    public abstract void buildPartA(int partA);

    public abstract void buildPartB(int partB);

    public abstract Product build();
}

ConcreteBuilder.class

public class ConcreteBuilder extends Builder{
    private Product product = new Product();

    @Override
    public void buildPartA(int partA) {
        product.setPartA(partA);
    }

    @Override
    public void buildPartB(int partB) {
        product.setPartB(partB);
    }

    @Override
    public Product build() {
        return product;
    }
}

Builder 這兩個類用來封裝對 Product 屬性的設置,最後在 build 方法中返回設置完屬性的 Product 對象,最後是 Director 角色:
Director.class

public class Director {
    private Builder builder;

    public Director(Builder builder) {
        this.builder = builder;
    }

    public void construct(int partA, int partB) {
        builder.buildPartA(partA);
        builder.buildPartB(partB);
    }
}

封裝了 Builder 對象,最後是測試程序:

Builder builder = new ConcreteBuilder();
Director director = new Director(builder);
director.construct(1, 2);
Product product = builder.build();
Log.e("shawn", product.toString());
break;

運行結果

com.android.builderpattern E/shawn: partA : 1   partB : 2

代碼一目了然,這裡需要提到的一點是針對不同的產品可以去構建不同的 ConcreteBuilder 類,使得一個 ConcreteBuilder 類對應一個 Product 類,這點和工廠方法模式很類似,我們後面也會介紹到他們兩者之間的區別。

示例與源碼

Builder 模式在實際開發中出現和使用的頻率也是很高的,比如上面提到的 AlertDialog.Builder ,還比如非常有名的第三方開源框架 Universal-Image-Loader 庫中的 ImageLoaderConfig ,他們都是使用的靜態內部 Builder 類。
這裡的 demo 也使用最簡單的內部靜態 Builder 類去實現,精簡完之後只有 ConcreteBuilder 和 Product 角色,並且使用鏈式調用去實現上面提到的 fluent interface:
Computer.class

public class Computer {
    private String CPU;
    private String GPU;
    private String memoryType;
    private int memorySize;
    private String storageType;
    private int storageSize;
    private String screenType;
    private float screenSize;
    private String OSType;

    public static class Builder {
        // Optional parameters - initialize with default values
        private String CPU = "inter-i3";
        private String GPU = "GTX-960";
        private String memoryType = "ddr3 1666MHz";
        private int memorySize = 8;//8GB
        private String storageType = "hdd";
        private int storageSize = 1024;//1TB
        private String screenType = "IPS";
        private float screenSize = 23.8f;
        private String OSType = "Windows 10";

        public Builder() {
        }

        public Builder setCPU(String CPU) {
            this.CPU = CPU;
            return this;
        }

        public Builder setGPU(String GPU) {
            this.GPU = GPU;
            return this;
        }
        public Builder setMemoryType(String memoryType) {
            this.memoryType = memoryType;
            return this;
        }

        public Builder setMemorySize(int memorySize) {
            this.memorySize = memorySize;
            return this;
        }
        public Builder setStorageType(String storageType) {
            this.storageType = storageType;
            return this;
        }

        public Builder setStorageSize(int storageSize) {
            this.storageSize = storageSize;
            return this;
        }

        public Builder setScreenType(String screenType) {
            this.screenType = screenType;
            return this;
        }
        public Builder setScreenSize(float screenSize) {
            this.screenSize = screenSize;
            return this;
        }

        public Builder setOSType(String OSType) {
            this.OSType = OSType;
            return this;
        }


        public Computer create() {
            return new Computer(this);
        }

    }

    private Computer(Builder builder) {
        CPU = builder.CPU;
        GPU = builder.GPU;
        memoryType = builder.memoryType;
        memorySize = builder.memorySize;
        storageType = builder.storageType;
        storageSize = builder.storageSize;
        screenType = builder.screenType;
        screenSize = builder.screenSize;
        OSType = builder.OSType;
    }
}

Computer 為產品類,它有一個 Builder 的靜態內部類用於設置相關屬性,測試代碼:

Computer computer = new Computer.Builder()
        .setCPU("inter-skylake-i7")
        .setGPU("GTX-Titan")
        .setMemoryType("ddr4-2133MHz")
        .setMemorySize(16)
        .setStorageType("ssd")
        .setStorageSize(512)
        .setScreenType("IPS")
        .setScreenSize(28)
        .setOSType("Ubuntu/Window10")
        .create();

這裡需要提到的關鍵點是關於相關屬性的默認值問題:

對於必要的屬性值,無法給出其默認值的最好是通過 Builder 類的構造函數傳入,比如 AlertDialog.Builder 類的 Context,這樣也能防止使用時的疏忽;對於非必要屬性來說,最好是為其生成一個默認的屬性值,這樣使用者只用設置需要更改的屬性即可;每個 setter 函數都加上 return this 用來實現優美的 fluent interface 設計。

 

總結

Builder 模式在 Android 開發中也很常用,通常作為配置類的構建器將配置的構建和表示分離開來,同時也將配置從目標類中隔離開來,避免了過多的 setter 方法。Builder 模式比較常見的實現形式是通過調用鏈實現,這樣的方式也會使得代碼更加簡潔和易懂,而且同時也可以避免了目標類被過多的接口“污染”。
Builder 模式的優點:

將一個復雜對象的創建過程封裝起來,使得客戶端不必知道產品內部組成的細節;允許對象通過多個步驟來創建,並且可以改變過程和選擇需要的過程;產品的實現可以被替換,因為客戶端只看到一個抽象的接口;創建者獨立,容易擴展。Builder 模式缺點:會產生多余的 Builder 對象以及 Director 對象,消耗內存;與工廠模式相比,采用 Builder 模式創建對象的客戶,需要具備更多的領域知識。

 

Builder 模式 VS 工廠方法模式

Builder 模式和工廠方法模式都是屬於創建型模式,他們有一些共同點:這兩種設計模式的都將一個產品類對象的創建過程封裝起來,讓客戶端從具體產品類的生成中解耦,不必了解產品類構造的細節。但是其實他們兩種設計模式還是有很多不同點:

Builder 模式允許對象的創建通過多個步驟來創建,而且可以改變這個過程,也可以選擇需要改變的屬性;工廠方法模式不一樣,它只有一個步驟,也就無法改變這個過程,更加無法選擇性改變屬性了;Builder 模式的目的是將復雜對象的構建和它的表示分離;而工廠方法模式則是定義一個創建對象的接口,由子類決定要實例化的類是哪一個,將實例化推遲到子類;最明顯的當然還是代碼的差異,Builder 模式中客戶端可以調用 set 方法,而工廠方法模式只能調用工廠類提供的相關方法。其次是 uml 類圖的差異:
Builder 模式 uml 類圖:
這裡寫圖片描述
注:Director 類和 Builder 虛擬類可以被精簡。
工廠方法模式 uml 類圖:
這裡寫圖片描述
uml 類圖的相似性還是很高的,所以通常我們會根據實際表現和用途來區別 Buidler 模式和工廠方法模式(這點和裝飾者模式與保護代理模式的區別類似,要從實際表現與使用的目的區別)。

 

源碼下載

https://github.com/zhaozepeng/Design-Patterns/tree/master/BuilderPattern

 
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved