建構一個「學生選課成績單」物件,包含:必修、選修、限修三類課程,每類課程又有不同科目與學分。若用建構函數,參數過多、順序易搞錯、難以擴充;若用屬性逐一設定,可能產生「半初始化」的無效物件。建造者模式透過分步驟建構、指導者控制流程、建造者封裝細節,優雅解決此類問題。
將一個複雜物件的建構與它的表示分離,使得相同的建構過程可以創建不同的表示。

public class CourseReport
{
private readonly Dictionary _courses = new();
public void AddCourse(string courseId, decimal score)
{
_courses[courseId] = score;
}
public decimal CalculateTotalScore()
{
decimal total = 0;
foreach (var kvp in _courses)
{
Console.WriteLine($"課程:{kvp.Key} 成績:{kvp.Value}");
total += kvp.Value;
}
return total;
}
}
public abstract class CourseReportBuilder
{
protected CourseReport _report = new();
// 固定建構步驟(演算法骨架)
public abstract void BuildRequiredCourses(); // 必修
public abstract void BuildElectiveCourses(); // 選修
public abstract void BuildRestrictedCourses(); // 限修
public CourseReport GetReport() => _report;
}
public class UndergraduateBuilder : CourseReportBuilder
{
public override void BuildRequiredCourses()
{
_report.AddCourse("UG-101", 80m); // 微積分
_report.AddCourse("UG-102", 85m); // 物理
}
public override void BuildElectiveCourses()
{
_report.AddCourse("UG-E01", 90m); // 程式設計入門
}
public override void BuildRestrictedCourses()
{
_report.AddCourse("UG-R01", 95m); // 體育(必選)
}
}
public class GraduateBuilder : CourseReportBuilder
{
public override void BuildRequiredCourses()
{
_report.AddCourse("GR-501", 70m); // 進階演算法
_report.AddCourse("GR-502", 75m); // 機器學習理論
}
public override void BuildElectiveCourses()
{
_report.AddCourse("GR-E01", 80m); // 深度學習實務
}
public override void BuildRestrictedCourses()
{
_report.AddCourse("GR-R01", 85m); // 學術倫理
}
}
public class ReportDirector
{
// 控制建構順序(不可變)
public void Construct(CourseReportBuilder builder)
{
builder.BuildRequiredCourses();
builder.BuildElectiveCourses();
builder.BuildRestrictedCourses();
}
}
class Program
{
static void Main()
{
var director = new ReportDirector();
// 建構大學部成績單
var ugBuilder = new UndergraduateBuilder();
director.Construct(ugBuilder);
var ugReport = ugBuilder.GetReport();
Console.WriteLine($"=== 大學部總分: {ugReport.CalculateTotalScore()} ===\n");
// 建構研究所成績單
var grBuilder = new GraduateBuilder();
director.Construct(grBuilder);
var grReport = grBuilder.GetReport();
Console.WriteLine($"=== 研究所總分: {grReport.CalculateTotalScore()} ===");
Console.ReadKey();
}
}
=== 大學部總分: 350 ===
課程:UG-101 成績:80
課程:UG-102 成績:85
課程:UG-E01 成績:90
課程:UG-R01 成績:95
=== 研究所總分: 310 ===
課程:GR-501 成績:70
課程:GR-502 成績:75
課程:GR-E01 成績:80
課程:GR-R01 成績:85
現代 C# 常用鏈式呼叫取代 Director,更直覺:
public class FluentCourseReportBuilder
{
private readonly CourseReport _report = new();
public FluentCourseReportBuilder AddRequired(string id, decimal score)
{
_report.AddCourse($"REQ-{id}", score);
return this;
}
public FluentCourseReportBuilder AddElective(string id, decimal score)
{
_report.AddCourse($"ELE-{id}", score);
return this;
}
public CourseReport Build() => _report;
}
// 使用
var report = new FluentCourseReportBuilder()
.AddRequired("Math", 90)
.AddRequired("Physics", 85)
.AddElective("CSharp", 95)
.Build();
| 模式 | 核心意圖 | 關鍵差異 |
|---|---|---|
| 建造者 | 分步驟建構複雜物件 | 強調建構過程、可產生不同表示 |
| 工廠方法 | 建立單一物件 | 一次建立完成、無建構步驟 |
| 抽象工廠 | 建立相關物件家族 | 多個產品、無建構順序 |
| 模板方法 | 定義演算法骨架 | 行為導向、非物件建構 |
| 優點 | 缺點 |
|---|---|
| ✅ 封裝建構細節,使用者只需指定型別 ✅ 可精確控制建構順序 ✅ 易於新增新的建造者(開閉原則) ✅ 避免超長建構函數/屬性初始化器 |
⚠️ 類別數量增加 ⚠️ 產品內部結構變動時,所有建造者需同步修改 ⚠️ 簡單物件使用過度設計 |
建造者模式以「分步驟、可控制、可擴充」的方式,解決複雜物件建構的痛點。現代 C# 開發中,流暢介面與不可變物件風格更受青睞,但核心思想不變:將「如何建構」與「建構什麼」分離。下次面對參數超過 5 個的建構函數,請考慮引入 Builder。