本文屬于系列文章《設(shè)計(jì)模式》,附上文集鏈接
建造者模式
- 定義:將一個(gè)復(fù)雜的對(duì)象的構(gòu)建與它的表示分離,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。
- 要解決的問題:看定義就看到了,首先,針對(duì)的問題是復(fù)雜對(duì)象的構(gòu)建,其次,對(duì)這些復(fù)雜的構(gòu)建還可以存在不同的結(jié)果。
- 屬于創(chuàng)建類模式
舉個(gè)栗子
builder,看到這個(gè)詞的第一印象就是工地里的建造者(最近剛好學(xué)校新建宿舍樓,天天見),然后嘛,工人要做的事,就是要建一棟宿舍樓,這就是構(gòu)建一個(gè)復(fù)雜的對(duì)象。而建宿舍這件事的構(gòu)建過程是相同的,但是新建的宿舍樓是要男女混住的,里面的具體構(gòu)造還不一樣(女生宿舍樓梯口裝攝像頭防狼賊。。),宿舍名也不一樣,所以相同的構(gòu)造過程得到不同的結(jié)果,就來實(shí)現(xiàn)這個(gè)栗子來玩一下。
要注意到的是,這里的明確需求,只是得到一個(gè)宿舍樓。
先用一下假設(shè)性原則,不然都體會(huì)不了這個(gè)模式的好處,我們先假設(shè),不用這個(gè)模式要怎么實(shí)現(xiàn)這個(gè)需求。
代碼:
// 抽象宿舍類,定義宿舍樓具有的幾個(gè)屬性
public abstract class Dormitory {
// 地基
protected String foundation;
// 宿舍的攝像頭
protected String cameraOnStairway;
// 是否需要裝攝像頭要
protected boolean needCamera;
// 宿舍顏色
protected String dormitoryColor;
public void setFoundation(String foundation) {
this.foundation = foundation;
}
public void setCameraOnStairway(String cameraOnStairway) {
if(needCamera == true){
this.cameraOnStairway = cameraOnStairway;
}
}
public void setNeedCamera(boolean needCamera) {
this.needCamera = needCamera;
}
public void setDormitoryColor(String dormitoryColor) {
this.dormitoryColor = dormitoryColor;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("這個(gè)"+this.getClass().getSimpleName()+"宿舍樓,");
sb.append(foundation+",");
if(needCamera == true){
sb.append(cameraOnStairway+",");
}else{
sb.append("不需要裝攝像頭,");
}
sb.append(dormitoryColor);
return sb.toString();
}
}
**********
// 男生宿舍
public class ManDormitory extends Dormitory{}
// 女生宿舍
public class WomanDormitory extends Dormitory {}
***********
// 場(chǎng)景類
public class Client {
public static void main(String[] args) {
Dormitory manDormitory = new ManDormitory();
manDormitory.setFoundation("打了一個(gè)堅(jiān)實(shí)的地基");
manDormitory.setNeedCamera(false);
manDormitory.setCameraOnStairway("裝了一個(gè)攝像頭");
manDormitory.setDormitoryColor("外表是酷酷的藍(lán)色");
System.out.println(manDormitory);
System.out.println("-------------------------------------");
Dormitory womanDormitory = new WomanDormitory();
womanDormitory.setFoundation("打了一個(gè)堅(jiān)實(shí)的地基");
womanDormitory.setNeedCamera(true);
womanDormitory.setCameraOnStairway("裝了一個(gè)酷酷的攝像頭");
womanDormitory.setDormitoryColor("外表是萌萌的粉色");
System.out.println(womanDormitory);
}
}
結(jié)果:
這個(gè)ManDormitory宿舍樓,打了一個(gè)堅(jiān)實(shí)的地基,不需要裝攝像頭,外表是酷酷的藍(lán)色
-------------------------------------
這個(gè)WomanDormitory宿舍樓,打了一個(gè)堅(jiān)實(shí)的地基,裝了一個(gè)酷酷的攝像頭,外表是萌萌的粉色
如無意外,到這里可能就看下去了,因?yàn)檫@完全不是建造者模式,完全是一個(gè)new。但我問一句,如果沒看設(shè)計(jì)模式的東西,會(huì)覺得這個(gè)東西有很大很大的問題嗎?對(duì)嘛,初學(xué)者,誰(shuí)新建對(duì)象不是new的,得到結(jié)果就行了,完全不考慮擴(kuò)展。
在這里我很想說一句話就是,設(shè)計(jì)模式和面向?qū)ο蟮臇|西,首先要明白面向?qū)ο罅笤瓌t出現(xiàn)的目的是什么,要解決什么問題,否則,設(shè)計(jì)模式不就成了迂腐的教規(guī)了?面向?qū)ο笫菫榱烁玫慕鉀Q需求變化的問題, 然后在變化的點(diǎn),采用方法去擁抱這些變化。這才是最要緊要記住一個(gè)東西。所有的設(shè)計(jì)模式或者面向?qū)ο蟮乃性瓌t都是奔向這個(gè)目的而去的。
看回代碼,在上面例子那里我就說了,要明確清楚的是,需求,只是得到宿舍樓,所以在場(chǎng)景類那里,直接new了男女兩個(gè)宿舍,也并沒有什么問題,確實(shí)實(shí)現(xiàn)了需求。但是考慮到變化的情況,假如有的男生宿舍樓處于學(xué)校外圍,經(jīng)常被盜,需要裝攝像頭,而有的女生宿舍處于學(xué)校中心,太安全了,不需要裝攝像頭,怎么辦?這個(gè)就是需求變化的點(diǎn),而如果使用上面那套代碼,我們只能在場(chǎng)景類那里再new對(duì)象。每出現(xiàn)一個(gè)不同構(gòu)建過程的對(duì)象,我們就new一個(gè),瘋狂new,問題就是這個(gè),擴(kuò)展性極其差。
來用一下建造者模式
// 抽象建造類
public abstract class Builder {
protected Dormitory dormitory;
public abstract Dormitory buildDormitory();
}
// 負(fù)責(zé)建造男生宿舍的工人
public class ManDormitoryBuilder extends Builder{
public ManDormitoryBuilder() {
super.dormitory = new ManDormitory();
}
@Override
public Dormitory buildDormitory() {
super.dormitory.setFoundation("打了一個(gè)堅(jiān)實(shí)的地基");
super.dormitory.setNeedCamera(false);
super.dormitory.setCameraOnStairway("裝了一個(gè)攝像頭");
super.dormitory.setDormitoryColor("外表是酷酷的藍(lán)色");
return super.dormitory;
}
}
// 負(fù)責(zé)建造女生宿舍的工人
public class WomanDormitoryBuilder extends Builder{
public WomanDormitoryBuilder() {
super.dormitory = new WomanDormitory();
}
@Override
public Dormitory buildDormitory() {
super.dormitory.setFoundation("打了一個(gè)堅(jiān)實(shí)的地基");
super.dormitory.setNeedCamera(true);
super.dormitory.setCameraOnStairway("裝了一個(gè)酷酷的攝像頭");
super.dormitory.setDormitoryColor("外表是萌萌的粉色");
return super.dormitory;
}
}
// 宿舍類都沒有變化,這里就不列出來了
// 場(chǎng)景類
public class Client {
public static void main(String[] args) {
Builder manDormitoryBuilder = new ManDormitoryBuilder();
Dormitory manDormitory = manDormitoryBuilder.buildDormitory();
System.out.println(manDormitory);
System.out.println("---------------");
Builder womanDormitoryBuilder = new WomanDormitoryBuilder();
Dormitory womanDormitory = womanDormitoryBuilder.buildDormitory();
System.out.println(womanDormitory);
}
}
結(jié)果:
這個(gè)ManDormitory宿舍樓,打了一個(gè)堅(jiān)實(shí)的地基,不需要裝攝像頭,外表是酷酷的藍(lán)色
\--------------------
這個(gè)WomanDormitory宿舍樓,打了一個(gè)堅(jiān)實(shí)的地基,裝了一個(gè)酷酷的攝像頭,外表是萌萌的粉色
分析下代碼:我們將new對(duì)象的過程封裝到builder那里,builder類里有個(gè)dormitory屬性,在子類builder構(gòu)造函數(shù)予以實(shí)現(xiàn),確保子類builder是對(duì)應(yīng)的dormitory的生產(chǎn)對(duì)象。然后在子類builder的buildDormitory方法實(shí)現(xiàn)dormitory的構(gòu)造過程。這么做有什么好處呢?我們的上層模塊Client以后再面對(duì)下層模塊Dormitory的變化時(shí),就不用做出太大的改動(dòng)了,比如上文中提到的新建兩種宿舍,我們可以怎么做?可以直接在子類中重載方法buildDormitory,傳入boolean參數(shù)來控制裝不裝攝像頭,也可以直接在子類加方法,怎樣的都行(擴(kuò)展性啥的各自考慮啦),然后我們的上層模塊Client都不需要再對(duì)這個(gè)Dormitory的構(gòu)建過程有很大的了解(迪米特法則),我們只需要在Client中做好與Builder的“交涉”即可。
后言:看了幾篇博客,發(fā)現(xiàn)還是沒看書這么好,博客往往直接將模式交代了就沒了。還有發(fā)現(xiàn)一件事,就是看到有博客下面有評(píng)論說例子不正確啥的,但是我看了之后,其實(shí)無非就是方法的實(shí)現(xiàn)沒有抽象好而已,而設(shè)計(jì)模式那個(gè)思想還是在那里的。所以我提醒自己,設(shè)計(jì)模式是一種思想,是一種方法,不是API這樣的方法,API根據(jù)參數(shù),能得到確定結(jié)果,而設(shè)計(jì)模式不是??丛O(shè)計(jì)模式類的文章的時(shí)候,看到高贊或者高踩,都要看作者對(duì)這個(gè)模式的理解,沒有帶個(gè)人理解的,都是筆記(雖說我也寫了幾篇設(shè)計(jì)模式的筆記),而已。