設計模式系列—建造者模式
前言
- 23種設計模式速記
- 單例(singleton)模式
- 工廠方法(factory method)模式
- 抽象工廠(abstract factory)模式
23種設計模式快速記憶的請看上面第一篇,本篇和大家一起來學習建造者模式相關內容。
模式定義
將一個復雜對象的創建與他的表示分離,使得同樣的構建過程可以創建不同的表示。
用戶只需要給出指定復雜對象的類型和內容;
建造者模式負責按順序創建復雜對象(把內部的建造過程和細節隱藏起來)
解決的問題
- 降低創建復雜對象的復雜度
- 隔離了創建對象的構建過程 & 表示
從而:
- 方便用戶創建復雜的對象(不需要知道實現過程)
- 代碼復用性 & 封裝性(將對象構建過程和細節進行封裝 & 復用)
模式組成
- 指揮者(Director)直接和客戶(Client)進行需求溝通;
- 溝通后指揮者將客戶創建產品的需求劃分為各個部件的建造請求(Builder);
- 將各個部件的建造請求委派到具體的建造者(ConcreteBuilder);
- 各個具體建造者負責進行產品部件的構建;
- 最終構建成具體產品(Product)。
實例說明
實例概況
- 背景 小張希望去中關村買一臺組裝的臺式主機
- 過程
- 中關村老板(Diretor)和小張(Client)進行需求溝通(買來打游戲?學習?看片?)
- 了解需求后,電腦城老板將小張需要的主機劃分為各個部件(Builder)的建造請求(CPU、主板......)
- 指揮裝機人員(ConcreteBuilder)去構建組件;
- 將組件組裝起來成小張需要的電腦(Product)
使用步驟
步驟1:定義具體產品類(Product):電腦
- class Computer{
- //電腦組件的集合
- private List<String> parts = new ArrayList<String>();
- //用于將組件組裝到電腦里
- public void Add(String part){
- parts.add(part);
- }
- public void Show(){
- for (int i = 0;i<parts.size();i++){
- System.out.println("組件" + parts.get(i) + "裝好了");
- }
- System.out.println("電腦組裝完成,請驗收");
- }
- }
步驟2:定義組裝的過程(Builder):組裝電腦的過程
- abstract class Builder {
- //第一步:裝CPU
- //聲明為抽象方法,具體由子類實現
- public abstract void BuildCPU();
- //第二步:裝主板
- //聲明為抽象方法,具體由子類實現
- public abstract void BuildMainboard();
- //第三步:裝硬盤
- //聲明為抽象方法,具體由子類實現
- public abstract void BuildHD();
- //返回產品的方法:獲得組裝好的電腦
- public abstract Computer GetComputer();
- }
步驟3: 中關村老板委派任務給裝機人員(Director)
- class Director{
- //指揮裝機人員組裝電腦
- public void Construct(Builder builder){
- builder. BuildCPU();
- builder.BuildMainboard();
- builder.BuildHD();
- }
- }
步驟4: 創建具體的建造者(ConcreteBuilder):裝機人員
- class ConcreteBuilder extends Builder{
- //創建產品實例
- Computer computer = new Computer();
- //組裝產品
- @Override
- public void BuildCPU(){
- computer.Add("組裝CPU");
- }
- @Override
- public void BuildMainboard() {
- computer.Add("組裝主板");
- }
- @Override
- public void BuildHD() {
- computer.Add("組裝主板");
- }
- //返回組裝成功的電腦
- @Override
- public Computer GetComputer(){
- return computer;
- }
- }
步驟5:客戶端調用-小張到電腦城找老板買電腦
- public class BuilderPattern<builder> {
- public static void main(String[] args) {
- // 步驟5:客戶端調用-小張到電腦城找老板買電腦
- //逛了很久終于發現一家合適的電腦店
- //找到該店的老板和裝機人員
- Director director = new Director();
- Builder builder = new ConcreteBuilder();
- //溝通需求后,老板叫裝機人員去裝電腦
- director.Construct(builder);
- //裝完后,組裝人員搬來組裝好的電腦
- Computer computer = builder.GetComputer();
- //組裝人員展示電腦給小張看
- computer.Show();
- }
- }
輸出結果
- 組件CPU裝好了
- 組件主板裝好了
- 組件硬盤裝好了
- 電腦組裝完成,請驗收
優點
- 良好的封裝性:建造者對客戶端屏蔽了產品內部組成的細節,客戶端不用關心每一個具體的產品內部是如何實現的。
- 符合開閉原則
- 便于控制細節風險:由于建造者是相互獨立的,因此可以對建造過程逐步細化,而不對其他的模塊產生任何影響。
每一個具體建造者都相對獨立,而與其他的具體建造者無關,因此可以很方便地替換具體建造者或增加新的具體建造者,用戶使用不同的具體建造者即可得到不同的產品對象。
缺點
- 建造者模式所創建的產品一般具有較多的共同點,其組成部分相似;如果產品之間的差異性很大,則不適合使用建造者模式,因此其使用范圍受到一定的限制。
- 如果產品的內部變化復雜,可能會導致需要定義很多具體建造者類來實現這種變化,導致系統變得很龐大。
應用場景
- 需要生成的對象具有復雜的內部結構
- 需要生成的對象內部屬性本身相互依賴
- 與不可變對象配合使用
與工廠方法模式的區別
建造者模式最主要的功能是基本方法的調用順序安排,基本方法已經實現,我們可以理解為零件的裝配,順序不同產生的對象也不同;而工廠方法的注重點是創建,創建零件是其主要職責,不關心組裝順序。
源碼中的應用
- # jdk
- java.lang.StringBuilder
- # Spring源碼
- org.springframework.web.servlet.mvc.method.RequestMappingInfo
- org.springframework.beans.factory.support.BeanDefinitionBuilder
- ......
StringBuilder源碼分析
在jdk中StringBuilder類的實現中,采用建造者模式的思想。具體分析如下:
StringBuilder類繼承自AbstractStringBuilder,而AbstractStringBuilder實現了Appendable接口。AbstractStringBuilder雖然是一個抽象類,但是它實現了Appendable接口中的各個append()方法,因此在這里Appendable接口是一個抽象建造者,而AbstractStringBuilder是建造者,只是不能實例化。對于StringBuilder類,它既充當了指揮者角色,同時充當了具體的建造者,建造方法的具體實現是由AbstractStringBuilder完成,StringBuilder繼承了AbstractStringBuilder。
Appendable接口
- public interface Appendable {
- Appendable append(CharSequence csq) throws IOException;
- Appendable append(CharSequence csq, int start, int end) throws IOException;
- Appendable append(char c) throws IOException;
- }
AbstractStringBuilder類
- abstract class AbstractStringBuilder implements Appendable, CharSequence {
- char[] value;//The value is used for character storage.
- int count;//The count is the number of characters used.
- AbstractStringBuilder() { }
- AbstractStringBuilder(int capacity) {
- value = new char[capacity];
- }
- public AbstractStringBuilder append(String str) {
- if (str == null)
- return appendNull();
- int len = str.length();
- ensureCapacityInternal(count + len);
- str.getChars(0, len, value, count);
- count += len;
- return this;
- }
- private AbstractStringBuilder appendNull() {
- int c = count;
- ensureCapacityInternal(c + 4);
- final char[] value = this.value;
- value[c++] = 'n';
- value[c++] = 'u';
- value[c++] = 'l';
- value[c++] = 'l';
- count = c;
- return this;
- }
- private void ensureCapacityInternal(int minimumCapacity) {
- // overflow-conscious code
- if (minimumCapacity - value.length > 0) {
- value = Arrays.copyOf(value,
- newCapacity(minimumCapacity));
- }
- }
- public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
- if (srcBegin < 0) {
- throw new StringIndexOutOfBoundsException(srcBegin);
- }
- if (srcEnd > value.length) {
- throw new StringIndexOutOfBoundsException(srcEnd);
- }
- if (srcBegin > srcEnd) {
- throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
- }
- System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
- }
- // 此次省略......
- }
StringBuilder類:
- public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence {
- //雖說是重寫,但還是調用的AbstractStringBuilder方法
- @Override
- public StringBuilder append(String str) {
- super.append(str);
- return this;
- }
- }
PS:以上代碼提交在 Github :
https://github.com/Niuh-Study/niuh-designpatterns.git