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