閱讀原文請?jiān)L問我的博客BrightLoong's Blog
一. 概述
建造者模式(Builder)
弧岳,又叫生成器模式,它將一個(gè)復(fù)雜對象的構(gòu)建與它的表示分離涧卵,使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示腹尖。
建造者模式可以將一個(gè)產(chǎn)品的內(nèi)部表象與產(chǎn)品的生成過程分割開來热幔,使用建造者模式,用戶就只需指定需要建造的類型就可以得到它們近尚,二具體建造的過程和細(xì)節(jié)就不需要知道了肿男。
建造者模式相比于工廠模式更加關(guān)注組成部分的裝配細(xì)節(jié)和順序却嗡。
創(chuàng)建者模式屬于創(chuàng)建型模式嘹承。
二. UML類圖解析
建造者模式的UML類圖如下:
- Builder:為創(chuàng)建一個(gè)Product對象的各個(gè)部件指定的抽象接口叹卷,buildPart()是對象組成部分構(gòu)造的抽象方法,根據(jù)需求可以有多個(gè)組成部分的構(gòu)造方法往毡。
- ConcreteBuilder:具體建造者靶溜,繼承自Builder,構(gòu)造和裝配各個(gè)部件嗤详,getResult()返回構(gòu)造后的實(shí)例葱色。
- Director:指揮者娘香,持有一個(gè)Builder引用,調(diào)用具體的創(chuàng)建者的各個(gè)部件來創(chuàng)建對象舞痰,負(fù)責(zé)保證對象各部分完整創(chuàng)建或者按某種順序創(chuàng)建 响牛。
- Product:產(chǎn)品赫段,要創(chuàng)建的具體對象糯笙,一般來說是一個(gè)復(fù)雜的對象,包含多個(gè)組成部分豺憔。
三. 代碼實(shí)現(xiàn)
這里把電腦作為一個(gè)產(chǎn)品够庙,一般來說一個(gè)可以使用的臺式電腦包括顯示器耘眨、主機(jī)、鍵盤和鼠標(biāo)胆屿,各個(gè)組成部分可以有不同的品牌、性能环鲤、型號等細(xì)節(jié)冷离,下面就使用建造者模式來對電腦進(jìn)行實(shí)現(xiàn)唇兑。
產(chǎn)品類——Computer
package io.github.brightloong.design.builder;
/**
* 產(chǎn)品類Computer,為了方便實(shí)現(xiàn)蔫耽,所有組成均用String來表示匙铡。
* Created by BrightLoong on 2018/5/24.
*/
public class Computer {
/**
* 顯示器
*/
public String displayer;
/**
* 主機(jī)
*/
public String host;
/**
* 鍵盤
*/
public String keyboard;
/**
* 鼠標(biāo)
*/
public String mouse;
public String getDisplayer() {
return displayer;
}
public void setDisplayer(String displayer) {
this.displayer = displayer;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public String getKeyboard() {
return keyboard;
}
public void setKeyboard(String keyboard) {
this.keyboard = keyboard;
}
public String getMouse() {
return mouse;
}
public void setMouse(String mouse) {
this.mouse = mouse;
}
/**
* 覆蓋toString方法.
* @return
*/
@Override
public String toString() {
return "Computer{" +
"displayer='" + displayer + '\'' +
", host='" + host + '\'' +
", keyboard='" + keyboard + '\'' +
", mouse='" + mouse + '\'' +
'}';
}
}
構(gòu)建抽象——ComputerBuilder
package io.github.brightloong.design.builder;
/**
* Created by BrightLoong on 2018/5/24.
*/
public abstract class ComputerBuilder {
abstract void buildDisplay();
abstract void buildHost();
abstract void buildKeyBoard();
abstract void buildMouse();
abstract Computer getComputer();
}
具體構(gòu)建類——SuperComputerBuilder
package io.github.brightloong.design.builder;
/**
* Created by BrightLoong on 2018/5/24.
*/
public class SuperComputerBuilder extends ComputerBuilder {
Computer computer;
public SuperComputerBuilder() {
computer = new Computer();
}
void buildDisplay() {
computer.setDisplayer("37.5英寸顯示器IPS曲面屏微邊框防眩光4K屏幕");
}
void buildHost() {
computer.setHost("i7-6950X/TiTANX 高端硬管水冷電腦六核游戲主機(jī)");
}
void buildKeyBoard() {
computer.setKeyboard("猛禽競技機(jī)械鍵盤");
}
void buildMouse() {
computer.setMouse("逆天懸浮鼠標(biāo)");
}
Computer getComputer() {
return computer;
}
}
指揮者——ComputerDirector
package io.github.brightloong.design.builder;
/**
* Created by BrightLoong on 2018/5/24.
*/
public class ComputerDirector {
private ComputerBuilder computerBuilder;
public ComputerDirector(ComputerBuilder computerBuilder) {
this.computerBuilder = computerBuilder;
}
public Computer build() {
computerBuilder.buildDisplay();
computerBuilder.buildHost();
computerBuilder.buildKeyBoard();
computerBuilder.buildMouse();
return computerBuilder.getComputer();
}
}
客戶端調(diào)用和輸出
package io.github.brightloong.design.builder;
/**
* Created by BrightLoong on 2018/5/24.
*/
public class Client {
public static void main(String[] args) {
Computer computer = new ComputerDirector(new SuperComputerBuilder()).build();
System.out.println(computer);
}
}
輸出:
Computer{displayer='37.5英寸顯示器IPS曲面屏微邊框防眩光4K屏幕', host='i7-6950X/TiTANX 高端硬管水冷電腦六核游戲主機(jī)', keyboard='猛禽競技機(jī)械鍵盤', mouse='逆天懸浮鼠標(biāo)'}
四. 總結(jié)
使用場景
- 用于創(chuàng)建一些復(fù)雜的對象嚼摩,這些對象內(nèi)部構(gòu)建的順序通常是穩(wěn)定的,但對象內(nèi)部的構(gòu)建通常面臨著復(fù)雜的變化愿卒。
- 當(dāng)創(chuàng)建復(fù)雜的對象的算法應(yīng)該獨(dú)立于該對象的組成部分已經(jīng)它們的裝配方法時(shí)琼开。
優(yōu)點(diǎn)
- 建造者模式使得建造代碼與表示代碼分離,隱藏了產(chǎn)品實(shí)現(xiàn)細(xì)節(jié)枕荞。
- 如果要改變一個(gè)產(chǎn)品的內(nèi)部表示柜候,只要再定義一個(gè)具體的建造者就可以了,方便擴(kuò)展躏精。
- 建造者模式通過指揮者可以控制組件建造順序渣刷,能實(shí)現(xiàn)一定程度的細(xì)節(jié)把控,特別是那種生成的產(chǎn)品對象之間存在屬性依賴的情況玉控。
缺點(diǎn)
- 如果產(chǎn)品多變飞主,會生成大量的建造類,造成類膨脹高诺。
五. 擴(kuò)展——構(gòu)建器
在《Effective Java》一書中碌识,第2條提到:
遇到多個(gè)構(gòu)造器參數(shù)時(shí)要考慮用構(gòu)建器虱而。
當(dāng)有多個(gè)參數(shù)的時(shí)候筏餐,客戶端代碼不僅難編寫,也很難閱讀牡拇,再有魁瞪,客戶端可能不清楚每個(gè)參數(shù)到底代表什么。
這里也使用到了Builder模式惠呼,不直接生成想要的對象导俘,二是讓客戶端利用所有必要的參數(shù)調(diào)用Builder構(gòu)造器,得到一個(gè)builder對象剔蹋。然后客戶端在builder對象上調(diào)用類似于setter的方法旅薄,來設(shè)置每個(gè)相關(guān)的可選參數(shù)。最后泣崩,客戶端調(diào)用無參的build方法來生成不可變(相比較JavaBeans模式少梁,也就是調(diào)用set方法來進(jìn)行設(shè)值,這種方式可以生成不可變對象)的對象矫付。下面的是具體的代碼凯沪,來自《Effective Java》書上的列子。
package io.github.brightloong.design.builder;
/**
* Created by BrightLoong on 2018/5/24.
*/
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
public static class Builder {
//Required parameters
private final int servingSize;
private final int servings;
//Optional parameters - initialized to default values
private int calories = 0;
private int fat = 0;
private int sodium = 0;
private int carbohydrate = 0;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val) {
calories = val;
return this;
}
public Builder fat(int val) {
fat = val;
return this;
}
public Builder sodium(int val) {
sodium = val;
return this;
}
public Builder carbohydrate(int val) {
carbohydrate = val;
return this;
}
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
@Override
public String toString() {
return "NutritionFacts{" +
"servingSize=" + servingSize +
", servings=" + servings +
", calories=" + calories +
", fat=" + fat +
", sodium=" + sodium +
", carbohydrate=" + carbohydrate +
'}';
}
}
調(diào)用和輸出:
package io.github.brightloong.design.builder;
/**
* Created by BrightLoong on 2018/5/24.
*/
public class Client2 {
public static void main(String[] args) {
NutritionFacts cocaCola = new NutritionFacts.Builder(240,8)
.calories(100).sodium(35)
.carbohydrate(27)
.build();
System.out.println(cocaCola);
}
}
輸出:
NutritionFacts{servingSize=240, servings=8, calories=100, fat=0, sodium=35, carbohydrate=27}