重學設計模式之建造者模式

建造者模式

定義

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

首先這是一個復雜的對象,也就是說對象的創建過程比較復雜,可能需要N多個組件來完成整個對象的創建,這個時候為了符合“單一職責”,我們將對象的構建過程分離出來,通過建造者來完成對象組件的創建,再通過一個指揮者類完成組裝生成一個完整的產品。

UML圖

四要素

根據UML圖可知道,建造者模式有4個非常重要的角色

  • Builder:一個抽象接口,用于規范子類建造者的創建產品對象的各個組件。
  • ConcreteBuilder: 實現抽象類的所有未實現的方法,不同的建造者有不同的構建產品組件的方法。
  • Director:指揮者類,負責完成整個產品的組裝,不同的建造者創建一個產品不同的組件,通過指揮者類將整個產品組裝完成。
  • Product:產品類,一般是一個比較復雜的對象,也就是說這個產品的創建過程比較復雜。


下面還是通過一個實例來認識一下建造者模式

實例

一軟件公司欲開發一個音樂播放軟件,為了給用戶使用提供方便,該播放軟件提供多種界面顯示模式,如完整模式、精簡模式、記憶模式、網絡模式等。在不同的顯示模式下主界面的組成元素有所差異,如在完整模式下將顯示菜單、播放列表、主窗口、控制條等,在精簡模式下只顯示主窗口和控制條,而在記憶模式下將顯示主窗口、控制條、收藏列表等。

UML圖

根據需求我們可以設計UML圖如下

代碼

MusicPlayer類

public class MusicPlayer {
    private String type;
    private String menu;
    private String playerList;
    private String mainWindows;
    private String controlStrip;
    private String collectionList;

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("這是一個 ").append(type).append(" 音樂播放器\n\n");
        sb.append("播放器擁有:\n");
        if (menu != null) sb.append(menu).append("\n");
        if (playerList != null) sb.append(playerList).append("\n");
        if (mainWindows != null) sb.append(mainWindows).append("\n");
        if (controlStrip != null) sb.append(controlStrip).append("\n");
        if (collectionList != null) sb.append(collectionList).append("\n");
        return sb.toString();
    }

    //get set方法
    ...    
}

PlayerBuilder 抽象接口

public abstract class PlayerBuilder {

    protected MusicPlayer player = new MusicPlayer();

    public abstract void buildType();
    public abstract void buildMenu();
    public abstract void buildPlayerList();
    public abstract void buildMainWindows();
    public abstract void buildControlStrip();
    public abstract void buildCollectionList();

    public MusicPlayer createPlayer(){
        return player;
    }

}

根據需求建立三個不同的建造者

// 完整模式
public class FullPlayerBuilder extends PlayerBuilder {
    @Override
    public void buildType() {
        player.setType("完整模式");
    }

    @Override
    public void buildMenu() {
        player.setMenu("菜單");
    }

    @Override
    public void buildPlayerList() {
        player.setPlayerList("播放列表");
    }

    @Override
    public void buildMainWindows() {
        player.setMainWindows("主界面");
    }

    @Override
    public void buildControlStrip() {
        player.setControlStrip("控制條");
    }

    @Override
    public void buildCollectionList() {
        player.setCollectionList("收藏列表");
    }
}
//精簡模式
public class SimplePlayerBuilder extends PlayerBuilder {
    @Override
    public void buildType() {
        player.setType("精簡模式");
    }

    @Override
    public void buildMenu() {
        player.setMenu(null);
    }

    @Override
    public void buildPlayerList() {
        player.setPlayerList(null);
    }

    @Override
    public void buildMainWindows() {
        player.setMainWindows("主界面");
    }

    @Override
    public void buildControlStrip() {
        player.setControlStrip("控制條");
    }

    @Override
    public void buildCollectionList() {
        player.setCollectionList(null);
    }
}
//記憶模式
public class MemoryPlayerBuilder extends PlayerBuilder {

    @Override
    public void buildType() {
        player.setType("記憶模式");
    }

    @Override
    public void buildMenu() {
        player.setMenu(null);
    }

    @Override
    public void buildPlayerList() {
        player.setPlayerList(null);
    }

    @Override
    public void buildMainWindows() {
        player.setMainWindows("主界面");
    }

    @Override
    public void buildControlStrip() {
        player.setControlStrip("控制條");
    }

    @Override
    public void buildCollectionList() {
        player.setCollectionList("收藏列表");
    }
}

PlayerController類,也就是指揮者類

public class PlayerController {

    public MusicPlayer construct(PlayerBuilder pb) {
        pb.buildType();
        pb.buildMenu();
        pb.buildPlayerList();
        pb.buildMainWindows();
        pb.buildControlStrip();
        pb.buildCollectionList();
        return pb.createPlayer();
    }
}

客戶端測試

public class Client {

    public static void main(String[] args) {
        MusicPlayer player;
        PlayerBuilder builder;
        PlayerController controller;

        builder = new SimplePlayerBuilder();
        controller = new PlayerController();

        player = controller.construct(builder);

        System.out.println(player.toString());
    }
}

測試結果如下

這是一個 精簡模式 音樂播放器

播放器擁有:
主界面
控制條

選擇不同的建造者即可以生產出不同模式的音樂播放器,建造者完成了創建播放器組件的過程,而指揮者類完成最后的組裝。對于客戶端來說不需要了解具體的創建步驟,只需要選擇對應的建造者即可。

變種1:省略Director

Director類在建造者模式中扮演十分重要的作用,它按一定的順序調用Builder的buildPartX()方法,像客戶端返回一個完整的產品。

但是在有些情況下,為了簡化系統,我們可以將Director和抽象建造者Builder進行合并,例如下面的代碼

public class Product {

    private String name;
    private String price;

    @Override
    public String toString() {
        return "Product{" +
                "name='" + name + '\'' +
                ", price='" + price + '\'' +
                '}';
    }

    ...
}
public abstract  class Builder {

    protected Product product = new Product();

    public abstract void buildName();

    public abstract void buildPrice();

    public Product createProduct(){
        this.buildName();
        this.buildPrice();
        return product;
    }
}

public class Product1Builder extends Builder {
    @Override
    public void buildName() {
        product.setName("product1");
    }
    @Override
    public void buildPrice() {
        product.setPrice("1234");
    }
}

public class Product2Builder extends Builder {
    @Override
    public void buildName() {
        product.setName("product2");
    }

    @Override
    public void buildPrice() {
        product.setPrice("6789");
    }
}

客戶端調用

public class Client {
    public static void main(String[] args) {
        Product product;
        Builder builder;

        builder = new Product1Builder();

        product = builder.createProduct();

        System.out.println(product.toString());
    }
}

變種2:內部Builder

先給大家看一段代碼

public class Client {

    public static void main(String[] args) {
        Person.Builder builder = new Person.Builder();

        Person person = builder.setAge(14)
                                .setCountry("中國")
                                .setName("張三")
                                .setSex("男")
                                .create();

        System.out.println(person.toString());
    }
}

這熟悉的內部類Builder,這風騷的鏈式調用,相信作為Android開發程序猿應該非常熟悉吧,不管是Android系統AlertDialog、Notification的創建還是第三方開源庫Retrofit、Universal-Image-Loader的配置都是這熟悉的套路,沒錯這也是建造者模式的一個變種。

先看看上面示例中Person的代碼

public class Person {

    private String name;
    private int age;
    private String sex;
    private String country;

    private Person() {
    }

    private Person(String name, int age, String sex, String country) {
        this.name = name;
        this.age = age;
        this.sex = sex;
        this.country = country;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", country='" + country + '\'' +
                '}';
    }

    public static class Builder {
        private String name;
        private int age;
        private String sex;
        private String country;

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

        public Builder setAge(int age) {
            this.age = age;
            return this;
        }

        public Builder setSex(String sex) {
            this.sex = sex;
            return this;
        }

        public Builder setCountry(String country) {
            this.country = country;
            return this;
        }

        public Person create() {
            return new Person(name, age, sex, country);
        }
    }
}

從代碼中我們可以知道,這種模式是對前一個變種的進一步簡化,將Builder類置于Product類的內部,通過create()方法完成產品的組裝。雖然代碼簡化只有一個類,但依舊符合建造者模式的思想,依然屬于建造者模式的一種。有興趣的同學可以扒一扒AlertDialog的源碼看看,受益良多。

建造者模式與抽象工廠模式

有些同學可能會發現,建造者模式完成的事,我們都可以通過抽象工廠模式完成,那么他們有什么區別呢?

  • 與建造者模式相比,抽象工廠模式返回的是一系列相關的產品,這些產品位于不同的產品等級結構,構成產品族,而建造者模式返回的是一個組裝好的完整產品。抽象工廠模式更像一個汽車零件生產商,生產不同品牌汽車的各種零件。而建造者模式更像一個汽車裝配廠,通過一系列零件的組裝,最終生產出的是一個完整的汽車。
  • 抽象工廠模式中,客戶端需實例化工廠類,然后通過工廠類獲取所需的產品對象。而建造者模式更側重于將復雜的構造對象的方法交給建造者去做,而客戶端只需要通過指揮者就能創建一個完整的產品實例。

小結

建造者模式將一個復雜對象的構建與它的表示分離,使得客戶端通過創建不同的建造者即可創建出不同的對象。且通過分離的方式,將復雜的創建過程分離出來,符合“單一職責”規范。新的不同對象可以通過新增建造者類生成,符合“開閉原則”。但建造者類的使用范圍也有一定的限制,建造者模式創建的產品都有一定的相似性,如果所創建的產品差異性很大,則不適合建造者模式。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容