建造者模式:把類(lèi)對(duì)象的構(gòu)造與裝配分別實(shí)現(xiàn)。
單例、工廠、原型這幾種模式的重點(diǎn)在于創(chuàng)建出一個(gè)個(gè)的實(shí)例對(duì)象來(lái),而這個(gè)建造者模式的重點(diǎn)在于對(duì)某個(gè)對(duì)象的組成部分的裝配。
在實(shí)際開(kāi)發(fā)中,一個(gè)類(lèi)不可能只有簡(jiǎn)單的幾個(gè)屬性,往往是有大量的屬性。創(chuàng)建對(duì)象的同時(shí)或者之后,給屬性也設(shè)置好對(duì)應(yīng)的值,這個(gè)對(duì)象才有使用的價(jià)值。否則一個(gè)空對(duì)象我們用它做什么呢。
如果一個(gè)表有20個(gè)字段,那么它對(duì)應(yīng)的實(shí)體類(lèi)就有20個(gè)屬性,如何給這個(gè)類(lèi)的對(duì)象的屬性賦值呢。要么是利用構(gòu)造方法在創(chuàng)建對(duì)象的同時(shí)就賦值,要么就是創(chuàng)建一個(gè)空對(duì)象,然后用set方法給賦值。
構(gòu)造器的方法問(wèn)題就是不夠靈活,而set方法就是代碼量大。
建造者模式可以完美的解決這個(gè)問(wèn)題。
先看一下建造者模式的一般方式:
這里有三個(gè)基本角色:產(chǎn)品(有比較多、復(fù)雜的組件),構(gòu)造者,裝配者
這里以電腦這種產(chǎn)品為例:
public class Computer {
private String cpu;
private String memery;
private String screen;
@Override
public String toString() {
return "Computer{" +
"cpu='" + cpu + '\'' +
", memery='" + memery + '\'' +
", screen='" + screen + '\'' +
'}';
}
// 省略get與set方法
}
構(gòu)造者,用來(lái)模擬構(gòu)造產(chǎn)品的各個(gè)組成部分
// 抽象構(gòu)造者
public abstract class Builder {
Computer computer = new Computer();
// 構(gòu)建cpu
protected abstract void buildCpu();
// 構(gòu)建memery
protected abstract void buildMemery();
// 構(gòu)建screen
protected abstract void buildScreen();
}
// 具體的建造者
public class HuaweiComputer extends Builder{
@Override
protected void buildCpu() {
computer.setCpu("only amd ...");
}
@Override
protected void buildMemery() {
computer.setMemery("sangsam 1024G");
}
@Override
protected void buildScreen() {
computer.setScreen("huawei 27 big screen");
}
}
public class AppleComputer extends Builder{
@Override
protected void buildCpu() {
computer.setCpu("inter i7");
}
@Override
protected void buildMemery() {
computer.setMemery("kinston 256G");
}
@Override
protected void buildScreen() {
computer.setScreen("apple cloure");
}
}
裝配者,不同的產(chǎn)品只是組件不同,但是裝配流程是相同的
public class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public Computer createComputer(){
builder.buildCpu();
builder.buildMemery();
builder.buildScreen();
return builder.computer;
}
}
測(cè)試
public class ClientDemo {
public static void main(String[] args) {
// 通過(guò)給裝配者傳遞不同的產(chǎn)品構(gòu)造者來(lái)得到不同的產(chǎn)品
// Director director = new Director(new AppleComputer());
Director director = new Director(new HuaweiComputer());
Computer computer = director.createComputer();
System.out.println(computer);
}
}
當(dāng)然從這里我們可以看到這種模式的適用場(chǎng)景,那就是產(chǎn)品應(yīng)當(dāng)是同一類(lèi)產(chǎn)品,產(chǎn)品的組成部分可能很多,各個(gè)組成部分可能不相同,但是他們組成這個(gè)產(chǎn)品的整理邏輯(裝配流程)是相同的。
其實(shí)本質(zhì)上還是封裝,我們把不同的部分(組成部分)單獨(dú)實(shí)現(xiàn),相同的部分(裝配)統(tǒng)一實(shí)現(xiàn)。
也可以把構(gòu)造者和裝配者這兩個(gè)角色放在一起來(lái)實(shí)現(xiàn),如下:
public abstract class BuilderTotal {
Computer computer = new Computer();
// 構(gòu)造者:構(gòu)造產(chǎn)品的各個(gè)組成部分
protected abstract void buildCpu();
protected abstract void buildMemry();
protected abstract void buildScreen();
// 裝配者:完成組裝
public Computer createCoumputer(){
this.buildCpu();
this.buildMemry();
this.buildScreen();
return this.computer;
}
}
不同的產(chǎn)品
public class XiaomiComputer extends BuilderTotal{
@Override
protected void buildCpu() {
computer.setCpu("xiaomi cpu");
}
@Override
protected void buildMemry() {
computer.setMemery("xiaomi memery");
}
@Override
protected void buildScreen() {
computer.setScreen("xiaomi screen");
}
}
public class HpComputer extends BuilderTotal{
@Override
protected void buildCpu() {
computer.setCpu("hp special cpu");
}
@Override
protected void buildMemry() {
computer.setMemery("hp special memery");
}
@Override
protected void buildScreen() {
computer.setScreen("hp special screen");
}
}
測(cè)試:
public class ClientDemo_02 {
public static void main(String[] args) {
BuilderTotal hp = new HpComputer();
Computer coumputer = hp.createCoumputer();
System.out.println(coumputer);
System.out.println("========================");
BuilderTotal xiaomi = new XiaomiComputer();
Computer computer = xiaomi.createCoumputer();
System.out.println(computer);
}
}
雖然這樣是把不同的功能放在同一個(gè)類(lèi)中實(shí)現(xiàn),不符合單一原則,但其實(shí)我覺(jué)得這樣的代碼可讀性要高一點(diǎn),不過(guò)當(dāng)屬性比較多的時(shí)候,也會(huì)比較亂。
接下來(lái)看一種比較巧妙的方式,利用內(nèi)部類(lèi)的方式,對(duì)產(chǎn)品的各個(gè)部分(屬性)進(jìn)行組裝,而且非常靈活。
public class Phone {
private String brand;
private String memory;
private String screen;
private String camera;
private String clour;
// 私有構(gòu)造方法,創(chuàng)建對(duì)象由內(nèi)部類(lèi)的方法實(shí)現(xiàn)(最后用到)
private Phone(String brand, String memory, String screen, String camera, String clour) {
this.brand = brand;
this.memory = memory;
this.screen = screen;
this.camera = camera;
this.clour = clour;
}
// 借助一個(gè)內(nèi)部類(lèi),用來(lái)構(gòu)造屬性的各個(gè)部分,并創(chuàng)建調(diào)用它的構(gòu)造方法創(chuàng)建對(duì)象返回給調(diào)用者
public static PhoneBuilder phoneBuilder(){
return new PhoneBuilder();
}
// 靜態(tài)內(nèi)部類(lèi)實(shí)現(xiàn)
public static class PhoneBuilder{
// 擁有和外部類(lèi)相同的參數(shù)
private String brand;
private String memory;
private String screen;
private String camera;
private String clour;
// 對(duì)每一個(gè)屬性提供一個(gè)方法來(lái)設(shè)置它的值,這其實(shí)就是構(gòu)造組件的部分
public PhoneBuilder brand(String brand){
this.brand = brand;
// 返回自身,這樣方便使用鏈?zhǔn)骄幊? return this;
}
public PhoneBuilder memory(String memory){
this.memory = memory;
return this;
}
public PhoneBuilder screen(String screen){
this.screen = screen;
return this;
}
public PhoneBuilder camera(String camera){
this.camera = camera;
return this;
}
public PhoneBuilder clour(String clour){
this.clour = clour;
return this;
}
// 最后提供一個(gè)方法,創(chuàng)建產(chǎn)品對(duì)象,參數(shù)值就是上面的方法接受到的
public Phone build(){
return new Phone(brand, memory, screen, camera, clour);
}
}
// 只為了測(cè)試方便,忽略
@Override
public String toString() {
return "Phone{" +
"brand='" + brand + '\'' +
", memory='" + memory + '\'' +
", screen='" + screen + '\'' +
", camera='" + camera + '\'' +
", clour='" + clour + '\'' +
'}';
}
}
只看這個(gè)類(lèi)的設(shè)計(jì)會(huì)覺(jué)得很復(fù)雜,但是使用的時(shí)候就覺(jué)得爽了:
public class PhoneClient {
public static void main(String[] args) {
Phone phone = Phone.phoneBuilder() // 這一步是得到它內(nèi)部的一個(gè)構(gòu)造器
.brand("huawei")
.clour("black")
// .memory("64G")
// .camera("1000 wan")
.screen("18 ch")
.build(); // 這一步才是真正創(chuàng)建一個(gè)Phone對(duì)象,并使用上面接受的參數(shù)
System.out.println(phone);
}
}
可以看到,在得到構(gòu)造器和利用構(gòu)造器創(chuàng)建對(duì)象中間,都是設(shè)置對(duì)象的屬性值,最爽的地方就在于:隨意。想設(shè)置幾個(gè)就設(shè)置幾個(gè),而且順序隨意,而且代碼可讀性很好,很明確要給給哪些屬性設(shè)置什么值。
其實(shí)這完全就是lomboc中的注解@Builder的功能。之前只是照著別人用覺(jué)得方便,也沒(méi)有琢磨琢磨。最近學(xué)到了這個(gè),覺(jué)得這個(gè)設(shè)計(jì)真是巧妙。平時(shí)工作中只是簡(jiǎn)單的crud,動(dòng)腦筋比較少,多學(xué)學(xué)這些優(yōu)秀的東西,真的可以開(kāi)闊思路。