Design Pattern_Skill_6_建造者模式(Builder Design Pattern)
最近在和同事合作過程,就時常會跟SD 合作。
SD主要工作會負責設計一些DB 的Schema , 撰寫系統預期流程和規格書、畫面操作互動
SD通常就會向我們這些PG規劃和分配任務、負責監工等等
SD通常會跟院內User 一起討論瞭解需求後
在經過自己的統整、文件撰寫來和PG溝通User需要的是捨麼請幫忙開發
PG可能在開發階段只負責程式撰寫工作而不會cover到系統設計規劃
這之間的關係就好比
找多個工人施工蓋房子和一個監工分配工作任務的工頭一樣
這裡以生產產品為例
一個產品從製造、加工都會有不同製程步驟
如下是要取得待在生產線上工作的特定認證(必須精熟和落實會這些工法)
1 2 3 4 5 6 7 8 | public interface IBuilder { void BuildPartA(); void BuildPartB(); void BuildPartC(); } |
一個產品中會區分不同的組件在製造生產(需透過不同組件組合而成)
不同產品會有需要對應生產不同的組件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class Product { private List<object> lsParts = new List<object>(); public void Add(string part) { this.lsParts.Add(part); } public string ListParts() { return "Product Parts:" + string.Join(",", lsParts.ToArray()) + "\n"; } } |
負責在產線中製造、加工的作業工程人員
會實踐具體的特定產品規格生產工法流程
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 | public class ConcreteBuilder : IBuilder { private Product _product = new Product(); public ConcreteBuilder() { this.Reset(); } public void Reset() { this._product = new Product(); } public void BuildPartA() { this._product.Add("PartA"); } public void BuildPartB() { this._product.Add("PartB"); } public void BuildPartC() { this._product.Add("PartC"); } public Product GetProduct() { Product result = this._product; this.Reset(); return result; } } |
一個產線上可能都會有一些負責特定產品的組長
會知曉特定產品的流程
再去負責監工指派給底下作業人員
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public class Director { private IBuilder _builder; public IBuilder Builder { get => _builder; set => _builder = value; } //minimal valuable product public void buildMinimalValuableProduct() { this._builder.BuildPartA(); } public void builderFeatureProduct() { this._builder.BuildPartA(); this._builder.BuildPartB(); this._builder.BuildPartC(); } } |
完整的Code
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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace BuilderPattern { public interface IBuilder { void BuildPartA(); void BuildPartB(); void BuildPartC(); } public class Product { private List<object> lsParts = new List<object>(); public void Add(string part) { this.lsParts.Add(part); } public string ListParts() { return "Product Parts:" + string.Join(",", lsParts.ToArray()) + "\n"; } } public class ConcreteBuilder : IBuilder { private Product _product = new Product(); public ConcreteBuilder() { this.Reset(); } public void Reset() { this._product = new Product(); } public void BuildPartA() { this._product.Add("PartA"); } public void BuildPartB() { this._product.Add("PartB"); } public void BuildPartC() { this._product.Add("PartC"); } public Product GetProduct() { Product result = this._product; this.Reset(); return result; } } public class Director { private IBuilder _builder; public IBuilder Builder { get => _builder; set => _builder = value; } //minimal valuable product public void buildMinimalValuableProduct() { this._builder.BuildPartA(); } public void builderFeatureProduct() { this._builder.BuildPartA(); this._builder.BuildPartB(); this._builder.BuildPartC(); } } } |
呼叫端Code
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 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace BuilderPattern { class Program { static void Main(string[] args) { var builder = new ConcreteBuilder(); var director = new Director(); director.Builder = builder; Console.WriteLine("Standard basic product:"); director.buildMinimalValuableProduct(); Console.WriteLine(builder.GetProduct().ListParts()); Console.WriteLine("Standard full featured product:"); director.builderFeatureProduct(); Console.WriteLine(builder.GetProduct().ListParts()); // Remember, the Builder pattern can be used without a Director class Console.WriteLine("Custom product:"); builder.BuildPartA(); builder.BuildPartC(); Console.WriteLine(builder.GetProduct().ListParts()); Console.ReadKey(); } } } |
演示結果
我們可以直接指定建構標準最小可行性產品
也可以建構比較多工序的較複雜特色產品
更可以去動態調整不同流程來創建包含不同工序的產品
這裡演示的是所謂「由 Client、Director、Builder 和 Product 形成的」建造者模式
Client就是所謂程式實際呼叫端(就是主程式中間接呼叫的地方)
Builder 是所謂的公產生產可上線負責產品的認證
實作了該介面(認證)代表是具有資格的作業員也就是ConcreteBuilder
Product 就是我們的產品(通常是類似加裝許多部份的集中產物)
Director則是一個監工者(有點類似導演、主管的角色...)
這裡再用一個寫程式工作情境來舉例
Builder模式第一種.
「由 Client、Director、Builder 和 Product 形成的」
一個需求資訊是由多種不同規格、時程要求等組建而成
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 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace BuilderPattern { public class WorkInfo { private List<string> lsWorkItem = new List<string>(); public void AddWorkInfo(string workItem) { lsWorkItem.Add(workItem); } public IEnumerable<string> GetDetail() { foreach(var item in lsWorkItem) { yield return item; } } } } |
在日常需求程式開發過程難免會需要做到
有明確任務需求名稱、時數、備註等等
來對每一個工程師進行需求開發控管
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace BuilderPattern { public interface IDevelopmentBuilder { void Reset(); void SetTaskName(string _task_title); void SetWorkHour(double _work_hour); void SetRemark(); WorkInfo GetTaskDetail(); } } |
SD或者PM是負責監工指派的角色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace BuilderPattern { //SD public class WorkInfoDirector { public WorkInfo CreateWorkInfo(IDevelopmentBuilder developmentBuilder, string task_name,double workHour) { developmentBuilder.SetTaskName(task_name); developmentBuilder.SetWorkHour(workHour); developmentBuilder.SetRemark(); return developmentBuilder.GetTaskDetail(); } } } |
而對應去落實需求資訊紀錄和管理的則是一些PG
這裡我們假設ProgrammerA 負責的都是固定備註
然而可能後續備註會因為不同需求功能、平台、時程而有不同變化
這裡只是暫時做一個簡單演示
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 40 41 42 43 44 45 46 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace BuilderPattern { public class ProgrammerA : IDevelopmentBuilder { private WorkInfo _work_info; private string _remark = "要使用C# 跟 MS SQL"; public ProgrammerA() { Reset(); } public void Reset() { _work_info = new WorkInfo(); } public void SetTaskName(string _task_title) { _work_info.AddWorkInfo(_task_title); } public void SetWorkHour(double _work_hour) { string workTime = string.Format("工時:{0}小時", _work_hour); _work_info.AddWorkInfo(workTime); } public void SetRemark() { string remark = string.Format("備註:{0}",this._remark); _work_info.AddWorkInfo(remark); } public WorkInfo GetTaskDetail() { return _work_info; } } } |
呼叫端Code
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 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace BuilderPattern { class Program { static void PrintWorkInfoDetail(IEnumerable<object> workDetail) { foreach(var item in workDetail) { Console.WriteLine(item); } Console.WriteLine("= = = = = = = ="); } static void Main(string[] args) { var workInfoDirector = new WorkInfoDirector(); Console.WriteLine("ProgrammerA 工作任務列表"); var workInfo1 = workInfoDirector.CreateWorkInfo(new ProgrammerA(), "訂單管理報表微調", 2.5); var workDetail_1 = workInfo1.GetDetail(); PrintWorkInfoDetail(workDetail_1); var workInfo2 = workInfoDirector.CreateWorkInfo(new ProgrammerA(), "文管系統簽核流程更改", 4); var workDetail_2 = workInfo2.GetDetail(); PrintWorkInfoDetail(workDetail_2); Console.ReadKey(); } } } |
在此就能打印出
因應不同需求和相應備註、時程產生的需求開發資訊
(各個實體就這樣被創造出來)
Builder模式第二種.藉由靜態內部類方式實踐
(少監工、派工角色)
將IDevelopmentBuilder裡面定義要實踐的method
改為統一回傳IDevelopmentBuilder本身的函數
具體去落實的ProgrammerA則會在設置完相應返回IDevelopmentBuilder的介面
使後期能夠產生鏈結風格( someBuilder->setValueA(1)->setValueB(2)->create() )
於最後定義返回WorkInfo的單一集中物的型別
WorkInfo則和上述例子不同在於裡面By特定欄位串值一一去實作
而最終一樣加到大的List中給予函數返回
和第一種Builder模式最大差異在於沒有Director的角色
而是可由具體的ProgrammerA , ProgrammerB ,....去自行控制增減調整一些屬性和工作流程
第二種code
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 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace BuilderPattern2 { public interface IDevelopmentBuilder { IDevelopmentBuilder SetTaskName(string _task_title); IDevelopmentBuilder SetWorkHour(double _work_hour); IDevelopmentBuilder SetRemark(string _remark); WorkInfo Build(); } public class WorkInfo { private string _task_tile; private string _work_hour; private string _remark; private List<string> lsWorkItem = new List<string>(); public IEnumerable<string> GetDetail() { foreach (var item in lsWorkItem) { yield return item; } } public void SetTaskName(string _task_title) { this._task_tile = _task_title; lsWorkItem.Add(_task_title); } public void SetWorkHour(double _work_hour) { this._work_hour = _work_hour.ToString(); lsWorkItem.Add(string.Format("工時:{0}小時", _work_hour)); } public void SetRemark(string _remark) { this._remark = _remark; lsWorkItem.Add(string.Format("備註:{0}", this._remark)); } } public class ProgrammerA : IDevelopmentBuilder { private WorkInfo workInfo; public ProgrammerA() { workInfo = new WorkInfo(); } public IDevelopmentBuilder SetTaskName(string _task_title) { workInfo.SetTaskName(_task_title); return this; } public IDevelopmentBuilder SetWorkHour(double _work_hour) { workInfo.SetWorkHour(_work_hour); return this; } public IDevelopmentBuilder SetRemark(string _remark) { workInfo.SetRemark(_remark); return this; } public WorkInfo Build() { return workInfo; } } class Program { static void PrintWorkInfoDetail(IEnumerable<object> workDetail) { foreach (var item in workDetail) { Console.WriteLine(item); } Console.WriteLine("= = = = = = = ="); } static void Main(string[] args) { Console.WriteLine("ProgrammerA 工作任務列表"); WorkInfo workInfo = new ProgrammerA() .SetTaskName("文管系統權限調整") .SetWorkHour(1.5) .SetRemark("需要在11/20之前完成").Build(); PrintWorkInfoDetail(workInfo.GetDetail()); Console.WriteLine("ProgrammerA 工作任務列表"); WorkInfo workInfo_2 = new ProgrammerA() .SetTaskName("業管系統邏輯調整") .SetWorkHour(3.5) .SetRemark("需要在12/10之前完成").Build(); PrintWorkInfoDetail(workInfo_2.GetDetail()); WorkInfo workInfo_3 = new ProgrammerA() .SetTaskName("商標管理系統查詢報錯問題修正").Build(); PrintWorkInfoDetail(workInfo_3.GetDetail()); Console.ReadKey(); } } } |
Ref:
Builder in C#
https://refactoring.guru/design-patterns/builder/csharp/example
Builder Pattern In C#
https://www.c-sharpcorner.com/article/builder-pattern-in-c-sharp/
Hierarchically Implementing the Bloch's Builder Pattern in C#
https://www.codeproject.com/Articles/240756/Hierarchically-Implementing-the-Bolchs-Builder-Pat
留言
張貼留言