如何使用建造者模式構建複雜對象?

『建造者模式』是一種簡化複雜對象構建過程的設計模式,他的核心夙願是:把對象的構建和表述分離java

舉個栗子

每種食品包裝上都會有一個養分成分表,每份的含量、每罐的含量、每份卡路里、脂肪、碳水化合物、鈉等,還可能會有其餘 N 種可選數據,大多數產品的某幾個成分都有值git

那麼咱們表述這個食品類:程序員

public class Nutrition {
    private int servingSize;// required
    private int servings;// required
    private int calories;// optional
    private int fat;// optional
    private int sodium;// optional
    private int carbohydrate;// optional

    public Nutrition(int servingSize,int servings) {
        //.....
    }

    public Nutrition(int servingSize,int servings, int calories) {
        //.....
    }

    public Nutrition(int servingSize,int servings,int calories,int fat) {
        //.....
    }

    public Nutrition(int servingSize, int servings,int calories, int fat,int sodium) {
        //.....
    }

    public Nutrition(int servingSize,int servings,int calories,int fat,int sodium,int carbohydrate) {
        //.....
    }
}
複製代碼

這裏的字段還很少,構造一個對象已經如此複雜了,可想而知,真實業務中十幾二十個屬性該如何構建?github

有人說,只使用構造函數傳遞必須參數,可選參數經過 setter 方法調用傳入。不錯,這種方式應該也是你們業務中處理的方式吧,沒別的,『建造者模式』僅僅表示,這種構造函數+setter方法的 方式不夠優雅。面試

建造者模式

定義一個抽象 Builder:設計模式

public abstract class AbstractBuilder {

    protected Nutrition nutrition;

    public AbstractBuilder setServingSizeServings(int size,int servings){
        nutrition.setServingSize(size);
        nutrition.setServings(servings);
        return this;
    }
    public AbstractBuilder setCalories(int calories){
        nutrition.setCalories(calories);
        return this;
    }
    public AbstractBuilder setFat(int fat){
        nutrition.setFat(fat);
        return this;
    }
    public AbstractBuilder setSodium(int sodium){
        nutrition.setSodium(sodium);
        return this;
    }
    public AbstractBuilder setCarbohydrate(int carbohydrate){
        nutrition.setCarbohydrate(carbohydrate);
        return this;
    }

    public Nutrition build(){
        return nutrition;
    }
}
複製代碼

定義一個默認實現的 Builder:微信

public class DefaultBuilder extends AbstractBuilder{
}
複製代碼

客戶端構建一個對象:markdown

Nutrition nutrition = new DefaultBuilder().
    setServingSizeServings(10, 20).
    setCalories(100).
    build();
複製代碼

建造者模式實現完了。你會發現 Nutrition 對象的表述和他的構造是徹底分離的。mybatis

至於和構造函數+setter方式有什麼區別,我想比較重要的一點區別就是,setter 方法能夠被任意調用,你沒法準確斷定對象初始化生成時候的初始參數值是什麼,使用構造者就會比較明顯,構造這個對象使用了哪些參數,一目瞭然。app

而且,我這裏只提供了一個默認 DefaultBuilder,若是你有特殊需求,你能夠自定義實現一個 Builder,設置他的某些字段值爲一個固定值,這樣 build 出來的對象在某些屬性上就是固定的,是一種特殊對象。

哪些源碼在實踐

一、JDK 中的 StringBuilder、StringBuffer最顯而易見了,他們的目標是建造一個 String 對象,建造的方法就是 toString 方法,經過各類 append 方法 「參數化」 對象。

二、mybatis 中的 SqlSessionFactoryBuilder

三、SpringMVC 中的 UriComponentsBuilder

你還知道哪些在使用建造者模式的優秀框架?


關注公衆不迷路,一個愛分享的程序員。

公衆號回覆「1024」加做者微信一塊兒探討學習!

公衆號回覆「面試題」送你一份面試題以及做者的做答答案

每篇文章用到的全部案例代碼素材都會上傳我我的 github

github.com/SingleYam/o…

歡迎來踩!

相關文章
相關標籤/搜索