設計之禪——生成器模式

概述

Java是一門面向對象的語言,所以在使用它的時候我們首先就需要new一個對象,在創建一個簡單對象new當然是沒有任何問題的,但是在實際項目中我們往往需要構建一個個複雜的對象,且當某個對象需要多次創建時,我們再通過new去創建,不僅會產生大量冗餘代碼,而且極不利於維護(想象一下,某個多處被引用的對象需要修改內部屬性,那麼所有創建對象的代碼都需要改變,簡直是場災難)。因此,生成器模式就出現了。

定義

生成器模式對外暴露一個接口,當調用此接口便自動創建好所需對象。它將對象固定的創建流程和其組件的具體實現解耦,使得客戶可以專注於組件的實現。

生成器模式類圖
通過類圖我們不難發現生成器模式包含了四個角色,主導者、產品、抽象生成器接口以及具體生成器接口,需要注意的是產品最終是從具體生成器獲取的而不是主導者。舉個例子,比如你要修房子,房子包含了門、窗、牆等等,那你首先要找一隊工人和設計師,這裏工人就是具體的生成器,設計師則是主導者,由設計師告訴工人該做什麼,也就是建造流程,具體怎麼修建完成則是工人的事,最終房子修好了你是去找工人要,而不是找設計師要。

Coding

生成器模式的實現有兩種,一種是按固定順序構建,另一種是無序構建,先來看看第一種:

public class Room {

    private String door;
    private String window;
    private String floor;

    public void setDoor(String door) {
        this.door = door;
    }
    public void setWindow(String window) {
        this.window = window;
    }
    public void setFloor(String floor) {
        this.floor = floor;
    }
}

生成器族:

public interface Builder {

    Room room = new Room();

    void makeDoor(String door);

    void makeWindow(String window);

    void makeFloor(String floor);

    default Room getRoom() {
        return room;
    }

}

public class RoomBuilder implements Builder {

    @Override
    public void makeDoor(String door) {
        room.setDoor(door);
    }

    @Override
    public void makeWindow(String window) {
        room.setWindow(window);
    }

    @Override
    public void makeFloor(String floor) {
        room.setFloor(floor);
    }
}

主導者:

public class Designer {

    private Builder builder;

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

    public void build(String door, String window, String floor) {
        builder.makeDoor(door);
        builder.makeDoor(window);
        builder.makeFloor(floor);
    }

}

最後測試:

public static void main(String[] args) {
        Builder builder = new RoomBuilder();
        Designer designer = new Designer(builder);
        designer.build("door", "window", "floor");
        Room room = builder.getRoom();
}

如果產品的生成需要按照固定的順序構建就可以採用上述的實現方式,而當需要靈活設置產品屬性時就可以採用靜態內部類的方式來實現無序的生成器模式,代碼如下:

public class Room {

    private String door;
    private String window;
    private String floor;

    public Room(Builder builder) {
        this.door = builder.door;
        this.floor = builder.floor;
        this.window = builder.window;
    }

    public static class Builder {
        private String door;
        private String window;
        private String floor;

        public Builder makeDoor(String door) {
            this.door = door;
            return this;
        }
        public Builder makeWindow(String window) {
            this.window = window;
            return this;
        }
        public Builder makeFloor(String floor) {
            this.floor = floor;
            return this;
        }

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

}

測試

public static void main(String[] args) {
        Room room = new Room.Builder().makeDoor("door").makeWindow("window").makeFloor("floor").build();
}

可以看到這種方式更加簡潔明瞭,並且用戶可以自行設置所需屬性,所以在《Effective Java 第2版》中也是推薦用來替代重疊構造器模式JavaBean模式

當遇到需要重載多個構造器時,考慮用生成器模式來實現,它既保證了代碼的整潔可閱讀性,還可以避免JavaBean模式的不安全。

所謂重疊構造器模式也就是根據產品的必須屬性和可選屬性重載多個構造器,這種方式不好的就是當屬性多了後代碼就難以閱讀,用戶也難以正確選擇構造器;JavaBean模式即我們常用的setter方法,這種方式好在於可以靈活的選擇屬性,但卻無法保證安全性,尤其在多線程環境下,一個線程new完對象還未調用setter時,另一個線程就調用getter,就無法保證類的一致性;並且該方式產生的重複代碼也非常多,所以當遇到需要多個構造器的情況下,考慮使用生成器模式

總結

生成器模式也是平時常用到的,弄清楚它的兩種實現方式,在實際項目中遇到需要構建複雜對象時考慮使用它吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章