文章目錄
- 建造者模式簡介
- 建造者模式結構
- 建造者模式代碼實例
- 定義產品類House
- 定義建造者
- 定義抽象建造者AbstractBuilder
- 定義具體建造者
- 定義指揮者
- 客戶端代碼示例
- 運行結果
- 建造者模式總結
代碼倉庫
建一棟房子總共分幾步?建造者模式告訴你答案!
“把大象裝冰箱,總共分幾步?”
“三步。第一步,打開冰箱門;第二步,把大象裝進冰箱;第三步,把冰箱門關上。”
Jungle活了這20多年,全靠這個笑話活著! 把大象裝冰箱竟然只需要三步?那到底是怎么把大象裝進冰箱呢?你問我,我問誰?再說,我也不關心這個呀!這……來點實際的吧,如果Jungle要建一棟房子,總共分幾步?本文的建造者模式將聲情并茂地向您娓娓道來……
建造者模式簡介
建造者模式用于創建過程穩定,但配置多變的對象。在《設計模式》一書中的定義是:將一個復雜的構建與其表示相分離,使得同樣的構建過程可以創建不同的表示。
建造者模式將客戶端與包含多個部件的復雜對象的創建過程分離,客戶端不必知道復雜對象的內部組成方式與裝配方式(就好像Jungle不知道到底是如何把大象裝進冰箱一樣),只需知道所需建造者的類型即可。
建造者模式定義:
將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。
“同樣的構建過程可以創建不同的表示”??這句話是什么意思呢?想象一下,建造一棟房子,建造過程無非都是打地基、筑墻、安裝門窗等過程,但不同的客戶可能希望不同的風格或者過程,最終建造出來的房子當然就呈現不同的風格啦!
經典的“建造者-指揮者”模式現在已經不太常用了,現在建造者模式主要用來通過鏈式調用生成不同的配置。比如我們要制作一杯珍珠奶茶。它的制作過程是穩定的,除了必須要知道奶茶的種類和規格外,是否加珍珠和是否加冰是可選的。
使用建造者模式的好處是不用擔心忘了指定某個配置,保證了構建過程是穩定的。在 OkHttp、Retrofit 等著名框架的源碼中都使用到了建造者模式。
建造者模式結構
建造者模式UML類圖如下:
建造者模式代碼實例
考慮這樣一個場景,如下圖:
Jungle想要建造一棟簡易的房子(地板、墻和天花板),兩個工程師帶著各自的方案找上門來,直接給Jungle看方案和效果圖。
猶豫再三,Jungle最終選定了一位工程師……交房之日,Jungle滿意的看著建好的房子,
開始思考:這房子究竟是怎么建成的呢?這地板、墻和天花板是怎么建造的呢?
工程師笑著說:“It’s none of your business”
UML圖如下:
定義產品類House
House是本實例中的產品,具有floor、wall和roof三個屬性。
class House {
public:House() {}void setFloor(string iFloor) {this->floor = iFloor;}void setWall(string iWall) {this->wall = iWall;}void setRoof(string iRoof) {this->roof = iRoof;}// 打印House信息void printfHouseInfo() {// this->floor 是一個 std::string 類型,而C語言函數 printf 無法直接輸出 std::string 類型。// 使用 .c_str() 就能將 std::string 轉換為一個 const char*(C風格字符串) ,供 printf 使用。printf("Floor:%s\t\n", this->floor.c_str());printf("Wall:%s\t\n", this->wall.c_str());printf("Roof:%s\t\n", this->roof.c_str());}private:string floor;string wall;string roof;
};
定義建造者
定義抽象建造者AbstractBuilder
// 抽象建造者AbstractBuilder
class AbstractBuilder {
public:AbstractBuilder() : house(std::make_unique<House>()) {}virtual ~AbstractBuilder() = default; // 使用默認析構函數即可AbstractBuilder(const AbstractBuilder&) = delete;AbstractBuilder& operator=(const AbstractBuilder&) = delete;virtual void buildFloor() = 0;virtual void buildWall() = 0;virtual void buildRoof() = 0;virtual std::unique_ptr<House> getHouse() {return std::move(house); // 轉移所有權}protected:std::unique_ptr<House> house;
};
定義具體建造者
// 具體建造者ConcreteBuilderA
// 子類無需再定義getHouse()和析構函數,因為基類已經完成這些任務。
class ConcreteBuilderA: public AbstractBuilder {
public:ConcreteBuilderA() { printf("ConcreteBuilderA\n"); }void buildFloor() override { house->setFloor("Floor_A"); }void buildWall() override { house->setWall("Wall_A"); }void buildRoof() override { house->setRoof("Roof_A"); }
};// 具體建造者ConcreteBuilderB
class ConcreteBuilderB: public AbstractBuilder {
public:ConcreteBuilderB() { printf("ConcreteBuilderB\n"); }void buildFloor() override { house->setFloor("Floor_B"); }void buildWall() override { house->setWall("Wall_B"); }void buildRoof() override { house->setRoof("Roof_B"); }
};
定義指揮者
// 指揮者Director
class Director {
public:Director(): builder(nullptr) {}// Builder的生命周期應由調用方自己管理,不能由Director刪除,否則可能造成未知問題。~Director() = default; Director(const Director&) = delete;Director& operator=(const Director&) = delete;void setBuilder(AbstractBuilder* iBuilder) {this->builder = iBuilder;}std::unique_ptr<House> construct() {builder->buildFloor();builder->buildWall();builder->buildRoof();return builder->getHouse(); // 返回智能指針}private:AbstractBuilder* builder;
};
客戶端代碼示例
#include "BuilderPattern.h"int main() {// 抽象建造者AbstractBuilder* builder = nullptr;// 指揮者 (生命周期在main中)Director director; // 指定具體建造者Abuilder = new ConcreteBuilderA();director.setBuilder(builder);// house的所有權轉移給調用方std::unique_ptr<House> houseA = director.construct();houseA->printfHouseInfo();// 注意:house內存已被外部接管,不應由builder釋放delete builder; // 此時builder的析構函數應避免刪除house// delete houseA; // 由調用方手動釋放house內存// 指定具體建造者B (這里應改成ConcreteBuilderB)builder = new ConcreteBuilderB();director.setBuilder(builder);std::unique_ptr<House> houseB = director.construct();houseB->printfHouseInfo();delete builder; // 同上// delete houseB; // 調用方負責釋放內存return 0;
}
運行結果
建造者模式總結
從客戶端代碼可以看到,客戶端只需指定具體建造者,并作為參數傳遞給指揮者,通過指揮者即可得到結果。**客戶端無需關心House的建造方法和具體流程。如果要更換建造風格,只需更換具體建造者即可,不同建造者之間并無任何關聯,方便替換。**從代碼優化角度來看,其實可以不需要指揮者Director的角色,而直接把construct方法放入具體建造者當中。
之后我會持續更新,如果喜歡我的文章,請記得一鍵三連哦,點贊關注收藏,你的每一個贊每一份關注每一次收藏都將是我前進路上的無限動力 !!!↖(▔▽▔)↗感謝支持!