1 建造者模式的概念
建造者模式(Builder Pattern)是一種創建型設計模式,也被稱為生成器模式。它的核心思想是將一個復雜對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。
在建造者模式中,通常包括以下幾個角色:
(1)Builder: 這是一個抽象接口,定義了產品對象的各個組成成分的建造。它規定了要實現復雜對象的哪些部分的創建,但并不涉及具體的對象部件的創建。
(2)ConcreteBuilder: 這是實現了Builder接口的具體類,它針對不同的商業邏輯,具體化了復雜對象的各部分的創建。在建造過程完成后,它提供產品的實例。
(3)Director: 這是一個指揮者角色,它調用具體建造者來創建復雜對象的各個部分。在指導者中,不涉及具體產品的信息,只負責保證對象各部分完整創建或按某種順序創建。
(4)Product: 這是最終要創建的復雜對象。
建造者模式允許用戶只通過指定復雜對象的類型和內容來構建對象,而不需要知道內部的具體構建細節。這種模式特別適用于構建具有多個組成部分的復雜對象,且這些組成部分的構建過程可能會因具體需求而有所不同的情況。通過建造者模式,可以使得同樣的構建過程能夠創建出不同的產品表示,提高了系統的靈活性和可擴展性。
1.1 建造者模式的應用場景
建造者模式通常適用于以下應用場景:
(1)對象結構復雜: 當需要創建的對象具有復雜的內部結構,包含多個組件或屬性時,可以使用建造者模式來構建這些對象。通過將構建過程分解為多個步驟,可以更加清晰地管理和控制對象的創建過程。
(2)創建流程固定: 當對象的創建流程是固定的,即無論創建多少個對象,其構建步驟都是相同的時候,可以使用建造者模式。通過將這些步驟封裝在建造者類中,可以確保每次創建對象時都遵循相同的流程。
(3)需要控制創建過程: 當需要更加靈活地控制對象的創建過程,例如根據用戶輸入或運行時條件來決定對象的某些屬性時,可以使用建造者模式。通過在指揮者類中引入邏輯來控制對象的創建,可以實現更加靈活的構建過程。
(4)代碼易于閱讀和維護: 當需要創建的對象具有大量的參數或配置選項時,使用建造者模式可以將這些參數和選項分組并封裝在不同的建造者類中,從而減少構造函數的復雜度,使代碼更加易讀和易于維護。
在實際應用中,建造者模式常用于構建具有多個組件或屬性的復雜對象,如UI界面、配置文件、數據庫查詢結果等。通過使用建造者模式,可以更加清晰地組織代碼,提高代碼的可讀性和可維護性,同時降低對象創建的復雜性。
1.2 建造者模式的優點和缺點
建造者模式的優點主要包括:
(1)封裝性好: 建造者模式將對象的構建與它的表示分離,使得客戶端不必知道產品內部組成的細節,隱藏了產品的內部實現細節,提供了更好的封裝性。
(2)擴展性好: 由于具體的建造者是相互獨立的,因此易于擴展。如果需要增加新的部件或修改現有部件的創建方式,只需要增加或修改相應的建造者類,而不會影響其他已構建的部件和客戶端代碼。
(3)控制細節風險: 建造者模式允許對創建過程逐步細化,而不對其他模塊產生任何影響,便于控制細節風險。客戶端只需要指定需要構建的對象類型和內容,而不需要了解具體的構建細節,從而降低了客戶端代碼的復雜性。
然而,建造者模式也存在一些缺點:
(1)使用范圍受限: 建造者模式所創建的產品一般具有較多的共同點,其組成部分相似。如果產品之間的差異性很大,例如很多組成部分都不相同,不適合使用建造者模式,因此其使用范圍受到一定的限制。
(2)可能導致系統龐大: 如果產品的內部變化復雜,可能需要定義很多具體建造者類來實現這種變化,導致系統變得很龐大。這會增加系統的理解難度和運行成本,因為需要維護大量的建造者類。
綜上所述,建造者模式在創建具有復雜內部結構和多個組件的對象時非常有用,可以提供良好的封裝性、擴展性和對細節的控制。然而,它也有一些限制,特別是在產品差異性大或內部變化復雜的情況下。因此,在使用建造者模式時需要權衡其優點和缺點,并根據具體的應用場景做出決策。
2 建造者模式的實現步驟
本章節使用一個建造房子的樣例來講解建造者模式的實現步驟。
2.1 定義產品(Product)類
首先,定義 Product 類,它表示最終要構建的對象。在本章節的例子中,構建一個 House 類:
#include <iostream>
#include <memory>
#include <string> class House
{
public:void setWalls(const std::string& walls) {this->walls = walls;}void setRoof(const std::string& roof) {this->roof = roof;}void setFloors(const std::string& floors) {this->floors = floors;}void showHouseDetails() const {std::cout << "Walls: " << walls << std::endl;std::cout << "Roof: " << roof << std::endl;std::cout << "Floors: " << floors << std::endl;}private:std::string walls;std::string roof;std::string floors;
};
2.2 定義抽象建造者(Builder)接口
接下來,定義一個 Builder 抽象接口,它聲明了構建 House 對象所需的方法:
class HouseBuilder
{
public:virtual ~HouseBuilder() = default;virtual void buildWalls() = 0;virtual void buildRoof() = 0;virtual void buildFloors() = 0;virtual std::shared_ptr<House> getHouse() = 0;
};
2.3 定義具體建造者(ConcreteBuilder)類
然后,創建實現 HouseBuilder 接口的 ConcreteBuilder 類。這個類將包含構建 House 對象所需的具體實現:
class ConcreteHouseBuilder : public HouseBuilder
{
public:ConcreteHouseBuilder() : house(std::make_shared<House>()) {}void buildWalls() override {house->setWalls("Concrete walls");}void buildRoof() override {house->setRoof("Concrete roof");}void buildFloors() override {house->setFloors("Concrete floors");}std::shared_ptr<House> getHouse() override{return std::move(house);}private:std::shared_ptr<House> house;
};
2.4 定義指揮者(Director)類
接下來,創建一個 Director 類,它負責指揮 Builder 對象來構建 Product 對象:
class HouseDirector
{
public:HouseDirector(std::shared_ptr<HouseBuilder> builder) : builder(builder) {}void constructHouse() {builder->buildWalls();builder->buildRoof();builder->buildFloors();}std::shared_ptr<House> getHouse(){return builder->getHouse();}private:std::shared_ptr<HouseBuilder> builder;};
2.5 客戶端使用
最后,在 main 函數中,使用這些類來構建 House 對象:
int main()
{// 創建具體建造者對象 auto builder = std::make_shared<ConcreteHouseBuilder>();// 創建指揮者對象,并將建造者傳遞給指揮者 HouseDirector director(builder);// 通過指揮者構建產品 director.constructHouse();// 獲取并顯示構建好的產品 auto house = director.getHouse();house->showHouseDetails();return 0;
}
上面代碼的輸出為:
Walls: Concrete walls
Roof: Concrete roof
Floors: Concrete floors
在上面代碼中,使用 std::make_shared 來創建 ConcreteHouseBuilder 對象,并將其傳遞給 HouseDirector 。HouseDirector 通過調用 concreteHouseBuilder 的方法逐步構建了一個 House 對象,并通過 getHouse 方法返回了 House 對象的所有權。最后,獲取這個構建好的 House 對象,并顯示其對象細節。
3 建造者模式的案例解析
3.1 對象結構復雜場景下使用建造者模式
如下的樣例將展示如何使用建造者模式來構建一個 Computer 對象,其中包含 Processor 和 Memory 兩個子組件(為了簡化代碼,實際一個電腦的子組件會很多)。
首先,定義各個子組件以及 Computer 類:
#include <iostream>
#include <memory>
#include <string> // 子組件類
class Processor
{
public:void setSpeed(int speed) { m_speed = speed; }void setCores(int cores) { m_cores = cores; }void showDetails() const {std::cout << "Processor: " << m_speed << " MHz, " << m_cores << " cores" << std::endl;}private:int m_speed;int m_cores;
};class Memory
{
public:void setSize(int size) { m_size = size; }void setType(const std::string& type) { m_type = type; }void showDetails() const {std::cout << "Memory: " << m_size << " GB, " << m_type << " RAM" << std::endl;}private:int m_size;std::string m_type;
};// 產品類
class Computer {
public:void setProcessor(std::shared_ptr<Processor> processor) { m_processor = processor; }void setMemory(std::shared_ptr<Memory> memory) { m_memory = memory; }void showDetails() const {m_processor->showDetails();m_memory->showDetails();}private:std::shared_ptr<Processor> m_processor;std::shared_ptr<Memory> m_memory;
};
接下來,定義抽象建造者接口和具體的建造者類:
// 產品類
class Computer {
public:void setProcessor(std::shared_ptr<Processor> processor) { m_processor = processor; }void setMemory(std::shared_ptr<Memory> memory) { m_memory = memory; }void showDetails() const {m_processor->showDetails();m_memory->showDetails();}private:std::shared_ptr<Processor> m_processor;std::shared_ptr<Memory> m_memory;
};// 抽象建造者接口
class ComputerBuilder
{
public:virtual ~ComputerBuilder() = default;virtual void buildProcessor() = 0;virtual void buildMemory() = 0;virtual std::shared_ptr<Computer> getComputer() = 0;
};// 具體建造者類
class GamingComputerBuilder : public ComputerBuilder
{
public:GamingComputerBuilder() : m_computer(std::make_shared<Computer>()) {m_processor = std::make_shared<Processor>();m_memory = std::make_shared<Memory>();}void buildProcessor() override {m_processor->setSpeed(3500);m_processor->setCores(6);}void buildMemory() override {m_memory->setSize(16);m_memory->setType("DDR4");}std::shared_ptr<Computer> getComputer() override {m_computer->setProcessor(m_processor);m_computer->setMemory(m_memory);return m_computer;}private:std::shared_ptr<Computer> m_computer;std::shared_ptr<Processor> m_processor;std::shared_ptr<Memory> m_memory;
};
然后,定義 Director 類,負責指導 Builder 對象如何構建最終的產品:
class Director
{
public:Director(std::shared_ptr<ComputerBuilder> builder) : m_builder(builder) {}void constructComputer() {m_builder->buildProcessor();m_builder->buildMemory();}std::shared_ptr<Computer> getComputer() {return m_builder->getComputer();}private:std::shared_ptr<ComputerBuilder> m_builder;
};
最后,在 main 函數中使用 Director 和 GamingComputerBuilder 來構建一個 Computer 對象:
int main()
{// 創建具體的建造者對象 std::shared_ptr<ComputerBuilder> builder = std::make_shared<GamingComputerBuilder>();// 創建導演對象,并將建造者對象傳遞給它 Director director(builder);// 指導構建過程 director.constructComputer();// 獲取構建好的產品 std::shared_ptr<Computer> computer = director.getComputer();// 展示產品詳情 computer->showDetails();return 0;
}
上面代碼的輸出為:
Processor: 3500 MHz, 6 cores
Memory: 16 GB, DDR4 RAM
3.2 對象結構復雜場景下使用建造者模式
在創建流程固定的場景下,可以使用一個簡單的 Builder 類來封裝一個固定流程的構建過程。這種場景下,Builder 類會提供一系列的方法來逐步構建對象,并最終提供一個方法來獲取構建好的對象。客戶端代碼則直接調用 Builder 的方法來構建對象。
如下為樣例代碼:
#include <iostream>
#include <memory>
#include <string> // 產品類
class Product
{
public:void show() const {std::cout << "Product built with parts: " << partA << ", " << partB << ", " << partC << std::endl;}public:std::string partA;std::string partB;std::string partC;
};// 建造者類
class Builder
{
public:Builder() { m_product = std::make_shared<Product>(); }// 初始化產品 void setPartA(const std::string& partA) { m_product->partA = partA; }void setPartB(const std::string& partB) { m_product->partB = partB; }void setPartC(const std::string& partC) { m_product->partC = partC; }// 構建并返回產品 std::shared_ptr<Product> build() {// 在這里可以添加一些額外的構建邏輯return m_product;}private:std::shared_ptr<Product> m_product;
};int main()
{// 創建建造者對象 Builder builder;// 通過建造者設置產品的各個部分 builder.setPartA("Part A content");builder.setPartB("Part B content");builder.setPartC("Part C content");// 構建并獲取產品 auto product = builder.build();// 展示產品 product->show();return 0;
}
上面代碼的輸出為:
Product built with parts: Part A content, Part B content, Part C content
在上面代碼中,Product 類是一個簡單的產品類,包含三個私有成員變量。Builder 類封裝了 Product 對象的構建過程,它提供了三個方法來設置產品的各個部分,以及一個 Build 方法來構建并返回產品對象。
在 main 函數中,客戶端代碼創建了 Builder 對象,并通過調用 setPartA、setPartB 和 setPartC 方法來設置產品的各個部分。然后,調用 Build 方法來構建產品對象,并存儲在 std::shared_ptr 中。最后,調用Show方法來展示構建好的產品。
上面代碼展示了如何在創建流程固定的情況下使用建造者模式。Builder 類確保了產品總是按照相同的步驟和順序被構建,而客戶端代碼則不需要了解具體的構建細節。這有助于保持代碼的清晰和易于維護。
4 建造者模式與工廠模式的比較
建造者模式和工廠模式都是面向對象設計模式中常用的創建型模式,它們的主要區別在于關注點、對象創建過程以及復雜度。
關注點: 工廠模式主要關注對象的創建,而建造者模式不僅關注對象的創建,還關注對象的組成部分以及這些部分的構建順序。
對象創建過程: 工廠模式通常提供一個統一的接口來創建對象,而不需要知道具體創建對象的類。而建造者模式則更加關注對象的構建過程,它允許用戶逐步添加對象的各個部分,并控制這些部分的構建順序。
復雜度: 工廠模式通常用于創建簡單對象,而建造者模式更適合用于創建復雜對象。建造者模式允許用戶將復雜對象的構建過程分解為多個步驟,每個步驟只關注對象的一個或幾個部分,從而降低了構建過程的復雜度。
總的來說,工廠模式和建造者模式都是用于創建對象的模式,但它們在關注點、對象創建過程以及復雜度上有所不同。選擇哪種模式取決于具體的應用場景和需求。