天天看點

一段對話講完建造者模式

截止今天,小秋學習Java剛剛滿三個月。此刻的小秋感覺自己Java學的還不錯,想去帥地面前炫耀一番,于是,就發生了一下一番對話.....

得意的小秋

帥地:嗨,小秋,看你今天氣色不錯啊。最近Java學的怎麼樣了?

小秋:說實話,比起三個月前我學指針的那段日子,這Java太友好了,感覺就是分分種的事(暗自得意)。

帥地:我靠,你這口氣還挺大啊。從c的面向過程到Java的面向對象,還習慣不?

小秋:哎,還行,無非就是“一切皆對象”,Java中的對象感覺類似于C中的結構體。反正不過三七二十一,我腦子裡都把他們當成是一個對象就得了。(内心自我感覺良好)

帥地:看你也學了三個月了,要不我随便拿道題考考你?(讓你不謙虛,暗自偷笑)

小秋:好啊,正好練練手(嘿嘿,終于可以展現實力了)。

重載多次的構造函數

帥地:假如有一個蛋糕Cake對象,蛋糕這個對象有一個必選屬性size,還有一些可選屬性apple,banana,orange,mango等,必選屬性代表使用者必須要指定蛋糕的大小,可選屬性代表這些蛋糕要加哪些材料。

小秋:這個很簡單啊,建立一個Cake類,裡面有size,apple,banana,orange,mango屬性,然後構造器的參數裡指定size這個參數就可以了。我直接上代碼吧:

public class Cake {
    private int size;
    private String apple;
    private String banana;
    private String orange;
    private String mango;
    //new時必須給出size
    public Cake(int size) {
        this.size = size;
    }
}           

帥地:可選參數呢?我要new一個size=30,并且添加apple的蛋糕怎麼辦?

小秋:哦,我寫的太快,忘了重載了,稍等(心想,這還不簡單)。

public class Cake {
    private int size;
    private String apple;
    private String banana;
    private String orange;
    private String mango;

    public Cake(int size) {
        this.size = size;
    }
    public Cake(int size, String apple) {
        this.size = size;
        this.apple = apple;
    }
    public Cake(int size, String apple, String orange) {
        this.size = size;
        this.apple = apple;
        this.orange = orange;
    }
    public Cake(int size, String apple, String orange, String mango) {
        this.size = size;
        this.apple = apple;
        this.orange = orange;
        this.mango = mango;
    }
}           

小秋:這下總可以了吧,你要加哪些料,你就使用哪個構造器。全部給你重載了。

帥地:(露出狡猾的表情)寫構造函數倒是挺快的,那如果我要隻加apple和mango的蛋糕呢?

小秋:啊?好吧,你這是逼我把所有組合的構造器都寫出來。

于是,小秋把所有構造器的組合都寫了出來。由于size是個必須參數,把其他四個可選參數進行組合,一共有16種。

噼裡啪啦,劈裡啪啦,小秋一口氣把他們全部寫出來了

public class Cake {
    private int size;
    private String apple;
    private String banana;
    private String orange;
    private String mango;
    
    public Cake(int size){
        this.size = size;
    }
    public Cake(int size, String apple){
        this.size = size;
        this.apple = apple;
    }
    public Cake(int size, String banana){
        this.size = size;
        this.banana = banana;
    }
    .....
}
           

小秋:好了,這下,你要啥組合有啥組合了。

帥地:四個可選參數你就寫了這麼一大堆參數了。确定這樣寫?

小秋:我覺得挺好的啊,反正很快,多寫幾個就多寫幾個吧。

帥地:那如果給你6個可選參數呢?

這時候小秋偷偷算了一些,發現一共有74種組合?

小秋:不就是74種組合,我覺得問題不是很大(心有點虛)。

帥地:那萬一有10個可選參數呢?

小秋:.....

帥地:而且你重載那麼多構造器,使用者在在new的時候,第一個參數和第二個參數代表什麼,使用者混亂了怎麼吧?例如,你有個apple+banana的構造器

public Cake(int size, String apple, String banana){
    this.size = size;
    this.apple = apple;
    this.banana = banana;
}           

但是使用者在new的時候,可能忘記了參數的順序

Cake cake = new Cake(size,“banana”,“apple”)。
           

小秋:我會提供相應的文檔啊,忘記了可以看文檔勒。

帥地:幾百個構造函數,而且還那麼相似,你去看下文檔試試,然後說說你的心情。

小秋:......(不知所措)。

通過Javabean的模式

帥地:有沒其他什麼辦法?

小秋:我想到另一種辦法了,我可以通過set和get方法來設定可選參數的。我直接上代碼你看看

public class Cake {
    private int size;
    private String apple;
    private String banana;
    private String orange;
    private String mango;

    public Cake(int size) {
        this.size = size;
    }
    //通過set來添加材料
    public void setApple(String apple) {
        this.apple = apple;
    }

    public void setBanana(String banana) {
        this.banana = banana;
    }

    public void setMango(String mango) {
        this.mango = mango;
    }

    public void setOrange(String orange) {
        this.orange = orange;
    }
}           

此時的小秋有點得意....

帥地:挺不錯,這種方法比剛才的好多了,又簡潔。

此時如果要new一個apple+orange+mango的蛋糕的話,代碼如下:

Cake cake = new Cake(30);
cake.setApple("apple");
cake.setOrange("orange");
cake.setMange("mange");           
參數依賴檢查問題

帥地:這種方法也是有缺點,例如用構造器重載時一行代碼就可以搞定了,現在要用四行代碼。

小秋:反正我覺得這樣很nice(得意中...)。

帥地:不過這樣寫有一個緻命的缺點,假如那些屬性之間存在依賴性的話,怎麼辦?例如Cake多了A,B兩個屬性,并且這兩個屬性之間存在依賴關系。如果你設定了屬性A,但是沒有設定屬性B,那麼這個Cake對象就會出問題。或者屬性的先後順序設定也可能會導緻出現問題。對于這種情況,你在什麼地方檢查這種互相依賴的邏輯?

小秋:有點蒙蔽,不知所措....。

小秋:那你說怎麼辦?

靜态内部類

帥地:其實你已經做的相當不錯了,不過我今天就教你另外一個辦法,我們可以開放一個靜态内部類專門用來與外界打交道,用來收集使用者想要設定的屬性并且做檢查。直接上代碼:

public class Cake {
    private int size;
    private String apple;
    private String banana;
    private String orange;
    private String mango;
    //private,讓外面無法直接建立
    private Cake(Builer builer) {
        this.size = builer.size;
        this.apple = builer.apple;
        .....
    }
    //專門用來與外界打交道
    public static class Builer {
        private int size;
        private String apple;
        private String banana;
        private String orange;
        private String mango;
        public void setSize(int size) {
            this.size = size;
        }
        //為了省點代碼,其他的省略
        public Cake build() {
            //檢查參數之間的依賴關系是否正确
            return new Cake(this);
        }
        .....
    }
}           

假如我要new一個apple+orange的Cake

Cake.Builer builer = new Cake.Builer();
builer.setSize(30);
builer.setApple("apple");
builer.setOrange("orange");
//建立一個蛋糕
Cake cake = builer.build();           

帥地:這種方法牛吧?這還不夠,我們還可以采用鍊式調用的方法。

鍊式調用
public class Cake {
    private int size;
    private String apple;
    private String banana;
    private String orange;
    private String mango;
    //private,讓外面無法直接建立
    private Cake(Builer builer) {
        this.size = builer.size;
        this.apple = builer.apple;
        .....
    }
    //專門用來與外界打交道
    public static class Builer {
        private int size;
        private String apple;
        private String banana;
        private String orange;
        private String mango;
        //傳回參數改為Builer
        public Builer setSize(int size) {
            this.size = size;
            return this;
        }

        public Builer setApple(String apple) {
            this.apple = apple;
            return this;
        }
        //為了省點代碼,其他的省略
        public Cake build() {
            //檢查參數之間的依賴關系是否正确
            return new Cake(this);
        }
    }
}           

如何使用?

Cake cake = new Cake.Builer()
            .setSize(30)
            .setApple("apple")
            .setOrange("orange")
            .build();           

一行代碼就搞定了。

帥地:厲害吧?

小秋:漲知識了,看來我還是太年輕了,以後得好好向帥地學習。

建造者模式

帥地:其實,上面那種方法算是23種設計模式中的其中一種---建造者模式。不過這隻是一個簡化版的建造者模式。

對于建造者模式,具體的UML圖是這樣的:

在這個UML圖中,Builder是一個接口,定義一套規範,而我們使用的例子中,沒有使用接口,直接使用具體類,但核心思想還是一樣的。

其核心思想就是:将一個複雜的對象的建構與它的表示分離,使得同樣的建構過程可以建立不同的表示。

小秋:哇,強啊。我要給你點贊.....

算是第二次采取對話的方式寫....,以後會多采取這種方式來寫勒。

關注公我的衆号:苦逼的碼農,擷取更多原創文章,背景回複禮包送你一份時下熱門的資源大禮包。同時也感謝把文章介紹給更多需要的人