建造者模式(Builder)
流程的抽象
如果你需要將一個複雜物件的構造與它的表示分離,使得同樣的構建過程可以建立不同的表示之意圖時,我們需要應用於一個設計模式「建造者模式(Builder)」,又叫「產生器模式」。
建造者模式可以將一個產品的內部表像與產品的產生過程分割開來,從而可以使一個建造過程產生具有不同之內部表象的產品物件。
如果我們用了建造者模式(Builder),那麼用戶就只需要指定需要建造的類型就可以得到它們,而具體建造的過程和細節就不需知道了。
建造者模式(Builder),將一個複雜的構建與它的表示分離,使得同樣構建過程可以建立不同的表示。
建造者模式解析
建造者模式(Builder)結構
Director指揮者,是構造一個使用Builder介面的物件Builder是為建立一個Product物件的各個零件指定的抽象介面ConcreteBuilder具體建造者,實現Builder介面,構造和裝配各個零件Product具體產品(產品角色)
1
2
3
4
5
6
7
8
9
10
11
12
Director 指揮者,是構造一個使用Builder介面的物件
- builder
+ Construct()
Builder 是為建立一個Product物件的各個零件指定的抽象介面
+ BuilderPart()
ConcreteBuilder 具體建造者,實現Builder介面,構造和裝配各個零件
+ BuilderPart()
+ GetResult()
Product 具體產品(產品角色)
建造者模式基本程式碼
Product類別
Product類別:產品類別,由多個零件組成。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//Product類別:產品類別,由多個零件組成
class Product {
IList<string> parts = new List<string>();
//加入產品零件
public void Add(string part) {
parts.Add(part);
}
//列出所有的產品零件
public void Show() {
Console.WriteLine("產品零件:");
foreach(string part in parts) {
Console.WriteLine(part);
}
}
}
Builder類別
Builder類別:抽象建造者類別,確定產品由兩個零件 PartA 和 PartB 組成,並宣告一個得到產品建造後結果的方法 GetResult()。
1
2
3
4
5
6
//Builder類別:抽象建造者類別,確定產品由兩個零件 PartA 和 PartB 組成,並宣告一個得到產品建造後結果的方法 GetResult()。
abstract class Builder {
public abstract void BuildPartA(); //零件A
public abstract void BuildPartB(); //零件B
public abstract Product GetResult(); //得到產品建造後結果
}
ConcreteBuilder類別
ConcreteBuilder類別:具體建造者類別
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
27
28
29
30
31
32
33
34
35
36
37
38
39
//ConcreteBuilder1類別:具體建造者類別,具體建造兩個零件:零件A、零件B
class ConcreteBuilder1: Builder {
Product product = new Product();
//具體建造 零件A
public override void BuildPartA() {
product.Add("零件A");
}
//具體建造 零件B
public override void BuildPartB() {
product.Add("零件B");
}
//得到產品建造後結果
public override Product GetResult() {
return product;
}
}
//ConcreteBuilder2類別:具體建造者類別,具體建造兩個零件:零件X、零件Y
class ConcreteBuilder2: Builder {
Product product = new Product();
//具體建造 零件X
public override void BuildPartA() {
product.Add("零件X");
}
//具體建造 零件Y
public override void BuildPartB() {
product.Add("零件Y");
}
//得到產品建造後結果
public override Product GetResult() {
return product;
}
}
Director類別
Director類別,指揮者類別,用來指揮建結過程。
1
2
3
4
5
6
7
class Director {
//用來指揮建結過程
public void Construct(Builder builder) {
builder.BuildPartA();
builder.BuildPartB();
}
}
用戶端程式碼
用戶端程式碼,客戶不需知道具體的建造過程。
1
2
3
4
5
6
7
8
9
10
11
12
13
Director director = new Director();
Builder b1 = new ConcreteBuilder1();
Builder b2 = new ConcreteBuilder2();
//指揮者用ConcreteBuilder1的方法來建造產品
director.Construct(b1);
Product p1 = b1.GetResult();
pl.Show();
//指揮者用ConcreteBuilder2的方法來建造產品
director.Construct(b2);
Product p2 = b2.GetResult();
p2.Show();
所以說,建造者模式是在當建立複雜物件的演算法應該獨立於該物件的組成部分,以及它們裝配方式時適用的模式。
建造小人
首先我們要畫小人,都需要畫什麼?頭、身體、雙手、雙腳。所以我們定義一個抽象的建造人的類別,來把這個過程給穩定住(流程的抽象),不讓任何人遺忘的任何一步。
程式碼結構圖
PersonDirector指揮者(Director),建造模式中一個很重要的類別,指揮者(Director),用它來控制建造過程,也用它來隔離用戶與建造過程的關聯。
PersonDirector類別的目的就是根據用戶的選擇一步一步建造小人,而建造的過程在指揮者這裡就完成了,用戶就不需要知道了,而且,由於這個過程每一步都是一定要的,那就不會少畫一隻手,少畫一條腿的問題出現了。
PersonBuilder建造人的抽象類別,定義一個抽象的建造人的類別,來把這個過程給穩定住(流程的抽象),不讓任何人遺忘的任何一步。PersonThinBuilder建造瘦的小人,建造瘦小人,讓這個瘦子類別去繼承這抽象類別,去重寫這些抽象方法。
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
27
28
PersonDirector 指揮者(Director)
- PersonBuilder
+ CreatePerson()
PersonBuilder 建造人的抽象類別
+ BuildHead()
+ BuildBody()
+ BuildArmRight()
+ BuildArmLeft()
+ BuildLegRight()
+ BuildLegLeft()
PersonThinBuilder 瘦的人
+ BuildHead()
+ BuildBody()
+ BuildArmRight()
+ BuildArmLeft()
+ BuildLegRight()
+ BuildLegLeft()
PersonFatBuilder 胖的人
+ BuildHead()
+ BuildBody()
+ BuildArmRight()
+ BuildArmLeft()
+ BuildLegRight()
+ BuildLegLeft()
建造小人程式碼
建造者 Builder
抽象的建造人的類別,我們定義一個抽象的建造人的類別,來把這個過程給穩定住(流程的抽象),不讓任何人遺忘的任何一步。
如果需要更加精細,比如人的五官,手的上臂、前臂和手掌,大腿小腿這些,就需要權衡,如果這些細節是每個具體的小人都需要構建的,那就應該要加進去,反之,就沒必要。
其實建造者的Builder類別裡的那些建造方法必須足夠普遍,以便為各種類型的具體建造者構造。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//Builder是一個建造小人各個部分的抽象類別
abstract class PersonBuilder {
Grapthics g;
Pen p;
public Person(Grapthics g, Pen p) {
this.g = g;
this.p = p;
}
public abstract void BuildHead(); //頭
public abstract void BuildBody(); //身體
public abstract void BuildArmLeft(); //左手
public abstract void BuildArmRight(); //右手
public abstract void BuildLegLeft(); //左腳
public abstract void BuildLegRight(); //右腳
}
建造瘦的小人
建造瘦的小人,讓這個瘦子類別去繼承這抽象類別,去重寫這些抽象方法。
胖子、高個子都是用類似的程式碼去實現這個類別就可以了。
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
27
28
class PersonThinBuilder: PersonBuilder {
public PersonThinBuilder(Grapthics g, Pen p): base(g, p) { }
//頭
public override void BuildHead() {
g.DrawEllipse(p, 50, 20, 30, 30);
}
//身體
public override void BuildBody() {
g.DrawEllipse(p, 60, 50, 10, 50);
}
//左手
public override void BuildArmLeft() {
g.DrawEllipse(p, 60, 50, 40, 100);
}
//右手
public override void BuildArmRight() {
g.DrawEllipse(p, 70, 50, 90, 100);
}
//左腳
public override void BuildLegLeft() {
g.DrawEllipse(p, 60, 100, 45, 150);
}
//右腳
public override void BuildLegRight() {
g.DrawEllipse(p, 70, 100, 85, 150);
}
}
指揮者(Director)
建造模式中一個很重要的類別,指揮者(Director),用它來控制建造過程,也用它來隔離用戶與建造過程的關聯。
PersonDirector類別的目的就是根據用戶的選擇一步一步建造小人,而建造的過程在指揮者這裡就完成了,用戶就不需要知道了,而且,由於這個過程每一步都是一定要的,那就不會少畫一隻手,少畫一條腿的問題出現了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class PersonDirector {
//用戶告訴指揮者,我需要什麼樣的小人
PersonBuilder pb;
public PersonDirector(PersonBuilder pb) {
this.pb = pb
}
//根據用戶的選擇建造小人
public void CreatePerson() {
pb.BuildHead();
pb.BuildBody();
pb.BuildArmLeft();
pb.BuildArmRight();
pb.BuildLegLeft();
pb.BuildLegRight();
}
}
用戶端
1
2
3
4
5
6
7
8
9
Pen pen = new Pen(Color.Yellow);
//瘦的小人
PersonThinBuilder ptb = new PersonThinBuilder(pictureBox1.CreateGraphies(), p);
PersonDirector thinPerson = new PersonDirector(ptb);
thinPerson.CreatePerson();
//胖的小人
PersonFatBuilder pfb = new PersonFatBuilder(pictureBox1.CreateGraphies(), p);
PersonDirector fatPerson = new PersonDirector(pfb);
fatPerson.CreatePerson();
總結
Builder是一個建造小人各個部分的抽象類別。概括地說,是為建立一個Product物件的各個零件指定的抽象介面。ConcreteBuilder是具體小人的建造者,具體實現如何畫出小人的頭身手腳各個部分。它是具體建造者,實現Builder介面,構造和裝配各個與件。Product當然就是那些具體的小人,產品的角色了。Director指揮者,是根據用戶的需求構建小人物件。它是構建一個使用Builder介面的物件。
那都是什麼時候需要建造者模式呢?
它主要是用於建立一些複雜的物件,這些物件內部構建間的建造順序通常是穩定的,但物件內部的構建通常面臨著複雜的變化。
建造者模式的好處就是讓建造程式碼與表示程式碼分離,由於建造者隱藏了該產品是如何組裝的,所以若需要改變一個產品的內部表示,只需要再定義一個具體的建造者就可以了。