C++ 設計模式 - 建造者模式 Builder Pattern

當你進入餐廳點餐時,菜單上琳瑯滿目的選擇可能讓你感到無從下手。你可以選擇一個套餐,但如果你有特別的需求,例如多點一份沙拉,少放一點醬料,這時候就需要進行個性化的訂單。而這樣的流程,正是建造者模式 Builder Pattern 的精髓所在。它不僅在生活中隨處可見,在軟體設計中更是強大而實用的一種模式。

什麼是建造者模式?

建造者模式是一種建立型設計模式,它將物件的建立過程分解為多個步驟,並且可以根據需求有選擇地組合這些步驟,最終產生我們所需的物件。這樣的設計模式特別適合用於那些需要一步步構建,且每一步可能有不同選擇的複雜物件。

建造者模式通常包含以下角色:

  1. Product(產品):最終被構建的複雜物件。
  2. Builder(建造者):定義構建物件的抽象介面。
  3. ConcreteBuilder(具體建造者):實現 Builder 介面,提供具體的構建步驟。
  4. Director(指揮者):負責使用建造者來構建產品,控制建造的過程。

建造者模式在訂製汽車中的應用

讓我們以訂製汽車為例來說明建造者模式的應用。想像你正在購買一輛新車,經銷商會給你提供一系列選項,從引擎、輪胎到內裝,你可以自由選擇搭配,打造一輛專屬於你的車輛。這正是建造者模式最典型的應用場景。

我們需要定義最終要建造的產品 Car 類別,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 產品類 Product
class Car {
private:
std::string engine;
std::string wheels;
std::string interior;

public:
void setEngine(const std::string& engineType) {
engine = engineType;
}

void setWheels(const std::string& wheelType) {
wheels = wheelType;
}

void setInterior(const std::string& interiorType) {
interior = interiorType;
}

void showSpecifications() const {
std::cout << "Engine: " << engine << "\n"
<< "Wheels: " << wheels << "\n"
<< "Interior: " << interior << std::endl;
}
};

再定義一個 Builder 介面,它包含了構建汽車各部分的方法,

1
2
3
4
5
6
7
8
9
// 建造者介面 Builder
class CarBuilder {
public:
virtual void buildEngine() = 0;
virtual void buildWheels() = 0;
virtual void buildInterior() = 0;
virtual Car* getCar() = 0;
virtual ~CarBuilder() = default;
};

接著我們建立具體的建造者類別,例如一個豪華汽車的建造者,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 具體建造者 ConcreteBuilder
class LuxuryCarBuilder : public CarBuilder {
private:
Car* car;

public:
LuxuryCarBuilder() {
car = new Car();
}

void buildEngine() override {
car->setEngine("V8 Turbo Engine");
}

void buildWheels() override {
car->setWheels("Alloy Wheels");
}

void buildInterior() override {
car->setInterior("Leather Interior");
}

Car* getCar() override {
return car;
}
};

然後我們建立一個指揮者來控制建造的過程,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 指揮者 Director
class CarDirector {
private:
CarBuilder* builder;

public:
CarDirector(CarBuilder* builder) : builder(builder) {}

Car* construct() {
builder->buildEngine();
builder->buildWheels();
builder->buildInterior();
return builder->getCar();
}
};

當你需要訂製一輛豪華汽車時,只需透過建造者模式來構建它,

1
2
3
4
5
6
7
8
9
10
11
12
int main() {
CarBuilder* builder = new LuxuryCarBuilder();
CarDirector director(builder);
Car* car = director.construct();

car->showSpecifications();

delete car;
delete builder;

return 0;
}

這樣的設計讓我們可以輕鬆地改變汽車的設定,或者在需要時引入新的建造者,而不必修改現有的邏輯。建造者模式提供了靈活的物件構建過程,使得我們可以在不同需求下產生不同的產品。

建造者模式的優缺點

建造者模式的優點在於它的靈活性和可擴展性。由於構建過程是分步驟進行的,我們可以輕鬆地在不同的情境下進行客製化,無論是增加新功能還是修改現有步驟,都不會影響整個系統的設計。這樣的設計模式特別適合那些物件結構複雜且需要逐步構建的場景。

建造者模式也有缺點。由於每一個具體建造者都必須實現所有的建造步驟,這會導致大量的程式碼重複,特別是在類似的建造者之間。並且當物件的構建步驟過多時,可能會使得建造者變得臃腫,降低了程式碼的可讀性與維護性。另外指揮者雖然讓建造過程更具組織性,但也可能產生額外的複雜度。

總結

建造者模式特別適合於建立複雜物件或者需要靈活設定的物件。它就像是一個精密的工廠流水線,可以根據不同的需求生產出不同的產品,而且每個產品都保持品質與一致性。在現代軟體開發中,無論是複雜的汽車製造、蓋房屋,還是表單產生器,建造者模式都能幫助我們應對,當你下次面對需要建立複雜物件,或者需要根據不同情況建立不同類型物件的場景時,不妨考慮使用建造者模式。它可能正是你解決問題的關鍵。