截止今天,小秋學習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是一個接口,定義一套規範,而我們使用的例子中,沒有使用接口,直接使用具體類,但核心思想還是一樣的。
其核心思想就是:将一個複雜的對象的建構與它的表示分離,使得同樣的建構過程可以建立不同的表示。
小秋:哇,強啊。我要給你點贊.....
算是第二次采取對話的方式寫....,以後會多采取這種方式來寫勒。
完
關注公我的衆号:苦逼的碼農,擷取更多原創文章,背景回複禮包送你一份時下熱門的資源大禮包。同時也感謝把文章介紹給更多需要的人