1. 程式人生 > 其它 >04-04-設計模式 建造者模式

04-04-設計模式 建造者模式

需求

需要建造房子, 過程為: 打樁->砌牆->封頂

房子有各式各樣的, 比如普通房, 高樓, 別墅, 過程雖然一樣, 要是要求不要相同的

傳統方式解決

新建抽象類

package com.dance.design.designmodel.buildmodel;

import lombok.Builder;
import lombok.Data;

/**
 * 抽象房子
 */
public abstract class AbsHouse {

    /**
     * 建造
     */
    public void build(){
        buildBasic();
        buildWalls();
        buildTop();
    }

    /**
     * 打樁
     * @param basic 地基
     */
    public abstract void buildBasic();

    /**
     * 砌牆
     * @param walls 牆
     */
    public abstract void buildWalls();

    /**
     * 封頂
     * @param top 頂部顏色
     */
    public abstract void buildTop();
}

新建實現類

package com.dance.design.designmodel.buildmodel;

public class CommonHouse extends AbsHouse{

    @Override
    public void buildBasic() {
        System.out.println("建造普通房子的地基");
    }

    @Override
    public void buildWalls() {
        System.out.println("建造普通房子的牆");
    }

    @Override
    public void buildTop() {
        System.out.println("建造普通房子的頂");
    }
}

新建客戶端

package com.dance.design.designmodel.buildmodel;

public class Client {
    public static void main(String[] args) {
        AbsHouse absHouse = new CommonHouse();
        absHouse.build();
    }
}

傳統方式的問題分析

  1. 優點是比較好理解, 簡單易操作
  2. 設計的程式結構, 過於簡單, 沒有設計快取層物件, 程式的擴充套件和維護不好, 也就是說這種設計方案把產品(房子)和建立的過程封裝在一起,耦合性增強了
  3. 解決方案: 將產品和產品的建造過程解耦 => 建造者模式

建造者模式

基本介紹

  1. 建造者模式又叫生成器模式,是一種物件構建模式,他可以將複雜的物件的建造過程抽象出來(抽象類別), 使這個抽象過程的不同實現方法可以構造出不同表現(屬性)的物件
  2. 建造者模式是一步一步建立一個複雜的物件, 它允許使用者只通過指定複雜物件的型別和內容就可以構建他們,使用者不需要知道內部的具體構建細節

建造者模式的四個角色

  1. Product(產品角色) : 一個具體的產品物件
  2. Builder(抽象建造者) : 建立一個Product物件的各個部件指定的 介面/抽象類
  3. ConcreateBuilder(具體建造者): 實現介面, 構建和裝配各個部件
  4. Director(指揮者): 構建一個使用Builder介面的物件, 它主要是用於建立一個複雜的物件, 它主要有兩個作用
    1. 隔離了客戶與物件的生產過程
    2. 負責控制產品物件的生產過程

建造者模式原理類圖

使用建造者模式改進需求

類圖

  1. 抽象建造者組合了產品
  2. 具體建造者實現了抽象建造者的抽象方法
  3. 建造指揮者聚合了抽象建造者, 提供設定方法
  4. 客戶端(呼叫方)依賴建造指揮者

實現

package com.dance.design.designmodel.buildmodel.build;

import lombok.Data;
import lombok.Setter;

public class Client2 {
    public static void main(String[] args) {
        // 建造普通的房子
        // 建立指揮者 傳入建造的具體實現
        HouseDirector houseDirector = new HouseDirector(new CommonHouse());
        // 建造
        House house = houseDirector.constructHouse();

        // 設定建造高樓大廈
        houseDirector.setHouseBuilder(new HighHouse());
        // 建造
        house = houseDirector.constructHouse();
    }
}

/**
 * 產品
 */
@Data
class House{
    private String basic;
    private String wall;
    private String roofed;
}

/**
 * 抽象建造者
 */
abstract class HouseBuilder{

    // 組合產品
    protected House house = new House();

    public abstract void buildBasic();

    public abstract void buildWalls();

    public abstract void buildRoofed();

    /**
     * 建造
     * @return 房子
     */
    public House buildHouse(){
        return house;
    }

}

/**
 * 普通房子
 */
class CommonHouse extends HouseBuilder{
    @Override
    public void buildBasic() {
        System.out.println("打地基5m");
    }

    @Override
    public void buildWalls() {
        System.out.println("砌牆10m");
    }

    @Override
    public void buildRoofed() {
        System.out.println("封頂10m2");
    }
}
/**
 * 高樓大廈
 */
class HighHouse extends HouseBuilder{
    @Override
    public void buildBasic() {
        System.out.println("打地基20m");
    }

    @Override
    public void buildWalls() {
        System.out.println("砌牆200m");
    }

    @Override
    public void buildRoofed() {
        System.out.println("封頂200m2");
    }
}

/**
 * 指揮者
 */
class HouseDirector{

    @Setter
    private HouseBuilder houseBuilder = null;

    /**
     * 傳入具體的 要建造的房子
     * @param houseBuilder
     */
    public HouseDirector(HouseBuilder houseBuilder) {
        this.houseBuilder = houseBuilder;
    }

    // 建造流程交給指揮者
    public House constructHouse(){
        houseBuilder.buildBasic();
        houseBuilder.buildWalls();
        houseBuilder.buildRoofed();
        return houseBuilder.buildHouse();
    }

}

原始碼剖析

JDK中的建造者模式

StringBuilder 從名字就能看出來,這時一個建造者

  1. CharSequence : 字元序列 屬於產品 因為要建立的為字串
  2. Appendable : 功能介面, 支援追加功能append介面
  3. AbstractStringBuilder : 抽象建造者
    1. 實現了CharSequence(介面)
    2. 實現了Appendable(新增功能介面)
  1. 具體建造者
    1. 具體實現有StringBuffer 和 StringBuilder
  1. StringBuilder : 具體建造者, 兼職指揮者 繼承了 抽象建造者
  2. 使用的時候直接使用具體建造者

建造者模式的注意事項和細節

  1. 客戶端(使用程式)不必知道產品內部組成的細節, 將產品本身與產品的建立過程解耦,使得相同的建立過程可以建立不同的產品物件
  2. 每一個具體建造者都相對獨立, 而與其他的具體建造者無關,因此可以很方便的替換具體建造者或增加新的具體建造者, 使用者使用不同的具體建造者即可得到不同的產品物件
  3. 可以更加精細的控制產品的建立過程, 將複雜產品的建立步驟分解在不同的方法中, 使得建立過程更加清晰,也更方便使用程式來控制建立過程
  4. 增加新的具體建造者無需修改原有類庫的程式碼, 指揮者針對抽象建造者程式設計, 系統擴充套件方便, 符合開閉原則
  5. 建造者模式所建立的產品一般具有較多的共同點, 其組成部分相似,如果產品之間差異性很大,則不適合使用建造者模式,因為此使用範圍受到一定的限制
  6. 如果產品內部的變化複雜, 可能會導致需要定義很多具體建造者類來實現這種變化, 導致系統變得很龐大, 因此這種請款下, 要考慮是否使用建造者模式
  7. 抽象工廠模式 VS 建造者模式
    1. 抽象工廠模式實現對產品家族的建立, 一個產品家族是這樣的一系列產品, 具有不同分類維度的產品組合, 採用抽象工廠模式不關係建造過程,只關心什麼產品由什麼工廠產生即可, 而建造者模式則是要求按照指定的藍圖建造產品,他的主要目的是通過組裝零配件而生產一個新產品
    2. 其實就是關注粒度不同, 工廠模式只關注由那個工廠生產, 建造者需要關心建造過程

擴充套件: 為什麼StringBuilder不是執行緒安全的?

一般我們面試的時候會問?

面試官: StringBuilder和StringBuffer有什麼區別?

我: StringBuilder執行緒不安全, StringBuffer是執行緒安全的

面試官: StringBuilder 哪裡不安全? StringBuffer為啥安全?

我 : ........

沒得聊了

接下來說一下

我們先分析Stringbuilder的append方法

從原始碼可以看出呼叫的是父類的append方法

他的父類就是抽象建造者 AbstractStringBuilder ,從程式碼可以看出在進行count(統計char陣列的長度)累加的時候 是一個+=的操作符,這個操作就不是一個原子操作, 所以會導致多執行緒操作不安全

那為什麼StringBuffer是執行緒安全的呢?

從原始碼可以看出 StringBuffer的append方法添加了synchronized修飾, 這個直接是物件鎖, 直接將方法加了鎖,導致了哪怕是多執行緒操作一個物件的append方法也要乖乖的等著另一個執行緒執行完成....